diff --git a/crossrepostatus.md b/crossrepostatus.md index 1d119ce..35421d9 100644 --- a/crossrepostatus.md +++ b/crossrepostatus.md @@ -82,7 +82,7 @@ sha256sum ../{java-llama.cpp,BitcoinAddressFinder,srcmorph,streambuffer}/.github - **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) = 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`. +- **`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 (LWJGL's per-platform `natives-*` classifiers all coexist on one classpath), 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/guides/src/CODE_WRITING_GUIDE-21.md b/guides/src/CODE_WRITING_GUIDE-21.md index 6b3139a..1bb0f81 100644 --- a/guides/src/CODE_WRITING_GUIDE-21.md +++ b/guides/src/CODE_WRITING_GUIDE-21.md @@ -229,4 +229,5 @@ until there's a real use case in the BAF codebase: - **Structured concurrency (`StructuredTaskScope`)** — preview API as of JDK 21; revisit when GA. - **Foreign Function & Memory API** — relevant for any future direct - native-memory work, but BAF currently uses JOCL for GPU off-heap. + native-memory work, but BAF currently uses LWJGL (its own `MemoryStack` / + `MemoryUtil` off-heap allocators) for GPU memory. diff --git a/llama.cpp/contributions.md b/llama.cpp/contributions.md new file mode 100644 index 0000000..4a18466 --- /dev/null +++ b/llama.cpp/contributions.md @@ -0,0 +1,83 @@ +# Contributions and open items + +Upstream: [ggml-org/llama.cpp](https://github.com/ggml-org/llama.cpp). Downstream carrier is +jllama, which applies `llama/patches/*.patch` to the FetchContent-ed tree via +`cmake/apply-llama-patches.cmake`. + +**Nothing submitted yet.** The patches below are maintained downstream; this file records +which of them are upstream-worthy and what is still missing before they can be offered. + +## Ground rules for this repo + +llama.cpp's `AGENTS.md` forbids agents from writing pull-request descriptions, issue bodies +and review responses - explicitly "non-overridable under any circumstances", with a +contributor ban as the stated consequence. The prose has to be written by hand. An agent may +gather facts, verify claims against the code and run experiments, and that is where its +value lies here. + +`AGENTS.md` also asks for an **issue first** on anything sizeable, rather than an +unannounced pull request. + +## Patch status + +| Patch | Assessment | +|---|---| +| `0001` win32 arg-parse embed guard | **Issue [#26416](https://github.com/ggml-org/llama.cpp/issues/26416) opened 2026-08-01**, awaiting a maintainer's answer on which direction they want | +| `0002` preserve caller load-progress callback | **Not upstream-worthy as it stands.** Stays downstream | +| `0009` guard `addchdir_np` on old glibc | **Resolved elsewhere.** Went to sheredom instead; see below | + +### `0001` - `common_params_parse` discards the caller's argv on Windows + +`common/arg.cpp:1203-1209` replaces the caller's `argv` with one rebuilt from +`GetCommandLineW()` whenever the element counts match. Contents are never compared, so a +caller that builds its own argv can silently get the process command line parsed. + +Reproduced on unmodified `master` @ `ddd4ec142`, MSVC 19.44, no JNI involved: llama.cpp's +own `test-arg-parser` passes when started with one command line token and fails at +`tests/test-arg-parser.cpp:96` when started with three, because the negative test's +three-element argv is swapped for the process command line. + +Full write-up, with the exact output and the build recipe: +`../java-llama.cpp/docs/upstream-investigation-win32-argv-substitution.md`. + +Two directions to offer, both described there: split the entry point +(`common_params_parse` parses what it is given, a new `common_params_parse_main` does the +recovery for the standalone tools - clean but 37 files), or tighten the condition to compare +contents (much smaller, but the CRT-argv-versus-ANSI-round-trip comparison may not hold in +every codepage, which would silently disable the #24779 fix it protects). + +**Filed as [#26416](https://github.com/ggml-org/llama.cpp/issues/26416)** on 2026-08-01, +labelled `bug-unconfirmed`, first bad commit `508a475` (the #24779 merge - verified: it is +where the substitution entered; the count guard only narrowed it later). The issue reports +the defect, links the analysis, and asks how they want to proceed rather than opening a PR +unannounced. Next step is their answer, not more code. + +### `0002` - server clobbers a caller-provided `load_progress_callback` + +`tools/server/server-context.cpp` installs the server's own progress reporter +unconditionally. Guarding it with `if (params_base.load_progress_callback == nullptr)` is an +eight-line change and provably inert for the standalone server, where the field is always +null. + +Left downstream because there is **no in-tree beneficiary**: the only caller that sets the +field beforehand is an embedder, and embeddability itself is a downstream patch (`0006`). +`AGENTS.md` is explicit that a working, in-scope change is not sufficient - a maintainer has +to want it. The honest answer to "who sets this?" is "my JNI binding", which is not an +upstream use case. + +### `0009` - resolved through sheredom, not llama.cpp + +The patch modified `vendor/sheredom/subprocess.h`. That file is **re-downloaded verbatim** +by `scripts/sync_vendor.py` from a pinned upstream commit, so a local patch there would have +been silently reverted at the next vendor sync. The fix went to +[sheredom/subprocess.h#104](https://github.com/sheredom/subprocess.h/pull/104) instead; see +`../subprocess.h/contributions.md`. + +What remains for llama.cpp is a one-line pin bump in `scripts/sync_vendor.py:24` plus the +re-synced file, once sheredom merges. jllama's patch `0009` is byte-identical to that PR's +head, so `apply-llama-patches.cmake` will report "already applied" and skip it from then on, +rather than failing the build. + +**General lesson:** before patching anything under `vendor/`, check whether a sync script +owns the file. A downstream patch is fine there as a stopgap, but the fix belongs upstream +of the vendor pin, not in the vendored copy. diff --git a/policies/fat-jar-release-assets.md b/policies/fat-jar-release-assets.md index 07e88c9..1fb6aea 100644 --- a/policies/fat-jar-release-assets.md +++ b/policies/fat-jar-release-assets.md @@ -20,7 +20,7 @@ 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 + (`net.ladenthin:llama`, LWJGL natives, …) 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. @@ -55,7 +55,7 @@ attached). Symptom of forgetting: a tag release with `assets: []`. |---|---|---|---| | **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), 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`. | +| **BAF** (`BitcoinAddressFinder`) | **Single** fat jar (LWJGL ships one `natives-*` classifier jar per platform, but they may all sit on one classpath — LWJGL picks the match at runtime — so there is still 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 diff --git a/subprocess.h/contributions.md b/subprocess.h/contributions.md new file mode 100644 index 0000000..3d29b1f --- /dev/null +++ b/subprocess.h/contributions.md @@ -0,0 +1,70 @@ +# Contributions and open items + +Upstream: [sheredom/subprocess.h](https://github.com/sheredom/subprocess.h), vendored by +llama.cpp as `vendor/sheredom/subprocess.h` and therefore reaching jllama through it. + +## Submitted 2026-08-01, awaiting review + +Base for all three is `8671cee` (`Fix strict builds with current toolchains (#99)`). + +| PR | Branch | Content | +|---|---|---| +| [#104](https://github.com/sheredom/subprocess.h/pull/104) | `fix/addchdir-np-old-glibc` | overridable `SUBPROCESS_HAVE_CWD` probe; `ENOSYS` where `posix_spawn_file_actions_addchdir_np` is missing (glibc < 2.29). 2 commits, head `620ce44`, 3 files, +25 | +| [#105](https://github.com/sheredom/subprocess.h/pull/105) | `fix/test-cxx20-flag-fallback` | `check_cxx_compiler_flag` instead of the CMake feature, falling back to `-std=c++2a`. 1 commit `3370251`, 1 file | +| [#106](https://github.com/sheredom/subprocess.h/pull/106) | `fix/spawn-exec-errors-old-glibc` | `access(X_OK)` pre-check where `posix_spawn` cannot report a failed exec (glibc < 2.24). 1 commit `71c3516`, 1 file, **depends on #104** | + +#105 is deliberately based on `main` rather than stacked on #104: it only touches +`test/CMakeLists.txt` and was demonstrated on Ubuntu 20.04 (glibc 2.31 + GCC 8), where the +library builds without #104. #106 could not be separated - every glibc old enough to show +its symptom is also too old to build without #104. + +**No CI has run on any of them.** Fork PRs from a first-time contributor need the +maintainer to approve the workflow. Not a signal about the changes: the repo's last commit +is 2026-07-20, and #101 (2026-07-25) and #102 (2026-07-28) are likewise untouched. + +## Verification carried out + +Container matrix per change, sanitizer probes pinned off for reproducibility, Windows built +natively with MSVC 19.44: + +glibc 2.17 / 2.28 / 2.29 / 2.31 / 2.39, musl 1.1.22 and 1.2, GCC 8/9/10/13, clang 18 and 21, +tcc 0.9.27, mingw-w64, MSVC. macOS was not reachable locally; upstream CI covers it. + +The single most useful control was **AlmaLinux 8**: glibc 2.28 is the only tested version +that sits *between* the two boundaries (< 2.29, so no `addchdir_np`; >= 2.24, so exec +failures are reported). It is the one environment where the two probes must disagree, and +therefore the only one where a mixed-up version number would show. + +## Known limitations, deliberately left in + +| Item | Why left alone | +|---|---| +| #106 covers `posix_spawn` only, not `posix_spawnp` | closing it means reimplementing the `PATH` search, including empty entries meaning "current directory" and the `ENOEXEC` shell fallback. Measured on glibc 2.17: explicit path returns `-4`, `PATH` search still returns `0` | +| #106 has a TOCTOU window between `access` and `posix_spawn` | on a platform whose `posix_spawn` cannot report the failure at all, a best-effort check beats none. Gated to those platforms only, so nothing else inherits the race | +| musl older than 1.1.24 still fails to link (#104) | musl exposes no version macro, so it cannot be detected. This is what the override in #104 is for; `-DSUBPROCESS_HAVE_CWD=0` builds cleanly there | + +The clean fix for the first two is to drop `posix_spawn` on the affected platforms and use +`fork` + `exec` + a `O_CLOEXEC` pipe carrying `errno` - which is what glibc 2.24 itself +does, closes the race *and* the `posix_spawnp` gap, and would also report failures `access` +cannot see (missing interpreter, `ENOEXEC`). It replaces the library's central mechanism on +a whole platform class and is roughly 60-100 lines of the most delicate code in the file. +Not offered unsolicited; the maintainer's call. + +## Cautionary notes worth carrying forward + +- **`__GLIBC_PREREQ` must not appear in a flat `#if ... && ...`.** The preprocessor replaces + unknown identifiers with `0` before evaluating, so `#if defined(__GLIBC__) && !__GLIBC_PREREQ(2, 29)` + becomes `0 (2, 29)` on macOS, musl and Windows - a syntax error. `&&` does not help; this + fails at parse time, not evaluation time. Nest the checks. +- **An undefined macro is `0` inside `#if`.** A capability macro defined only under + `#if !defined(_WIN32)` silently evaluates to "not available" on Windows. Define it on + every platform, or the guard quietly removes code where it should not. +- **A workaround must be gated to the platforms that need it.** `access()` uses the *real* + UID while `exec` uses the *effective* one, and it cannot see `ENOEXEC`. Unconditional, it + would change behaviour on healthy platforms; gated, the claim "unchanged everywhere else" + stays true and reviewable. +- **The project's sanitizer detection is not deterministic under WSL2.** `test/CMakeLists.txt` + probes each sanitizer with `try_run` at configure time, but the real test runs later in a + different process with a fresh ASLR draw. Whether `subprocess_thread_test` exists at all + varied between identical runs. Pin the probes + (`-DSUBPROCESS_SAN_{thread,memory,address,undefined}_RUNS=FALSE`) before comparing numbers.