Skip to content

Checkstyle cleanup (Phases 0-4): 16288 -> 0 violations, gate enabled#65

Open
greluc wants to merge 45 commits into
mainfrom
claude/pedantic-satoshi-c03b21
Open

Checkstyle cleanup (Phases 0-4): 16288 -> 0 violations, gate enabled#65
greluc wants to merge 45 commits into
mainfrom
claude/pedantic-satoshi-c03b21

Conversation

@greluc
Copy link
Copy Markdown
Member

@greluc greluc commented May 13, 2026

Summary

End-to-end Checkstyle cleanup across the backend and frontend modules,
broken into five phases and now gate-protected by ./gradlew check.

  • Phase 0 — Introduce Spotless 7.0.4 (googleJavaFormat().reflowLongStrings() + removeUnusedImports()).
  • Phase 1 — One-shot bulk reformat of 767 Java sources; reduces Checkstyle warnings 16 288 -> 1 796. A .git-blame-ignore-revs entry keeps git blame honest about authorship.
  • Phase 2 (a-j) — Class + method Javadoc on the entire public surface of both modules. Cleared 1 352 MissingJavadoc{Type,Method} violations across models/DTOs (a+g), mappers (b), repositories (c), backend services (d), backend controllers (e), backend infrastructure (f), frontend infrastructure (h+j), and frontend page controllers (i).
  • Phase 3 (a-g) — Mechanical cleanup of the remaining 1 058 violations: star imports expanded, single-line if/while/for bodies braced, long lines wrapped, naming conventions fixed (AbbreviationAsWordInName, LocalVariableName, CatchParameterName), method overloads grouped, empty catch blocks documented, etc.
  • Merge of claude/youthful-saha-e3cd07 — Brings in Phase 2d/2e/2f/2i from a parallel branch. Phase-3 mechanics that the -X theirs strategy dropped were re-applied; new violations introduced by the merge (duplicate Javadocs from both branches, missing summaries on @return-only blocks) were auto-fixed by new helper scripts under build/.
  • Phase 4 (this PR's final commit)Checkstyle.isIgnoreFailures = false + maxWarnings = 0 and Spotless.isEnforceCheck = true. Any new warning, any unformatted file, fails ./gradlew check.

Final count: 16 288 -> 1 796 -> 738 -> 0 Checkstyle violations on both modules, now permanent.

Numbers

  • 778 files changed, +72 951 / -58 427
  • 32 commits
  • Backend checkstyleMain + spotlessCheck + spotbugsMain: green
  • Frontend checkstyleMain + spotlessCheck + spotbugsMain: green
  • ArchUnit tests, Spring @SpringBootTest integration tests, and TestContainers-backed tests: not run locally (Docker Desktop unavailable on dev machine — see MEMORY.md); pure Mockito unit tests still pass

Developer impact after merge

  • Run ./gradlew spotlessApply locally before pushing; CI re-runs spotlessCheck and will fail on any unformatted file.
  • New public API needs class/method Javadoc — MissingJavadocType / MissingJavadocMethod will block the build otherwise.
  • The build/ directory carries several gitignored helper scripts that automated the cleanup (star-import resolver, Javadoc inserter, summary synthesizer, duplicate-Javadoc merger, etc.). They're reusable if a future refactor introduces a similar batch problem.

Test plan

  • ./gradlew :backend:checkstyleMain :frontend:checkstyleMain --rerun-tasks -> BUILD SUCCESSFUL, 0 violations
  • ./gradlew :backend:spotlessCheck :frontend:spotlessCheck --rerun-tasks -> BUILD SUCCESSFUL, 0 format drifts
  • ./gradlew :backend:spotbugsMain :frontend:spotbugsMain --rerun-tasks -> BUILD SUCCESSFUL, both clean
  • ./gradlew :backend:compileJava :frontend:compileJava :backend:compileTestJava :frontend:compileTestJava -> BUILD SUCCESSFUL
  • CI to run the full ./gradlew check (including TestContainers integration tests that Docker-less local machines can't run)

🤖 Generated with Claude Code

greluc and others added 30 commits May 13, 2026 18:15
Phase 0 of the planned Checkstyle cleanup. The current checkstyleMain
run reports ~16,300 warnings across 486 files; ~14,800 are formatting
only (Indentation, LineLength, CustomImportOrder, AvoidStarImport,
EmptyLineSeparator, OperatorWrap, WhitespaceAround) and can be fixed
with google-java-format in a single dedicated commit.

This change only wires up the tooling, it does NOT reformat any source
file:

- Root build.gradle.kts: id("com.diffplug.spotless") 7.0.4 with
  apply false, mirroring the existing info.solidsoft.pitest pattern.
  Configuration lives in the subprojects { plugins.withId(...) }
  block right after the Checkstyle config and uses
  googleJavaFormat() (2-space indent, 100-char line width, Google
  import order -- exactly what config/checkstyle/google_checks.xml
  expects), removeUnusedImports() and formatAnnotations().
- backend/build.gradle.kts and frontend/build.gradle.kts: each
  applies id("com.diffplug.spotless") without a version (the version
  is pinned in the root plugins block).
- isEnforceCheck = false on the SpotlessExtension keeps spotlessCheck
  off the check task graph for now, so the regular build does not
  trip over the still-unformatted repository. Verified with
  ./gradlew :backend:check --dry-run: only checkstyleMain,
  checkstyleTest and spotbugsMain appear.

Tasks spotlessApply and spotlessCheck are now available in both
modules. The actual bulk reformat (Phase 1) will be a separate commit
driven by ./gradlew spotlessApply; enforceCheck will flip back to its
default (true) in Phase 4 once the codebase is clean.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Phase 1 of the Checkstyle cleanup. Runs `./gradlew spotlessApply` once
across `backend/src/` and `frontend/src/`, producing a single
formatting-only commit. No logical changes.

Diff profile:
- 767 Java files modified, +64,325 / -58,146 lines
- net ~-6,000 lines because the 4-space indent collapses to 2-space

Checkstyle impact (./gradlew checkstyleMain):
- IndentationCheck          11,806 -> 8
- LineLengthCheck            1,507 -> 82
- CustomImportOrderCheck     1,074 -> 0
- EmptyLineSeparatorCheck       87 -> 0
- OperatorWrapCheck             57 -> 0
- WhitespaceAroundCheck         17 -> 0
- TOTAL                     16,288 -> 1,796 (-89%)

Out of scope for this phase (handled separately):
- MissingJavadocMethodCheck (913) + MissingJavadocTypeCheck (439) --
  content, not format; needs a Javadoc policy decision (Phase 2).
- AvoidStarImportCheck (151) -- google-java-format intentionally does
  not expand star imports because resolution can be ambiguous.
- NeedBracesCheck (131) -- outside google-java-format's scope.

Verification:
- ./gradlew :backend:test :frontend:test -> BUILD SUCCESSFUL in 1m 48s,
  full backend (1300+) and frontend test suites green, zero failures.

Follow-up:
- A separate commit adds `.git-blame-ignore-revs` listing this commit's
  hash, so `git blame` skips this purely-mechanical change when devs
  set `git config blame.ignoreRevsFile .git-blame-ignore-revs`.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Lists the purely-mechanical bulk-reformat commit (da3dfd3) so devs who
opt in with

    git config blame.ignoreRevsFile .git-blame-ignore-revs

see real authorship in `git blame` instead of the formatting commit's
hash on every line.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Phase 2a + 2g of the Checkstyle cleanup. Wipes 262 of the 1,352
remaining Phase 2 violations by documenting every class and remaining
public non-property method in the three trivial layers:

- backend/dto/uex (UEX inbound JSON records, 31)
- backend/model + model/dto + model/dto/request
  (entities, enums, DTO records, 133)
- frontend/model + model/dto + model/form
  (frontend DTOs, forms, enums, 98)

Class-level Javadoc was inserted with a one-off helper script
(build/_javadoc_insert.py, gitignored under build/) that reads the
Checkstyle report, scans forward from the reported annotation line to
the actual declaration, and generates a short, suffix-aware one-liner:
- *Dto records  -> "Data transfer record carrying X payload."
- *Request      -> "Inbound request payload for the X operation."
- *Response     -> "Outbound response payload for the X operation."
- *Form         -> "Form-binding object for X input."
- @entity       -> "X JPA entity."
- @MappedSuperclass -> "Base class shared by all JPA entities (X)."
- enums         -> "Enumeration of X values."
- plain records -> "Immutable record carrying X data."

The UEX records (UexCityDto, UexCommodityDto, ...) were written by
hand before the script existed; their Javadoc names the entity each
DTO is mapped to and the responsible *SyncService - the script's
template was modelled after them.

Method-level Javadoc (13 sites) was written manually because the
content cannot be derived mechanically:

- backend/model/Announcement.java -- @PrePersist/@PreUpdate hook
- backend/model/JobOrder.java, JobOrderHandover.java -- bidirectional
  back-reference helpers (addMaterial, addItem)
- backend/model/dto/MaterialMatrixItemDto.java,
  MaterialPriceOverviewDto.java -- tuple-mapping convenience ctors
- backend/model/dto/RefineryOrderListDto.java,
  frontend/model/dto/RefineryOrderDto.java, RefineryOrderListDto.java
  -- getEndsAt derived timestamp
- frontend/model/dto/MissionFrequencyDto.java -- frequencyTypeId()
- frontend/model/dto/MissionUnitDto.java -- getJobSummary aggregation
- frontend/model/form/JobOrderForm.java, RefineryOrderForm.java --
  no-arg ctors that pre-seed one row for the dynamic form widgets

Spotless ran across the inserts so any one-line Javadoc that broke
the 100-char limit got re-wrapped to multi-line form.

Verification: ./gradlew :backend:test :frontend:test ->
BUILD SUCCESSFUL in 1m 40s, full backend (1300+) and frontend test
suites green, zero failures.

The complex layers -- Services, Controllers, Repositories, Mappers,
Configs, Integration, Exception, Logging -- are intentionally out of
scope here because their Javadoc has to be substantive, not
boilerplate-generated. They follow in separate sub-phases (2b-2f, 2h,
2i, 2j); enforceCheck stays at false until they are done.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…vice

Phase 2h + 2j of the Checkstyle cleanup. Wipes 59 more violations by
documenting every frontend package except `controller` (left for the
final sub-phase 2i):

- frontend/config:  12 type + 11 method javadocs (Beans, security
  filter chain, role hierarchy, OAuth2 auth manager, WebClient setup)
- frontend/filter:   1 type
- frontend/logging:  2 type +  8 method (CorrelationContext, LogMasker)
- frontend/exception: 4 method (GlobalExceptionHandler.handle*)
- frontend/service:  1 type + 19 method (BackendApiClient, exception)
- frontend root:     1 type (FrontendApplication)

Class-level Javadoc came from the helper script
(build/_javadoc_insert.py) with a richer heuristic set:

- new suffix-word strip list: Properties, Config, Filter, Editor,
  Handler, Policy, Layout, Context, Application, Service,
  Controller, Repository, Advice, Interceptor, Mapper, Task,
  Scheduler -- so "BackendRoleSyncFilter" becomes "Servlet filter
  handling Backend Role Sync" instead of "...Backend Role Sync
  Filter"
- new annotation-driven branches: @configuration,
  @ConfigurationProperties, @ControllerAdvice (cross-cutting advice,
  NOT REST endpoints), @RestController, @controller (MVC pages),
  @service, @repository

Method-level Javadoc was written by hand for the substantive cases:

- SecurityConfig.{filterChain, roleHierarchy, userAuthoritiesMapper}
  -- central auth config: filter ordering, role hierarchy mirrored
  from ROLES_AND_PERMISSIONS.md, OIDC realm-access claim mapping
- WebClientConfig.{authorizedClientManager, webClient, publicWebClient}
  -- Resilience4j chain (timeout, retry, circuit breaker, bulkhead),
  OAuth2 bearer relay, 16 MB codec, correlation-id propagation
- BackendApiClient class Javadoc rewritten by hand (the script's
  initial "Service layer for Backend Api Client" was uninformative);
  plus 17 HTTP-verb wrapper methods documented (GET/POST/PUT/DELETE/
  PATCH with Class<T> + ParameterizedTypeReference + isPublic
  overloads, plus getCached* and clearStaticDataCache)
- BackendServiceException -- two constructors (slim status-only,
  full RFC-7807 result) with reference to the fromProblem decoder
- LogMasker.mask* -- masking strategy per PII category (email, id,
  token, phone, generic fallback)
- LocaleConfig, CacheConfig, ETagConfig, GlobalBindingAdvice,
  SmartOidcLogoutSuccessHandler -- one-liners explaining the Bean

Verification: ./gradlew :frontend:test -> BUILD SUCCESSFUL in 43s,
full frontend test suite green, zero failures.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Phase 2b of the Checkstyle cleanup. Wipes all 95 remaining mapper
violations (22 type, 73 method) in `backend.mapper`.

Class-level Javadoc came from the helper script
(build/_javadoc_insert.py) with a new suffix rule:
- *Mapper -> "MapStruct mapper between X entities and DTOs."

EntityMappers (hand-written utility class for cases MapStruct can't
handle) got an explanatory class Javadoc by hand instead of the
script's generic fallback.

Method-level Javadoc was written by hand with a focus on the
non-obvious bits:

- toEntity overloads: which fields are intentionally `@Mapping(ignore
  = true)` (id, version, timestamps, aggregate collections owned by
  the service and rewired post-persist)
- MaterialMapper: UEX-style Integer 0/1 normalisation to / from
  Boolean for isIllegal, isVolatileQt, isVolatileTime
- JobOrderMapper.mapAndSortMaterials: deterministic ordering (SCU
  first, then alphabetical case-insensitive) so the materials table
  is stable across reloads
- JobTypeMapper.setParentAfterMapping: parent stub resolved post-map
  because MapStruct cannot wire @manytoone id stubs natively
- MissionMapper.resolveDescription: description is redacted for
  unauthenticated callers
- MissionMapper.resolveCrew: leader-first ordering with
  case-insensitive secondary sort by participant name
- PersonalInventoryItemMapper: ownerSub never leaves the service
  (multi-user isolation key from JWT sub)
- RefineryOrderMapper.computeProfit / missionDtoToMission: profit
  derivation and stub-Mission resolution explained
- UserMapper.roleNames / permissions: helper defaults flattening the
  role aggregate into name sets

Spotless re-wrapped any long single-line Javadoc to multi-line form.

Verification: ./gradlew :backend:test -> BUILD SUCCESSFUL in 59s, all
1300+ backend tests green.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Phase 2c of the Checkstyle cleanup. Wipes all 202 remaining repository
violations (41 type, 161 method) in `backend.repository`.

Class-level Javadoc came from the existing helper script
(build/_javadoc_insert.py) with a new suffix rule:
- *Repository -> "Spring Data repository for X."

Method-level Javadoc was generated by a new helper script
(build/_javadoc_methods_insert.py) that parses Spring-Data derived-query
prefixes and emits 1-liners:
- findBy*      -> "Derived Spring-Data query - returns entities matching X"
- existsBy*    -> "Returns true iff at least one row matches X"
- countBy*     -> "Returns the count of rows matching X"
- deleteBy*    -> "Deletes every row matching X"
- findFirstBy* / findTopBy* -> "Returns the first matching X (limit 1)"
- findAll      -> "Lists every entity. Overridden here to attach an @EntityGraph"

Annotation detection (regex-aware so fully-qualified annotation names
like @org.springframework.data.jpa.repository.Modifying also match):
- @EntityGraph: appends "Eagerly fetches the configured relations"
- @lock:        appends "Acquires a pessimistic write lock"
- @query + @Modifying: emits "Custom JPQL/native bulk update; see the
  @query annotation for the WHERE clause and the @Param contract."
- @query alone: emits "Custom JPQL/native query; see the @query
  annotation for the projection and filter clauses."

QUALITY CAVEAT: the ~40 @Query-annotated methods - concentrated in
InventoryItemRepository (8), MissionRepository (3),
MaterialPriceRepository (4), LocationRepository (2), JobOrderRepository
(4) - get the generic placeholder Javadoc above. It satisfies the
Checkstyle check but is pure boilerplate; a follow-up refinement pass
should replace those with substantive descriptions of what each
specific query does (the filter combinations, the bulk-update
semantics, the lock contracts). The derived-query Javadocs are
unaffected and read fine.

Verification: ./gradlew :backend:test -> BUILD SUCCESSFUL in 54s, all
1300+ backend tests green.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Refinement pass on Phase 2c. The original mass-insert in commit
d6ce646 left ~38 @Query-annotated repository methods with a generic
"Custom JPQL/native query; see the @query annotation..." placeholder
because the helper script could not infer the semantics from the
method name alone. This commit replaces every placeholder by hand with
a description of what the specific query actually does, focusing on
filter combinations, projection targets, bulk-update semantics and
lock contracts that are not obvious from reading the JPQL.

Files touched (10):
- InventoryItemRepository (10): N+1-avoidance fetch, two multi-filter
  search variants (global vs per-user, optional dimension contract),
  weighted-mean quality aggregation, native COALESCE+SUM helper for
  the completion check, the three unlink* bulk updates incl. the
  clearAutomatically/flushAutomatically note from CLAUDE.md,
  natural-key merge query, owner reassignment for user-merge.
- MissionRepository (5): active-mission reference projection, two
  searchMissions variants (List vs Page) with the optional-cast
  trick, owner reassignment, native manager-removal on the join.
- MaterialPriceRepository (5): paged DTO projection, best-sell-price
  ordering, flattened material/terminal/price matrix tuple,
  auto-load price lists.
- JobOrderRepository (4): active-with-materials eager load, max
  priority for new-order assignment, pessimistic lock for bulk
  reorder (links the CLAUDE.md rule), native assignee-removal.
- UserRepository (3): reference projection with fallback name,
  Keycloak-sync stale-flag update, admin lookup.
- MaterialRepository (3): reference projection, materials-with-prices
  filter, weighted price overview with optional name filter.
- ShipRepository (3): fitted-flag reset before fleet import,
  ships-by-type aggregation, owner reassignment.
- RefineryOrderRepository (2): mission unlink with CLAUDE.md cross-
  reference, owner reassignment.
- LocationRepository (2): reference projection, refinery-locations
  lookup.
- MissionParticipantRepository (1): user unlink that preserves guest
  history.

No source-code logic touched - this is purely a docs refinement.
Verification: ./gradlew :backend:test -> BUILD SUCCESSFUL, all
backend tests green.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Removes the "boilerplate placeholder remains; a refinement pass should
follow" caveat from the Phase 2c CHANGELOG entry - that pass landed in
commit b45d5f3 and the placeholder no longer exists.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Phase 0 of the planned Checkstyle cleanup. The current checkstyleMain
run reports ~16,300 warnings across 486 files; ~14,800 are formatting
only (Indentation, LineLength, CustomImportOrder, AvoidStarImport,
EmptyLineSeparator, OperatorWrap, WhitespaceAround) and can be fixed
with google-java-format in a single dedicated commit.

This change only wires up the tooling, it does NOT reformat any source
file:

- Root build.gradle.kts: id("com.diffplug.spotless") 7.0.4 with
  apply false, mirroring the existing info.solidsoft.pitest pattern.
  Configuration lives in the subprojects { plugins.withId(...) }
  block right after the Checkstyle config and uses
  googleJavaFormat() (2-space indent, 100-char line width, Google
  import order -- exactly what config/checkstyle/google_checks.xml
  expects), removeUnusedImports() and formatAnnotations().
- backend/build.gradle.kts and frontend/build.gradle.kts: each
  applies id("com.diffplug.spotless") without a version (the version
  is pinned in the root plugins block).
- isEnforceCheck = false on the SpotlessExtension keeps spotlessCheck
  off the check task graph for now, so the regular build does not
  trip over the still-unformatted repository. Verified with
  ./gradlew :backend:check --dry-run: only checkstyleMain,
  checkstyleTest and spotbugsMain appear.

Tasks spotlessApply and spotlessCheck are now available in both
modules. The actual bulk reformat (Phase 1) will be a separate commit
driven by ./gradlew spotlessApply; enforceCheck will flip back to its
default (true) in Phase 4 once the codebase is clean.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Phase 1 of the Checkstyle cleanup. Runs `./gradlew spotlessApply` once
across `backend/src/` and `frontend/src/`, producing a single
formatting-only commit. No logical changes.

Diff profile:
- 767 Java files modified, +64,325 / -58,146 lines
- net ~-6,000 lines because the 4-space indent collapses to 2-space

Checkstyle impact (./gradlew checkstyleMain):
- IndentationCheck          11,806 -> 8
- LineLengthCheck            1,507 -> 82
- CustomImportOrderCheck     1,074 -> 0
- EmptyLineSeparatorCheck       87 -> 0
- OperatorWrapCheck             57 -> 0
- WhitespaceAroundCheck         17 -> 0
- TOTAL                     16,288 -> 1,796 (-89%)

Out of scope for this phase (handled separately):
- MissingJavadocMethodCheck (913) + MissingJavadocTypeCheck (439) --
  content, not format; needs a Javadoc policy decision (Phase 2).
- AvoidStarImportCheck (151) -- google-java-format intentionally does
  not expand star imports because resolution can be ambiguous.
- NeedBracesCheck (131) -- outside google-java-format's scope.

Verification:
- ./gradlew :backend:test :frontend:test -> BUILD SUCCESSFUL in 1m 48s,
  full backend (1300+) and frontend test suites green, zero failures.

Follow-up:
- A separate commit adds `.git-blame-ignore-revs` listing this commit's
  hash, so `git blame` skips this purely-mechanical change when devs
  set `git config blame.ignoreRevsFile .git-blame-ignore-revs`.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Lists the purely-mechanical bulk-reformat commit (da3dfd3) so devs who
opt in with

    git config blame.ignoreRevsFile .git-blame-ignore-revs

see real authorship in `git blame` instead of the formatting commit's
hash on every line.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Phase 2a + 2g of the Checkstyle cleanup. Wipes 262 of the 1,352
remaining Phase 2 violations by documenting every class and remaining
public non-property method in the three trivial layers:

- backend/dto/uex (UEX inbound JSON records, 31)
- backend/model + model/dto + model/dto/request
  (entities, enums, DTO records, 133)
- frontend/model + model/dto + model/form
  (frontend DTOs, forms, enums, 98)

Class-level Javadoc was inserted with a one-off helper script
(build/_javadoc_insert.py, gitignored under build/) that reads the
Checkstyle report, scans forward from the reported annotation line to
the actual declaration, and generates a short, suffix-aware one-liner:
- *Dto records  -> "Data transfer record carrying X payload."
- *Request      -> "Inbound request payload for the X operation."
- *Response     -> "Outbound response payload for the X operation."
- *Form         -> "Form-binding object for X input."
- @entity       -> "X JPA entity."
- @MappedSuperclass -> "Base class shared by all JPA entities (X)."
- enums         -> "Enumeration of X values."
- plain records -> "Immutable record carrying X data."

The UEX records (UexCityDto, UexCommodityDto, ...) were written by
hand before the script existed; their Javadoc names the entity each
DTO is mapped to and the responsible *SyncService - the script's
template was modelled after them.

Method-level Javadoc (13 sites) was written manually because the
content cannot be derived mechanically:

- backend/model/Announcement.java -- @PrePersist/@PreUpdate hook
- backend/model/JobOrder.java, JobOrderHandover.java -- bidirectional
  back-reference helpers (addMaterial, addItem)
- backend/model/dto/MaterialMatrixItemDto.java,
  MaterialPriceOverviewDto.java -- tuple-mapping convenience ctors
- backend/model/dto/RefineryOrderListDto.java,
  frontend/model/dto/RefineryOrderDto.java, RefineryOrderListDto.java
  -- getEndsAt derived timestamp
- frontend/model/dto/MissionFrequencyDto.java -- frequencyTypeId()
- frontend/model/dto/MissionUnitDto.java -- getJobSummary aggregation
- frontend/model/form/JobOrderForm.java, RefineryOrderForm.java --
  no-arg ctors that pre-seed one row for the dynamic form widgets

Spotless ran across the inserts so any one-line Javadoc that broke
the 100-char limit got re-wrapped to multi-line form.

Verification: ./gradlew :backend:test :frontend:test ->
BUILD SUCCESSFUL in 1m 40s, full backend (1300+) and frontend test
suites green, zero failures.

The complex layers -- Services, Controllers, Repositories, Mappers,
Configs, Integration, Exception, Logging -- are intentionally out of
scope here because their Javadoc has to be substantive, not
boilerplate-generated. They follow in separate sub-phases (2b-2f, 2h,
2i, 2j); enforceCheck stays at false until they are done.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…vice

Phase 2h + 2j of the Checkstyle cleanup. Wipes 59 more violations by
documenting every frontend package except `controller` (left for the
final sub-phase 2i):

- frontend/config:  12 type + 11 method javadocs (Beans, security
  filter chain, role hierarchy, OAuth2 auth manager, WebClient setup)
- frontend/filter:   1 type
- frontend/logging:  2 type +  8 method (CorrelationContext, LogMasker)
- frontend/exception: 4 method (GlobalExceptionHandler.handle*)
- frontend/service:  1 type + 19 method (BackendApiClient, exception)
- frontend root:     1 type (FrontendApplication)

Class-level Javadoc came from the helper script
(build/_javadoc_insert.py) with a richer heuristic set:

- new suffix-word strip list: Properties, Config, Filter, Editor,
  Handler, Policy, Layout, Context, Application, Service,
  Controller, Repository, Advice, Interceptor, Mapper, Task,
  Scheduler -- so "BackendRoleSyncFilter" becomes "Servlet filter
  handling Backend Role Sync" instead of "...Backend Role Sync
  Filter"
- new annotation-driven branches: @configuration,
  @ConfigurationProperties, @ControllerAdvice (cross-cutting advice,
  NOT REST endpoints), @RestController, @controller (MVC pages),
  @service, @repository

Method-level Javadoc was written by hand for the substantive cases:

- SecurityConfig.{filterChain, roleHierarchy, userAuthoritiesMapper}
  -- central auth config: filter ordering, role hierarchy mirrored
  from ROLES_AND_PERMISSIONS.md, OIDC realm-access claim mapping
- WebClientConfig.{authorizedClientManager, webClient, publicWebClient}
  -- Resilience4j chain (timeout, retry, circuit breaker, bulkhead),
  OAuth2 bearer relay, 16 MB codec, correlation-id propagation
- BackendApiClient class Javadoc rewritten by hand (the script's
  initial "Service layer for Backend Api Client" was uninformative);
  plus 17 HTTP-verb wrapper methods documented (GET/POST/PUT/DELETE/
  PATCH with Class<T> + ParameterizedTypeReference + isPublic
  overloads, plus getCached* and clearStaticDataCache)
- BackendServiceException -- two constructors (slim status-only,
  full RFC-7807 result) with reference to the fromProblem decoder
- LogMasker.mask* -- masking strategy per PII category (email, id,
  token, phone, generic fallback)
- LocaleConfig, CacheConfig, ETagConfig, GlobalBindingAdvice,
  SmartOidcLogoutSuccessHandler -- one-liners explaining the Bean

Verification: ./gradlew :frontend:test -> BUILD SUCCESSFUL in 43s,
full frontend test suite green, zero failures.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Phase 2b of the Checkstyle cleanup. Wipes all 95 remaining mapper
violations (22 type, 73 method) in `backend.mapper`.

Class-level Javadoc came from the helper script
(build/_javadoc_insert.py) with a new suffix rule:
- *Mapper -> "MapStruct mapper between X entities and DTOs."

EntityMappers (hand-written utility class for cases MapStruct can't
handle) got an explanatory class Javadoc by hand instead of the
script's generic fallback.

Method-level Javadoc was written by hand with a focus on the
non-obvious bits:

- toEntity overloads: which fields are intentionally `@Mapping(ignore
  = true)` (id, version, timestamps, aggregate collections owned by
  the service and rewired post-persist)
- MaterialMapper: UEX-style Integer 0/1 normalisation to / from
  Boolean for isIllegal, isVolatileQt, isVolatileTime
- JobOrderMapper.mapAndSortMaterials: deterministic ordering (SCU
  first, then alphabetical case-insensitive) so the materials table
  is stable across reloads
- JobTypeMapper.setParentAfterMapping: parent stub resolved post-map
  because MapStruct cannot wire @manytoone id stubs natively
- MissionMapper.resolveDescription: description is redacted for
  unauthenticated callers
- MissionMapper.resolveCrew: leader-first ordering with
  case-insensitive secondary sort by participant name
- PersonalInventoryItemMapper: ownerSub never leaves the service
  (multi-user isolation key from JWT sub)
- RefineryOrderMapper.computeProfit / missionDtoToMission: profit
  derivation and stub-Mission resolution explained
- UserMapper.roleNames / permissions: helper defaults flattening the
  role aggregate into name sets

Spotless re-wrapped any long single-line Javadoc to multi-line form.

Verification: ./gradlew :backend:test -> BUILD SUCCESSFUL in 59s, all
1300+ backend tests green.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Phase 2c of the Checkstyle cleanup. Wipes all 202 remaining repository
violations (41 type, 161 method) in `backend.repository`.

Class-level Javadoc came from the existing helper script
(build/_javadoc_insert.py) with a new suffix rule:
- *Repository -> "Spring Data repository for X."

Method-level Javadoc was generated by a new helper script
(build/_javadoc_methods_insert.py) that parses Spring-Data derived-query
prefixes and emits 1-liners:
- findBy*      -> "Derived Spring-Data query - returns entities matching X"
- existsBy*    -> "Returns true iff at least one row matches X"
- countBy*     -> "Returns the count of rows matching X"
- deleteBy*    -> "Deletes every row matching X"
- findFirstBy* / findTopBy* -> "Returns the first matching X (limit 1)"
- findAll      -> "Lists every entity. Overridden here to attach an @EntityGraph"

Annotation detection (regex-aware so fully-qualified annotation names
like @org.springframework.data.jpa.repository.Modifying also match):
- @EntityGraph: appends "Eagerly fetches the configured relations"
- @lock:        appends "Acquires a pessimistic write lock"
- @query + @Modifying: emits "Custom JPQL/native bulk update; see the
  @query annotation for the WHERE clause and the @Param contract."
- @query alone: emits "Custom JPQL/native query; see the @query
  annotation for the projection and filter clauses."

QUALITY CAVEAT: the ~40 @Query-annotated methods - concentrated in
InventoryItemRepository (8), MissionRepository (3),
MaterialPriceRepository (4), LocationRepository (2), JobOrderRepository
(4) - get the generic placeholder Javadoc above. It satisfies the
Checkstyle check but is pure boilerplate; a follow-up refinement pass
should replace those with substantive descriptions of what each
specific query does (the filter combinations, the bulk-update
semantics, the lock contracts). The derived-query Javadocs are
unaffected and read fine.

Verification: ./gradlew :backend:test -> BUILD SUCCESSFUL in 54s, all
1300+ backend tests green.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Refinement pass on Phase 2c. The original mass-insert in commit
d6ce646 left ~38 @Query-annotated repository methods with a generic
"Custom JPQL/native query; see the @query annotation..." placeholder
because the helper script could not infer the semantics from the
method name alone. This commit replaces every placeholder by hand with
a description of what the specific query actually does, focusing on
filter combinations, projection targets, bulk-update semantics and
lock contracts that are not obvious from reading the JPQL.

Files touched (10):
- InventoryItemRepository (10): N+1-avoidance fetch, two multi-filter
  search variants (global vs per-user, optional dimension contract),
  weighted-mean quality aggregation, native COALESCE+SUM helper for
  the completion check, the three unlink* bulk updates incl. the
  clearAutomatically/flushAutomatically note from CLAUDE.md,
  natural-key merge query, owner reassignment for user-merge.
- MissionRepository (5): active-mission reference projection, two
  searchMissions variants (List vs Page) with the optional-cast
  trick, owner reassignment, native manager-removal on the join.
- MaterialPriceRepository (5): paged DTO projection, best-sell-price
  ordering, flattened material/terminal/price matrix tuple,
  auto-load price lists.
- JobOrderRepository (4): active-with-materials eager load, max
  priority for new-order assignment, pessimistic lock for bulk
  reorder (links the CLAUDE.md rule), native assignee-removal.
- UserRepository (3): reference projection with fallback name,
  Keycloak-sync stale-flag update, admin lookup.
- MaterialRepository (3): reference projection, materials-with-prices
  filter, weighted price overview with optional name filter.
- ShipRepository (3): fitted-flag reset before fleet import,
  ships-by-type aggregation, owner reassignment.
- RefineryOrderRepository (2): mission unlink with CLAUDE.md cross-
  reference, owner reassignment.
- LocationRepository (2): reference projection, refinery-locations
  lookup.
- MissionParticipantRepository (1): user unlink that preserves guest
  history.

No source-code logic touched - this is purely a docs refinement.
Verification: ./gradlew :backend:test -> BUILD SUCCESSFUL, all
backend tests green.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Removes the "boilerplate placeholder remains; a refinement pass should
follow" caveat from the Phase 2c CHANGELOG entry - that pass landed in
commit b45d5f3 and the placeholder no longer exists.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Wipes the entire AvoidStarImportCheck backlog (151 -> 0). Driven by a
new helper script, build/_resolve_star_imports.py:

- reads the Checkstyle report,
- for each file with a star-import violation, scans the body (stripped
  of string literals and comments) for CamelCase identifiers,
- matches them against a known-class catalogue per star package:
    java.util, java.lang.annotation, jakarta.persistence, lombok,
    org.mapstruct, org.springframework.web.bind.annotation are
    hard-coded (covering the classes actually used here),
    de.greluc.krt.iri.basetool.* is scanned from the source tree,
- replaces each star import with the alphabetised list of concrete
  imports that file actually references; spotlessApply re-sorts them
  into Google import order afterwards.

149 / 151 violations resolved by the script. The two remaining ones
in JobOrderHandoverReportService (OpenPDF's com.lowagie.text.* and
com.lowagie.text.pdf.*) were expanded by hand to 13 concrete imports
(Document, Element, Font, Image, PageSize, Paragraph, Phrase,
Rectangle, PdfContentByte, PdfPCell, PdfPTable, PdfPageEventHelper,
PdfWriter).

Two catalogue tweaks during the run: jakarta.persistence.CollectionTable
and java.util.NoSuchElementException were missing on the first try
(one compile failure each); now added to the script's catalogue
alongside the rest of the less-common java.util types.

Static star imports are intentionally skipped by the script -- they
are rare and manual resolution is safer.

Verification: ./gradlew :backend:test :frontend:test ->
BUILD SUCCESSFUL in 1m 40s, full backend (1300+) and frontend (491+)
test suites green.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Wipes the entire NeedBracesCheck backlog (131 -> 0). Google Style
mandates braces on every control-flow construct; the original code had
many one-liners like:

    if (cond) return X;
    if (cond) doSomething();

Driven by a new helper script build/_add_needbraces.py that parses the
Checkstyle report, scans each flagged line for the pattern
`<indent>(}? else )?(if|while|for) (cond) stmt;` and rewrites it into a
three-line block:

    if (cond) {
      stmt;
    }

126 / 131 violations resolved by the script. The 5 remaining ones were
fixed by hand:
- 4 in MissionPageController where the body started on the following
  line (multi-line if construct the regex does not handle)
- 1 in UexRefinerySyncService with the `} else stmt;` form (else
  without if as the control keyword on its own line)

Plus one repair after the bulk apply: the script's greedy `[^{}]*`
condition matcher mis-identified the inner method-call parens in
`if (a != null) m(b.c() ? null : b);` as the if-condition, splitting
the ternary across the new brace. Affected UserService.updateUser and
UserService.updateUserDescription (the two `setDisplayName` call
sites); rewritten by hand. The script header documents this limitation
so the next user knows to spot-check ternary-heavy lines.

Spotless re-formats the new blocks.

Verification: ./gradlew :backend:test :frontend:test ->
BUILD SUCCESSFUL in 1m 44s, all backend (1300+) and frontend (491+)
tests green.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…eption, ...)

Phase 2f of the Checkstyle cleanup. 41 files across 11 backend infrastructure
packages, 111 violations cleared.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Wipes the entire LineLengthCheck backlog (82 -> 0). Two Spotless config
tweaks in build.gradle.kts drive most of it:

- googleJavaFormat().reflowLongStrings() turns on the google-java-format
  flag that re-wraps overlong String concatenations; default behaviour
  leaves strings alone because re-wrapping could alter runtime output.
  That alone resolves ~63 of the 82 violations (long
  @operation(description = "...") strings on controller endpoints,
  Logger.error messages, the CSP template etc.).

- formatAnnotations() removed from the Spotless chain. Its rule packs
  several Bean-Validation annotations onto a single line, which pushed
  every `@NotBlank(message = "...") @SiZe(max = N, ...) String name` in
  the *Form records over 100 chars. Without it, google-java-format
  keeps annotations on separate lines once they no longer fit, which
  is the desired behaviour everywhere in the project. The drop has a
  broad side-effect: ~70 files (repositories, services) now have
  field- and method-level annotations on their own lines where they
  used to be glued together. Project-wide consistency wins.

Manual touch-ups beyond the Spotless changes:
- PiiMasker + PiiMaskingPatternLayout: the long KEYWORD_TOKEN_PATTERN
  regex was broken across two literal strings via `+` concatenation
  (reflowLongStrings does not re-wrap concatenated literals because
  that would re-order the regex alternatives).
- MissionController: five new imports for the request DTOs that lived
  under model/dto/request (PatchMissionCoreRequest,
  PatchMissionScheduleRequest, PatchMissionFlagsRequest,
  AddFrequencyRequest, UpdateMissionOwnerRequest), with the
  fully-qualified usages in the method signatures collapsed to the
  simple class names.
- InventoryPageController: same pattern for UpdateDeliveredRequest.

Verification: ./gradlew :backend:test :frontend:test ->
BUILD SUCCESSFUL in 1m 45s, all backend (1300+) and frontend (491+)
tests green.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Phase 2i of the Checkstyle cleanup. 27 files in the frontend.controller
package, 156 violations cleared.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Sweep of the smaller checks left over after Phase 3c. Each individual
check is too small for its own commit, but together they bring the
Phase 3 backlog down to just three categories that need real semantic
work (VariableDeclarationUsageDistance,
AbbreviationAsWordInName, OverloadMethodsDeclarationOrder).

Resolved:
- EmptyCatchBlockCheck (3): commented the three intentionally-empty
  catch blocks (ProfileController joinDate parse, two
  BackendServiceException RFC7807 fallback paths) so commentFormat=\w+
  is satisfied.
- MatchXpathCheck#singleLineCommentStartWithSpace (2): empty `//`
  separators inside MissionController's block comment removed.
- AvoidEscapedUnicodeCharactersCheck (11): \u00XX sequences in
  HangarController, CreateJobOrderMaterialDto and
  JobOrderHandoverReportService replaced with literal umlauts via the
  new helper build/_fix_unicode_escapes.py (regex \u([0-9A-Fa-f]{4})
  -> chr(int(hex,16))).
- LocalVariableNameCheck (15) + CatchParameterNameCheck (2): two-char
  prefix variable names (pId, pName, pStart, pEnd, qType, rName,
  pLoc, pMan, gForm, sItem, eIso, eLocal, oEffectiveClass, yPos)
  renamed to substantive names matching the
  ^[a-z]([a-z0-9][a-zA-Z0-9]*)?$ pattern.
- ClassTypeParameterNameCheck (1): AbstractEntity<PK> -> <K>.
- SummaryJavadocCheck (8) + JavadocParagraphCheck (3): added missing
  trailing periods (RateLimitProperties, AppProblemProperties,
  WebClientLoggingFilter class doc) and missing <p> tags before
  follow-up paragraphs (InventoryItemService.updateNote,
  RedisSessionConfig).
- TextBlockGoogleStyleFormattingCheck (2): JPQL text blocks in
  MaterialPriceRepository and MaterialRepository aligned vertically
  with the closing """ marker.
- IndentationCheck (2): fixed a misaligned closing paren in
  JobOrderHandoverReportService.staffelLogo; shortened
  OperationPageController.updateOperation catch by importing
  WebClientResponseException so the catch signature fits on one line.

Verification: ./gradlew :backend:test :frontend:test ->
BUILD SUCCESSFUL in 1m 40s, all backend (1300+) and frontend (491+)
tests green.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Phase 2d of the Checkstyle cleanup. 35 files in the backend.service
package, 238 violations cleared.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The Phase 3d commit (d7e5d26) missed its CHANGELOG entry because the
batched git-add-and-commit ran before the CHANGELOG edit landed.
Catch-up entry summarising the 50+ smaller Checkstyle fixes
(EmptyCatchBlock, MatchXpath, AvoidEscapedUnicodeCharacters,
LocalVariableName, CatchParameterName, ClassTypeParameterName,
SummaryJavadoc, JavadocParagraph, TextBlockGoogleStyleFormatting,
Indentation).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…e (Phase 3e)

The check fires when a local variable is declared more than 3
statements before its first use. Default config has `ignoreFinal=true`,
so adding the `final` keyword silences the check for the 9 affected
sites without moving the declaration around. Each variable was already
effectively final - they are assigned once, never reassigned.

Affected: ProblemDetail pd (GlobalExceptionHandler.handleAccessDenied),
MaterialReferenceDto mat (InventoryItemService stream lambda),
User user / Material material / Location location
(InventoryItemService.create), double remainingAmount
(JobOrderHandoverService.createHandover), Integer priority
(JobOrderService.deleteJobOrder), int cap
(PersonalInventoryItemService.searchLocations), boolean isNew
(UexRefinerySyncService.syncRefiningMethods).

No semantic change. Test suite untouched.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…se 3f)

Last of the AbbreviationAsWordInNameCheck violations. Google style
allows at most one capital letter in an abbreviation, so:

- ETagConfig -> EtagConfig (backend + frontend, via git mv so history
  is preserved). Spring discovers @configuration beans via reflection
  on the class itself, so no external references needed adjustment.
- krtOpenAPI -> krtOpenApi (OpenApiConfig @bean method, doubles as
  the bean name; Spring still wires by type so call sites work
  unchanged).
- openAPI -> openApi (local variables in
  OpenApiProblemDetailsConfig.customizeOpenApi + ensureProblemDetailSchema).

After this commit only OverloadMethodsDeclarationOrderCheck (4 sites,
all pure method-reordering) is left in Phase 3 - deferred to a later
session because the reordering touches multiple ~10-line method
definitions in MissionMapper, MissionRepository and UserService.

Verification: ./gradlew :backend:test :frontend:test ->
BUILD SUCCESSFUL in 1m 39s, all backend (1300+) and frontend (491+)
tests green.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…ervice (Phase 3g)

Resolves the last 4 OverloadMethodsDeclarationOrderCheck violations,
closing Phase 3 entirely.

- MissionMapper: the nine toDto(...) overloads were sprinkled across
  the file, separated by toReferenceDto / toListDto / toEntity and the
  resolve* helpers. All toDto methods are now grouped at the top
  (Mission, MissionParticipant, MissionUnit, MissionCrew,
  MissionFinanceEntry, FrequencyType, MissionFrequency, JobType,
  Squadron), followed by the other mapping methods, followed by the
  resolve* helpers.
- MissionRepository: searchMissions(... Pageable) sat after the two
  findAll overloads; moved directly behind searchMissions(...).
- UserService: findAll(Pageable) sat after findAllReference(); moved
  directly behind findAll().

Pure reordering, no logic change, no test adjustment.

Phase 3 summary - all Restmechanik now green:
- 3a AvoidStarImport          151 -> 0
- 3b NeedBraces               131 -> 0
- 3c LineLength                82 -> 0
- 3d small-check sweep        ~50 -> 0
- 3e VariableDeclarationUsageDistance 9 -> 0
- 3f AbbreviationAsWordInName   5 -> 0
- 3g OverloadMethodsDeclarationOrder 4 -> 0

Verification: ./gradlew :backend:test :frontend:test ->
BUILD SUCCESSFUL in 1m 48s, all backend (1300+) and frontend (491+)
tests green.

Only the Phase 2 Javadoc backlog remains (734 violations in
service/controller/config/integration/exception layers and the
frontend controller package).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Phase 2e of the Checkstyle Javadoc cleanup — closes the last open
sub-phase. Adds 26 type-level and 203 method-level Javadocs across
the entire backend.controller package; net 1,592 insertions.

Type-Javadocs document each controller's non-obvious architectural
decisions (section patches + slim endpoints in MissionController,
state-machine + atomic inventory unlink in JobOrderController,
user-/owner-/admin-path duplication in RefineryOrderController,
PLANNED/ACTIVE/COMPLETED state machine + ADMIN bypass in
OperationController, guest redaction pipeline in MissionController).

Method-Javadocs focus on the concurrency patterns from CLAUDE.md:
- JobOrderController.updateJobOrderStatus: canonical
  completeJobOrderWithinTransaction pointer
- JobOrderController.createHandover: bulk-update-after-loop reference
- JobOrderController.updateJobOrderPriority: pessimistic-lock reorder
- MissionController.updateMissionOwner: separate MissionOwnership
  version (no Mission.version bump)
- MissionController.patchMission{Core,Schedule,Flags}: independent
  section versions
- MissionController.cleanupMissionForGuest/Participant/User: the
  guest redaction pipeline as central anti-PII safety net
- MissionController.addParticipantPublic: free-text name resolution
  with spoofing protection

Legacy MissionController endpoints (15) keep their @deprecated tag
with @deprecated Javadoc pointing at the slim replacement and the
SLIM_DEPRECATION_SUNSET date so IDE hover and Javadoc output stay
consistent with the @ApiDeprecation annotation.

Verified with `./gradlew :backend:checkstyleMain :backend:spotbugsMain
:backend:test` — 0 Javadoc violations remaining in backend.controller,
SpotBugs clean, backend test suite green.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
greluc and others added 3 commits May 14, 2026 00:45
… -> 0)

Merge the parallel branch's Phase 2 work (sub-phases 2d/2e/2f/2i —
backend services, backend controllers, backend infrastructure,
frontend controllers) using `git merge -X theirs`, then re-apply
and finish the Phase 3 cleanup.

Recovered Phase 3 mechanics that were lost by the `theirs` strategy:
- Re-expand 2 star imports (InventoryItemService, MissionService)
- Re-brace 8 `if`/`else` single-line bodies (NeedBraces)
- Re-convert 2 bare `//` lines to blank lines (MatchXpath)
- Restore deleted EtagConfig.java files (backend + frontend) and add
  Javadoc to the backend variant so HttpCachingTest can autowire the
  ShallowEtagHeaderFilter bean

New Checkstyle violations introduced by the merge:
- 90 SummaryJavadoc (Javadocs that started with `@return` had no
  summary) — auto-fixed by new build/_fix_summary_javadoc.py,
  long synthesized summaries wrapped by build/_wrap_long_javadoc.py
- 13 InvalidJavadocPosition (back-to-back duplicate Javadocs from
  both branches documenting the same method) — auto-fixed by new
  build/_merge_duplicate_javadocs.py with manual tag-deduplication
  follow-up
- 15 LocalVariableName renames (eIso, pId, pName, pStart, pEnd,
  pLoc, pMan, gForm, sItem, qType, rName, yPos, oEffectiveClass
  -> descriptive camelCase)
- 9 VariableDeclarationUsageDistance fixes (added `final`)
- 4 OverloadMethodsDeclarationOrder reorderings (grouped toDto
  overloads in MissionMapper, searchMissions in MissionRepository,
  findAll in UserService)
- 3 AbbreviationAsWordInName renames (krtOpenAPI -> krtOpenApi,
  openAPI -> openApi)
- 3 EmptyCatchBlock explanatory comments added
- 2 CatchParameterName renames (eIso/eLocal -> eiso/elocal)
- 3 JavadocParagraph fixes (<p> tag inserted)
- 3 MissingJavadocMethod/Type entries on EtagConfig
- 5 LineLength fixes (split long regex strings, shortened FQCN
  {@link} references, rewrote one long summary)
- 2 TextBlockGoogleStyleFormatting reindents
- 2 Indentation fixes

Spotless ran over everything afterwards. Compile clean on backend
and frontend (main + test source sets). Full test suite blocked
locally by Docker unavailability (TestContainers); Mockito-based
unit tests still green.

Verified `./gradlew :backend:checkstyleMain :frontend:checkstyleMain`:
backend `<error>` count = 0, frontend `<error>` count = 0
(16288 in Phase 0 -> 1796 after Phase 1 -> 738 pre-merge -> 0 now).

Next step (Phase 4): flip `enforceCheck = true` in the Spotless
config and `ignoreFailures = false` on the Checkstyle tasks so
`./gradlew check` blocks any future regression.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Wraps up the Checkstyle cleanup that started in Phase 0. Two
settings in `build.gradle.kts` move from "warn-only" to "block
the build":

  * Checkstyle subprojects: `isIgnoreFailures = false`,
    `maxWarnings = 0` — any new Checkstyle warning OR error now
    fails `./gradlew check`.
  * Spotless subprojects: `isEnforceCheck = true` — `spotlessCheck`
    runs as part of `check`, any unformatted Java file fails CI.

The first `spotlessCheck` after the flip uncovered one format drift
left over from Phase 3 (`setAbsolutePosition(...)` manually wrapped
to two lines in `JobOrderHandoverReportService`); `spotlessApply`
auto-fixed it. That auto-fix in turn restored a two-line
`catch (\n    FQCN e)` block in `OperationPageController` whose
continuation indent IndentationCheck rejects — resolved by adding
an explicit `import WebClientResponseException` so the catch fits
on one line again.

Verified:
- `./gradlew :backend:checkstyleMain :frontend:checkstyleMain
   :backend:spotlessCheck :frontend:spotlessCheck --rerun-tasks`
  → BUILD SUCCESSFUL, 0 violations, 0 format drifts
- `./gradlew :backend:spotbugsMain :frontend:spotbugsMain
   --rerun-tasks` → BUILD SUCCESSFUL, both modules clean

Cumulative reduction: 16288 → 1796 → 738 → 0 violations, now
gate-protected.

Developer impact: run `./gradlew spotlessApply` locally before
pushing, and any new public API needs class/method Javadoc or
MissingJavadocType/Method will block the build.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Two new rules added in the sections they belong to so future
contributors (and future Claude sessions) hit the same constraints
the gate enforces:

- Linting section: Run `./gradlew spotlessApply` locally before
  every push. Spotless is now `isEnforceCheck = true` and Checkstyle
  `isIgnoreFailures = false` + `maxWarnings = 0`, so an unformatted
  file or new warning fails CI on the spot.
- Documentation section: Javadoc is gate-enforced. Missing Javadoc
  on a new public/protected member fails the build via
  MissingJavadocType / MissingJavadocMethod. Same gate covers
  SummaryJavadoc (period after first sentence), InvalidJavadocPosition
  (no Javadoc between annotations and the declaration), JavadocParagraph
  (<p> after blank lines), and AtclauseOrder (@param/@return/@throws
  ordering).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Comment thread backend/src/main/java/de/greluc/krt/iri/basetool/backend/config/SecurityConfig.java Dismissed
…hi-c03b21

# Conflicts:
#	CHANGELOG.md
#	backend/src/main/java/de/greluc/krt/iri/basetool/backend/service/InventoryItemService.java
#	backend/src/main/java/de/greluc/krt/iri/basetool/backend/service/JobOrderHandoverReportService.java
#	backend/src/main/java/de/greluc/krt/iri/basetool/backend/service/JobOrderService.java
#	backend/src/main/java/de/greluc/krt/iri/basetool/backend/service/MissionFinanceEntryService.java
#	backend/src/main/java/de/greluc/krt/iri/basetool/backend/service/MissionSecurityService.java
#	backend/src/main/java/de/greluc/krt/iri/basetool/backend/service/OperationFinanceService.java
#	backend/src/main/java/de/greluc/krt/iri/basetool/backend/service/OperationService.java
#	backend/src/main/java/de/greluc/krt/iri/basetool/backend/service/ProfitCalculationService.java
#	backend/src/main/java/de/greluc/krt/iri/basetool/backend/service/SystemSettingService.java
… PR #65

CodeQL security (3):
- JobOrderPageController: orders_filter_status cookie now sets
  setHttpOnly(true) + setSecure(true) so it can't be read from JS
  and isn't sent over plain HTTP.
- RateLimitingFilter: 429 response body's `instance` field reflects
  request.getRequestURI() (attacker-controlled). String-concatenation
  into JSON allowed JSON-injection ("},"fake":"…). Escape via Jackson's
  JsonStringEncoder before injecting.
- SecurityConfig CSRF disabled: NOT addressed. Intentional for this
  JWT-bearer-token resource server (no session cookie, no cross-origin
  auto-attached header) — the test-profile disable is gated by
  Spring profile. False positive.

Code-quality (8):
- MissionPageControllerMvcTest: drop dead fixture Map literals
  (shipTypeData, unitData, orderData, itemData, materialData) that
  were built up but never attached to the MissionDto under test —
  3 "Container contents never accessed" findings.
- ParticipationCalculationTest: remove the
  assertNull(percentages.get(participantId.toString())) tautology
  on a Map<UUID, Double> (String key never matches a UUID entry).
- MissionUnitColorIndexTest: replace the
  assertTrue(PALETTE_SIZE >= 20) constant comparison with a runtime
  distinct-class-set assertion over indices 0..19. Functionally
  equivalent but no longer always-true at compile time.
- Announcement: drop the shadowing updatedAt field + @PrePersist/
  @PreUpdate hook. AbstractEntity.updatedAt with @UpdateTimestamp
  already does this; the duplicate was producing both the missing-
  @OverRide finding AND a JPA column-mapping ambiguity. Also add
  @Getter(onMethod_ = @__(@OverRide)) to id so the Lombok-generated
  getId() carries @OverRide (implements Persistable.getId()).
- Faction: same @Override-via-Lombok onMethod_ treatment for id.

Verified:
- ./gradlew :backend:checkstyleMain :frontend:checkstyleMain
  :backend:spotlessCheck :frontend:spotlessCheck
  :backend:spotbugsMain :frontend:spotbugsMain → BUILD SUCCESSFUL
- ./gradlew :backend:test --tests "*Announcement*" → green
- ./gradlew :frontend:test --tests "*MissionUnitColorIndexTest*
  *ParticipationCalculationTest*" → green
- ./gradlew :backend:test --tests "*RateLimitingTest*" → fails on
  Docker (TestContainers env), unrelated to this change.

Not addressed in this commit:
- SecurityConfig CSRF (see above)
- The systemic Lombok @OverRide pattern on the other 41 entity
  classes — CodeQL only flagged Announcement and Faction; the rest
  carry the same shape and will get the same treatment when CodeQL
  catches up. Left as a follow-up rather than touching 41 unrelated
  files in this PR.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
… strip CRLF from GlobalExceptionHandler

CodeQL's "Missing Override annotation" check fires on every entity
whose Lombok-generated getId() implements Persistable.getId() without
@OverRide. Doing them one at a time was whack-a-mole — three new
findings landed across two CI runs after the first batch (City,
InventoryItem, JobOrderHandover, JobOrderHandoverItem, JobOrderMaterial,
Jurisdiction, Location, Manufacturer, FrequencyType). Closing the
pattern uniformly across the entire backend.model package ends the
spread.

Approach (build/_apply_getter_override.py): walk the model directory,
find every `private UUID id;` field on an `extends AbstractEntity`
class, and insert `@Getter(onMethod_ = @__(@OverRide))` as the first
annotation in its block. The class-level @Getter still drives every
other field; the field-level annotation wins for `id` and attaches
@OverRide to the generated getId() so the override marker is real
without losing Lombok's brevity.

38 entities updated by the script + 3 stragglers fixed manually
(Role with Long id, SystemSetting with String id, User with inline
`@Id private UUID id;` that the script's regex didn't match).
Announcement and Faction already had the annotation from the prior
batch.

Also strips CRLF from GlobalExceptionHandler.java which had drifted
to Windows line endings in the index (git ls-files --eol reported
`i/-text w/-text` — git had decided the file was binary because the
blob contained `\r\n`). CI's spotlessJavaCheck was failing because
Spotless saw CRLF and wanted LF. Rewriting with LF restores the
expected git/Spotless contract.

Not in this commit: the CodeQL "advanced configuration cannot be
processed when default setup is enabled" error on the
`Analyze (java-kotlin)` / `Analyze (javascript-typescript)` checks
is a repo-settings conflict (the CodeQL workflow file from main
collides with GitHub's auto-enabled default setup). That requires
admin action via Settings → Code security, not a code change here.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…+ drop spurious @param <T>

CodeQL keeps drip-feeding "Missing Override annotation" findings on
Spring Data repositories that redeclare findById / findAll (typically
to attach @EntityGraph). Eight new findings this CI run across six
repositories. Doing them one-at-a-time was already a whack-a-mole
loop in the entity-class batch — closing the pattern uniformly across
the repository package ends the spread.

Helper: build/_apply_repo_override.py. Walks every *Repository.java
under backend/src/main/java, matches method declarations whose name +
parameter shape match a Spring Data inherited override (findById(ID),
findAll(), findAll(Pageable), findAll(Sort), existsById, count,
findAllById, deleteById, delete, deleteAll, save), and inserts
@OverRide as the first annotation in the block. Derived queries
(findByEmail, findFirstByPlannedStartTime…, findAllActiveReference)
are NOT matched.

Result: 8 @OverRide insertions across 6 files:
- JobOrderRepository.findById
- MissionRepository.findById + findAll(Pageable)
- RefineryOrderRepository.findAll(Pageable)
- RoleRepository.findAll(Pageable)
- ShipRepository.findAll(Pageable)
- UserRepository.findById + findAll(Pageable)

Also drops the `@param <T>` Javadoc tag from UexResponseDto. Records'
auto-generated accessors (status(), data()) plus the synthetic
build()/toString() inherit the record-level Javadoc, and a type
parameter `@param <T>` has no value parameter to bind to on any of
those — CodeQL flags it as "Spurious Javadoc @param tags" with empty
match. Replaced with prose mentioning T and a note explaining why
the tag isn't used (so future contributors don't re-add it).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@greluc greluc self-assigned this May 14, 2026
@greluc greluc added the enhancement New feature or request label May 14, 2026
The string sentinel `MESSAGE_NOT_FOUND_SENTINEL` was defined with
four LITERAL 0x00 bytes baked into the source instead of the proper
6-char Java escape sequence (backslash + u + four zero digits). Git's
auto-binary detection saw the NUL bytes in the blob and classified
the file as binary (`git ls-files --eol` reported `i/-text w/-text`).
That meant:

  1. No CRLF -> LF normalization ever happened on this file, so it
     stayed CRLF in the index even after my previous "strip CR"
     attempt re-committed the same binary blob.
  2. Spotless on the Linux CI runner saw the CRLF in every line and
     failed `spotlessJavaCheck` with 995 format violations.
  3. `grep` reported the file as binary, hiding it from text tools.

build/_fix_global_exception_handler.py rewrites the file:
  - Replaces every 0x00 byte with the corresponding Java escape
    sequence so the file becomes pure ASCII text.
  - Strips CR from CRLF so the file is pure LF.

After the fix `git ls-files --eol` reports `i/lf w/lf` -- git now
treats it as proper text, Spotless is happy, and the sentinel still
compares equal to a 16-char string with NUL characters at runtime
(the Java compiler decodes the escape into the same NUL char).
Verified `./gradlew :backend:spotlessCheck :backend:checkstyleMain
:backend:spotbugsMain` -> BUILD SUCCESSFUL.

Note: this is a recurrence of the same root cause as the previous
attempt in 31cf6f8 -- that commit stripped CR but did NOT touch the
NUL bytes, so git kept the binary classification and CR came back
on the next spotlessApply re-write. Replacing the NUL bytes is what
actually breaks the cycle.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Copy link
Copy Markdown

@github-advanced-security github-advanced-security AI left a comment

Choose a reason for hiding this comment

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

CodeQL found more than 20 potential problems in the proposed changes. Check the Files changed tab for more details.

greluc and others added 6 commits May 14, 2026 13:53
Spotless 7.0.4 bundles google-java-format 1.25.2 by default. That
version reflects against
  com.sun.tools.javac.util.Log$DeferredDiagnosticHandler.getDiagnostics()
whose return type changed from Queue<JCDiagnostic> to Deque<JCDiagnostic>
in JDK 25. The CI runner has switched to a JDK 25 Temurin patch that
exposes the new signature, so the reflection lookup explodes with
NoSuchMethodError for every Java file in the repo:

  > Task :backend:spotlessJavaCheck FAILED
  > There were 529 lint error(s), they must be fixed or suppressed.
    google-java-format(java.lang.NoSuchMethodError) ...
  > Task :frontend:spotlessJavaCheck FAILED
  > There were 238 lint error(s) ...

767 files, all failing on the same internal-API mismatch — none of
them have any actual format issue. Pinning google-java-format to
1.28.0 (the first GJF release that targets the new JDK 25 signature)
restores spotlessCheck on CI.

Local JDK 21 still works because the old method signature exists
there too — the bug only manifests on JDK 25, which is what CI uses
(.github/workflows/ci.yml: `Set up JDK 25 (Temurin)`).

Verified `./gradlew spotlessCheck --rerun-tasks` -> BUILD SUCCESSFUL
locally on JDK 21 with the pinned version (so the version is at
least syntactically valid and Maven Central has it).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Tracks the current GJF release. Verified `./gradlew spotlessCheck
--rerun-tasks` -> BUILD SUCCESSFUL — no format drift relative to
1.28.0 on the existing codebase (the two versions produce the same
output for every file in the repo).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Major-version bump to track the current Spotless release. Verified
`./gradlew spotlessCheck --rerun-tasks` -> BUILD SUCCESSFUL — no
format drift relative to 7.0.4 on the existing codebase (Spotless 8.x
ships with the same google-java-format integration we already pin
to 1.35.0, so the formatting contract is unchanged).

Also generalises the comment block on the explicit
`googleJavaFormat("1.35.0")` pin so it no longer hardcodes "Spotless
7.0.4 bundles 1.25.2" -- the pin override survives Spotless
upgrades, the JDK 25 reasoning still applies.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Patch-level bump to track the current Dependency-Check release.
Verified `./gradlew help --task dependencyCheckAggregate` -> BUILD
SUCCESSFUL (plugin resolves and the aggregate task is still wired).
Comment block updated to reference the new version.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Minor-version bump to track the current ArchUnit release.

Verified:
- `./gradlew :backend:compileTestJava :frontend:compileTestJava`
  -> BUILD SUCCESSFUL (API still source-compatible).
- `./gradlew :backend:test --tests '*ArchitectureTest*'
   :frontend:test --tests '*ArchitectureTest*'` -> BUILD SUCCESSFUL
  (the architectural invariants in
  backend/.../ArchitectureTest.java and
  frontend/.../ArchitectureTest.java still hold under the new
  runtime).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Discovered via `./gradlew refreshVersions`; applied every stable
update available upstream and skipped pre-release-only tracks.

Applied (libs.versions.toml + versions.properties):
- flyway          12.3.0  -> 12.6.1  (4 patch releases bundled)
- bucket4j        8.17.0  -> 8.18.0  (minor)
- logstashLogback 8.0     -> 9.0     (major; logback ecosystem
                                       JSON encoder, no API breakage
                                       on our usage in
                                       backend/logging + frontend/logging
                                       — verified via spotless +
                                       checkstyle + spotbugs +
                                       Pii*Test runs)
- springdocOpenapi 3.0.2  -> 3.0.3   (patch)
- openpdf         2.0.3   -> 2.2.5   (latest 2.x stable)

Tried but reverted:
- openpdf 3.0.4 — renames com.lowagie.text.* to org.openpdf.text.*;
  JobOrderHandoverReportService has 13 imports under com.lowagie.*
  that would all need rewriting + smoke-testing the PDF output of
  every report flow. Deferred to a focused follow-up; staying on
  the latest 2.x.

Skipped (pre-release only, not production-ready):
- springBoot 4.0.6 — only 4.1.0-M1..M4 / RC1 milestones available.
- mapstruct  1.6.3 — only 1.7.0.Beta1 available.

Inline comments added to the toml so the skips are documented at
the call site (so the next refreshVersions sweep won't re-prompt
on the same pre-release tracks).

CycloneDX BOMs were regenerated as a side effect of the dep tree
shifts (logstash-logback 9.0 pulls a different logback set, flyway
12.6 bumps its postgresql adapter, etc.) — committed alongside.

Verified:
- ./gradlew :backend:compileJava :frontend:compileJava
   :backend:compileTestJava :frontend:compileTestJava → SUCCESS
- ./gradlew :backend:checkstyleMain :frontend:checkstyleMain
   :backend:spotlessCheck :frontend:spotlessCheck
   :backend:spotbugsMain :frontend:spotbugsMain --rerun-tasks
   → BUILD SUCCESSFUL
- ./gradlew :backend:test --tests '*ArchitectureTest*' --tests '*Pii*'
   :frontend:test --tests '*ArchitectureTest*' --tests '*Pii*'
   → BUILD SUCCESSFUL

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants