Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ policies/spotless-formatting.md Spotless/Palantir formatting; run spotl
policies/jqwik-prompt-injection.md jqwik pin + incident note
policies/lombok-config.md Canonical lombok.config content for Lombok-using repos
policies/jpms-module-descriptor.md module-info compile/javadoc/test interactions + the Java-bump javadoc trap
policies/fat-jar-release-assets.md fat jar (jar-with-dependencies) = GitHub-Release asset only, never Central, signed .asc
policies/code-quality-todos.md Recurring per-repo audits
policies/ci-test-diagnostics.md Test-JVM heap (-Xmx2g, no -Xms) + crash-dump CI standard
policies/pit-mutation-testing.md PIT version/threshold + the test-compile/@{argLine} invocation rule
Expand Down
34 changes: 32 additions & 2 deletions crossrepostatus.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,37 @@ Legend: ✅ done · 🚧 in progress · ❌ open · ➖ N/A · 📌 standing pol
| ArchUnit standard set (`noSystemExit` / `noNewRandom` / `Thread.sleep` / sun-com.sun-jdk.internal bans / public-fields-final / `noTestFrameworksInProduction` / `noPackageCycles`) | All 4 ✅ |
| `javac -Werror` + `-Xlint:all,-serial,-options,-classfile,-processing` | All 4 ✅ |
| GPG signing-key preflight (`verify-signing-key` job) | All 4 wired **byte-identically** in `publish.yml`: a standalone job (**no `needs:`**, runs in parallel at pipeline start on **every** trigger) under `environment: maven-central` that reproduces what **maven-gpg-plugin** does at deploy time — import the key into an ephemeral keyring, assert it is present / not expired / signing-capable, then a **passphrase-unlock → detached-sign → verify roundtrip** — so a bad/expired key or wrong passphrase reds in ~20s instead of failing the publish stage. **Prints only PUBLIC key metadata** (key id, fingerprint, owner UID, algo, created/expiry); passphrase fed on **fd 3** (never argv/logs), `set -x` never enabled, passphrase `::add-mask::`ed. **Red-by-design** on refs where the secret is not delivered (fork PRs / other contributors' branches — the `maven-central` environment gate rejects them *before a runner is assigned*, so `runner_id: 0` + ~2s failure is the gate, not a runner shortage). Verified green on jllamacpp-ai-index `main` 2026-07-09 (key `ED0D9440BF148ED2`, "Good signature"). Added 2026-07-09, branch `claude/android-signing-failure-q7zml9`. A companion **Gradle/BouncyCastle** preflight is likewise byte-identical in all 4 — see the next row. |
| GPG signing-key preflight — Gradle/BouncyCastle (`verify-signing-key-gradle` job) | All 4 wired **byte-identically** (same job + the throwaway `.github/signing-selftest/` Gradle project). Companion to the gpg row above: it drives **Gradle's `signing` plugin + `useInMemoryPgpKeys` (BouncyCastle)** — the path any Gradle-based publish (e.g. an Android AAR) uses — which is a **stricter** armored-key parser than gpg, so it catches key/format problems gpg tolerates (it is exactly what surfaced the primary-vs-signing-subkey `null PGPPrivateKey`). It signs a throwaway Zip (**no repo build involved**), so it runs even in repos that don't publish via Gradle yet — **"prepared for Gradle"**, uniform-by-choice (identical pipelines preferred over minimalism). Same standalone / no-`needs:` / parallel / `environment: maven-central` / red-by-design / no-secret-material properties as the gpg row; selects the signing subkey via `MAVEN_GPG_KEY_ID` (env secret `GPG_KEY_ID = 07D2D767`, a public key id). Added 2026-07-09, branch `claude/android-signing-failure-q7zml9`. |
| GPG signing-key preflight — Gradle/BouncyCastle (`verify-signing-key-gradle` job) | All 4 wired **byte-identically** (same job + the throwaway `.github/signing-selftest/` Gradle project). Companion to the gpg row above: it drives **Gradle's `signing` plugin + `useInMemoryPgpKeys` (BouncyCastle)** — the path any Gradle-based publish (e.g. an Android AAR) uses — which is a **stricter** armored-key parser than gpg, so it catches key/format problems gpg tolerates (it is exactly what surfaced the primary-vs-signing-subkey `null PGPPrivateKey`). It signs a throwaway Zip (**no repo build involved**), so it runs even in repos that don't publish via Gradle yet — **"prepared for Gradle"**, uniform-by-choice (identical pipelines preferred over minimalism). Same standalone / no-`needs:` / parallel / `environment: maven-central` / red-by-design / no-secret-material properties as the gpg row; selects the signing subkey via `MAVEN_GPG_KEY_ID` (env secret `GPG_KEY_ID = 07D2D767`, a public key id). Added 2026-07-09, branch `claude/android-signing-failure-q7zml9`. **The `.github/signing-selftest/` `.kts` files are now *literally* byte-identical in all 4** (dual-licensed `MIT OR Apache-2.0`) — until 2026-07-24 the body matched but the SPDX header drifted (jllama `MIT`, the others `Apache-2.0`); unified + `streambuffer/LICENSES/MIT.txt` added. Checksums recorded in the drift-check below. |

### Cross-repo byte-identical files — checksum drift check

Files kept **byte-identical across repos** (sync any edit to every copy AND the hash here):

| File | SHA-256 | Copies |
|---|---|---|
| `.github/signing-selftest/build.gradle.kts` | `ab45f5c102b47dd16c325d4d9c283d158ba90c05f484eac45b2767885c4462f9` | all 4 repos |
| `.github/signing-selftest/settings.gradle.kts` | `9b2ea5b5ff8d48607e26e4e211ad6d496f7660e71c84e42caaa82b84f7001710` | all 4 repos |
| `.github/sign-fatjars.sh` | `3a240faac46c35d3ac4a11dc2969648e2134906b90a79b990ce2b713c7a96b36` | jllama + srcmorph (see [`policies/fat-jar-release-assets.md`](policies/fat-jar-release-assets.md)) |
| `lombok.config` (jllama: `llama/lombok.config`) | `42f1842270af691bdfe561355bee4eb9ae326383f1852db19763abb888d6b90e` | the 3 Lombok repos: jllama + BAF + srcmorph (sb has no Lombok). Canonical content in [`policies/lombok-config.md`](policies/lombok-config.md) |
| `.github/ISSUE_TEMPLATE/bug_report.md` | `7232b092d3ba49b97bee7b539aaf6ee4c698e86bd3d4dd256e8ae2f85f653ee9` | all 4 repos |
| `.github/ISSUE_TEMPLATE/feature_request.md` | `0f08122e597f93dbbdc9c80e88984b4bf4738951d5902813df3d4640cdb11bac` | all 4 repos |
| `.github/PULL_REQUEST_TEMPLATE.md` | `ebfcc0adf59f5858bbe4dc077c906304a197f72a55256f0d5aac669bee5e871f` | all 4 repos |

Verify from the `workspace` repo root (siblings checked out alongside):

```bash
# Repo names are listed explicitly in each {…} — bash runs brace expansion BEFORE
# variable expansion, so a $var inside {…} would not expand into the repo list.
sha256sum ../{java-llama.cpp,BitcoinAddressFinder,srcmorph,streambuffer}/.github/signing-selftest/build.gradle.kts \
../{java-llama.cpp,BitcoinAddressFinder,srcmorph,streambuffer}/.github/signing-selftest/settings.gradle.kts \
../{java-llama.cpp,srcmorph}/.github/sign-fatjars.sh \
../java-llama.cpp/llama/lombok.config ../{BitcoinAddressFinder,srcmorph}/lombok.config \
../{java-llama.cpp,BitcoinAddressFinder,srcmorph,streambuffer}/.github/ISSUE_TEMPLATE/bug_report.md \
../{java-llama.cpp,BitcoinAddressFinder,srcmorph,streambuffer}/.github/ISSUE_TEMPLATE/feature_request.md \
../{java-llama.cpp,BitcoinAddressFinder,srcmorph,streambuffer}/.github/PULL_REQUEST_TEMPLATE.md
# each file's copies must all show the hash in the table above; a mismatch = drift (re-sync) or an
# intentional edit (update every copy AND this table in the same change set).
```

## Deliberate non-parity (NOT drift)

Expand All @@ -47,7 +77,7 @@ Legend: ✅ done · 🚧 in progress · ❌ open · ➖ N/A · 📌 standing pol
- **jllama's PIT gate is environment-conditional (not fully hermetic).** It reaches 100% only when the **audio test fixture** is present; without it the run is **98%** (4 `NO_COVERAGE` mutants in `value.ContentPart.audioFile(Path)` — the null-name guard, the `.wav`/`.mp3` dispatch, and `Files.readAllBytes`). The only test exercising that method is the model-/fixture-gated `AudioInputIntegrationTest`, which self-skips (`Assume`) when no audio clip is supplied (`net.ladenthin.llama.audio.input` — no committed default). Contrast the sibling `value.ContentPart.imageFile(Path)`, which **is** hermetic via temp-file unit tests (PNG/JPG/GIF/WEBP). So a green jllama PIT gate requires the CI audio fixture; the hermetic fix (a temp-`.wav`/`.mp3` unit test mirroring the image tests) is tracked in jllama `TODO.md`. The other three gates are fully hermetic. See [`policies/pit-mutation-testing.md`](policies/pit-mutation-testing.md) §4.
- **BAF's Coveralls/Codecov coverage source is the `test-opencl` (pocl) job, not the ubuntu `test` matrix** — intentional, and only BAF has this distinction (it is the only repo with OpenCL code). The ubuntu `test` matrix has no OpenCL ICD installed, so `@OpenCLTest`-annotated classes (`OpenCLContext`, `OpenClTask`, `OpenCLGridResult`, `ProducerOpenCL`, etc.) self-skip via `OpenCLPlatformAssume`, producing 0% coverage for the entire GPU pipeline. The `test-opencl` job installs pocl (a conformant OpenCL 3.0 CPU implementation), so the full test suite — including all `@OpenCLTest` classes — actually executes there. Both jobs run on every push/PR; only the JaCoCo artifact consumed by Coveralls/Codecov changed source (`jacoco-report-opencl` from `test-opencl`, not `jacoco-report` from `test`). BAF `publish.yml` commit `5d5db1a`.
- **Depot / `sccache` shared compiler cache — java-llama.cpp only.** jllama's CI fronts the C/C++ compiler with `sccache` backed by **Depot Cache** over sccache's WebDAV backend (`SCCACHE_WEBDAV_ENDPOINT: https://cache.depot.dev`, `SCCACHE_WEBDAV_TOKEN: ${{ secrets.DEPOT_TOKEN }}`, plus `BUILD_JOBS` to bound macOS-runner memory) so its heavy native build (134 llama.cpp TUs + ggml + the 16.6k-line `httplib.cpp`, all `-O3`) recompiles only changed files and shares the cache across branches. Wiring lives in jllama `.github/build.sh` + `.github/workflows/publish.yml`; rationale in jllama `CLAUDE.md` "CI build cache & parallelism (sccache + Depot)". **This is jllama-only by nature, not drift:** `sccache` caches *compiler* output (C/C++/Rust/CUDA) and jllama is the only sibling with a native (C++/JNI) compile. The three pure-Maven siblings (BAF, sb, plugin) have no C/C++ to cache, run on **GitHub-hosted** runners (Depot's *GitHub Actions* cache backend activates only on **Depot-hosted** runners — see [Depot docs](https://depot.dev/docs/cache/integrations/github-actions)), and already cache Maven deps via `actions/setup-java`'s `cache: maven` (GitHub's per-branch cache). The `DEPOT_TOKEN` organization secret was added to **all** repos (2026-06-20) but is **inert** outside jllama. The README "Build cache by Depot" badge (jllama `README.md`) is therefore kept **jllama-only on purpose** — adding it to the Maven repos would advertise a capability they don't have. Same shape as the BAF-only pocl/OpenCL coverage entry above (a real per-repo capability difference, not a parity gap to close).
- **`jar-with-dependencies` (fat/uber JAR) built + uploaded as a per-run CI artifact — BAF + jllama only.** Both build a `*-jar-with-dependencies.jar` via an `assembly` Maven profile and let it ride along in the existing per-run `upload-artifact` bundle — a **CI run artifact only**, not a Maven Central or GitHub-Release asset (the signed thin/sources/javadoc/classifier JARs remain the only published coordinates). **BAF**: the assembly machinery (managed `maven-assembly-plugin`, `Main-Class: …cli.Main`, `assembly` profile) already existed for the runnable CLI distributable; this round only added `-P assembly` to the `build` job so the fat jar joins the `jars` artifact. **jllama**: a library, so the uber JAR bundles the Java runtime deps + the default-platform native libs (`src/main/resources`, i.e. CPU/Metal/Windows/Android-CPU — *not* the cuda/opencl classifier trees) into one drop-on-classpath JAR with **no `Main-Class`**; the `assembly` profile + managed `maven-assembly-plugin` 3.8.0 were **added this round**, and `assembly` was appended to the `package` job's `-P release,cuda,opencl-android` list so the fat jar joins `llama-jars`. **sb** and **plugin** deliberately have **none** — sb is a single-class `Closeable` library and the plugin is a Maven plugin consumed via its coordinate; neither has a standalone-distribution use case, so an uber JAR would be dead weight. Branch `claude/cool-curie-ym3acr`.
- **`jar-with-dependencies` (fat/uber JAR) = GitHub-Release asset only, never Central, signed `.asc` — BAF + jllama + srcmorph (not sb).** *(Updated 2026-07-24, branch `claude/bitcoinaddressfinder-jar-upload-k0tkj7` — supersedes the earlier "per-run CI artifact only" state.)* The full convention + per-repo shapes live in the canonical [`policies/fat-jar-release-assets.md`](policies/fat-jar-release-assets.md). Summary: the uber jar is **never deployed to Maven Central** (redundant + large +, where it bundles a native binary, platform-specific) and is **attached to the GitHub Release with a detached GPG `.asc`** (authenticity parity with the thin jars). Per-repo **shape differs by design** (deliberate non-parity, not drift): **BAF** — one fat jar (jocl bundles all-platform OpenCL natives), built off-Central via `mvn -P release,assembly verify` (stops before `deploy`), signed by `maven-gpg`. **jllama** — multi-backend `all-<os>-<arch>` jars (default CPU + per-backend subdirs, `LlamaLoader`-selected) via `package-fatjars.sh`, signed by `.github/sign-fatjars.sh` in the attach jobs (`.sha256` **and** `.asc`). **srcmorph** (`srcmorph-cli`, the repo the workspace still labels `plugin`) — one fat jar **per `net.ladenthin:llama` classifier** (default CPU + 16 GPU classifiers), kept off Central via `<attach>false</attach>`, built + `gpg`-signed in a `publish.yml` loop. **sb** — ➖ still **none** (single-class `Closeable` library, no runnable entry point). **Gating caveat (all repos):** assets attach only on the `workflow_dispatch` + `publish_to_central=true` path; a plain `v*` tag push attaches nothing (symptom: a release with `assets: []`, as jllama v5.0.6 showed). Original CI-artifact wiring was branch `claude/cool-curie-ym3acr`.
- **Actual Gradle-based *publishing* (the `llama-android` AAR) — java-llama.cpp only.** The `verify-signing-key-gradle` **harness** is now in parity across all 4 (see "In parity" above — kept byte-identical as a "prepared for Gradle" canary, a deliberate uniformity choice). What stays jllama-only is a real **Gradle publish**: the `llama-android` AAR (`publishAllPublicationsToCentralSnapshotsRepository` / `…StagingRepository`, `llama-android/build.gradle.kts`) signs with Gradle's `useInMemoryPgpKeys` (BouncyCastle), where the AAR snapshot signing originally failed with a **null `PGPPrivateKey`**. **Root cause** (2026-07-09, jllama `main` run 29012094281, reproduced in isolation by the harness): Gradle's **2-arg** `useInMemoryPgpKeys` selects the **primary** key, whose secret this BouncyCastle can't unlock, while `gpg`/maven-gpg-plugin auto-select the key's **4096-bit signing subkey** `07D2D767`. **Fix:** the **3-arg** `useInMemoryPgpKeys(keyId, key, passphrase)` when `MAVEN_GPG_KEY_ID` is set, driven by the **`GPG_KEY_ID`** env secret (`= 07D2D767`) added to the `maven-central` environment in all 4 repos — **consumed only by jllama** (the three Maven siblings' gpg agent already picks the subkey, so their shared harness reads it too but their Maven publish never needs it). Confirmed green: `verify-signing-key-gradle` on jllama `main`, so the identical-code AAR publish signs correctly. The harness project (`.github/signing-selftest/`) was also refactored from an opaque base64 blob to committed, readable `.kts` files. PRs: jllama #306 (preflights) / #307 (subkey fix); cross-repo harness sync on branch `claude/android-signing-failure-q7zml9`. Same shape as the Depot/sccache and pocl/OpenCL entries (a real per-repo capability difference — jllama is the only repo with a Gradle-published artifact — not a parity gap to close).

---
Expand Down
Loading