From 97bfddad3a803318f5128b9b9274cfbf992c906b Mon Sep 17 00:00:00 2001 From: Claude Date: Fri, 26 Jun 2026 06:38:29 +0000 Subject: [PATCH 1/2] Run SpotBugs early in CI code-style gate; suppress CE_CLASS_ENVY (provisional) Add a SpotBugs step to the fast code-style job (after Spotless, before the informational jdeps step) so spotbugs:check runs early on every PR/push, consistent with the other repos (publish-* already needs: code-style). The early gate surfaced a pre-existing, already-merged CE_CLASS_ENVY in PackageIndexer.appendPackageHeaderLines (it hand-renders a .ai.md header that AiMdHeaderCodec.write() already emits byte-for-byte). Provisionally suppress it and track the delegate-to-codec fix in TODO.md. Co-Authored-By: Claude Opus 4.8 Claude-Session: https://claude.ai/code/session_0137c1LhUbNvW3kt4eF9Kqyb --- .github/workflows/publish.yml | 2 ++ TODO.md | 2 ++ spotbugs-exclude.xml | 17 +++++++++++++++++ 3 files changed, 21 insertions(+) diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index 5e95f9f..d7bad1a 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -43,6 +43,8 @@ jobs: distribution: temurin - name: Spotless check (fail fast on format violations) run: mvn -B --no-transfer-progress spotless:check + - name: SpotBugs check (fail fast on static-analysis findings) + run: mvn -B --no-transfer-progress -DskipTests -Denforcer.skip=true compile spotbugs:check - name: Print internal package dependency graph (jdeps, informational) continue-on-error: true run: | diff --git a/TODO.md b/TODO.md index 7bab3cc..d964306 100644 --- a/TODO.md +++ b/TODO.md @@ -23,6 +23,8 @@ cross-cutting initiative. - **Cross-repo code-quality TODOs** — see [`../workspace/policies/code-quality-todos.md`](../workspace/policies/code-quality-todos.md) for the canonical `@VisibleForTesting` design-fit review, package hierarchy review, and class/method naming review. This repo has no `@VisibleForTesting` usages today; the package and naming reviews are still open here. +- **SpotBugs `CE_CLASS_ENVY` on `PackageIndexer.appendPackageHeaderLines` (provisional suppression).** fb-contrib flags this method — it renders the package `.ai.md` header from all eight `AiMdHeader` accessors plus `AiMdHeaderCodec` prefix constants, so it uses other classes more than its own. It is **provisionally suppressed** in `spotbugs-exclude.xml` so the early SpotBugs `code-style` gate is green. **Deep-check / resolve:** `AiMdHeaderCodec.write(AiMdHeader)` already emits byte-for-byte identical text, so the clean fix is to delegate — add an `AiMdHeaderCodec` field and replace the body with `builder.append(headerCodec.write(header))`, then drop the suppression and confirm `PackageIndexerTest` still passes. Surfaced (already merged, via PR #117) when SpotBugs moved to the early `code-style` CI gate. See [`../workspace/policies/spotbugs-suppressions.md`](../workspace/policies/spotbugs-suppressions.md). + ## Done (kept for history) ### Layered package restructure (flat plugin package → layered hierarchy) diff --git a/spotbugs-exclude.xml b/spotbugs-exclude.xml index a37fd20..681ba61 100644 --- a/spotbugs-exclude.xml +++ b/spotbugs-exclude.xml @@ -279,4 +279,21 @@ SPDX-License-Identifier: Apache-2.0 + + + + + + + From a15ad5f67916f6a51a8363ca5438e98939896e87 Mon Sep 17 00:00:00 2001 From: Claude Date: Fri, 26 Jun 2026 06:54:08 +0000 Subject: [PATCH 2/2] Remove PackageIndexer header duplication; drop CE_CLASS_ENVY suppression MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit appendPackageHeaderLines hand-rendered the package .ai.md header by appending all eight AiMdHeader accessors with AiMdHeaderCodec prefix constants — byte for byte the same output AiMdHeaderCodec.write(AiMdHeader) already produces (the same call AiMdDocumentCodec uses to serialise a header). fb-contrib flagged it as CE_CLASS_ENVY. Delegate to AiMdHeaderCodec.write() via a new headerCodec field, deleting the duplicated eight-field block. Output is byte-identical (PackageIndexerTest + AiMdDocumentCodecTest + AiMdHeaderCodecTest all green; compile clean through NullAway/ErrorProne; spotbugs 0 bugs with the suppression removed), so the provisional CE_CLASS_ENVY suppression is dropped rather than kept and the deep-check item is closed in TODO.md. Co-Authored-By: Claude Opus 4.8 Claude-Session: https://claude.ai/code/session_0137c1LhUbNvW3kt4eF9Kqyb --- TODO.md | 2 - spotbugs-exclude.xml | 17 -------- .../aiindex/indexer/PackageIndexer.java | 39 +++---------------- 3 files changed, 6 insertions(+), 52 deletions(-) diff --git a/TODO.md b/TODO.md index d964306..7bab3cc 100644 --- a/TODO.md +++ b/TODO.md @@ -23,8 +23,6 @@ cross-cutting initiative. - **Cross-repo code-quality TODOs** — see [`../workspace/policies/code-quality-todos.md`](../workspace/policies/code-quality-todos.md) for the canonical `@VisibleForTesting` design-fit review, package hierarchy review, and class/method naming review. This repo has no `@VisibleForTesting` usages today; the package and naming reviews are still open here. -- **SpotBugs `CE_CLASS_ENVY` on `PackageIndexer.appendPackageHeaderLines` (provisional suppression).** fb-contrib flags this method — it renders the package `.ai.md` header from all eight `AiMdHeader` accessors plus `AiMdHeaderCodec` prefix constants, so it uses other classes more than its own. It is **provisionally suppressed** in `spotbugs-exclude.xml` so the early SpotBugs `code-style` gate is green. **Deep-check / resolve:** `AiMdHeaderCodec.write(AiMdHeader)` already emits byte-for-byte identical text, so the clean fix is to delegate — add an `AiMdHeaderCodec` field and replace the body with `builder.append(headerCodec.write(header))`, then drop the suppression and confirm `PackageIndexerTest` still passes. Surfaced (already merged, via PR #117) when SpotBugs moved to the early `code-style` CI gate. See [`../workspace/policies/spotbugs-suppressions.md`](../workspace/policies/spotbugs-suppressions.md). - ## Done (kept for history) ### Layered package restructure (flat plugin package → layered hierarchy) diff --git a/spotbugs-exclude.xml b/spotbugs-exclude.xml index 681ba61..a37fd20 100644 --- a/spotbugs-exclude.xml +++ b/spotbugs-exclude.xml @@ -279,21 +279,4 @@ SPDX-License-Identifier: Apache-2.0 - - - - - - - diff --git a/src/main/java/net/ladenthin/maven/llamacpp/aiindex/indexer/PackageIndexer.java b/src/main/java/net/ladenthin/maven/llamacpp/aiindex/indexer/PackageIndexer.java index a86beda..39e13dc 100644 --- a/src/main/java/net/ladenthin/maven/llamacpp/aiindex/indexer/PackageIndexer.java +++ b/src/main/java/net/ladenthin/maven/llamacpp/aiindex/indexer/PackageIndexer.java @@ -116,6 +116,7 @@ public class PackageIndexer { private final AiMdHeaderSupport headerSupport = new AiMdHeaderSupport(); private final AiMdChildEntryLineFormatter childEntryLineFormatter = new AiMdChildEntryLineFormatter(); private final AiMdDocumentCodec documentCodec = new AiMdDocumentCodec(); + private final AiMdHeaderCodec headerCodec = new AiMdHeaderCodec(); private final Java8CompatibilityHelper compatibilityHelper = new Java8CompatibilityHelper(); private final AiFieldGenerationSupport fieldGenerationSupport; @@ -365,44 +366,16 @@ private String buildPackageSourceText( } /** - * Appends the package's own deterministic header block (title plus single-letter fields) - * to {@code builder}, mirroring the on-disk {@code .ai.md} header layout. + * Appends the package's own deterministic header block (title plus single-letter fields) to + * {@code builder} by delegating to {@link AiMdHeaderCodec#write(AiMdHeader)} — the single source + * of truth for the {@code .ai.md} header layout, and the same call {@link AiMdDocumentCodec} uses + * to serialise a persisted header. * * @param builder target string builder * @param header the package header to render */ private void appendPackageHeaderLines(final StringBuilder builder, final AiMdHeader header) { - builder.append(AiMdHeaderCodec.HEADER_TITLE_PREFIX) - .append(header.title()) - .append('\n'); - builder.append(AiMdHeaderCodec.HEADER_FIELD_PREFIX) - .append("H: ") - .append(header.h()) - .append('\n'); - builder.append(AiMdHeaderCodec.HEADER_FIELD_PREFIX) - .append("C: ") - .append(header.c()) - .append('\n'); - builder.append(AiMdHeaderCodec.HEADER_FIELD_PREFIX) - .append("D: ") - .append(header.d()) - .append('\n'); - builder.append(AiMdHeaderCodec.HEADER_FIELD_PREFIX) - .append("T: ") - .append(header.t()) - .append('\n'); - builder.append(AiMdHeaderCodec.HEADER_FIELD_PREFIX) - .append("G: ") - .append(header.g()) - .append('\n'); - builder.append(AiMdHeaderCodec.HEADER_FIELD_PREFIX) - .append("A: ") - .append(header.a()) - .append('\n'); - builder.append(AiMdHeaderCodec.HEADER_FIELD_PREFIX) - .append("X: ") - .append(header.x()) - .append('\n'); + builder.append(headerCodec.write(header)); } /**