diff --git a/docs/wasm-runtime-architecture.md b/docs/wasm-runtime-architecture.md index 316f0f8..2fb23b8 100644 --- a/docs/wasm-runtime-architecture.md +++ b/docs/wasm-runtime-architecture.md @@ -423,6 +423,10 @@ header free-functions are `inline`. The wasm backend exposes only declarations placeholder-example errors. The dependency-invalidation gate changes a transitive `#load`, then replaces a warmed worker artifact while preserving its whole-second mtime to prove both compiler and worker caches invalidate it. It + also breaks a loaded dependency, verifies the parent compile fails, restores + the dependency byte-for-byte, and requires the parent to recover. Failed + builds persist the input signature that actually failed, so metadata from an + older successful artifact cannot indefinitely defer that rebuild. also rejects an unreadable unit without publishing a wasm artifact, restores its permissions and proves the next CLI request compiles it, then sends 48 requests and asserts the observed worker PID set does not exceed diff --git a/scripts/test_dependency_invalidation.sh b/scripts/test_dependency_invalidation.sh index 5944a9e..1e3b2b7 100755 --- a/scripts/test_dependency_invalidation.sh +++ b/scripts/test_dependency_invalidation.sh @@ -69,6 +69,29 @@ if [[ "$(http_marker)" != *"dependency-marker-a"* ]]; then exit 1 fi +# A failed parent build must remember the exact dependency graph that failed. +# If a dependency is restored byte-for-byte to the last working source, the +# old successful metadata must not make that transient failure permanent. +printf '%s\n' \ + '#ifndef UCE_DEPENDENCY_CACHE_CHILD' \ + '#define UCE_DEPENDENCY_CACHE_CHILD' \ + 'String dependency_cache_marker() { return(deliberate_dependency_compile_failure); }' \ + '#endif' >"$source_dir/child.uce" +if restored_failure=$(scripts/uce-cli --get "/$test_name/parent.uce" __uce_expected_compile_failure=1 2>&1); then + echo "invalid dependency unexpectedly compiled: $restored_failure" >&2 + exit 1 +fi +if [[ "$restored_failure" != *"deliberate_dependency_compile_failure"* ]]; then + echo "invalid dependency did not report the expected compiler failure: $restored_failure" >&2 + exit 1 +fi +printf '%s\n' \ + '#ifndef UCE_DEPENDENCY_CACHE_CHILD' \ + '#define UCE_DEPENDENCY_CACHE_CHILD' \ + 'String dependency_cache_marker() { return("dependency-marker-a"); }' \ + '#endif' >"$source_dir/child.uce" +assert_marker parent dependency-marker-a + # The load-graph signature is content-addressed. A byte-identical dependency # touch must invalidate stat caches without recompiling its parent artifact. parent_wasm="$cache_dir/parent.uce.wasm" diff --git a/src/lib/compiler.cpp b/src/lib/compiler.cpp index 7e0b0ea..ca45667 100644 --- a/src/lib/compiler.cpp +++ b/src/lib/compiler.cpp @@ -910,6 +910,7 @@ void compile_shared_unit(Request* context, SharedUnit* su) String raw_messages = su->compiler_messages; file_put_contents(su->compile_output_file_name, raw_messages + "\n"); file_put_contents(su->wasm_check_file_name, raw_messages + "\n"); + file_put_contents(su->meta_file_name, compiler_unit_metadata_text(context, su, compiled_input_signature)); compiler_unlink_unit_wasm_artifacts(su); compiler_record_compile_result(su, time_precise() - comp_start, false, "compile_error", raw_messages); printf("%s \n", compiler_format_compile_failure(context, su, raw_messages).c_str());