From e047268935e925b85b65f51e8a9c946f42ae6c1d Mon Sep 17 00:00:00 2001 From: Claude Date: Fri, 24 Jul 2026 05:12:43 +0000 Subject: [PATCH 1/6] docs(policy): add cross-repo fat-jar release-asset convention MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Document the shared invariant that a jar-with-dependencies is a GitHub-Release download asset only (never Maven Central), attached with a detached GPG .asc signature, plus the deliberately different per-repo shapes (BAF single jar; jllama multi-backend all- jars; srcmorph per-llama-classifier CLI jars; sb none) and the workflow_dispatch/publish_to_central gating caveat. - New canonical policies/fat-jar-release-assets.md. - List it in the CLAUDE.md layout. - Rewrite the outdated crossrepostatus "per-run CI artifact only β€” BAF + jllama" entry: the fat jars are now signed GitHub-Release assets across BAF, jllama and srcmorph, and point it at the new policy. Co-Authored-By: Claude Opus 4.8 Claude-Session: https://claude.ai/code/session_01TJzCezSnQ8FxpFdeVxYxQY --- CLAUDE.md | 1 + crossrepostatus.md | 2 +- policies/fat-jar-release-assets.md | 71 ++++++++++++++++++++++++++++++ 3 files changed, 73 insertions(+), 1 deletion(-) create mode 100644 policies/fat-jar-release-assets.md diff --git a/CLAUDE.md b/CLAUDE.md index b0cade0..6f287d7 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -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 diff --git a/crossrepostatus.md b/crossrepostatus.md index 3772731..aac1f26 100644 --- a/crossrepostatus.md +++ b/crossrepostatus.md @@ -47,7 +47,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--` 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 `false`, 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). --- diff --git a/policies/fat-jar-release-assets.md b/policies/fat-jar-release-assets.md new file mode 100644 index 0000000..88ca27a --- /dev/null +++ b/policies/fat-jar-release-assets.md @@ -0,0 +1,71 @@ + + +# Fat-jar (jar-with-dependencies) release assets + +Cross-repo convention for the runnable **fat jars** (`*-jar-with-dependencies*.jar`) that the +sibling repos ship. It captures one invariant and the per-repo shapes that implement it, so the +three repos that ship a fat jar stay conceptually aligned even though the *artifacts* differ. + +## The invariant + +> A `jar-with-dependencies` (uber jar) is a **GitHub-Release download asset only** β€” it is +> **never deployed to Maven Central** β€” and it is attached **with a detached, armored GPG +> `.asc` signature**. + +Why never Central: + +- A jar-with-dependencies is **redundant** on Central: consumers depend on the *plain* jar and + let Maven resolve the dependency graph; nobody ``-references the uber jar. +- It is **large** (bundles every runtime dependency), and where it bundles a **native binary** + (`net.ladenthin:llama`, jocl, …) it is also **platform-specific** β€” the wrong shape for a + Central artifact that is meant to be portable coordinates. +- Not shipping it to Central also avoids any redistribution obligation for bundled vendor + binaries. + +Why signed (`.asc`): signature **parity** with the thin jars (which `maven-gpg-plugin` signs at +`deploy`) and with each other. A `.sha256` (integrity) may sit alongside it, but a checksum is not +a signature β€” the `.asc` provides **authenticity**. Keep both where both exist. + +## Where the signing key lives (and why signing happens where it does) + +The GPG signing secret (`GPG_PRIVATE_KEY` / `GPG_PASSPHRASE`) is scoped to the **`maven-central` +GitHub Environment**. That environment has **no approval gate** (the standalone +`verify-signing-key*` jobs use it on every push), so any job may declare +`environment: maven-central` to obtain the key without blocking the pipeline. + +Fat-jar signing therefore happens in whatever job **both** (a) runs on the dispatch-gated publish +path where the key is delivered and (b) has the fat jar on disk β€” see the per-repo shapes below. +Signing is never done in a job that runs on fork PRs (the key is withheld there). + +## The release-asset gating caveat (applies to all repos) + +GitHub-Release assets β€” thin jars **and** fat jars β€” are attached **only** when the publish +workflow runs as a **`workflow_dispatch` with `publish_to_central=true`** (the release-attach jobs +`need` the `publish-{release,snapshot}` jobs, which are `if: … && inputs.publish_to_central`). A +plain `git push` of a `v*` tag does **not** attach any assets. To ship the fat jars on a tag, run +the publish workflow manually with that input set (the same way the thin jars have always been +attached). Symptom of forgetting: a tag release with `assets: []`. + +## Per-repo shapes + +| Repo | Fat jar(s) | Kept off Central by | Built + signed by | +|---|---|---|---| +| **jllama** (`java-llama.cpp`) | Multi-backend **`all--`** jars (default CPU + every GPU backend of that OS/arch in `net/ladenthin/llama////` subdirs, runtime-selected by `LlamaLoader` via the `jllama-backends.txt` manifest) + the default CPU fat jar | The Central `deploy` runs **without** the `assembly` profile; the fat jars are assembled by a separate `package-fatjars` job | `.github/package-fatjars.sh` assembles them; `.github/sign-fatjars.sh` GPG-signs each (`.asc`) in the `github-release-signed` / `github-snapshot` attach jobs (which declare `environment: maven-central` + `checkout`). `.sha256` **and** `.asc`. | +| **srcmorph** (`srcmorph-cli`) | One CLI fat jar **per `net.ladenthin:llama` classifier** (default all-platform CPU + one per GPU classifier: `cuda13-*`, `vulkan-*`, `opencl-*`, `rocm-*`, `sycl-*`, `openvino-*`, `msvc-windows`) named `srcmorph-cli--jar-with-dependencies[-].jar` | `srcmorph-cli/pom.xml` sets `false` on the assembly execution β†’ built into `target/` but never installed/deployed | The `publish-{release,snapshot}` jobs loop over the classifier set (`mvn -pl srcmorph-cli -am -Dllama.classifier= package`), rename per classifier (default built **last** = unsuffixed CPU jar), then `gpg --detach-sign` each. `.asc` only. | +| **BAF** (`BitcoinAddressFinder`) | **Single** fat jar (`jocl` bundles all-platform OpenCL natives in one jar, so no classifier split) | The Central `deploy` runs `-P release` **without** `assembly`; the fat jar is built by a **second** invocation that stops at `verify` (never reaching `deploy`), so `central-publishing`'s deploy-bound publish goal never runs | `mvn -P release,assembly verify` in the `publish-{release,snapshot}` jobs; `maven-gpg-plugin` (bound to `verify`) signs the attached fat jar β†’ `.asc`. | +| **sb** (`streambuffer`) | βž– N/A β€” a pure library with no runnable entry point, so no fat jar is produced or shipped | β€” | β€” | + +## Keep-in-sync notes + +- **jllama / srcmorph classifier lists.** The set of GPU classifiers is defined by + `java-llama.cpp/llama/pom.xml`'s `` entries. jllama's `package-fatjars.sh` + enumerates it from the pom and **fails loud** on any new classifier until it is consciously + ranked/excluded. srcmorph's `publish.yml` hardcodes the classifier array β€” **on a + `net.ladenthin:llama` version bump, re-check that list against the pom** (and confirm every + classifier is actually published on Central for the pinned version). +- **Signature convention.** New fat-jar-shipping surfaces should sign with a detached armored + `.asc` using the `maven-central`-scoped key, in a dispatch-gated job, mirroring + `.github/sign-fatjars.sh`. From 03fa25bde8c40dc10e20655eebc575666b310935 Mon Sep 17 00:00:00 2001 From: Claude Date: Fri, 24 Jul 2026 05:23:33 +0000 Subject: [PATCH 2/6] docs(policy): note the byte-identical shared sign-fatjars.sh jllama and srcmorph now sign their loose fat jars with a byte-identical .github/sign-fatjars.sh (dual MIT OR Apache-2.0); record the sync discipline and that BAF instead signs its attached fat jar via maven-gpg. Co-Authored-By: Claude Opus 4.8 Claude-Session: https://claude.ai/code/session_01TJzCezSnQ8FxpFdeVxYxQY --- policies/fat-jar-release-assets.md | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/policies/fat-jar-release-assets.md b/policies/fat-jar-release-assets.md index 88ca27a..e926f15 100644 --- a/policies/fat-jar-release-assets.md +++ b/policies/fat-jar-release-assets.md @@ -54,7 +54,7 @@ attached). Symptom of forgetting: a tag release with `assets: []`. | Repo | Fat jar(s) | Kept off Central by | Built + signed by | |---|---|---|---| | **jllama** (`java-llama.cpp`) | Multi-backend **`all--`** jars (default CPU + every GPU backend of that OS/arch in `net/ladenthin/llama////` subdirs, runtime-selected by `LlamaLoader` via the `jllama-backends.txt` manifest) + the default CPU fat jar | The Central `deploy` runs **without** the `assembly` profile; the fat jars are assembled by a separate `package-fatjars` job | `.github/package-fatjars.sh` assembles them; `.github/sign-fatjars.sh` GPG-signs each (`.asc`) in the `github-release-signed` / `github-snapshot` attach jobs (which declare `environment: maven-central` + `checkout`). `.sha256` **and** `.asc`. | -| **srcmorph** (`srcmorph-cli`) | One CLI fat jar **per `net.ladenthin:llama` classifier** (default all-platform CPU + one per GPU classifier: `cuda13-*`, `vulkan-*`, `opencl-*`, `rocm-*`, `sycl-*`, `openvino-*`, `msvc-windows`) named `srcmorph-cli--jar-with-dependencies[-].jar` | `srcmorph-cli/pom.xml` sets `false` on the assembly execution β†’ built into `target/` but never installed/deployed | The `publish-{release,snapshot}` jobs loop over the classifier set (`mvn -pl srcmorph-cli -am -Dllama.classifier= package`), rename per classifier (default built **last** = unsuffixed CPU jar), then `gpg --detach-sign` each. `.asc` only. | +| **srcmorph** (`srcmorph-cli`) | One CLI fat jar **per `net.ladenthin:llama` classifier** (default all-platform CPU + one per GPU classifier: `cuda13-*`, `vulkan-*`, `opencl-*`, `rocm-*`, `sycl-*`, `openvino-*`, `msvc-windows`) named `srcmorph-cli--jar-with-dependencies[-].jar` | `srcmorph-cli/pom.xml` sets `false` on the assembly execution β†’ built into `target/` but never installed/deployed | The `publish-{release,snapshot}` jobs loop over the classifier set (`mvn -pl srcmorph-cli -am -Dllama.classifier= package`), rename per classifier (default built **last** = unsuffixed CPU jar), collect them into the asset dir, then sign via `.github/sign-fatjars.sh`. `.asc` only. | | **BAF** (`BitcoinAddressFinder`) | **Single** fat jar (`jocl` bundles all-platform OpenCL natives in one jar, so no classifier split) | The Central `deploy` runs `-P release` **without** `assembly`; the fat jar is built by a **second** invocation that stops at `verify` (never reaching `deploy`), so `central-publishing`'s deploy-bound publish goal never runs | `mvn -P release,assembly verify` in the `publish-{release,snapshot}` jobs; `maven-gpg-plugin` (bound to `verify`) signs the attached fat jar β†’ `.asc`. | | **sb** (`streambuffer`) | βž– N/A β€” a pure library with no runnable entry point, so no fat jar is produced or shipped | β€” | β€” | @@ -66,6 +66,12 @@ attached). Symptom of forgetting: a tag release with `assets: []`. ranked/excluded. srcmorph's `publish.yml` hardcodes the classifier array β€” **on a `net.ladenthin:llama` version bump, re-check that list against the pom** (and confirm every classifier is actually published on Central for the pinned version). +- **Shared signing script (`.github/sign-fatjars.sh`).** jllama and srcmorph sign their loose fat + jars with a **byte-identical** `.github/sign-fatjars.sh` (dual-licensed `MIT OR Apache-2.0`, the + cross-repo-synced-file convention) β€” it imports the key into an ephemeral keyring and produces a + verified detached armored `.asc` for every `*-jar-with-dependencies*.jar` in a directory. **Sync + any edit to both copies** (same discipline as the byte-identical `verify-signing-key` job). + BAF does **not** use it: its single fat jar is an *attached* Maven artifact, so `maven-gpg-plugin` + signs it directly during the `verify` run. - **Signature convention.** New fat-jar-shipping surfaces should sign with a detached armored - `.asc` using the `maven-central`-scoped key, in a dispatch-gated job, mirroring - `.github/sign-fatjars.sh`. + `.asc` using the `maven-central`-scoped key, in a dispatch-gated job, reusing `sign-fatjars.sh`. From e7b85416eeeb6f89658ac4873511798dc4c1b5df Mon Sep 17 00:00:00 2001 From: Claude Date: Fri, 24 Jul 2026 05:28:32 +0000 Subject: [PATCH 3/6] docs(policy): record sign-fatjars.sh SHA-256 for a workspace drift check Add the canonical checksum of the byte-identical shared script plus a one-liner to verify both repo copies from the workspace repo, and require updating the hash on any intentional edit. Co-Authored-By: Claude Opus 4.8 Claude-Session: https://claude.ai/code/session_01TJzCezSnQ8FxpFdeVxYxQY --- policies/fat-jar-release-assets.md | 26 +++++++++++++++++++++++++- 1 file changed, 25 insertions(+), 1 deletion(-) diff --git a/policies/fat-jar-release-assets.md b/policies/fat-jar-release-assets.md index e926f15..bf0d9da 100644 --- a/policies/fat-jar-release-assets.md +++ b/policies/fat-jar-release-assets.md @@ -70,8 +70,32 @@ attached). Symptom of forgetting: a tag release with `assets: []`. jars with a **byte-identical** `.github/sign-fatjars.sh` (dual-licensed `MIT OR Apache-2.0`, the cross-repo-synced-file convention) β€” it imports the key into an ephemeral keyring and produces a verified detached armored `.asc` for every `*-jar-with-dependencies*.jar` in a directory. **Sync - any edit to both copies** (same discipline as the byte-identical `verify-signing-key` job). + any edit to both copies, and update the recorded checksum below** (same discipline as the + byte-identical `verify-signing-key` job). BAF does **not** use it: its single fat jar is an *attached* Maven artifact, so `maven-gpg-plugin` signs it directly during the `verify` run. - **Signature convention.** New fat-jar-shipping surfaces should sign with a detached armored `.asc` using the `maven-central`-scoped key, in a dispatch-gated job, reusing `sign-fatjars.sh`. + +## Drift check β€” `sign-fatjars.sh` checksum + +The shared script must be **byte-identical** in both repos. Its canonical SHA-256 is recorded here +so the two copies can be verified from the workspace repo without diffing across trees: + +``` +sign-fatjars.sh SHA-256: 3a240faac46c35d3ac4a11dc2969648e2134906b90a79b990ce2b713c7a96b36 + ../java-llama.cpp/.github/sign-fatjars.sh + ../srcmorph/.github/sign-fatjars.sh +``` + +Verify (run from the `workspace` repo root β€” the siblings are checked out next to it): + +```bash +EXPECT=3a240faac46c35d3ac4a11dc2969648e2134906b90a79b990ce2b713c7a96b36 +sha256sum ../java-llama.cpp/.github/sign-fatjars.sh ../srcmorph/.github/sign-fatjars.sh +# both hashes must equal $EXPECT. A mismatch means either the copies have drifted +# (re-sync them) or the script was intentionally edited (update BOTH copies AND this hash). +``` + +On any intentional edit to `sign-fatjars.sh`, update both copies **and** the hash above in the same +change set. From d2c716b0d7286b1b22139f290abbe0e7386376be Mon Sep 17 00:00:00 2001 From: Claude Date: Fri, 24 Jul 2026 05:36:52 +0000 Subject: [PATCH 4/6] docs: consolidated cross-repo byte-identical-file checksum drift check Record the signing-selftest .kts SHA-256s (now literally byte-identical in all 4 after the MIT OR Apache-2.0 unification) plus sign-fatjars.sh in one drift-check table in crossrepostatus.md, with a single verify command runnable from the workspace repo. Correct the verify-signing-key-gradle row's "byte-identical" claim (the SPDX header had drifted until now). Point the fat-jar policy at this one table instead of duplicating the sign-fatjars hash. Co-Authored-By: Claude Opus 4.8 Claude-Session: https://claude.ai/code/session_01TJzCezSnQ8FxpFdeVxYxQY --- crossrepostatus.md | 22 +++++++++++++++++++++- policies/fat-jar-release-assets.md | 24 +++++------------------- 2 files changed, 26 insertions(+), 20 deletions(-) diff --git a/crossrepostatus.md b/crossrepostatus.md index aac1f26..402ba66 100644 --- a/crossrepostatus.md +++ b/crossrepostatus.md @@ -36,7 +36,27 @@ 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)) | + +Verify from the `workspace` repo root (siblings checked out alongside): + +```bash +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 +# 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) diff --git a/policies/fat-jar-release-assets.md b/policies/fat-jar-release-assets.md index bf0d9da..07e88c9 100644 --- a/policies/fat-jar-release-assets.md +++ b/policies/fat-jar-release-assets.md @@ -79,23 +79,9 @@ attached). Symptom of forgetting: a tag release with `assets: []`. ## Drift check β€” `sign-fatjars.sh` checksum -The shared script must be **byte-identical** in both repos. Its canonical SHA-256 is recorded here -so the two copies can be verified from the workspace repo without diffing across trees: - -``` -sign-fatjars.sh SHA-256: 3a240faac46c35d3ac4a11dc2969648e2134906b90a79b990ce2b713c7a96b36 - ../java-llama.cpp/.github/sign-fatjars.sh - ../srcmorph/.github/sign-fatjars.sh -``` - -Verify (run from the `workspace` repo root β€” the siblings are checked out next to it): - -```bash -EXPECT=3a240faac46c35d3ac4a11dc2969648e2134906b90a79b990ce2b713c7a96b36 -sha256sum ../java-llama.cpp/.github/sign-fatjars.sh ../srcmorph/.github/sign-fatjars.sh -# both hashes must equal $EXPECT. A mismatch means either the copies have drifted -# (re-sync them) or the script was intentionally edited (update BOTH copies AND this hash). -``` - -On any intentional edit to `sign-fatjars.sh`, update both copies **and** the hash above in the same +The shared script must be **byte-identical** in both repos (jllama + srcmorph). Its canonical +SHA-256 and a one-line verify command live in the single consolidated drift-check table β€” +**"Cross-repo byte-identical files β€” checksum drift check"** in +[`../crossrepostatus.md`](../crossrepostatus.md) (alongside the `signing-selftest` `.kts` files). +On any intentional edit to `sign-fatjars.sh`, update both copies **and** that table in the same change set. From 1a556f6c7cf6147fa0b8570dd901f74f33a12b5c Mon Sep 17 00:00:00 2001 From: Claude Date: Fri, 24 Jul 2026 05:47:16 +0000 Subject: [PATCH 5/6] docs: track lombok.config in the cross-repo checksum drift check lombok.config is now byte-identical across the 3 Lombok repos (jllama/BAF/srcmorph) after syncing them to the canonical policy block verbatim. Add its SHA-256 as the 4th row of the drift-check table and update lombok-config.md's Sync check to point at it. Co-Authored-By: Claude Opus 4.8 Claude-Session: https://claude.ai/code/session_01TJzCezSnQ8FxpFdeVxYxQY --- crossrepostatus.md | 4 +++- policies/lombok-config.md | 15 ++++++++++----- 2 files changed, 13 insertions(+), 6 deletions(-) diff --git a/crossrepostatus.md b/crossrepostatus.md index 402ba66..464337b 100644 --- a/crossrepostatus.md +++ b/crossrepostatus.md @@ -47,13 +47,15 @@ Files kept **byte-identical across repos** (sync any edit to every copy AND the | `.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) | Verify from the `workspace` repo root (siblings checked out alongside): ```bash 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,srcmorph}/.github/sign-fatjars.sh \ + ../java-llama.cpp/llama/lombok.config ../{BitcoinAddressFinder,srcmorph}/lombok.config # 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). ``` diff --git a/policies/lombok-config.md b/policies/lombok-config.md index 1af259b..ca4fca3 100644 --- a/policies/lombok-config.md +++ b/policies/lombok-config.md @@ -120,8 +120,13 @@ supplement rather than weakening the workspace policy. ## Sync check -The file lives in each sibling repo's root and the canonical content -above is the source of truth. Manual sync is the only mechanism -today β€” when the workspace policy changes, update each sibling repo's -`lombok.config` in the same series of commits and bump the corresponding -row in `../crossrepostatus.md`. +The file lives in each sibling repo's root (jllama: `llama/lombok.config`) and the +canonical content above is the source of truth. Since 2026-07-24 all three copies are +**byte-identical** to this block β€” the repo-specific rationale that used to drift in +per-file comments now lives only here (that is why the file can be identical). The SHA-256 +is recorded in the **"Cross-repo byte-identical files β€” checksum drift check"** table in +[`../crossrepostatus.md`](../crossrepostatus.md), verifiable from the workspace repo. + +Manual sync is the only mechanism today β€” when this canonical content changes, update every +sibling repo's `lombok.config` **and** the checksum row in `../crossrepostatus.md` in the same +series of commits. From cdc6e2533df4ba5a98f44f61e0c5b76413894af7 Mon Sep 17 00:00:00 2001 From: Claude Date: Fri, 24 Jul 2026 05:49:29 +0000 Subject: [PATCH 6/6] docs: track the 3 shared GitHub templates in the checksum drift check MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add bug_report.md / feature_request.md / PULL_REQUEST_TEMPLATE.md (already byte-identical across all 4 repos) as rows 5-7 of the cross-repo checksum drift-check table, and extend the verify command. Fix the verify snippet to list repo names explicitly inside each brace group (bash brace expansion runs before variable expansion, so a $var inside {…} would not expand). Verified: all 25 files across the 7 tracked paths collapse to one hash per file. Co-Authored-By: Claude Opus 4.8 Claude-Session: https://claude.ai/code/session_01TJzCezSnQ8FxpFdeVxYxQY --- crossrepostatus.md | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/crossrepostatus.md b/crossrepostatus.md index 464337b..cd3cf8e 100644 --- a/crossrepostatus.md +++ b/crossrepostatus.md @@ -48,14 +48,22 @@ Files kept **byte-identical across repos** (sync any edit to every copy AND the | `.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/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). ```