What:
- add the missing changelog entry for the npm-shrinkwrap default slice
- replace the long performance run diary with a compact current-state audit
- move CI audit tooling under maintainers/scripts and trim the log parser
- make optional closure/evaluator metering opt-in by default
- remove the unused nix-eval-jobs cache parser from the PR surface
Why:
- keep PR #100 focused on fast, simple builds rather than audit-tool sprawl
- preserve decision-grade performance evidence without public scratch history
- separate maintainer audit tooling from product/release scripts
Tests:
- git diff --check
- bash syntax checks for changed shell scripts
- node syntax checks for changed JS audit/package scripts
- workflow YAML parse
- moved CI wrapper dry-run smoke
- GitHub CI log parser smoke on run 27062082132
What:
- add a NixOS test log timing summarizer for existing test-driver output
- add a non-blocking wrapper for the Linux hm-activation check log
- run the timing summary after Linux aggregate CI in PR and pin workflows
Why:
- expose what the Linux apply proof spends time on without adding a new Nix dependency
- keep CI speed work tied to phase-level remote evidence instead of opaque aggregate duration
Tests:
- node --check scripts/summarize-nixos-test-log.mjs
- bash -n scripts/summarize-hm-activation-timing.sh scripts/ci-nix-build.sh
- git diff --check
- RUNNER_TEMP=/tmp scripts/summarize-hm-activation-timing.sh x86_64-linux local-linux-hm-activation-timing
What:
- let the macOS activation smoke use a prebuilt activation package path
- pass the Darwin CI aggregate result into the activation step in CI and pin validation
- keep the old local fallback that builds hm-activation-macos-package when no path is provided
Why:
- avoid re-evaluating/rebuilding the Home Manager activation package immediately after the aggregate already built it
- keep the impure launchd/apply proof intact while removing duplicate workflow work
Tests:
- bash -n scripts/hm-activation-macos.sh scripts/ci-nix-build.sh scripts/update-pins.sh
- ruby -e 'require "yaml"; ARGV.each { |path| YAML.load_file(path) }; puts "yaml ok"' .github/workflows/ci.yml .github/workflows/pin-stable-openclaw-version.yml garnix.yaml
- node --check scripts/summarize-nix-build-log.mjs
- node --check scripts/summarize-nix-build-closure.mjs
- node --check scripts/summarize-nix-eval-jobs.mjs
- RUNNER_TEMP=/tmp NIX_METER_BUILD_CLOSURE=0 scripts/ci-nix-build.sh local-darwin-ci-prebuilt-activation --accept-flake-config --option max-jobs 2 .#checks.aarch64-darwin.ci
- /usr/bin/time -p env OPENCLAW_HM_ACTIVATION_PACKAGE="$PWD/result" scripts/hm-activation-macos.sh
- cmp -s "$PWD/result/activate" "$(nix build --accept-flake-config --no-link --print-out-paths .#checks.aarch64-darwin.hm-activation-macos-package)/activate"
What:
- default the CI wrapper to the timestamped stderr meter only
- create and parse the json-log-path sidecar only when NIX_METER_JSON_LOG is enabled
- avoid printing a json-log path when the sidecar is disabled
Why:
- keep deep Nix internal-json telemetry available for investigations
- avoid paying structured sidecar parse overhead on every default CI run
- reduce misleading empty sidecar artifacts in normal proof logs
Tests:
- bash -n scripts/ci-nix-build.sh
- node --check scripts/summarize-nix-build-log.mjs
- RUNNER_TEMP=/tmp NIX_METER_BUILD_CLOSURE=0 scripts/ci-nix-build.sh local-json-sidecar-default-off --accept-flake-config --no-link .#checks.aarch64-darwin.config-validity
- RUNNER_TEMP=/tmp NIX_METER_JSON_LOG=1 NIX_METER_BUILD_CLOSURE=0 scripts/ci-nix-build.sh local-json-sidecar-opt-in --accept-flake-config --no-link .#checks.aarch64-darwin.config-validity
- git diff --check
What:
- capture Nix internal-json events with json-log-path in the CI meter wrapper
- parse bare internal-json event lines in the existing build-log summarizer
- print a separate structured summary without changing normal stderr logs
Why:
- improve CI build attribution without making GitHub logs unreadable
- catch built derivations that human stderr can undercount during rebuild/check paths
Tests:
- bash -n scripts/ci-nix-build.sh: pass
- node --check scripts/summarize-nix-build-log.mjs: pass
- scripts/summarize-nix-build-log.mjs --label local-json-log-sidecar /tmp/nix-openclaw-json-log.tneQIN.jsonl: pass
- RUNNER_TEMP=/tmp NIX_METER_BUILD_CLOSURE=0 scripts/ci-nix-build.sh local-json-sidecar-wrapper --accept-flake-config --no-link .#checks.aarch64-darwin.config-validity: pass
- RUNNER_TEMP=/tmp NIX_METER_BUILD_CLOSURE=0 scripts/ci-nix-build.sh local-json-sidecar-rebuild --accept-flake-config --rebuild --no-link .#checks.aarch64-darwin.config-validity: pass
What:
- track copied store-path names by substituter in the Nix CI log summarizer
- render bounded copied-name lists for custom substituters such as Garnix
- keep default NixOS and Determinate cache path names out of the summary
Why:
- make Garnix shutdown risk measurable from every CI log
- identify which OpenClaw/package/check artifacts are coming from non-default caches without adding CI work
Tests:
- node --check scripts/summarize-nix-build-log.mjs
- scripts/summarize-nix-build-log.mjs --github-log /tmp/nix-openclaw-ci-logs/run-27052991416.log
- git diff --check
What:
- capture Nix build-result JSON in the CI meter by default
- read drvPath values from the captured build result before falling back to output derivers
- keep plain output-path parsing for compatibility with older/manual meter runs
Why:
- substituted outputs may not retain deriver metadata on hosted macOS runners
- using Nix's build-result JSON gives the closure meter the exact derivation path without a second eval
- keeps Linux and macOS closure hotspot summaries comparable
Tests:
- node --check scripts/summarize-nix-build-closure.mjs && bash -n scripts/ci-nix-build.sh && git diff --check
- tmp=$(mktemp /tmp/nix-openclaw-json-output.XXXXXX); nix build --accept-flake-config --json --no-link .#checks.aarch64-darwin.ci > "$tmp"; scripts/summarize-nix-build-closure.mjs --label json-darwin-ci --limit 5 "$tmp"
- RUNNER_TEMP=/tmp NIX_METER_BUILD_CLOSURE=1 scripts/ci-nix-build.sh local-darwin-ci-json-closure-probe --accept-flake-config --no-link .#checks.aarch64-darwin.ci
- RUNNER_TEMP=/tmp NIX_METER_BUILD_CLOSURE=1 scripts/ci-nix-build.sh local-linux-ci-json-closure-probe --accept-flake-config --no-link .#checks.x86_64-linux.ci
What:
- capture metered nix build output paths without changing the requested installable
- add a post-build closure summarizer that walks the built output deriver and reports the heaviest build-closure store paths
- append the closure summary to the same CI step summary as the existing Nix meter
Why:
- make CI cost attributable to concrete store paths instead of only aggregate fetch/copy counts
- keep the proof path intact while exposing the next optimization targets
- use Nix JSON format v2 path-info output so the parser avoids the deprecated implicit JSON format
Tests:
- node --check scripts/summarize-nix-build-closure.mjs && node --check scripts/summarize-nix-build-log.mjs
- bash -n scripts/ci-nix-build.sh
- git diff --check
- RUNNER_TEMP=/tmp NIX_METER_BUILD_CLOSURE=0 scripts/ci-nix-build.sh local-darwin-ci-no-closure --accept-flake-config --dry-run .#checks.aarch64-darwin.ci
- RUNNER_TEMP=/tmp NIX_METER_BUILD_CLOSURE=1 scripts/ci-nix-build.sh local-darwin-ci-closure-probe --accept-flake-config --no-link .#checks.aarch64-darwin.ci
- RUNNER_TEMP=/tmp NIX_METER_BUILD_CLOSURE=1 scripts/ci-nix-build.sh local-linux-ci-closure-probe --accept-flake-config --no-link .#checks.x86_64-linux.ci
What:\n- remove the rejected Magic Nix Cache workflow steps\n- timestamp Nix stderr sidecar logs in the CI meter\n- capture NIX_SHOW_STATS and render eval/phase hints in build summaries\n\nWhy:\n- remote proof showed the GitHub Actions Nix cache made Linux slower and blocked macOS proof\n- eval counters and phase offsets give us a better signal for the next CI speed pass without changing the derivations under test\n\nTests:\n- node --check scripts/summarize-nix-build-log.mjs\n- bash -n scripts/ci-nix-build.sh\n- ruby -e 'require "yaml"; ARGV.each { |path| YAML.load_file(path) }; puts "yaml ok"' .github/workflows/ci.yml .github/workflows/pin-stable-openclaw-version.yml garnix.yaml\n- scripts/summarize-nix-build-log.mjs --github-log /tmp/nix-openclaw-ci-logs/run-27047041028.log\n- RUNNER_TEMP=/tmp scripts/ci-nix-build.sh local-darwin-ci --accept-flake-config --no-link .#checks.aarch64-darwin.ci\n- git diff --check
What:
- add a CI nix-build wrapper that tees Nix stderr and appends a compact build summary
- add a log summarizer for local wrapper logs and downloaded GitHub Actions logs
- wrap the Linux and Darwin aggregate build steps in CI and stable-pin validation
Why:
- make remote CI bottlenecks visible by step without changing the check graph
- distinguish planned fetches, copied paths, built derivations, warnings, and elapsed time
Tests:
- node --check scripts/summarize-nix-build-log.mjs
- bash -n scripts/ci-nix-build.sh
- scripts/summarize-nix-build-log.mjs --github-log /tmp/nix-openclaw-ci-logs/run-27046069426.log
- RUNNER_TEMP=/tmp scripts/ci-nix-build.sh local-darwin-ci --accept-flake-config --no-link .#checks.aarch64-darwin.ci
- ruby -e 'require "yaml"; ARGV.each { |path| YAML.load_file(path) }; puts "yaml ok"' .github/workflows/ci.yml .github/workflows/pin-stable-openclaw-version.yml garnix.yaml
- git diff --check
What:
- localize the bundled ACPX package under the npm gateway derivation
- remove the old source-check derivation and pnpm hash refresh from the stable pin path
- narrow CI/Garnix to the stable npm package surface and keep dogfood out of the default aggregate
- add a minimal maintainer performance audit with commands and current measurements
Why:
- make upstream npm shrinkwrap the default build path without keeping the old source-build checks in normal CI
- reduce special-case knobs, workflow path duplication, and unnecessary Garnix expansion
- keep future packaging changes measurable instead of relying on one-off notes
Tests:
- bash -n scripts/update-pins.sh nix/scripts/check-package-contents.sh nix/scripts/openclaw-gateway-npm-install.sh nix/scripts/openclaw-bundled-acpx-install.sh
- node --check nix/scripts/patch-openclaw-npm-dist.mjs && node --check nix/scripts/update-openclaw-runtime-plugin-locks.mjs
- ruby -e 'require "yaml"; YAML.load_file("garnix.yaml"); YAML.load_file(".github/workflows/ci.yml"); YAML.load_file(".github/workflows/pin-stable-openclaw-version.yml")'
- GITHUB_ACTIONS=true /usr/bin/time -p scripts/update-pins.sh apply v2026.6.1 2e08f0f4221f522b60423ed6ffd83427942b28de v2026.6.1 https://github.com/openclaw/openclaw/releases/download/v2026.6.1/OpenClaw-2026.6.1.zip (80.28s)
- nix build --accept-flake-config --no-link --print-out-paths .#checks.aarch64-darwin.ci (pass; 42.56s cached, later 111.16s with auto-GC)
- nix build --accept-flake-config --no-link --print-out-paths .#checks.x86_64-linux.ci (pass; 43.58s cached, later 216.83s with auto-GC)
Include the OpenClaw and ACPX wrapper package.json/package-lock files in pin validation digests, promote digests, quiet checks, and git add so future source version bumps commit the lockfiles that the npm builders assert against.
Also back up and restore the wrapper files during update-pins failure cleanup.
Proof: ruby YAML parse for pin-stable workflow; bash -n scripts/update-pins.sh; git diff --check; GITHUB_ACTIONS=true scripts/update-pins.sh select.
Build the default OpenClaw gateway from the published npm package path and its lockfile-backed dependency cache, while keeping the source builder as the explicit fallback for custom gatewaySrc/dogfood use.
Add wrapper lockfiles and hashes for openclaw and @openclaw/acpx, stage the published dist into the legacy runtime layout expected by nix-openclaw checks, and patch the bundled discovery chunk so Nix-store runtime plugin roots are trusted in Nix mode.
Teach update-pins to refresh npm wrapper locks/hashes alongside the source fallback pnpm hash.
Proof: nix build --accept-flake-config --no-link --print-out-paths .#checks.aarch64-darwin.ci .#checks.x86_64-linux.ci; new default gateway forced rebuild 51.03s; old source-builder forced rebuild failed determinism after 399.37s.
Add Home Manager runtimePackages/environment options that feed the gateway wrapper without polluting the user PATH. Link the same runtime package set into Codex's isolated agent home so shell calls from the Codex harness see Nix-managed plugin and helper CLIs.
Tests: ./scripts/check-flake-lock-owners.sh; nix flake show --accept-flake-config; nix build --accept-flake-config .#checks.aarch64-darwin.ci --no-link; nix build --accept-flake-config .#checks.aarch64-darwin.qmd-runtime .#checks.aarch64-darwin.bin-surface .#checks.aarch64-darwin.package-contents --no-link; nix eval --accept-flake-config .#checks.x86_64-linux.default-instance.drvPath; ./scripts/hm-activation-macos.sh
Make QMD the Nix-supported batteries-included local memory backend by pinning the upstream QMD flake and adding qmd to the private openclaw wrapper PATH.
Keep QMD opt-in through upstream OpenClaw config with memory.backend = qmd, and document that builtin memorySearch.provider = local remains an escape hatch rather than the primary supported Nix path.
Also point nix run .#openclaw at the batteries-included bundle so app execution gets the same internal runtime PATH as the package.
Tests: sh -n nix/scripts/check-openclaw-qmd-runtime.sh; scripts/check-flake-lock-owners.sh; git diff --check; nix flake show --accept-flake-config --json; nix build .#checks.aarch64-darwin.qmd-runtime .#checks.aarch64-darwin.bin-surface .#packages.aarch64-darwin.openclaw --accept-flake-config --no-link --print-out-paths; nix build .#checks.x86_64-linux.qmd-runtime --accept-flake-config --no-link --print-out-paths; nix build .#checks.aarch64-darwin.ci --accept-flake-config --no-link --print-out-paths; nix build .#packages.x86_64-linux.openclaw .#checks.x86_64-linux.bin-surface --accept-flake-config --no-link --print-out-paths; scripts/hm-activation-macos.sh; nix build .#checks.x86_64-linux.gateway-smoke --accept-flake-config --no-link --print-out-paths; nix run .#openclaw --accept-flake-config -- --version; bash -n scripts/update-pins.sh; node --check scripts/select-openclaw-release.mjs; node --check scripts/select-openclaw-release.test.mjs; node scripts/select-openclaw-release.test.mjs
Fix the documented minimal Home Manager plugin option to use customPlugins, remove the stale duplicate skill-file builder, and assert duplicate plugin skill paths against the paths Home Manager actually installs.
Add local plugin fixtures so the default-instance check covers the agent-facing customPlugins path and duplicate skill collisions.
The stable mirror had advanced main to v2026.4.14 with an app hash that only
matched prefetch output, not the unpacked tree hash that fetchzip validates.
That left macOS CI red on the pinned stable release.
Compute the app hash from the unpacked zip contents in update-pins.sh and fix
the current v2026.4.14 app pin to the actual fetchzip hash.
Tests:
- bash -n scripts/update-pins.sh
- nix build .#openclaw-app --accept-flake-config -L
What:
- split the stable-release updater into read-only selection and pin materialization modes
- rewrite yolo into select, validate-linux, validate-macos, and promote jobs
- fail yolo when the newest stable release is incomplete instead of silently sticking
- update maintainer docs to describe the new safe promotion policy
Why:
- stop direct yolo pushes from moving main without the same Linux and macOS proof as CI
- keep mirroring the newest stable release while making broken upstream releases visible
Tests:
- bash -n scripts/update-pins.sh
- ruby -e 'require "yaml"; YAML.load_file(".github/workflows/yolo-update.yml"); puts "yaml-ok"'
- GITHUB_ACTIONS=true GH_TOKEN="$(gh auth token)" scripts/update-pins.sh select
- temp copy pinned to v2026.4.11: scripts/update-pins.sh select emits v2026.4.14 tuple
What:
- switch the pin updater from upstream-main commit selection to upstream stable release selection
- restore Yolo Update Pins as an hourly stable-release poller
- update maintainer and README docs to describe stable-release mirroring and the recovery lesson
Why:
- OpenClaw stable is already tag-based upstream, and nix-openclaw should mirror that release line directly
- this removes the old latest-green-main churn model and keeps source + app pins tied to one published upstream release
Tests:
- bash -n scripts/update-pins.sh
- GITHUB_ACTIONS=true GH_TOKEN="$(gh auth token)" scripts/update-pins.sh
What:
- export node_modules/.pnpm/node_modules/.bin in gateway build before canvas:a2ui:bundle
so rolldown is found in sandbox/offline builds
- track openclaw bump failure in scripts/update-pins.sh and fail the workflow when
openclaw upstream is ahead but no openclaw pin update was produced
Why:
- yolo was reporting success while silently restoring old pins
- openclaw bump attempts were repeatedly failing at A2UI bundling, keeping pins stale
Tests:
- bash -n scripts/update-pins.sh
- bash -n nix/scripts/gateway-build.sh
Add a Linux nixosTest for Home Manager activation and a macOS CI workflow to run the activation script.
Wire the new Linux check into flake checks and Garnix.
Tests: not run (CI wiring).
What: handle prefetched tarball store paths when regenerating config options
Why: nix store prefetch-file now returns tarball path even with --unpack
Tests: not run (script change)