From ef1644f18bfb1a098bc40a23aa9bb39ee8aac1a6 Mon Sep 17 00:00:00 2001 From: Daniele21 Date: Sun, 30 Aug 2026 20:03:29 +0200 Subject: [PATCH 01/11] test(llup): add automated v0.3 evidence replay --- .github/workflows/llup60-automated-replay.yml | 318 ++++++++++++++++++ .../workstreams/llup-v0-3-automated-replay.md | 61 ++++ 2 files changed, 379 insertions(+) create mode 100644 .github/workflows/llup60-automated-replay.yml create mode 100644 docs/workstreams/llup-v0-3-automated-replay.md diff --git a/.github/workflows/llup60-automated-replay.yml b/.github/workflows/llup60-automated-replay.yml new file mode 100644 index 000000000..7a296635a --- /dev/null +++ b/.github/workflows/llup60-automated-replay.yml @@ -0,0 +1,318 @@ +name: LLUP-60 automated evidence replay + +on: + workflow_dispatch: + push: + branches: + - agent/llup60-automated-replay + paths: + - ".github/workflows/llup60-automated-replay.yml" + - "docs/workstreams/llup-v0-3-automated-replay.md" + +permissions: + contents: read + +concurrency: + group: llup60-automated-replay-${{ github.ref }} + cancel-in-progress: true + +env: + RUNTIME_SOURCE_COMMIT: f796235f47899314b65a5abc95c998b396883c4b + EXPECTED_LLAMA_CPP_TAG: v0.3.0 + EXPECTED_LLAMA_CPP_REVISION: c1d0e7a004015f23bc0233470b747b596f29b264 + QWEN35_08B_SHA256: bd258782e35f7f458f8aced1adc053e6e92e89bc735ba3be89d38a06121dc517 + QWEN35_08B_BYTES: "532517120" + QWEN35_2B_SHA256: aaf42c8b7c3cab2bf3d69c355048d4a0ee9973d48f16c731c0520ee914699223 + QWEN35_2B_BYTES: "1280835840" + +jobs: + provenance: + name: Exact-source and identity provenance + runs-on: ubuntu-latest + timeout-minutes: 10 + steps: + - uses: actions/checkout@v7 + with: + fetch-depth: 0 + submodules: recursive + + - name: Verify stacked evidence scope + shell: bash + run: | + set -euo pipefail + git merge-base --is-ancestor "$RUNTIME_SOURCE_COMMIT" HEAD + changed="$(git diff --name-only "$RUNTIME_SOURCE_COMMIT"...HEAD)" + unexpected="$(printf '%s\n' "$changed" | grep -Ev '^(\.github/workflows/llup60-automated-replay\.yml|docs/workstreams/llup-v0-3-automated-replay\.md)$' || true)" + if [[ -n "$unexpected" ]]; then + echo "LLUP-60 evidence branch changes runtime/product files:" >&2 + printf '%s\n' "$unexpected" >&2 + exit 1 + fi + + - name: Verify authoritative backend identity + shell: bash + run: | + set -euo pipefail + ./scripts/verify-llama-cpp-pin.sh + python3 - <<'PY' + import json + import os + import subprocess + from pathlib import Path + + pin = json.loads(Path("backends/llama-cpp/llama-cpp-pin.json").read_text()) + expected_tag = os.environ["EXPECTED_LLAMA_CPP_TAG"] + expected_revision = os.environ["EXPECTED_LLAMA_CPP_REVISION"] + actual_submodule = subprocess.check_output( + ["git", "-C", "third_party/llama.cpp", "rev-parse", "HEAD"], text=True + ).strip() + if pin.get("tag") != expected_tag: + raise SystemExit(f"pin tag mismatch: {pin.get('tag')} != {expected_tag}") + if pin.get("commit") != expected_revision: + raise SystemExit(f"pin revision mismatch: {pin.get('commit')} != {expected_revision}") + if actual_submodule != expected_revision: + raise SystemExit(f"submodule mismatch: {actual_submodule} != {expected_revision}") + PY + bash scripts/test-verify-llama-cpp-pin.sh + + - name: Record provenance evidence + shell: bash + run: | + mkdir -p build/llup60 + python3 - <<'PY' + import json + import os + from pathlib import Path + + payload = { + "schemaVersion": 1, + "workstream": "LLUP-60A", + "runtimeSourceCommit": os.environ["RUNTIME_SOURCE_COMMIT"], + "evidenceHarnessCommit": os.environ["GITHUB_SHA"], + "llamaCppTag": os.environ["EXPECTED_LLAMA_CPP_TAG"], + "llamaCppRevision": os.environ["EXPECTED_LLAMA_CPP_REVISION"], + "identityGuard": "pass", + "realEnvironmentDeferred": True, + } + Path("build/llup60/provenance.json").write_text( + json.dumps(payload, indent=2, sort_keys=True) + "\n", encoding="utf-8" + ) + PY + + - uses: actions/upload-artifact@v7 + with: + name: llup60-provenance + path: build/llup60/provenance.json + if-no-files-found: error + retention-days: 14 + + native-host: + name: Native API and ownership replay + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - uses: actions/checkout@v7 + with: + submodules: recursive + + - name: Verify candidate pin + run: ./scripts/verify-llama-cpp-pin.sh + + - name: Build and run host-native suite + shell: bash + run: | + set -euo pipefail + cmake -S backends/llama-cpp/src/test-native -B build/llup60-native -DCMAKE_BUILD_TYPE=Release + cmake --build build/llup60-native --parallel 2 + ctest --test-dir build/llup60-native --output-on-failure + + jvm-contracts: + name: Q35 and runtime contract replay + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - uses: actions/checkout@v7 + + - uses: actions/setup-java@v5 + with: + distribution: temurin + java-version: "17" + check-latest: false + + - uses: android-actions/setup-android@v3 + with: + packages: platform-tools + + - name: Install Android SDK components + shell: bash + run: | + sdkmanager "platforms;android-36" "build-tools;36.0.0" + + - uses: gradle/actions/setup-gradle@v6 + with: + cache-provider: basic + + - name: Replay Q35 capability and runtime contracts + shell: bash + run: | + chmod +x gradlew + ./gradlew --stacktrace :models:model-profile:test :core:runtime-core:test + + qwen35-reference: + name: Exact Qwen3.5 host compatibility replay + runs-on: ubuntu-latest + timeout-minutes: 40 + steps: + - uses: actions/checkout@v7 + with: + submodules: recursive + + - name: Verify candidate pin + run: ./scripts/verify-llama-cpp-pin.sh + + - name: Build candidate llama.cpp smoke runner + shell: bash + run: | + set -euo pipefail + cmake -S third_party/llama.cpp -B build/llup60-qwen35 \ + -DLLAMA_CURL=OFF \ + -DGGML_NATIVE=OFF \ + -DGGML_OPENMP=OFF \ + -DLLAMA_BUILD_TOOLS=OFF \ + -DLLAMA_BUILD_TESTS=OFF \ + -DLLAMA_BUILD_SERVER=OFF \ + -DLLAMA_BUILD_EXAMPLES=ON + cmake --build build/llup60-qwen35 --target llama-simple -j2 + + - name: Download exact Qwen3.5 reference artifacts + shell: bash + run: | + set -euo pipefail + mkdir -p build/llup60-models + curl --fail --location --retry 3 --output build/llup60-models/qwen35-08b-q4-k-m.gguf \ + "https://huggingface.co/unsloth/Qwen3.5-0.8B-GGUF/resolve/5aea8824cba95d22990acc6ea66c2c1909530650/Qwen3.5-0.8B-Q4_K_M.gguf?download=true" + curl --fail --location --retry 3 --output build/llup60-models/qwen35-2b-q4-k-m.gguf \ + "https://huggingface.co/unsloth/Qwen3.5-2B-GGUF/resolve/802854bfd388ed92748de119df31327962811548/Qwen3.5-2B-Q4_K_M.gguf?download=true" + + - name: Verify exact Qwen3.5 artifact identity + shell: bash + run: | + set -euo pipefail + echo "$QWEN35_08B_SHA256 build/llup60-models/qwen35-08b-q4-k-m.gguf" | sha256sum --check + echo "$QWEN35_2B_SHA256 build/llup60-models/qwen35-2b-q4-k-m.gguf" | sha256sum --check + test "$(stat -c%s build/llup60-models/qwen35-08b-q4-k-m.gguf)" = "$QWEN35_08B_BYTES" + test "$(stat -c%s build/llup60-models/qwen35-2b-q4-k-m.gguf)" = "$QWEN35_2B_BYTES" + + - name: Smoke load tokenize generate + shell: bash + run: | + set -euo pipefail + cli=build/llup60-qwen35/bin/llama-simple + for model in build/llup60-models/qwen35-08b-q4-k-m.gguf build/llup60-models/qwen35-2b-q4-k-m.gguf; do + timeout 120 "$cli" -m "$model" -ngl 0 -n 1 "Reply OK." >/dev/null + done + + - name: Record Qwen3.5 compatibility evidence + shell: bash + run: | + mkdir -p build/llup60 + python3 - <<'PY' + import json + import os + from pathlib import Path + + payload = { + "schemaVersion": 1, + "workstream": "LLUP-60A", + "runtimeSourceCommit": os.environ["RUNTIME_SOURCE_COMMIT"], + "evidenceHarnessCommit": os.environ["GITHUB_SHA"], + "llamaCppRevision": os.environ["EXPECTED_LLAMA_CPP_REVISION"], + "artifacts": { + "qwen35-08b-q4-k-m": { + "sha256": os.environ["QWEN35_08B_SHA256"], + "bytes": int(os.environ["QWEN35_08B_BYTES"]), + "hostSmoke": "pass", + }, + "qwen35-2b-q4-k-m": { + "sha256": os.environ["QWEN35_2B_SHA256"], + "bytes": int(os.environ["QWEN35_2B_BYTES"]), + "hostSmoke": "pass", + }, + }, + } + Path("build/llup60/qwen35-host-compatibility.json").write_text( + json.dumps(payload, indent=2, sort_keys=True) + "\n", encoding="utf-8" + ) + PY + + - uses: actions/upload-artifact@v7 + with: + name: llup60-qwen35-host-compatibility + path: build/llup60/qwen35-host-compatibility.json + if-no-files-found: error + retention-days: 14 + + summary: + name: Automated replay evidence summary + runs-on: ubuntu-latest + if: always() + needs: + - provenance + - native-host + - jvm-contracts + - qwen35-reference + env: + PROVENANCE_RESULT: ${{ needs.provenance.result }} + NATIVE_HOST_RESULT: ${{ needs.native-host.result }} + JVM_CONTRACTS_RESULT: ${{ needs.jvm-contracts.result }} + QWEN35_RESULT: ${{ needs.qwen35-reference.result }} + steps: + - name: Write replay manifest + shell: bash + run: | + set -euo pipefail + mkdir -p build/llup60 + python3 - <<'PY' + import json + import os + from pathlib import Path + + checks = { + "provenance": os.environ["PROVENANCE_RESULT"], + "nativeHost": os.environ["NATIVE_HOST_RESULT"], + "jvmContracts": os.environ["JVM_CONTRACTS_RESULT"], + "qwen35ExactHostCompatibility": os.environ["QWEN35_RESULT"], + } + payload = { + "schemaVersion": 1, + "workstream": "LLUP-60A", + "runtimeSourceCommit": os.environ["RUNTIME_SOURCE_COMMIT"], + "evidenceHarnessCommit": os.environ["GITHUB_SHA"], + "llamaCppTag": os.environ["EXPECTED_LLAMA_CPP_TAG"], + "llamaCppRevision": os.environ["EXPECTED_LLAMA_CPP_REVISION"], + "checks": checks, + "automatedReplayComplete": all(value == "success" for value in checks.values()), + "realEnvironmentDeferred": True, + "deferred": [ + "LLUP-50 same-device physical A/B", + "LLRT KV-cache physical evidence", + "LLRT evaluation-batch physical evidence", + "device-only Q35 performance/memory/lifecycle evidence", + "output-quality replay whose canonical owner requires representative-device execution", + ], + "promotionDecisionAllowed": False, + } + Path("build/llup60/automated-replay-manifest.json").write_text( + json.dumps(payload, indent=2, sort_keys=True) + "\n", encoding="utf-8" + ) + if not payload["automatedReplayComplete"]: + raise SystemExit("one or more automated replay lanes failed") + PY + + - uses: actions/upload-artifact@v7 + if: always() + with: + name: llup60-automated-replay-manifest + path: build/llup60/automated-replay-manifest.json + if-no-files-found: error + retention-days: 14 diff --git a/docs/workstreams/llup-v0-3-automated-replay.md b/docs/workstreams/llup-v0-3-automated-replay.md new file mode 100644 index 000000000..8895010ad --- /dev/null +++ b/docs/workstreams/llup-v0-3-automated-replay.md @@ -0,0 +1,61 @@ +# LLUP v0.3.0 automated replay checkpoint + +Status: active +Document type: execution checkpoint +Owner: llama-cpp-runtime / runtime-memory +Parent candidate: `f796235f47899314b65a5abc95c998b396883c4b` +Candidate backend: `v0.3.0` / `c1d0e7a004015f23bc0233470b747b596f29b264` +Control remains: `dev@ab5b35519d51ee903e1127b2973ab8cf30407704` +Execution policy: deterministic automated work first; all `REAL_ENVIRONMENT` evidence last + +## Purpose + +This checkpoint changes execution order, not LLUP acceptance criteria. + +The production/control pin remains `b9637` until LLUP-70. The candidate migration in PR #490 stays frozen at the parent commit above so its exact-head FULL evidence remains valid. This stacked branch may add only replay workflow/documentation files and must not change runtime, backend, model-policy or product code. + +## Current automated state + +The parent candidate has repository-owned FULL evidence on exact head and unchanged control base. LLUP-20, LLUP-30 and LLUP-40 are therefore automated-preflight confirmed. + +LLUP-60 is split operationally into two evidence phases: + +- **LLUP-60A — automated replay now.** Re-run backend identity, host-native ownership/API behavior, Q35/runtime JVM contracts and exact Qwen3.5 host load/tokenize/generate compatibility against the candidate revision. +- **LLUP-60B — representative-device dependent replay later.** Consolidate evidence that actually depends on mobile memory, thermal state, device lifecycle or other representative-device behavior. + +This split does not weaken LLUP-60. Global LLUP-60 remains incomplete until both phases required by canonical policy are complete. + +## LLUP-60A lanes + +The repository-owned workflow `.github/workflows/llup60-automated-replay.yml` must fail closed unless: + +1. the evidence branch descends from the frozen candidate parent; +2. the branch diff contains only this checkpoint and its evidence workflow; +3. authoritative pin manifest, checked-out submodule, backend runtime revision and Q35 runtime capability identity remain consistent; +4. backend host-native tests pass on the exact candidate source; +5. Q35/model-profile and runtime-core JVM contracts pass; +6. exact Qwen3.5 0.8B and 2B GGUF identities match pre-existing reviewed digest/size values; +7. candidate `llama-simple` loads/tokenizes/generates against both exact Qwen3.5 artifacts; +8. a machine-readable evidence manifest records runtime source SHA separately from evidence-harness SHA. + +No result from this host/CI replay is treated as representative-device performance or memory certification. + +## Deferred REAL_ENVIRONMENT block + +Keep these lanes for the final physical phase: + +- LLUP-50 same-device `b9637` versus `v0.3.0` A/B; +- LLRT KV-cache evidence that is implemented through ADB/instrumentation; +- LLRT evaluation-batch evidence that is implemented through ADB/instrumentation; +- device-only Q35 performance, memory, thermal, cancellation/recovery and lifecycle evidence; +- any OMBRA/evaluation output replay whose canonical execution path genuinely requires representative-device/runtime evidence. + +The paired LLUP-50 APK artifacts and physical runner are already prepared; they remain frozen and are not executed during LLUP-60A. + +## Promotion and residency gates + +LLUP-70 remains blocked. Passing LLUP-60A does **not** authorize merging PR #490 or changing the production pin. + +MRES-10 remains blocked by LLUP-70. Bounded multi-residency implementation must not be started against an unpromoted backend baseline merely to keep work moving. + +After all deterministic work is exhausted, run the deferred REAL_ENVIRONMENT block, consolidate LLUP-60B, then make the explicit LLUP-70 decision. From 11e30b7ce505d9780093ae5a946acb4a5670360a Mon Sep 17 00:00:00 2001 From: Daniele21 Date: Sun, 30 Aug 2026 21:51:52 +0200 Subject: [PATCH 02/11] test(llup): extend automated replay to evaluation evidence --- .github/workflows/llup60-automated-replay.yml | 44 +++++++++++++++++++ 1 file changed, 44 insertions(+) diff --git a/.github/workflows/llup60-automated-replay.yml b/.github/workflows/llup60-automated-replay.yml index 7a296635a..036d707da 100644 --- a/.github/workflows/llup60-automated-replay.yml +++ b/.github/workflows/llup60-automated-replay.yml @@ -158,6 +158,47 @@ jobs: chmod +x gradlew ./gradlew --stacktrace :models:model-profile:test :core:runtime-core:test + evaluation-evidence: + name: Evaluation and benchmark contract replay + runs-on: ubuntu-latest + timeout-minutes: 35 + steps: + - uses: actions/checkout@v7 + + - uses: actions/setup-java@v5 + with: + distribution: temurin + java-version: "17" + check-latest: false + + - uses: android-actions/setup-android@v3 + with: + packages: platform-tools + + - name: Install Android SDK components + shell: bash + run: | + sdkmanager "platforms;android-36" "build-tools;36.0.0" + + - uses: gradle/actions/setup-gradle@v6 + with: + cache-provider: basic + + - name: Replay evaluation and benchmark evidence contracts + shell: bash + run: | + chmod +x gradlew + ./gradlew --no-configuration-cache --stacktrace \ + :evaluation:contracts:testDebugUnitTest \ + :evaluation:datasets:testDebugUnitTest \ + :evaluation:evaluators:testDebugUnitTest \ + :evaluation:engine:testDebugUnitTest \ + :evaluation:runtime-adapter:testDebugUnitTest \ + :evaluation:comparison:testDebugUnitTest \ + :evaluation:persistence:testDebugUnitTest \ + :observability:contracts:testDebugUnitTest \ + :observability:benchmark-engine:testDebugUnitTest + qwen35-reference: name: Exact Qwen3.5 host compatibility replay runs-on: ubuntu-latest @@ -260,11 +301,13 @@ jobs: - provenance - native-host - jvm-contracts + - evaluation-evidence - qwen35-reference env: PROVENANCE_RESULT: ${{ needs.provenance.result }} NATIVE_HOST_RESULT: ${{ needs.native-host.result }} JVM_CONTRACTS_RESULT: ${{ needs.jvm-contracts.result }} + EVALUATION_EVIDENCE_RESULT: ${{ needs.evaluation-evidence.result }} QWEN35_RESULT: ${{ needs.qwen35-reference.result }} steps: - name: Write replay manifest @@ -281,6 +324,7 @@ jobs: "provenance": os.environ["PROVENANCE_RESULT"], "nativeHost": os.environ["NATIVE_HOST_RESULT"], "jvmContracts": os.environ["JVM_CONTRACTS_RESULT"], + "evaluationAndBenchmarkContracts": os.environ["EVALUATION_EVIDENCE_RESULT"], "qwen35ExactHostCompatibility": os.environ["QWEN35_RESULT"], } payload = { From 12066a414124468181f3de8472980a56274d4a12 Mon Sep 17 00:00:00 2001 From: Daniele21 Date: Sun, 30 Aug 2026 21:52:20 +0200 Subject: [PATCH 03/11] docs(llup): record automated promotion readiness boundary --- .../workstreams/llup-v0-3-automated-replay.md | 82 +++++++++++++------ 1 file changed, 59 insertions(+), 23 deletions(-) diff --git a/docs/workstreams/llup-v0-3-automated-replay.md b/docs/workstreams/llup-v0-3-automated-replay.md index 8895010ad..3741d659d 100644 --- a/docs/workstreams/llup-v0-3-automated-replay.md +++ b/docs/workstreams/llup-v0-3-automated-replay.md @@ -1,7 +1,7 @@ # LLUP v0.3.0 automated replay checkpoint -Status: active -Document type: execution checkpoint +Status: active — automated work in progress; `REAL_ENVIRONMENT` intentionally deferred +Document type: execution checkpoint / promotion-readiness ledger Owner: llama-cpp-runtime / runtime-memory Parent candidate: `f796235f47899314b65a5abc95c998b396883c4b` Candidate backend: `v0.3.0` / `c1d0e7a004015f23bc0233470b747b596f29b264` @@ -14,48 +14,84 @@ This checkpoint changes execution order, not LLUP acceptance criteria. The production/control pin remains `b9637` until LLUP-70. The candidate migration in PR #490 stays frozen at the parent commit above so its exact-head FULL evidence remains valid. This stacked branch may add only replay workflow/documentation files and must not change runtime, backend, model-policy or product code. -## Current automated state +The checkpoint is also the pre-promotion evidence index. It must make clear which gates are already exact-head automated evidence, which are replayed here, and which are still blocked on representative-device execution. `WAITING_REAL_ENVIRONMENT` is a valid readiness boundary; it is not a promotion decision. + +## Frozen identities + +| Role | Harness/runtime source | llama.cpp | +|---|---|---| +| Control | `ab5b35519d51ee903e1127b2973ab8cf30407704` | `b9637` / `aedb2a5e9ca3d4064148bbb919e0ddc0c1b70ab3` | +| Candidate | `f796235f47899314b65a5abc95c998b396883c4b` | `v0.3.0` / `c1d0e7a004015f23bc0233470b747b596f29b264` | -The parent candidate has repository-owned FULL evidence on exact head and unchanged control base. LLUP-20, LLUP-30 and LLUP-40 are therefore automated-preflight confirmed. +Evidence-only branches must record their own harness SHA separately and must not replace either frozen runtime identity. + +## Current automated state -LLUP-60 is split operationally into two evidence phases: +The parent candidate has repository-owned FULL evidence on exact head `f796235f47899314b65a5abc95c998b396883c4b` against unchanged control base `ab5b35519d51ee903e1127b2973ab8cf30407704`. Authoritative FULL run: `33324145728`. -- **LLUP-60A — automated replay now.** Re-run backend identity, host-native ownership/API behavior, Q35/runtime JVM contracts and exact Qwen3.5 host load/tokenize/generate compatibility against the candidate revision. -- **LLUP-60B — representative-device dependent replay later.** Consolidate evidence that actually depends on mobile memory, thermal state, device lifecycle or other representative-device behavior. +Therefore: -This split does not weaken LLUP-60. Global LLUP-60 remains incomplete until both phases required by canonical policy are complete. +- **LLUP-20 — candidate native/API migration:** `PASS` / `AUTOMATED_PREFLIGHT_CONFIRMED`. +- **LLUP-30 — runtime correctness, lifecycle and execution identity propagation:** `PASS` / `AUTOMATED_PREFLIGHT_CONFIRMED`. +- **LLUP-40 — Android ARM64 build and package validation:** `PASS` / `AUTOMATED_PREFLIGHT_CONFIRMED`. +- **LLUP-50 — same-device A/B:** `PENDING_REAL_ENVIRONMENT` by deliberate execution order. +- **LLUP-60 — affected evidence replay:** split into automated LLUP-60A and representative-device LLUP-60B; global task remains incomplete until both required parts pass. +- **LLUP-70 — promotion decision:** `BLOCKED` until the deferred real-environment block is complete. +- **MRES-10+:** `BLOCKED` by LLUP-70; do not implement multi-residency against an unpromoted backend baseline. -## LLUP-60A lanes +## LLUP-60A — automated replay The repository-owned workflow `.github/workflows/llup60-automated-replay.yml` must fail closed unless: 1. the evidence branch descends from the frozen candidate parent; 2. the branch diff contains only this checkpoint and its evidence workflow; 3. authoritative pin manifest, checked-out submodule, backend runtime revision and Q35 runtime capability identity remain consistent; -4. backend host-native tests pass on the exact candidate source; +4. backend host-native ownership/API tests pass on the exact candidate source; 5. Q35/model-profile and runtime-core JVM contracts pass; -6. exact Qwen3.5 0.8B and 2B GGUF identities match pre-existing reviewed digest/size values; -7. candidate `llama-simple` loads/tokenizes/generates against both exact Qwen3.5 artifacts; -8. a machine-readable evidence manifest records runtime source SHA separately from evidence-harness SHA. +6. evaluation contracts that consume runtime execution identity pass, including engine/runtime-adapter/comparison/persistence and dataset/evaluator integrity; +7. observability execution/benchmark contracts preserve compatible evidence and fingerprint semantics; +8. exact Qwen3.5 0.8B and 2B GGUF identities match the existing reviewed digest/size values; +9. candidate `llama-simple` loads/tokenizes/generates against both exact Qwen3.5 artifacts; +10. a machine-readable replay manifest records runtime source SHA separately from evidence-harness SHA and marks promotion disallowed while representative-device evidence is pending. -No result from this host/CI replay is treated as representative-device performance or memory certification. +The evaluation/benchmark lane is deliberately a contract/integrity replay. It does not pretend to replace real output-quality, latency, memory or thermal evidence from representative Android execution. + +The first LLUP-60A replay (`33326979912`) and its STRONG validation (`33327011605`) passed before the explicit evaluation/benchmark lane was added. Those runs remain historical provenance, but exact-head acceptance for the expanded replay must come from the newest workflow and STRONG run after this checkpoint update. ## Deferred REAL_ENVIRONMENT block -Keep these lanes for the final physical phase: +Keep all of these lanes for the final physical phase: - LLUP-50 same-device `b9637` versus `v0.3.0` A/B; -- LLRT KV-cache evidence that is implemented through ADB/instrumentation; -- LLRT evaluation-batch evidence that is implemented through ADB/instrumentation; -- device-only Q35 performance, memory, thermal, cancellation/recovery and lifecycle evidence; +- LLRT KV-cache evidence implemented through ADB/instrumentation; +- LLRT evaluation-batch evidence implemented through ADB/instrumentation; +- device-only Q35 performance, model-load latency, memory/PSS, thermal, cancellation/recovery and lifecycle evidence; +- representative-device resident-count, warm-idle and prepare-after-release recovery evidence required by the LLUP acceptance contract; - any OMBRA/evaluation output replay whose canonical execution path genuinely requires representative-device/runtime evidence. -The paired LLUP-50 APK artifacts and physical runner are already prepared; they remain frozen and are not executed during LLUP-60A. +The paired LLUP-50 APK artifacts and exact-APK physical runner are prepared separately and remain frozen. They are not executed during LLUP-60A. + +## Promotion-readiness boundary + +Before entering the deferred physical block, the expected automated state is: + +- frozen control and candidate runtime identities still unchanged; +- PR #490 runtime diff exact-head FULL green and unmerged; +- LLUP-50 runner/tooling preflight green and unmerged; +- LLUP-60A replay manifest green on its exact evidence HEAD; +- STRONG preflight green on that same evidence HEAD; +- exact candidate package identity available from repository-owned automation; +- no unresolved deterministic validation failure or automation-capability gap that can be fixed before using a device. -## Promotion and residency gates +When all bullets above hold, classify the workstream as `WAITING_REAL_ENVIRONMENT`, not complete and not promoted. -LLUP-70 remains blocked. Passing LLUP-60A does **not** authorize merging PR #490 or changing the production pin. +## Final sequence after automated exhaustion -MRES-10 remains blocked by LLUP-70. Bounded multi-residency implementation must not be started against an unpromoted backend baseline merely to keep work moving. +1. Execute LLUP-50 same-device control/candidate A/B using the frozen APK identities. +2. Execute deferred device-only LLRT/Q35/memory/thermal/lifecycle and required output-quality evidence. +3. Consolidate LLUP-60B and verify all evidence still refers to the frozen candidate runtime SHA/backend revision. +4. Re-check target/base freshness and promotion FULL requirements. +5. Make the explicit LLUP-70 promotion/reject decision. +6. Only after a promotion decision authorizes it, merge/promote PR #490 and then unblock MRES-10. -After all deterministic work is exhausted, run the deferred REAL_ENVIRONMENT block, consolidate LLUP-60B, then make the explicit LLUP-70 decision. +No earlier automated success authorizes production promotion by itself. From 6afa2acc353a156e649d8daa24db4a846095dc97 Mon Sep 17 00:00:00 2001 From: Daniele21 Date: Sun, 30 Aug 2026 22:46:05 +0200 Subject: [PATCH 04/11] ci(llup): replay against refreshed v0.3 candidate --- .github/workflows/llup60-automated-replay.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/llup60-automated-replay.yml b/.github/workflows/llup60-automated-replay.yml index 036d707da..5a06640ba 100644 --- a/.github/workflows/llup60-automated-replay.yml +++ b/.github/workflows/llup60-automated-replay.yml @@ -17,7 +17,7 @@ concurrency: cancel-in-progress: true env: - RUNTIME_SOURCE_COMMIT: f796235f47899314b65a5abc95c998b396883c4b + RUNTIME_SOURCE_COMMIT: 59af48313b450d9cff13c7f43458c2e5e6560374 EXPECTED_LLAMA_CPP_TAG: v0.3.0 EXPECTED_LLAMA_CPP_REVISION: c1d0e7a004015f23bc0233470b747b596f29b264 QWEN35_08B_SHA256: bd258782e35f7f458f8aced1adc053e6e92e89bc735ba3be89d38a06121dc517 From 4a1f8b641c52c084d3615a71d8e7ab76fbb44de3 Mon Sep 17 00:00:00 2001 From: Daniele21 Date: Sun, 30 Aug 2026 22:48:06 +0200 Subject: [PATCH 05/11] docs(llup): refresh qualification identities after base movement --- .../workstreams/llup-v0-3-automated-replay.md | 114 +++++++----------- 1 file changed, 44 insertions(+), 70 deletions(-) diff --git a/docs/workstreams/llup-v0-3-automated-replay.md b/docs/workstreams/llup-v0-3-automated-replay.md index 3741d659d..cc6273467 100644 --- a/docs/workstreams/llup-v0-3-automated-replay.md +++ b/docs/workstreams/llup-v0-3-automated-replay.md @@ -1,97 +1,71 @@ # LLUP v0.3.0 automated replay checkpoint -Status: active — automated work in progress; `REAL_ENVIRONMENT` intentionally deferred +Status: active — refreshed after candidate/base movement; `REAL_ENVIRONMENT` intentionally deferred Document type: execution checkpoint / promotion-readiness ledger Owner: llama-cpp-runtime / runtime-memory -Parent candidate: `f796235f47899314b65a5abc95c998b396883c4b` -Candidate backend: `v0.3.0` / `c1d0e7a004015f23bc0233470b747b596f29b264` -Control remains: `dev@ab5b35519d51ee903e1127b2973ab8cf30407704` -Execution policy: deterministic automated work first; all `REAL_ENVIRONMENT` evidence last -## Purpose +## Frozen qualification identities -This checkpoint changes execution order, not LLUP acceptance criteria. +- Control: `dev@80164329bbc41a00b75721e3d0524294c03fdb56`, llama.cpp `b9637` / `aedb2a5e9ca3d4064148bbb919e0ddc0c1b70ab3`. +- Candidate: `59af48313b450d9cff13c7f43458c2e5e6560374`, llama.cpp `v0.3.0` / `c1d0e7a004015f23bc0233470b747b596f29b264`. +- Control LLUP-50 evidence ref: `evidence/llup50-control@fcbefc7cd9af84de570da96d039582175dd1700b`. +- Candidate LLUP-50 evidence ref: `evidence/llup50-candidate@a2a050d9551db541bb4c6b152cba8623c782164d`. +- LLUP-50 runner branch: `agent/llup50-physical-runner@cefb893ae95cfd95339de4a24a955a652f0011e6`. -The production/control pin remains `b9637` until LLUP-70. The candidate migration in PR #490 stays frozen at the parent commit above so its exact-head FULL evidence remains valid. This stacked branch may add only replay workflow/documentation files and must not change runtime, backend, model-policy or product code. +The previous candidate `f796235f47899314b65a5abc95c998b396883c4b` was refreshed only because `dev` gained the canonical Android AAB packaging command. The llama.cpp runtime behavior did not change, but packaging identity is part of qualification, so relevant exact-head evidence is refreshed rather than reused. -The checkpoint is also the pre-promotion evidence index. It must make clear which gates are already exact-head automated evidence, which are replayed here, and which are still blocked on representative-device execution. `WAITING_REAL_ENVIRONMENT` is a valid readiness boundary; it is not a promotion decision. - -## Frozen identities +## Current automated state -| Role | Harness/runtime source | llama.cpp | -|---|---|---| -| Control | `ab5b35519d51ee903e1127b2973ab8cf30407704` | `b9637` / `aedb2a5e9ca3d4064148bbb919e0ddc0c1b70ab3` | -| Candidate | `f796235f47899314b65a5abc95c998b396883c4b` | `v0.3.0` / `c1d0e7a004015f23bc0233470b747b596f29b264` | +PR #490 has repository-owned FULL evidence on exact candidate head `59af48313b450d9cff13c7f43458c2e5e6560374` against `dev@80164329bbc41a00b75721e3d0524294c03fdb56`. Authoritative run: `33332939707`. -Evidence-only branches must record their own harness SHA separately and must not replace either frozen runtime identity. +- LLUP-20: `PASS` / `AUTOMATED_PREFLIGHT_CONFIRMED`. +- LLUP-30: `PASS` / `AUTOMATED_PREFLIGHT_CONFIRMED`. +- LLUP-40: `PASS` / `AUTOMATED_PREFLIGHT_CONFIRMED`. +- LLUP-50: `PENDING_REAL_ENVIRONMENT`. +- LLUP-60: split into automated LLUP-60A and representative-device LLUP-60B; globally incomplete until both required parts pass. +- LLUP-70: `BLOCKED` until the physical block completes. +- MRES-10+: `BLOCKED` by LLUP-70. -## Current automated state +Previous LLUP-60A run `33332064271` and STRONG preflight `33332084155` remain historical provenance but are stale for the refreshed candidate/base pair. -The parent candidate has repository-owned FULL evidence on exact head `f796235f47899314b65a5abc95c998b396883c4b` against unchanged control base `ab5b35519d51ee903e1127b2973ab8cf30407704`. Authoritative FULL run: `33324145728`. +## LLUP-60A acceptance -Therefore: +The repository workflow `.github/workflows/llup60-automated-replay.yml` must prove on the refreshed exact candidate: -- **LLUP-20 — candidate native/API migration:** `PASS` / `AUTOMATED_PREFLIGHT_CONFIRMED`. -- **LLUP-30 — runtime correctness, lifecycle and execution identity propagation:** `PASS` / `AUTOMATED_PREFLIGHT_CONFIRMED`. -- **LLUP-40 — Android ARM64 build and package validation:** `PASS` / `AUTOMATED_PREFLIGHT_CONFIRMED`. -- **LLUP-50 — same-device A/B:** `PENDING_REAL_ENVIRONMENT` by deliberate execution order. -- **LLUP-60 — affected evidence replay:** split into automated LLUP-60A and representative-device LLUP-60B; global task remains incomplete until both required parts pass. -- **LLUP-70 — promotion decision:** `BLOCKED` until the deferred real-environment block is complete. -- **MRES-10+:** `BLOCKED` by LLUP-70; do not implement multi-residency against an unpromoted backend baseline. +1. evidence-only branch scope; +2. authoritative pin/submodule/backend/Q35 identity consistency; +3. native backend ownership/API contracts; +4. Q35 model-profile and runtime-core JVM contracts; +5. evaluation and observability/benchmark contracts; +6. exact Qwen3.5 0.8B and 2B GGUF identity; +7. host load/tokenize/generate compatibility for both curated GGUFs; +8. machine-readable provenance separating runtime source SHA from evidence-harness SHA; +9. promotion disabled while representative-device evidence is pending. -## LLUP-60A — automated replay +Automated replay does not replace output-quality, latency, memory, thermal or lifecycle evidence that genuinely requires representative Android execution. -The repository-owned workflow `.github/workflows/llup60-automated-replay.yml` must fail closed unless: +## LLUP-50 boundary -1. the evidence branch descends from the frozen candidate parent; -2. the branch diff contains only this checkpoint and its evidence workflow; -3. authoritative pin manifest, checked-out submodule, backend runtime revision and Q35 runtime capability identity remain consistent; -4. backend host-native ownership/API tests pass on the exact candidate source; -5. Q35/model-profile and runtime-core JVM contracts pass; -6. evaluation contracts that consume runtime execution identity pass, including engine/runtime-adapter/comparison/persistence and dataset/evaluator integrity; -7. observability execution/benchmark contracts preserve compatible evidence and fingerprint semantics; -8. exact Qwen3.5 0.8B and 2B GGUF identities match the existing reviewed digest/size values; -9. candidate `llama-simple` loads/tokenizes/generates against both exact Qwen3.5 artifacts; -10. a machine-readable replay manifest records runtime source SHA separately from evidence-harness SHA and marks promotion disallowed while representative-device evidence is pending. +The exact-APK runner remains isolated in draft PR #491. It consumes CI-built APKs, verifies package source revision and SHA-256, captures model-load/tuning/memory/cancellation/LOW_MEMORY/switch evidence and compares paired control/candidate results without inventing promotion thresholds. -The evaluation/benchmark lane is deliberately a contract/integrity replay. It does not pretend to replace real output-quality, latency, memory or thermal evidence from representative Android execution. - -The first LLUP-60A replay (`33326979912`) and its STRONG validation (`33327011605`) passed before the explicit evaluation/benchmark lane was added. Those runs remain historical provenance, but exact-head acceptance for the expanded replay must come from the newest workflow and STRONG run after this checkpoint update. +Before using a phone, repository-owned package automation must build exact-ref APK/manifests for the refreshed control and candidate evidence refs. Packaging is `REMOTE_AUTOMATED`; executing those artifacts on the representative phone is `REAL_ENVIRONMENT`. ## Deferred REAL_ENVIRONMENT block -Keep all of these lanes for the final physical phase: - - LLUP-50 same-device `b9637` versus `v0.3.0` A/B; -- LLRT KV-cache evidence implemented through ADB/instrumentation; -- LLRT evaluation-batch evidence implemented through ADB/instrumentation; -- device-only Q35 performance, model-load latency, memory/PSS, thermal, cancellation/recovery and lifecycle evidence; -- representative-device resident-count, warm-idle and prepare-after-release recovery evidence required by the LLUP acceptance contract; -- any OMBRA/evaluation output replay whose canonical execution path genuinely requires representative-device/runtime evidence. - -The paired LLUP-50 APK artifacts and exact-APK physical runner are prepared separately and remain frozen. They are not executed during LLUP-60A. +- LLRT KV-cache and evaluation-batch physical evidence; +- device-only Q35 load/performance/PSS/memory/thermal/cancellation/recovery/lifecycle evidence; +- representative-device resident-count, warm-idle and prepare-after-release recovery; +- output-quality evidence whose canonical execution path requires representative-device runtime execution. ## Promotion-readiness boundary -Before entering the deferred physical block, the expected automated state is: - -- frozen control and candidate runtime identities still unchanged; -- PR #490 runtime diff exact-head FULL green and unmerged; -- LLUP-50 runner/tooling preflight green and unmerged; -- LLUP-60A replay manifest green on its exact evidence HEAD; -- STRONG preflight green on that same evidence HEAD; -- exact candidate package identity available from repository-owned automation; -- no unresolved deterministic validation failure or automation-capability gap that can be fixed before using a device. - -When all bullets above hold, classify the workstream as `WAITING_REAL_ENVIRONMENT`, not complete and not promoted. - -## Final sequence after automated exhaustion +Classify the workstream as `WAITING_REAL_ENVIRONMENT` only when: -1. Execute LLUP-50 same-device control/candidate A/B using the frozen APK identities. -2. Execute deferred device-only LLRT/Q35/memory/thermal/lifecycle and required output-quality evidence. -3. Consolidate LLUP-60B and verify all evidence still refers to the frozen candidate runtime SHA/backend revision. -4. Re-check target/base freshness and promotion FULL requirements. -5. Make the explicit LLUP-70 promotion/reject decision. -6. Only after a promotion decision authorizes it, merge/promote PR #490 and then unblock MRES-10. +- PR #490 remains unmerged and exact-head FULL green; +- PR #491 tooling validation is green on its refreshed head; +- LLUP-60A replay and STRONG preflight are green on the same refreshed evidence head; +- exact control/candidate package identities are available from repository-owned automation; +- no deterministic automation gap remains. -No earlier automated success authorizes production promotion by itself. +Then execute LLUP-50 and LLUP-60B, re-check promotion FULL requirements, make the explicit LLUP-70 decision, and only after promotion authorization merge PR #490 and unblock MRES-10. From d7bb5a698ff0e5b41b0defff1cfd7109b52e70a7 Mon Sep 17 00:00:00 2001 From: Daniele21 Date: Sun, 30 Aug 2026 22:53:43 +0200 Subject: [PATCH 06/11] docs(llup): use canonical lifecycle status metadata --- docs/workstreams/llup-v0-3-automated-replay.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/workstreams/llup-v0-3-automated-replay.md b/docs/workstreams/llup-v0-3-automated-replay.md index cc6273467..6bf9a8c85 100644 --- a/docs/workstreams/llup-v0-3-automated-replay.md +++ b/docs/workstreams/llup-v0-3-automated-replay.md @@ -1,6 +1,6 @@ # LLUP v0.3.0 automated replay checkpoint -Status: active — refreshed after candidate/base movement; `REAL_ENVIRONMENT` intentionally deferred +Status: active Document type: execution checkpoint / promotion-readiness ledger Owner: llama-cpp-runtime / runtime-memory @@ -12,7 +12,7 @@ Owner: llama-cpp-runtime / runtime-memory - Candidate LLUP-50 evidence ref: `evidence/llup50-candidate@a2a050d9551db541bb4c6b152cba8623c782164d`. - LLUP-50 runner branch: `agent/llup50-physical-runner@cefb893ae95cfd95339de4a24a955a652f0011e6`. -The previous candidate `f796235f47899314b65a5abc95c998b396883c4b` was refreshed only because `dev` gained the canonical Android AAB packaging command. The llama.cpp runtime behavior did not change, but packaging identity is part of qualification, so relevant exact-head evidence is refreshed rather than reused. +The previous candidate `f796235f47899314b65a5abc95c998b396883c4b` was refreshed only because `dev` gained the canonical Android AAB packaging command. The llama.cpp runtime behavior did not change, but packaging identity is part of qualification, so relevant exact-head evidence is refreshed rather than reused. The automated work is refreshed for this candidate/base pair while `REAL_ENVIRONMENT` evidence remains intentionally deferred. ## Current automated state From eefac6cac4337e5d61fa135fb66a8969911768df Mon Sep 17 00:00:00 2001 From: Daniele21 Date: Sun, 30 Aug 2026 22:57:06 +0200 Subject: [PATCH 07/11] docs(llup): add active lifecycle metadata --- docs/workstreams/llup-v0-3-automated-replay.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/docs/workstreams/llup-v0-3-automated-replay.md b/docs/workstreams/llup-v0-3-automated-replay.md index 6bf9a8c85..2bcd1d1bc 100644 --- a/docs/workstreams/llup-v0-3-automated-replay.md +++ b/docs/workstreams/llup-v0-3-automated-replay.md @@ -3,6 +3,9 @@ Status: active Document type: execution checkpoint / promotion-readiness ledger Owner: llama-cpp-runtime / runtime-memory +Canonical scope: workstream.llup-v0-3-automated-replay +Read when: refreshing LLUP-60 evidence, preparing LLUP-50 device qualification, or evaluating LLUP-70 promotion readiness +Last reviewed: 2026-08-30 ## Frozen qualification identities From 9cd051b3c43a6fd61780626293af14de00263552 Mon Sep 17 00:00:00 2001 From: Daniele21 Date: Sun, 30 Aug 2026 22:58:28 +0200 Subject: [PATCH 08/11] docs(llup): use supported workstream-state type --- docs/workstreams/llup-v0-3-automated-replay.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/workstreams/llup-v0-3-automated-replay.md b/docs/workstreams/llup-v0-3-automated-replay.md index 2bcd1d1bc..b63d118c4 100644 --- a/docs/workstreams/llup-v0-3-automated-replay.md +++ b/docs/workstreams/llup-v0-3-automated-replay.md @@ -1,7 +1,7 @@ # LLUP v0.3.0 automated replay checkpoint Status: active -Document type: execution checkpoint / promotion-readiness ledger +Document type: workstream-state Owner: llama-cpp-runtime / runtime-memory Canonical scope: workstream.llup-v0-3-automated-replay Read when: refreshing LLUP-60 evidence, preparing LLUP-50 device qualification, or evaluating LLUP-70 promotion readiness From 256297353406d3c1656b2c930459ea659cbaebaf Mon Sep 17 00:00:00 2001 From: Daniele21 Date: Sun, 30 Aug 2026 23:00:51 +0200 Subject: [PATCH 09/11] docs(llup): route active replay workstream --- docs/workstreams/README.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/docs/workstreams/README.md b/docs/workstreams/README.md index 1d336eaf0..f736e6c98 100644 --- a/docs/workstreams/README.md +++ b/docs/workstreams/README.md @@ -5,7 +5,7 @@ Document type: documentation-governance Owner: repository Canonical scope: documentation.workstreams Read when: creating, locating, updating or finalizing a bounded implementation workstream -Last reviewed: 2026-08-26 +Last reviewed: 2026-08-30 This directory is the canonical home for repository-level **temporary implementation workstreams** that must preserve dependency, sequencing or handoff state across pull requests or coding agents. @@ -16,6 +16,7 @@ A workstream belongs here only while it is active. It is not a second project-st - [`q35-runtime-qualification-wave.md`](q35-runtime-qualification-wave.md) — coordinates the temporary Qwen3.5 CPU runtime qualification wave, including measured-profile acceptance, lifecycle/memory evidence, representative-device gates and final review dependencies. - [`application-control-plane-ux.md`](application-control-plane-ux.md) — coordinates the Applications -> assigned use case -> preset-control UX implementation, including parallel UI/control-plane slices, revision-safe mutations, adaptive/accessibility convergence and effective two-APK evidence. - [`control-plane-state-reconciliation.md`](control-plane-state-reconciliation.md) — coordinates startup reconciliation of persisted mandatory built-in control-plane state, conservative upgrade repair, cross-surface consistency and the dependent physical upgrade/two-APK gates. +- [`llup-v0-3-automated-replay.md`](llup-v0-3-automated-replay.md) — tracks the temporary LLUP v0.3.0 exact-head automated replay, LLUP-50 package/device qualification boundary and LLUP-70 promotion readiness. ## Lifecycle From bb991632e98b8ed170b1109f12c043195f0b5d1b Mon Sep 17 00:00:00 2001 From: Daniele21 Date: Sun, 30 Aug 2026 23:01:09 +0200 Subject: [PATCH 10/11] docs(llup): link integrated current state --- docs/workstreams/llup-v0-3-automated-replay.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/docs/workstreams/llup-v0-3-automated-replay.md b/docs/workstreams/llup-v0-3-automated-replay.md index b63d118c4..7e59b5139 100644 --- a/docs/workstreams/llup-v0-3-automated-replay.md +++ b/docs/workstreams/llup-v0-3-automated-replay.md @@ -7,6 +7,8 @@ Canonical scope: workstream.llup-v0-3-automated-replay Read when: refreshing LLUP-60 evidence, preparing LLUP-50 device qualification, or evaluating LLUP-70 promotion readiness Last reviewed: 2026-08-30 +Repository integrated state and blockers remain owned by [`../current-state.md`](../current-state.md); this file owns only the bounded LLUP qualification/replay sequence. + ## Frozen qualification identities - Control: `dev@80164329bbc41a00b75721e3d0524294c03fdb56`, llama.cpp `b9637` / `aedb2a5e9ca3d4064148bbb919e0ddc0c1b70ab3`. From 2e213ad465bef56b724708e9b2b641bba0de3be5 Mon Sep 17 00:00:00 2001 From: Daniele21 Date: Sun, 30 Aug 2026 23:01:47 +0200 Subject: [PATCH 11/11] ci(llup): allow canonical workstream routing --- .github/workflows/llup60-automated-replay.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/llup60-automated-replay.yml b/.github/workflows/llup60-automated-replay.yml index 5a06640ba..691e160a4 100644 --- a/.github/workflows/llup60-automated-replay.yml +++ b/.github/workflows/llup60-automated-replay.yml @@ -7,6 +7,7 @@ on: - agent/llup60-automated-replay paths: - ".github/workflows/llup60-automated-replay.yml" + - "docs/workstreams/README.md" - "docs/workstreams/llup-v0-3-automated-replay.md" permissions: @@ -42,7 +43,7 @@ jobs: set -euo pipefail git merge-base --is-ancestor "$RUNTIME_SOURCE_COMMIT" HEAD changed="$(git diff --name-only "$RUNTIME_SOURCE_COMMIT"...HEAD)" - unexpected="$(printf '%s\n' "$changed" | grep -Ev '^(\.github/workflows/llup60-automated-replay\.yml|docs/workstreams/llup-v0-3-automated-replay\.md)$' || true)" + unexpected="$(printf '%s\n' "$changed" | grep -Ev '^(\.github/workflows/llup60-automated-replay\.yml|docs/workstreams/(README\.md|llup-v0-3-automated-replay\.md))$' || true)" if [[ -n "$unexpected" ]]; then echo "LLUP-60 evidence branch changes runtime/product files:" >&2 printf '%s\n' "$unexpected" >&2