ADR-0203 stage 5: transparent on-disk compilation cache (--cache, closes D-508) - #141
Merged
Conversation
…closes D-508) `zwasm run --cache[=DIR] x.wasm` reuses the .cwasm artifact a previous run produced. Users keep deploying plain `.wasm` (the wasmtime Config::cache / wazero NewCompilationCacheWithDir model); the runtime keys artifacts by content hash in a versioned directory: <root>/zwasm-<version>-<arch>-<os>-<bounds>/<sha256-hex>.cwasm - src/cli/cache.zig: SHA-256 content key; versioned subdir (every codegen-affecting knob lives in the dir name, so stale entries are silently unreachable — no config hash needed); temp + atomic-rename store; ANY cache-side error degrades to miss/no-store (EXEMPT-FALLBACK per ADR-0203 D5 — the cache can never make `run` fail); --cache-clear deletes this build's subdir only. - main.zig: --cache[=DIR] / --cache-clear flags; default root = platform user-cache dir (HOME/Library/Caches, XDG_CACHE_HOME|~/.cache, LOCALAPPDATA) from the environ map; a HIT swaps the artifact bytes in and the existing CWAS branch runs it through the full-fidelity load path — cache-hit == cache-miss by construction. - Components + explicit .cwasm inputs bypass the cache. Measured (tinygo_json, ReleaseSafe, hyperfine): 9.2 ms → 4.1 ms cold start (2.2x; user CPU 7.2 → 2.2 ms — parse/validate/codegen skipped). Unit tests: subdir naming, stable content key, miss→store→hit byte-identity, corrupt-entry loud rejection at the load gate, --cache-clear idempotency + re-populate. zig build test green.
…5, DA critique) The stage-5 cache implemented the D5 silent-miss discipline for read errors only; the DA critique reproduced four ways a cache defect failed the run (corrupt entry x3 shapes, ZWASM_DEBUG instrumentation) plus an --engine interp override. All degrade now: - HIT gate: parseHeader (magic/version/arch) + section-bounds check before trusting an entry; a bad entry is deleted (self-heal) and recompiled — a corrupt cache can never brick the run. - Compile/produce refusal inside the cache path = cache BYPASS (fall through to normal dispatch), restoring `.auto`'s interp fallback and ZWASM_DEBUG runnability instead of exit(1). - Explicit `--engine interp` bypasses the cache entirely (D-496: the flag must keep forcing interp; the artifact is JIT code). - `--cache-clear` alone no longer implies caching this run. - store(): unique random temp suffix (a shared temp name let one writer's atomic rename publish another's partial bytes); artifacts above the 256 MiB read cap are not stored (never hittable). - Module doc states the trust model (cache-dir write access = native code execution as the user; wasmtime/wazero posture). - test-aot-diff: cache lanes (miss + hit vs fresh, 63/63) + interp- bypass probe + stored-entry check — the ADR-0203 D6 stage-5 ratchet. Claude-Session: https://claude.ai/code/session_01F2PGseEjGFR1uqid4FaXsm
chaploud
added a commit
that referenced
this pull request
Jul 9, 2026
…, debt reconcile (#142) * chore(campaign): ADR-0203 stage V retrospective — docs, bench record, lesson, debt reconcile Campaign close-out (stages 1-5 = PRs #137-#141): - ADR-0203 Status -> Implemented (aot-diff 63/63 incl. cache lanes; D-508/515(1)/516/517/518/519 closed). - docs/reference/cli.md: --cache/--cache-clear rows; stale claims fixed (.cwasm now sandboxed + full-runtime, artifact no longer "v0.1", cache/engine interaction); README CLI block gains --cache lines. - Explicit `.cwasm` + `--engine interp` is now a loud exit-2 refusal (contradictory request: the artifact IS JIT code; D-496 spirit) with an aot-diff E2E probe. - bench/results/history.yaml: per-merge record on the feature branch (campaign parity: worst +1%, median -8% vs pre-campaign entry). - Lesson: failure-path-tests-certified-the-defect (DA critique found the stage-5 unit test asserting the D5 violation it should have caught). - handover.md rewritten to the campaign-closing state (118 lines). Remaining campaign residual: D-515(2) spec-assert corpus under elision (harness memory provisioning), tracked in debt.yaml. Claude-Session: https://claude.ai/code/session_01F2PGseEjGFR1uqid4FaXsm * chore(release): bump version to 2.2.0 + CHANGELOG (USER-DIRECTED release, ADR-0156) Release directed by the user 2026-07-09 ("campaign Vが終わったら、タグを 切ってリリースしてください"): after this PR merges, the v2.2.0 tag on the merge commit triggers release.yml (auto-build + Release + Latest). Claude-Session: https://claude.ai/code/session_01F2PGseEjGFR1uqid4FaXsm
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Final implementation stage of the AOT-full-fidelity campaign (ADR-0203 D5/D6 stage 5):
zwasm run --cache[=DIR] x.wasmkeys the module by SHA-256 content hash and transparently reuses the.cwasmartifact a previous run produced — deploy artifact stays.wasm, the cache is invisible plumbing (the wasmtime/wazero model).src/cli/cache.zig(new): versioned cache dir<root>/zwasm-<version>-<arch>-<os>-<bounds>/<sha256>.cwasm; hit = full-fidelity CWAS load (cache-hit == cache-miss by construction, ADR-0203 D2); miss = compile + produce + atomic store.--cache/--cache=DIR/--cache-clearCLI flags; default root =~/Library/Caches/zwasm(macOS) /$XDG_CACHE_HOME|~/.cache/zwasm(Linux) /%LOCALAPPDATA%\zwasm(Windows).Failure discipline (DA critique fixes, second commit)
The independent DA critique (14/20 → fixes applied) reproduced four ways a cache defect could fail the run; all now degrade per the ADR-0203 D5 invariant "the cache can never make
runfail":.auto's interp fallback andZWASM_DEBUGrunnability under--cache.--engine interpbypasses the cache (D-496: the flag keeps forcing interp; the artifact is JIT code).--cache-clearalone no longer implies caching this run.store(): unique random temp suffix (shared temp name could publish a concurrent writer's partial bytes); artifacts over the 256 MiB read cap are not stored.Tests
zig build testgreen;zig build lint -- --max-warnings 0green.--cache-clearidempotency.test-aot-diff63/63 with new cache lanes (miss + hit vs fresh JIT per fixture, ADR-0203 D6 stage-5 ratchet) + interp-bypass probe + stored-entry existence check.Closes D-508. Campaign stage V (retrospective + docs) follows in the next PR.
https://claude.ai/code/session_01F2PGseEjGFR1uqid4FaXsm