Skip to content

TODO cleanup, start-up logging, extraction reuse across JVM starts, WDAC note, GraalVM evaluation - #503

Merged
bernardladenthin merged 5 commits into
mainfrom
claude/hopeful-pascal-9jlbqb
Oct 10, 2026
Merged

bernardladenthin merged 5 commits into
mainfrom
claude/hopeful-pascal-9jlbqb

Conversation

@bernardladenthin

@bernardladenthin bernardladenthin commented Oct 10, 2026 •

Copy link
Copy Markdown
Owner

Summary

  • TODO.md: finished and stale entries removed (Windows variants build, inference smokes, jbang example, macOS gate "not yet green", dead-flags entry, acquire/release guard tests, OSInfo alias test, Java8CompatibilityHelper; the SonarCloud and License-Compliance sections of the long-merged PRs Add Windows Ninja Multi-Config build with sccache caching #248/Feature wave: Android AAR + Kotlin facade, server attach/router modes, LangChain4j streaming, GGUF tooling (llama.cpp b9878) #298; the "Android example app" entry -- android-llmservice/ is that app, built and UI-tested on the emulator in CI). One upstream section with one policy: nothing is filed upstream before the release, with the inventory of the eleven patches. One section "Needs a machine the sessions do not have" (CUDA-as-module re-measurement, Windows reuse check, on-device Android, AVX-512/AMX, Kolibri-1 real model). The unsigned-DLL item records the signing answer (the OpenPGP release key cannot produce Authenticode signatures; a code-signing certificate is the owner's decision).
  • Loader: extracted natives are reused across JVM starts. cleanup() spares the per-build extraction directories of the natives on this classpath, nothing is deleteOnExit any more, extractFile's byte-for-byte reuse branch does the rest; a directory of a build not on the classpath is swept once it is 10 minutes old (a start touches its directory first). Measured: first JVM extracts 18 files, second extracts 0 and loads. LlamaLoaderTest covers the decision (isStaleExtraction, extractionDirectoriesOfThisClasspath).
  • Start-up logging: JNI_OnLoad logs how many backends ggml registered and which devices they report; LlamaLoader prints how many module files are in place. A module ggml cannot load fails silently, so a GPU module with a missing runtime was indistinguishable from one that was never there.
  • README: troubleshooting entry for WDAC/AppLocker-managed Windows clients (net.ladenthin.llama.lib.path to an allow-listed directory is the way out); the tmpdir row describes the persistent per-build directory.
  • docs/graalvm-native-image-evaluation.md: the Native Image evaluation (closed JNI surface, sidecar vs. embedded natives, cost, the one measurement that decides it); the TODO section now points at it.

Test plan

  • C++ suite 607/607 (static build); variants build compiles the new JNI_OnLoad block (it is GGML_BACKEND_DL-only)
  • LlamaLoaderTest, BackendLoadTest, NativeLibraryLoadSmokeTest, NativeLibraryPermissionSetterTest: 62/62 against the Linux variants library
  • Reuse measured: NativeLibraryLoadSmokeTest twice against a fresh -Dnet.ladenthin.llama.tmpdir: run 1 [jllama] extracted x18, run 2 x0, both load; 18 files, no .tmp left
  • spotless:apply, spotbugs:check, javadoc:jar, the five Python checks, buildcheck unit tests, reuse lint
  • CI is green on this branch -- not run on purpose (one full run at the end, as agreed); the Windows side of the reuse (locked DLLs) is for the local agent
  • Docs / CHANGELOG updated

Related issues / PRs

Follows #502; refs #500, #501 (the local agent's report).

Checklist

  • I have read CONTRIBUTING.md and CODE_OF_CONDUCT.md
  • My commits follow Conventional Commits
  • No security-sensitive changes

🤖 Generated with Claude Code

https://claude.ai/code/session_01AytmJF9faEiQEVt6eetQS2

…olicy

Finished and therefore deleted (completed work is not recorded here): the
Windows x86-64 variants build (shipped in #502), "smoke tests must run
inference through the loaded module" (the fat-jar smokes and Java jobs do),
the jbang one-file example (examples/jbang/Chat.java), the macOS packaged-
artifact gate's "not yet observed green" (green since run 37985786772), the
struck-through dead-flags entry, the acquire/release context-guard unit tests
(test_jni_helpers.cpp has them), the OSInfo alias test (OSInfoTest has it)
and the Java8CompatibilityHelper entry (the class is gone). The SonarCloud and
License-Compliance sections described PRs #248/#298, both merged in June/July;
main's HEAD carries a green SonarCloud check and no license-scanner status,
so they are replaced by one line: a dependency-license gate is the owner's
decision.

Upstream: one section, one policy -- nothing is filed before the release is
out, not even drafts -- with the inventory of the eleven patches (nine
submittable as they are, 0003 permanent, 0016 a temporary carry) and the two
upstream-only defects worth an issue on that day; the scattered "file X
upstream" bullets under RPC and the logging sink go. The two loader items
(reuse the extraction across runs; per-process isolation) are merged, since
both need the same Windows locked-file co-design. The unsigned-DLL item records
the answer: the OpenPGP release key cannot sign them (Authenticode needs an
X.509 code-signing certificate), the README names the way out, the certificate
is the owner's decision.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01AytmJF9faEiQEVt6eetQS2
…ules; README note for WDAC/AppLocker clients

A CPU or GPU module ggml cannot load fails silently: ggml logs it at
GGML_LOG_DEBUG only and on Windows SEM_FAILCRITICALERRORS suppresses the
dialog, so a GPU module with a missing runtime was indistinguishable from one
that was never there (TODO "Windows loader", from the Windows measurement).
JNI_OnLoad now logs, after ggml_backend_load_all_from_path, how many backends
ggml registered from the directory and which devices they report (an INFO line
of common_log, so it reaches LlamaModel.setLogger), and LlamaLoader prints how
many module files it extracted next to the library and where. Compiled in the
variants configuration only (the static library has no modules to load); the
loader line was seen on the Linux variants build ("17 file(s) extracted").

README: a client whose WDAC/AppLocker policy blocks unsigned DLLs or DLLs under
%TEMP% refuses the load; the way out is net.ladenthin.llama.lib.path pointing
at an allow-listed directory holding the natives jar's directory contents. The
note also says why the release-signing key is no help: it is an OpenPGP key,
Windows checks Authenticode. CLAUDE.md keeps the Windows loader facts that
left TODO (UTF-8 path for ggml_backend_load_all_from_path, the silent failure).

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01AytmJF9faEiQEVt6eetQS2
…le builds

LlamaLoader deleted every extracted file at exit and every jllama* entry of
the temp directory at the next start, so each JVM copied the whole backend
directory again -- 18 files for a CPU-variants build, ~1 s with an on-access
scanner on Windows (TODO "Loader: reuse the extraction across runs"), and the
same for every surefire fork. extractFile already had the reuse branch
(compare with the jar byte for byte, write only what differs, atomically);
cleanup() just never let it run.

Now cleanup(Set keep) spares the per-build extraction directories of the
natives on this classpath (extractionDirectoriesOfThisClasspath, computed
before anything is extracted, the forced backend included), nothing is
registered for deleteOnExit, and a start touches its directory's mtime before
extracting. It still removes the flat files of the pre-5.2.0 layout at once,
and the directory of a build that is not on the classpath once it is older
than STALE_EXTRACTION_AGE (10 min), so a JVM still starting from a directory
is never swept; deletes stay best-effort (a DLL a running JVM holds is locked
on Windows and stays, the unloaded modules may go and the next start of that
build re-extracts exactly those). Per-process isolation, the older half of
the TODO item, is rejected for the same reason: the shared per-build
directory with content-checked reuse is the cache.

Measured with NativeLibraryLoadSmokeTest against a fresh tmpdir: the first
JVM extracts 18 files, the second extracts 0 and loads; no .tmp left behind.
LlamaLoaderTest covers isStaleExtraction (own directory kept at any age,
another build's only once old, flat files at once, unrelated files never)
and extractionDirectoriesOfThisClasspath over the backendtest fixtures.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01AytmJF9faEiQEVt6eetQS2
…chine

The "Android example app" entry asked for a sample app under examples/ --
android-llmservice/ is that app, built and UI-tested on the emulator in CI,
so the entry goes. What no session has is hardware: a real arm64 device for
the Adreno/OpenCL flavor, AVX-512/AMX CPUs, a machine with more than 64 GB
for Kolibri-1, and the local agent's GPU for the CUDA-as-module re-measurement
and the Windows extraction-reuse check. Those are now one section, "Needs a
machine the sessions do not have", instead of being scattered.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01AytmJF9faEiQEVt6eetQS2
…sumer asks or a measurement says otherwise

docs/graalvm-native-image-evaluation.md replaces the TODO's investigation
plan with the evaluation itself, measured against the source: the JNI surface
is closed and small (16 FindClass, 16 GetMethodID, 1 GetFieldID, 7
GetStaticFieldID in jllama.cpp, two lookups each in native_server.cpp and
rpc_bridge.cpp -- a jni-config.json the tracing agent writes), the natives
either go into the image as resources or next to the executable through the
existing net.ladenthin.llama.lib.path (no loader change), Jackson is tree
model only, SLF4J's ServiceLoader and jdk.httpserver are supported, and the
thread attaches across JNI are part of the invocation interface. The cost is a
second build matrix and metadata that rots silently without an
image-build-plus-smoke job gating publish. The decision rests on one number
nobody has: the share of JVM start-up in a cold one-token run, which needs a
model and therefore the local agent; the measurement recipe is in the
document. Recommendation: do nothing until that number or a consumer request
exists, then sidecar natives + the NativeServer main + the tracing agent + the
CI smoke from day one.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01AytmJF9faEiQEVt6eetQS2
@bernardladenthin bernardladenthin changed the title TODO cleanup, start-up logging of backends/devices/modules, README note for WDAC/AppLocker clients TODO cleanup, start-up logging, extraction reuse across JVM starts, WDAC note, GraalVM evaluation Oct 10, 2026
@sonarqubecloud

Copy link
Copy Markdown

@bernardladenthin
bernardladenthin merged commit 217c1f7 into main Oct 10, 2026
17 of 82 checks passed
@bernardladenthin
bernardladenthin deleted the claude/hopeful-pascal-9jlbqb branch October 10, 2026 13:26

This branch had an error being deployed

1 failed deployment
maven-central — d755a4e7 Deployed Oct 10, 2026 by bernardladenthin via Verify GPG signing key (no secrets printed) #1160
startgate — d755a4e7 Deployed Oct 10, 2026 by bernardladenthin via Start gate (abort window) #1160
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants