diff --git a/README.md b/README.md
index 9e76892..67289cb 100644
--- a/README.md
+++ b/README.md
@@ -2,6 +2,11 @@
CPU scheduling that learns inside the Linux kernel.
+Lertick always owns its scheduling model. New installations automatically exchange
+compatible, fresh advice with a running Lerwatt; no peer-specific install flag is
+required. Missing or removed peers leave scheduling independent. Neither project
+starts, replaces or retrains the other. [Automatic coordination](docs/INTEGRATION.md).
+
## Performance at a glance
**Lertick online mode recorded 50.34% lower short-burst completion p99 than Linux EEVDF in our VM benchmark.** Mixed-contention tests also expose clear regressions:
diff --git a/deploy.sh b/deploy.sh
index b42f9b9..9141038 100755
--- a/deploy.sh
+++ b/deploy.sh
@@ -20,7 +20,7 @@ Usage: ./deploy.sh COMMAND [OPTIONS]
restart Restart this service; never stop another scheduler.
enable Start now and enable this service at boot.
disable Stop now and disable this service at boot.
- integration [on|off|status] Persist optional Lerwatt coordination (default off).
+ integration [auto|on|off|status] Persist optional Lerwatt coordination (default auto).
update Build/verify a candidate before stopping/replacing this service.
rollback Restore the previous complete release, preserving service intent.
uninstall Disable/stop this service and remove its installed files.
@@ -106,8 +106,8 @@ validate_options()
case $command in
plan|run|trial) validate_options "$@" ;;
integration)
- [ "$#" -le 1 ] || fail 'integration accepts on, off or status'
- case ${1:-status} in on|off|status) ;; *) fail 'integration accepts on, off or status' ;; esac
+ [ "$#" -le 1 ] || fail 'integration accepts auto, on, off or status'
+ case ${1:-status} in auto|on|off|status) ;; *) fail 'integration accepts auto, on, off or status' ;; esac
;;
report) for arg in "$@"; do case $arg in --include-logs|--help) ;; *) fail "unsupported report option: $arg" ;; esac; done ;;
*) [ "$#" -eq 0 ] || fail "$command does not accept options" ;;
@@ -115,7 +115,7 @@ esac
if [ "$command" = plan ]; then
printf 'Run plan (not executed; requires a compatible idle sched_ext kernel):\n'
if [ "$duration" -eq 0 ]; then set -- --summary-only --sample-ms 60000 "$@"; fi
- for word in sudo "$loader" --mode online --duration 0 --track-all --cpuperf auto "$@"; do
+ for word in sudo "$loader" --mode online --duration 0 --track-all --cpuperf auto --lerwatt-auto "$@"; do
escaped=$(printf '%s' "$word" | sed "s/'/'\\\\''/g"); printf " '%s'" "$escaped"
done
printf '\nNo scheduler was loaded or attached.\n'; exit 0
@@ -274,9 +274,9 @@ if [ "$duration" -eq 0 ]; then
else printf 'Lertick: starting finite foreground scheduler; Ctrl-C detaches it.\n' >&2; fi
[ -z "$pending_signal" ] || finish_run "$pending_status"
if [ "$stats_requested" = yes ]; then
- "$loader" --mode online --duration "$base_duration" --track-all --cpuperf auto "$@" &
+ "$loader" --mode online --duration "$base_duration" --track-all --cpuperf auto --lerwatt-auto "$@" &
else
- "$loader" --mode online --duration "$base_duration" --track-all --cpuperf auto "$@" >/dev/null &
+ "$loader" --mode online --duration "$base_duration" --track-all --cpuperf auto --lerwatt-auto "$@" >/dev/null &
fi
child=$!
[ -z "$pending_signal" ] || stop_run "$pending_status"
diff --git a/docs/DEPLOYMENT.md b/docs/DEPLOYMENT.md
index 5d21154..80b3419 100644
--- a/docs/DEPLOYMENT.md
+++ b/docs/DEPLOYMENT.md
@@ -385,8 +385,8 @@ overlay from the read-only base.
## CPU frequency targets and the Lerwatt plug-in
-Native service setup is now available through `lertick integration on` and
-Lerwatt's `./deploy.sh install --lertick`. Models and release versions remain
+New managed services and foreground helper runs discover optional Lerwatt advice
+automatically; ordinary installations need no peer-specific flag. Models and release versions remain
independent; compatible model-only updates do not change the contract ABI.
See [setup, ownership and ABI compatibility](INTEGRATION.md) before using the
raw-loader research commands below.
@@ -402,7 +402,7 @@ lowers idle CPUs' targets. `auto` selects `util` when any policy's
a VM without cpufreq, `auto` keeps the previous behavior. The registered event
records `cpuperf` and `cpuperf_reason`; snapshots then add a `cpus` array.
-`--lerwatt` pins `lw_contract` and `lt_demand` under `/sys/fs/bpf/lertick/`
+`--lerwatt-auto` enables best-effort discovery and keeps ordinary scheduling if publication is unavailable. It does not force `cpuperf util` merely because no peer is present. The advanced strict `--lerwatt` mode pins `lw_contract` and `lt_demand` under `/sys/fs/bpf/lertick/`
before attaching, publishes demand every 100 ms, and unpins them at exit; a
pin failure aborts before the default scheduler is replaced. Clusters are the
cpufreq policies sorted by first CPU. Without real policies, Lertick uses a
diff --git a/docs/INSTALL.md b/docs/INSTALL.md
index 14a369b..c4af518 100644
--- a/docs/INSTALL.md
+++ b/docs/INSTALL.md
@@ -138,7 +138,7 @@ lertick trial --mode shadow --duration 30 \
The default bounds are 0.5–5 ms after eight valid labels, with a 2 ms fallback. They bound newly assigned budgets, not exact execution time or the remaining budget of an already running task. Lower prediction error has not established a general throughput or latency advantage; consult the [recorded comparisons](PHASE2_RESULTS.md) and [long-window confirmation](../results/model-confirmation/README.md).
-The helper forwards `--mode`, `--duration`, `--sample-ms` and `--stats-json`; tracking and CPU-performance defaults are fixed for this simple path. Optional persistent Lerwatt coordination uses `lertick integration on`; follow the [shared integration guide](INTEGRATION.md) to configure both projects. For targeted tracking, partial switching, full research snapshots or model experiments, use the advanced loader instructions in [deployment and reproduction](DEPLOYMENT.md). Model tracking and the set of scheduled tasks are distinct: selecting a TGID for learning alone does not isolate scheduling to that process.
+The helper forwards `--mode`, `--duration`, `--sample-ms` and `--stats-json`; tracking and CPU-performance defaults are fixed for this simple path. New installations automatically discover compatible Lerwatt advice; no integration-enable command is needed. Existing saved opt-outs remain respected. Follow the [shared integration guide](INTEGRATION.md) to configure both projects. For targeted tracking, partial switching, full research snapshots or model experiments, use the advanced loader instructions in [deployment and reproduction](DEPLOYMENT.md). Model tracking and the set of scheduled tasks are distinct: selecting a TGID for learning alone does not isolate scheduling to that process.
## Telemetry and permissions
diff --git a/docs/INTEGRATION.md b/docs/INTEGRATION.md
index 0b339e0..674fba3 100644
--- a/docs/INTEGRATION.md
+++ b/docs/INTEGRATION.md
@@ -2,7 +2,7 @@
[← Project home](../README.md) · [Lertick](https://github.com/Denny-Lin/lertick) · [Lerwatt](https://github.com/Denny-Lin/lerwatt)
-Use each controller on its own, or enable an optional local link. **Lertick owns scheduling; Lerwatt owns CPU power constraints.** They exchange demand and limits while keeping their models, training and service lifecycles separate.
+Each controller works on its own and discovers an optional compatible local peer automatically. **Lertick owns scheduling; Lerwatt owns CPU power constraints.** They exchange demand and limits while keeping their models, training and service lifecycles separate.
```mermaid
flowchart LR
@@ -16,34 +16,60 @@ flowchart LR
linkStyle default stroke:#86868b,stroke-width:1.5px
```
-## Enable the link
+## Automatic discovery
-First satisfy each project's native Linux requirements. Lerwatt needs usable CPUFreq controls and temperature inputs. Lertick additionally needs a compatible running sched_ext kernel, BTF and its BPF toolchain. Installing these services does not install or change the kernel. A compatible running kernel needs no reboot for activation; changing kernels can require one.
+Install each project normally after satisfying its native Linux requirements.
+Lerwatt needs CPUFreq controls, temperature inputs and Linux userspace BPF
+headers. Lertick also needs a supported running sched_ext kernel, BTF and its
+BPF toolchain. Neither installer changes the kernel or installs its peer.
-From the **Lerwatt checkout**, as your normal user:
+From the Lerwatt checkout, as your normal user:
```sh
./deploy.sh check
-./deploy.sh install --lertick --period-ms 100
-lerwatt status
+./deploy.sh install
```
-This builds the BPF-capable daemon locally, then installs, enables and starts Lerwatt. Usable Linux userspace BPF headers are required; Lerwatt's contract uses raw BPF syscalls without libbpf. Its service configuration stores `contract=auto`. Coordination accepts a **10–100 ms** control period; the default is 100 ms. A normal independent installation keeps `contract=off` and the BPF-free build.
-
-From the **Lertick checkout**, as your normal user:
+From the Lertick checkout, as your normal user:
```sh
./deploy.sh install
-lertick integration on
lertick enable
-lertick status
```
-Lertick installation adds files without starting or enabling its service. `integration on` saves the optional link setting; `enable` starts now and at boot. Changing integration on an already running managed Lertick service restarts that service with verified settings, preserving its prior active/enabled intent. It does not restart Lerwatt.
-
-**Either project may start first.** An absent or incompatible peer leaves the other operating independently. Neither unit requires the peer through `Requires=` or `BindsTo=`, and neither project starts, stops, upgrades or restarts the peer.
-
-## Check or turn off coordination
+New Lerwatt installations save `contract=auto` and build the BPF-capable daemon;
+new Lertick installations save `lerwatt=auto`. No `--lertick` installation flag
+or `integration on` command is needed. Lertick installation still does not start
+or enable scheduling: `enable` is the user's normal service activation, not a
+peer-specific step. A plain upgrade preserves saved settings, including explicit
+`off` opt-outs from earlier versions. For such a saved opt-out, choose auto once
+with `./deploy.sh install --lertick` or `lertick integration auto` respectively.
+
+**Either project may start first.** A service may be installed without running;
+installation files alone do not prove a usable peer. Connection requires the
+real maps plus matching ABI, topology, attachment session and fresh records.
+Missing, stopped, removed, expired or incompatible peers leave local operation
+independent. Running peers reconnect automatically; uninstalling a peer does not
+stop the remaining controller. Neither service has `Requires=` or `BindsTo=` on
+the other, and neither starts, stops, upgrades or retrains its peer.
+
+Discovery is separate from prediction. Lerwatt keeps its own learned thermal
+model and MPC, Lertick keeps its own scheduler model. Peer appearance/disappearance
+never changes the selected prediction algorithm or private weights. New Lerwatt
+installs use nominal MPC; cooperative prediction can be selected separately
+with `--prediction cooperative` and retains all reliability gates. Earlier
+saved cooperative choices are preserved. Installing Lertick does not make
+Lerwatt forecasts more accurate.
+
+Lertick's automatic mode is best effort: inability to discover topology, obtain
+an attachment identity or publish maps leaves ordinary scheduling available.
+It never overwrites foreign pins. Transport unavailable at initial attachment
+requires a later local Lertick restart after the environment is repaired; this
+is distinct from an absent peer, which connects later without restart. Raw
+loader `--lerwatt` retains strict failure behavior for diagnostics. Raw loader
+users can select `--lerwatt-auto`; normal helper/service runs do so automatically.
+
+## Inspect or override discovery
```sh
lerwatt status
@@ -53,21 +79,27 @@ lertick status
sudo systemctl show lerwatt.service lertick.service --property=StatusText
```
-Configured `auto`/`on` means the link is requested. Inspect current worker status and logs for **connected**, **waiting** or **off**; an enabled service or saved setting alone does not prove a fresh compatible exchange. Waiting can mean the peer has not started, its records expired, or its ABI/topology/session is incompatible. Do not interpret a counter from an older run as proof of a current connection.
+Configured `auto` means discovery is enabled; it does not mean connected.
+Worker status/logs report **connected**, **waiting** or **off**. Waiting includes
+an absent/inactive peer and rejected stale/incompatible records. Do not interpret
+an older counter or an installed executable as current connectivity. Coordination
+uses a 10–100 ms control period, with 100 ms the default.
-To keep both controllers but remove their optional link, run this from the Lerwatt checkout:
+Advanced explicit opt-outs remain available:
```sh
+# From the Lerwatt checkout:
./deploy.sh install --no-lertick
-```
-
-Then:
-
-```sh
+# Only the Lertick managed service:
lertick integration off
```
-Plain Lerwatt reinstallation and Lertick updates preserve their saved integration settings. Lerwatt stores its setting in `/etc/lerwatt/lerwatt.conf`; Lertick uses `/etc/lertick/integration.conf`. Both are private root-owned data files, parsed without sourcing shell code. Turning off coordination does not disable either controller's own learning.
+These choices affect exchange, not either controller's learning. They do not
+change Lerwatt's selected nominal/cooperative predictor. Lertick changes only
+its own service when a managed integration setting is explicitly changed.
+The strict legacy `lertick integration on` mode remains available. Configuration
+is root-private strict data in `/etc/lerwatt/lerwatt.conf` and
+`/etc/lertick/integration.conf`, never sourced as shell commands.
## Control ownership
diff --git a/results/automatic-discovery/README.md b/results/automatic-discovery/README.md
new file mode 100644
index 0000000..a59d4a0
--- /dev/null
+++ b/results/automatic-discovery/README.md
@@ -0,0 +1,121 @@
+# Automatic independent peer discovery
+
+Validation snapshots: Lerwatt base `cc8b65f`, Lertick base `5ce8a3c`, both on
+`automatic-peer-discovery`. Each controller retains its own learning, controls
+and lifecycle. No peer-specific installation flag is needed for new managed
+installations. [Shared protocol and usage](../../docs/INTEGRATION.md).
+
+## Implemented behavior
+
+- New Lerwatt installations build the BPF-capable daemon and save
+ `contract=auto`. No peer is installed or started by Lerwatt. Its own learned
+ thermal model and MPC continue while waiting for a compatible running peer.
+- New Lertick installations save `lerwatt=auto`. Normal service/foreground
+ helper runs expose optional advice without an integration-enable command.
+ Installing Lertick still does not start/enable scheduling; the normal local
+ `lertick enable` command does that. Automatic mode preserves the previous
+ governor-based CPUperf selection rather than forcing util just for discovery.
+- Real maps plus fresh matching ABI 2, topology and attachment identities are
+ required. File/package presence is not connection proof. Either startup order,
+ peer stop and return, expired records and incompatible data retain independent
+ operation. Neither project starts, stops or retrains its peer.
+- Discovery never changes the selected predictor. New Lerwatt installs retain
+ nominal independent MPC; cooperative prediction can be selected separately.
+ Saved cooperative choices remain selected with or without Lertick. This
+ supersedes the short-lived coupling between `--lertick` and prediction default.
+ Existing saved settings, including explicit off opt-outs, survive upgrades.
+- Automatic Lertick discovery preserves existing pins. If topology/identity or
+ map publication is unavailable, it schedules independently. A writable BPF
+ guard disables advice consumption before attach after publication failure,
+ including a partial-publication case. The legacy strict `--lerwatt` diagnostic
+ path retains its existing semantics; it is not the normal auto mode.
+- An absent peer can join later without restart. A failed local transport setup
+ (for example conflicting pins) remains unavailable until its environment is
+ repaired and Lertick is locally restarted. Unknown pins are not force-cleaned.
+ A compatible peer's appearance is not evidence of better forecast accuracy.
+
+## Validation
+
+[Linux build/model/deployment/e2e log](linux-build-and-tests.log) passed, including
+both projects' required deployment and ABI checks. The peer's 56 compiled ABI
+values match; the shared contract layout and integration guide are unchanged
+between the two tested sources. [Source identity](source-identity.json) verifies
+62 Lerwatt and 45 Lertick source/test/protocol files against the VM.
+
+Real PID 1 service tests proved new defaults without explicit prediction/link
+opt-in, absent-peer waiting, unlimited ready operation, recovery/restoration,
+independent enablement intent, update/rollback and uninstall behavior. See
+[initial lifecycle log](initial-live-pair.log) and
+[final kernel/lifecycle log](status-fixture-failure.log); the latter's lifecycle
+checks pass, but its subsequent obsolete status-label fixture fails as recorded
+below. The final paired test is separately successful.
+
+The [final live test](final-live-pair.log), [machine-readable result](result.json)
+passed nine real sched_ext/BPF checks on Ubuntu 24.04.5 ARM64, kernel
+`7.0.0-38-generic`, systemd 255 and eight vCPUs:
+
+1. Lerwatt-first automatic connection, with actual BPF contract consumption.
+2. Scheduler restart with a new session and automatic reconnection.
+3. Changing scheduler online/EWMA mode leaves Lerwatt's PID running.
+4. Frozen demand expires; resuming its publisher restores fresh exchange.
+5. Lerwatt restart restores controls, expires advice and reconnects.
+6. Changing Lerwatt's temporal/adaptive forecaster leaves Lertick's PID/session.
+7. Lertick-first automatic connection.
+8. Final control restoration, default-scheduler fallback and owned-pin cleanup.
+9. A conflicting test-owned pin retains its map identity; automatic mode still
+ registers an independent scheduler, reports off, and applies no contract.
+
+All hardware controls were temporary simulator files. Kernel sched_ext/BPF and
+PID 1 were real. Production services and physical CPU controls were not changed;
+no VM reboot was performed. Final sched_ext state is disabled. Raw artifacts
+remain retained in the VM and an ignored local archive; see [archive identity](raw-archive.json).
+
+## Performance and retained failures
+
+The [60 finite-work rows](finite-jobs.jsonl) reproduce earlier Linux outcomes
+excluding CPU timing. All work completes, but the same two cooperative pairs
+still regress in makespan by 0.02 and 0.07 seconds. The strict nonregression gate
+is still failed, not waived: [summary](control-summary.json). No prediction
+thresholds or learning parameters were tuned in this change. There is no new
+hardware energy, throughput, model accuracy or overhead claim; default discovery
+adds bookkeeping whose physical-machine cost still needs measurement.
+
+Failures remain recorded:
+
+- Old service/default and foreground-argv fixtures expected off/no-auto; the first
+ runs failed. Their expectations now test the new behavior while retaining
+ explicit off/on transaction coverage. [Service failure](first-service-fixture-failure.log),
+ [argv failure](old-argv-fixture-failure.log),
+ [fixed service](portable-service-fixtures.log),
+ [fixed foreground](portable-foreground-fixtures.log).
+- The first conflicting-pin test exposed the legacy preparation routine deleting
+ the test-owned same-name pin. Auto mode now uses a separate non-destructive
+ preparation path; it never invokes that stale-pin cleanup. Its test cleanup
+ also handles an already absent owned directory. [Failed run](first-conflict-failure.log).
+- After the production fix, the test waited for the wrong status prefix because
+ its custom unit name lacked the scheduler classifier. The fixture name was
+ corrected and a periodic runtime snapshot is now required before checking zero
+ applied advice. [Failed fixture](status-fixture-failure.log),
+ [final pass](final-live-pair.log).
+- A local macOS plug-in compile attempt failed on Linux-only integer/header
+ dependencies (`__u32`, `sys/statfs.h`), and is not counted as a pass. The Linux
+ native plug-in, CPUperf and actual kernel tests pass. No platform-porting claim
+ is made. The first two shell fixture sequences also continued to a documentation
+ check after an earlier failure; those earlier failures are retained, not
+ inferred successful from the enclosing shell exit code. Later validations use
+ `set -e` or individually checked results.
+
+Shared weights, joint retraining, physical energy savings, universal hardware
+compatibility and long-duration reliability are not established by these tests.
+
+## CI fixture timing follow-up
+
+The initial musl/BusyBox CI failed an update transaction because the stub-only
+service fixture exceeded its fixed 15-second subprocess limit; other platforms
+passed. [CI failure excerpt](ci-musl-failure.log) preserves the timeout. The
+fixture now allows 45 seconds and reports the last 12 stub events if it still
+times out. All transaction assertions remain. No production watchdog, kernel
+thermal setting, learning gate or performance acceptance criterion changed.
+This is not a runtime speedup claim. The matching revision passed the full
+[Linux service fixture](linux-service-fixture-followup.log); CI must pass this
+revision before merge.
diff --git a/results/automatic-discovery/ci-musl-failure.log b/results/automatic-discovery/ci-musl-failure.log
new file mode 100644
index 0000000..ed7c9aa
--- /dev/null
+++ b/results/automatic-discovery/ci-musl-failure.log
@@ -0,0 +1,50 @@
+2026-11-02T17:34:53.3529830Z (62/72) Installing cfdisk (2.41.6-r1)
+2026-11-02T17:34:53.3598514Z (63/72) Installing umount (2.41.6-r1)
+2026-11-02T17:34:53.3663964Z (64/72) Installing flock (2.41.6-r1)
+2026-11-02T17:34:53.3735985Z (65/72) Installing lsblk (2.41.6-r1)
+2026-11-02T17:34:53.3830577Z (66/72) Installing libcap-ng (0.8.5-r0)
+2026-11-02T17:34:53.3904490Z (67/72) Installing setpriv (2.41.6-r1)
+2026-11-02T17:34:53.3969703Z (68/72) Installing lscpu (2.41.6-r1)
+2026-11-02T17:34:53.4064862Z (69/72) Installing logger (2.41.6-r1)
+2026-11-02T17:34:53.4142784Z (70/72) Installing partx (2.41.6-r1)
+2026-11-02T17:34:53.4218291Z (71/72) Installing fstrim (2.41.6-r1)
+2026-11-02T17:34:53.4299460Z (72/72) Installing findmnt (2.41.6-r1)
+2026-11-02T17:34:53.4381436Z Executing busybox-1.37.0-r20.trigger
+2026-11-02T17:34:53.4464693Z OK: 293 MiB in 88 packages
+2026-11-02T17:34:53.5459397Z mkdir -p build
+2026-11-02T17:34:53.5482540Z cc -Isrc/userspace -O2 -std=gnu11 -Wall -Wextra -Werror tests/test_loader_runtime.c src/userspace/runtime.c -o build/test_loader_runtime
+2026-11-02T17:34:53.8562611Z python3 tests/check_integration_abi.py
+2026-11-02T17:34:53.9594018Z Integration ABI 2 golden check passed (56 values).
+2026-11-02T17:34:53.9684474Z build/test_loader_runtime
+2026-11-02T17:34:53.9818330Z loader runtime: private output, lock ownership, idle-state guard, readiness notify and maintenance lifetime passed
+2026-11-02T17:34:53.9821555Z sh tests/test_deploy.sh
+2026-11-02T17:35:46.4816372Z test_deploy: plan/check/verify, indefinite/finite defaults, signals, trust paths, maintenance, staging and build fingerprints passed (stubs only)
+2026-11-02T17:35:46.4896827Z sh tests/test_service.sh
+2026-11-02T17:37:51.4804581Z Traceback (most recent call last):
+2026-11-02T17:37:51.4806631Z File "", line 162, in
+2026-11-02T17:37:51.4807536Z File "", line 96, in run
+2026-11-02T17:37:51.4808300Z File "/usr/lib/python3.12/subprocess.py", line 550, in run
+2026-11-02T17:37:51.4809244Z stdout, stderr = process.communicate(input, timeout=timeout)
+2026-11-02T17:37:51.4810045Z ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+2026-11-02T17:37:51.4810884Z File "/usr/lib/python3.12/subprocess.py", line 1209, in communicate
+2026-11-02T17:37:51.4814888Z stdout, stderr = self._communicate(input, endtime, timeout)
+2026-11-02T17:37:51.4816335Z ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+2026-11-02T17:37:51.4817202Z File "/usr/lib/python3.12/subprocess.py", line 2116, in _communicate
+2026-11-02T17:37:51.4826043Z self._check_timeout(endtime, orig_timeout, stdout, stderr)
+2026-11-02T17:37:51.4828508Z File "/usr/lib/python3.12/subprocess.py", line 1253, in _check_timeout
+2026-11-02T17:37:51.4833472Z raise TimeoutExpired(
+2026-11-02T17:37:51.4835818Z subprocess.TimeoutExpired: Command '['/tmp/lertick-service-fixtures-z46ppmz6/source/scripts/service_manager.sh', '/tmp/lertick-service-fixtures-z46ppmz6/prefix', 'update', '/tmp/lertick-service-fixtures-z46ppmz6/source']' timed out after 15 seconds
+2026-11-02T17:37:51.4921639Z make: *** [Makefile:61: test-deployment] Error 1
+2026-11-02T17:37:51.9770854Z ##[error]Process completed with exit code 2.
+2026-11-02T17:37:51.9919922Z Post job cleanup.
+2026-11-02T17:37:52.0831988Z [command]/usr/bin/git version
+2026-11-02T17:37:52.0882111Z git version 2.56.0
+2026-11-02T17:37:52.0927350Z Temporarily overriding HOME='/home/runner/work/_temp/2e16060e-2014-4a7c-82d8-cb90e20e31cb' before making global git config changes
+2026-11-02T17:37:52.0928753Z Adding repository directory to the temporary git global config as a safe directory
+2026-11-02T17:37:52.0933421Z [command]/usr/bin/git config --global --add safe.directory /home/runner/work/lertick/lertick
+2026-11-02T17:37:52.0983900Z [command]/usr/bin/git config --local --name-only --get-regexp core\.sshCommand
+2026-11-02T17:37:52.1025441Z [command]/usr/bin/git submodule foreach --recursive sh -c "git config --local --name-only --get-regexp 'core\.sshCommand' && git config --local --unset-all 'core.sshCommand' || :"
+2026-11-02T17:37:52.1350502Z [command]/usr/bin/git config --local --name-only --get-regexp http\.https\:\/\/github\.com\/\.extraheader
+2026-11-02T17:37:52.1399332Z [command]/usr/bin/git submodule foreach --recursive sh -c "git config --local --name-only --get-regexp 'http\.https\:\/\/github\.com\/\.extraheader' && git config --local --unset-all 'http.https://github.com/.extraheader' || :"
+2026-11-02T17:37:52.1828243Z Cleaning up orphan processes
+2026-11-02T17:37:52.2310638Z ##[warning]Node.js 20 is deprecated. The following actions target Node.js 20 but are being forced to run on Node.js 24: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683. For more information see: https://github.blog/changelog/2025-09-19-deprecation-of-node-20-on-github-actions-runners/
diff --git a/results/automatic-discovery/control-summary.json b/results/automatic-discovery/control-summary.json
new file mode 100644
index 0000000..46d52cc
--- /dev/null
+++ b/results/automatic-discovery/control-summary.json
@@ -0,0 +1,49 @@
+{
+ "paired_runs": 30,
+ "all_work_completed": true,
+ "pairs_with_regressions": 2,
+ "corrected_queries": 2,
+ "limits": [
+ "One simulated board model; no hardware energy or Linux superiority claim.",
+ "Finite FIFO work is fully drained; historical served percentages are not used.",
+ "Correction queries are evaluations, not distinct decisions or savings.",
+ "Controller CPU time is simulator-harness measurement, not a real-time guarantee.",
+ "Descriptive paired differences, not statistical significance."
+ ],
+ "same_outcomes_as_prior_excluding_cpu_timing": true,
+ "regressions": [
+ {
+ "pattern": 4,
+ "seed": 7,
+ "joint": false,
+ "delta": {
+ "makespan_s": 0.01999999999998181,
+ "simulated_board_j": -0.259262999999919,
+ "job_p99_s": 0.01999999999998181,
+ "point_mae_c": 0.0028409999999999824,
+ "ceiling_moves": 0
+ },
+ "regressions": [
+ "makespan_s",
+ "job_p99_s",
+ "point_mae_c"
+ ]
+ },
+ {
+ "pattern": 4,
+ "seed": 99,
+ "joint": false,
+ "delta": {
+ "makespan_s": 0.06999999999993634,
+ "simulated_board_j": -0.12945599999966362,
+ "job_p99_s": 0.05999999999994543,
+ "point_mae_c": -0.0020789999999999975,
+ "ceiling_moves": 0
+ },
+ "regressions": [
+ "makespan_s",
+ "job_p99_s"
+ ]
+ }
+ ]
+}
diff --git a/results/automatic-discovery/final-live-pair.log b/results/automatic-discovery/final-live-pair.log
new file mode 100644
index 0000000..075316a
--- /dev/null
+++ b/results/automatic-discovery/final-live-pair.log
@@ -0,0 +1,8 @@
+integration: Lerwatt-first startup connected; BPF consumed contracts
+integration: online-to-EWMA scheduler change reconnected while Lerwatt stayed running
+integration: frozen demand expired and resumed without stale control
+integration: temporal-to-adaptive predictor change preserved Lertick PID and session
+integration: reverse startup order connected
+integration: conflicting pin preserved; auto mode scheduled independently
+{"environment": {"kernel": "7.0.0-38-generic", "architecture": "aarch64", "os_release": "PRETTY_NAME=\"Ubuntu 24.04.5 LTS\"\nNAME=\"Ubuntu\"\nVERSION_ID=\"24.04\"\nVERSION=\"24.04.5 LTS (Noble Numbat)\"\nVERSION_CODENAME=noble\nID=ubuntu\nID_LIKE=debian\nHOME_URL=\"https://www.ubuntu.com/\"\nSUPPORT_URL=\"https://help.ubuntu.com/\"\nBUG_REPORT_URL=\"https://bugs.launchpad.net/ubuntu/\"\nPRIVACY_POLICY_URL=\"https://www.ubuntu.com/legal/terms-and-policies/privacy-policy\"\nUBUNTU_CODENAME=noble\nLOGO=ubuntu-logo\n", "systemd": "systemd 255 (255.4-1ubuntu8.18)"}, "hardware": "temporary RK3588 simulated CPU-control files; real sched_ext/BPF", "checks": {"lerwatt_first_continuous_connection": true, "lertick_restart_new_session_reconnects": true, "independent_scheduler_policy_change": true, "frozen_demand_expires_and_recovers": true, "lerwatt_restart_restores_expires_reconnects": true, "independent_power_predictor_change": true, "lertick_first_connection": true, "final_control_restore_scheduler_fallback_pin_cleanup": true, "foreign_pin_preserved_auto_falls_back_without_advice": true}, "result": "passed"}
+integration artifacts: /var/tmp/lertick-power-integration-s07845od
diff --git a/results/automatic-discovery/finite-jobs.jsonl b/results/automatic-discovery/finite-jobs.jsonl
new file mode 100644
index 0000000..489b575
--- /dev/null
+++ b/results/automatic-discovery/finite-jobs.jsonl
@@ -0,0 +1,60 @@
+{"pattern":0,"seed":42,"joint":false,"mode":"nominal","jobs_completed":2880,"backlog":0,"offered_work":1636992.0,"throttle_steps":0,"makespan_s":405.22,"simulated_board_j":3064.103829,"peak_c":81.307,"job_p99_s":45.29,"point_mae_c":0.618066,"reference_mae_c":0.934517,"forecast_points":12006,"correction_queries":0,"queries":15565,"ceiling_moves":15,"frequency_transitions":5032,"control_cpu_s":0.003446}
+{"pattern":0,"seed":42,"joint":false,"mode":"cooperative","jobs_completed":2880,"backlog":0,"offered_work":1636992.0,"throttle_steps":0,"makespan_s":405.22,"simulated_board_j":3064.103829,"peak_c":81.307,"job_p99_s":45.29,"point_mae_c":0.618066,"reference_mae_c":0.934517,"forecast_points":12006,"correction_queries":0,"queries":15565,"ceiling_moves":15,"frequency_transitions":5032,"control_cpu_s":0.003817}
+{"pattern":0,"seed":7,"joint":false,"mode":"nominal","jobs_completed":2880,"backlog":0,"offered_work":1636992.0,"throttle_steps":0,"makespan_s":419.52,"simulated_board_j":3140.746146,"peak_c":81.307,"job_p99_s":59.98,"point_mae_c":0.60829,"reference_mae_c":0.908556,"forecast_points":12435,"correction_queries":0,"queries":14279,"ceiling_moves":18,"frequency_transitions":5389,"control_cpu_s":0.003588}
+{"pattern":0,"seed":7,"joint":false,"mode":"cooperative","jobs_completed":2880,"backlog":0,"offered_work":1636992.0,"throttle_steps":0,"makespan_s":419.52,"simulated_board_j":3140.746146,"peak_c":81.307,"job_p99_s":59.98,"point_mae_c":0.60829,"reference_mae_c":0.908556,"forecast_points":12435,"correction_queries":0,"queries":14279,"ceiling_moves":18,"frequency_transitions":5389,"control_cpu_s":0.003681}
+{"pattern":0,"seed":99,"joint":false,"mode":"nominal","jobs_completed":2880,"backlog":0,"offered_work":1636992.0,"throttle_steps":0,"makespan_s":402.76,"simulated_board_j":3056.050846,"peak_c":81.307,"job_p99_s":42.82,"point_mae_c":0.598512,"reference_mae_c":0.901395,"forecast_points":11931,"correction_queries":0,"queries":15553,"ceiling_moves":15,"frequency_transitions":5019,"control_cpu_s":0.003332}
+{"pattern":0,"seed":99,"joint":false,"mode":"cooperative","jobs_completed":2880,"backlog":0,"offered_work":1636992.0,"throttle_steps":0,"makespan_s":402.76,"simulated_board_j":3056.050846,"peak_c":81.307,"job_p99_s":42.82,"point_mae_c":0.598512,"reference_mae_c":0.901395,"forecast_points":11931,"correction_queries":0,"queries":15553,"ceiling_moves":15,"frequency_transitions":5019,"control_cpu_s":0.003601}
+{"pattern":1,"seed":42,"joint":false,"mode":"nominal","jobs_completed":2880,"backlog":0,"offered_work":1548288.0,"throttle_steps":0,"makespan_s":503.04,"simulated_board_j":3412.501516,"peak_c":82.23,"job_p99_s":142.79,"point_mae_c":0.709844,"reference_mae_c":1.032735,"forecast_points":14940,"correction_queries":0,"queries":16479,"ceiling_moves":15,"frequency_transitions":1587,"control_cpu_s":0.004321}
+{"pattern":1,"seed":42,"joint":false,"mode":"cooperative","jobs_completed":2880,"backlog":0,"offered_work":1548288.0,"throttle_steps":0,"makespan_s":503.04,"simulated_board_j":3412.501516,"peak_c":82.23,"job_p99_s":142.79,"point_mae_c":0.709844,"reference_mae_c":1.032735,"forecast_points":14940,"correction_queries":0,"queries":16479,"ceiling_moves":15,"frequency_transitions":1587,"control_cpu_s":0.004525}
+{"pattern":1,"seed":7,"joint":false,"mode":"nominal","jobs_completed":2880,"backlog":0,"offered_work":1548288.0,"throttle_steps":0,"makespan_s":500.66,"simulated_board_j":3394.311558,"peak_c":82.23,"job_p99_s":140.41,"point_mae_c":0.719791,"reference_mae_c":1.044086,"forecast_points":14868,"correction_queries":0,"queries":15913,"ceiling_moves":17,"frequency_transitions":1514,"control_cpu_s":0.004368}
+{"pattern":1,"seed":7,"joint":false,"mode":"cooperative","jobs_completed":2880,"backlog":0,"offered_work":1548288.0,"throttle_steps":0,"makespan_s":500.66,"simulated_board_j":3394.311558,"peak_c":82.23,"job_p99_s":140.41,"point_mae_c":0.719791,"reference_mae_c":1.044086,"forecast_points":14868,"correction_queries":0,"queries":15913,"ceiling_moves":17,"frequency_transitions":1514,"control_cpu_s":0.004452}
+{"pattern":1,"seed":99,"joint":false,"mode":"nominal","jobs_completed":2880,"backlog":0,"offered_work":1548288.0,"throttle_steps":0,"makespan_s":497.11,"simulated_board_j":3383.829286,"peak_c":82.23,"job_p99_s":136.86,"point_mae_c":0.718125,"reference_mae_c":1.028734,"forecast_points":14763,"correction_queries":0,"queries":15518,"ceiling_moves":16,"frequency_transitions":1443,"control_cpu_s":0.004362}
+{"pattern":1,"seed":99,"joint":false,"mode":"cooperative","jobs_completed":2880,"backlog":0,"offered_work":1548288.0,"throttle_steps":0,"makespan_s":497.11,"simulated_board_j":3383.829286,"peak_c":82.23,"job_p99_s":136.86,"point_mae_c":0.718125,"reference_mae_c":1.028734,"forecast_points":14763,"correction_queries":0,"queries":15518,"ceiling_moves":16,"frequency_transitions":1443,"control_cpu_s":0.00448}
+{"pattern":2,"seed":42,"joint":false,"mode":"nominal","jobs_completed":2880,"backlog":0,"offered_work":1251792.0,"throttle_steps":0,"makespan_s":360.64,"simulated_board_j":2638.146268,"peak_c":81.307,"job_p99_s":1.59,"point_mae_c":0.53184,"reference_mae_c":0.859365,"forecast_points":10668,"correction_queries":0,"queries":10209,"ceiling_moves":6,"frequency_transitions":8519,"control_cpu_s":0.003051}
+{"pattern":2,"seed":42,"joint":false,"mode":"cooperative","jobs_completed":2880,"backlog":0,"offered_work":1251792.0,"throttle_steps":0,"makespan_s":360.64,"simulated_board_j":2638.146268,"peak_c":81.307,"job_p99_s":1.59,"point_mae_c":0.53184,"reference_mae_c":0.859365,"forecast_points":10668,"correction_queries":0,"queries":10209,"ceiling_moves":6,"frequency_transitions":8519,"control_cpu_s":0.003251}
+{"pattern":2,"seed":7,"joint":false,"mode":"nominal","jobs_completed":2880,"backlog":0,"offered_work":1251792.0,"throttle_steps":0,"makespan_s":360.63,"simulated_board_j":2630.93397,"peak_c":81.307,"job_p99_s":1.58,"point_mae_c":0.519062,"reference_mae_c":0.849117,"forecast_points":10668,"correction_queries":0,"queries":10066,"ceiling_moves":7,"frequency_transitions":8387,"control_cpu_s":0.003041}
+{"pattern":2,"seed":7,"joint":false,"mode":"cooperative","jobs_completed":2880,"backlog":0,"offered_work":1251792.0,"throttle_steps":0,"makespan_s":360.63,"simulated_board_j":2630.93397,"peak_c":81.307,"job_p99_s":1.58,"point_mae_c":0.519062,"reference_mae_c":0.849117,"forecast_points":10668,"correction_queries":0,"queries":10066,"ceiling_moves":7,"frequency_transitions":8387,"control_cpu_s":0.003235}
+{"pattern":2,"seed":99,"joint":false,"mode":"nominal","jobs_completed":2880,"backlog":0,"offered_work":1251792.0,"throttle_steps":0,"makespan_s":360.64,"simulated_board_j":2626.024997,"peak_c":81.307,"job_p99_s":1.59,"point_mae_c":0.536754,"reference_mae_c":0.852969,"forecast_points":10668,"correction_queries":0,"queries":9965,"ceiling_moves":9,"frequency_transitions":8378,"control_cpu_s":0.003089}
+{"pattern":2,"seed":99,"joint":false,"mode":"cooperative","jobs_completed":2880,"backlog":0,"offered_work":1251792.0,"throttle_steps":0,"makespan_s":360.64,"simulated_board_j":2626.024997,"peak_c":81.307,"job_p99_s":1.59,"point_mae_c":0.536754,"reference_mae_c":0.852969,"forecast_points":10668,"correction_queries":0,"queries":9965,"ceiling_moves":9,"frequency_transitions":8378,"control_cpu_s":0.003234}
+{"pattern":3,"seed":42,"joint":false,"mode":"nominal","jobs_completed":2880,"backlog":0,"offered_work":1020321.008,"throttle_steps":0,"makespan_s":359.87,"simulated_board_j":2372.53609,"peak_c":76.692,"job_p99_s":1.05,"point_mae_c":0.843208,"reference_mae_c":0.92945,"forecast_points":10644,"correction_queries":0,"queries":8067,"ceiling_moves":0,"frequency_transitions":7829,"control_cpu_s":0.002996}
+{"pattern":3,"seed":42,"joint":false,"mode":"cooperative","jobs_completed":2880,"backlog":0,"offered_work":1020321.008,"throttle_steps":0,"makespan_s":359.87,"simulated_board_j":2372.53609,"peak_c":76.692,"job_p99_s":1.05,"point_mae_c":0.843208,"reference_mae_c":0.92945,"forecast_points":10644,"correction_queries":0,"queries":8067,"ceiling_moves":0,"frequency_transitions":7829,"control_cpu_s":0.003215}
+{"pattern":3,"seed":7,"joint":false,"mode":"nominal","jobs_completed":2880,"backlog":0,"offered_work":1020321.008,"throttle_steps":0,"makespan_s":359.87,"simulated_board_j":2372.53609,"peak_c":77.615,"job_p99_s":1.05,"point_mae_c":0.850526,"reference_mae_c":0.92373,"forecast_points":10644,"correction_queries":0,"queries":8067,"ceiling_moves":0,"frequency_transitions":7829,"control_cpu_s":0.00296}
+{"pattern":3,"seed":7,"joint":false,"mode":"cooperative","jobs_completed":2880,"backlog":0,"offered_work":1020321.008,"throttle_steps":0,"makespan_s":359.87,"simulated_board_j":2372.53609,"peak_c":77.615,"job_p99_s":1.05,"point_mae_c":0.850526,"reference_mae_c":0.92373,"forecast_points":10644,"correction_queries":0,"queries":8067,"ceiling_moves":0,"frequency_transitions":7829,"control_cpu_s":0.003194}
+{"pattern":3,"seed":99,"joint":false,"mode":"nominal","jobs_completed":2880,"backlog":0,"offered_work":1020321.008,"throttle_steps":0,"makespan_s":359.87,"simulated_board_j":2372.53609,"peak_c":77.615,"job_p99_s":1.05,"point_mae_c":0.860971,"reference_mae_c":0.929357,"forecast_points":10644,"correction_queries":0,"queries":8067,"ceiling_moves":0,"frequency_transitions":7829,"control_cpu_s":0.003067}
+{"pattern":3,"seed":99,"joint":false,"mode":"cooperative","jobs_completed":2880,"backlog":0,"offered_work":1020321.008,"throttle_steps":0,"makespan_s":359.87,"simulated_board_j":2372.53609,"peak_c":77.615,"job_p99_s":1.05,"point_mae_c":0.860971,"reference_mae_c":0.929357,"forecast_points":10644,"correction_queries":0,"queries":8067,"ceiling_moves":0,"frequency_transitions":7829,"control_cpu_s":0.003214}
+{"pattern":4,"seed":42,"joint":false,"mode":"nominal","jobs_completed":2880,"backlog":0,"offered_work":2919628.8,"throttle_steps":0,"makespan_s":980.4,"simulated_board_j":6295.971093,"peak_c":81.307,"job_p99_s":610.53,"point_mae_c":0.478139,"reference_mae_c":0.640429,"forecast_points":29259,"correction_queries":0,"queries":35913,"ceiling_moves":36,"frequency_transitions":42,"control_cpu_s":0.008729}
+{"pattern":4,"seed":42,"joint":false,"mode":"cooperative","jobs_completed":2880,"backlog":0,"offered_work":2919628.8,"throttle_steps":0,"makespan_s":980.4,"simulated_board_j":6295.971093,"peak_c":81.307,"job_p99_s":610.53,"point_mae_c":0.478139,"reference_mae_c":0.640429,"forecast_points":29259,"correction_queries":0,"queries":35913,"ceiling_moves":36,"frequency_transitions":42,"control_cpu_s":0.008973}
+{"pattern":4,"seed":7,"joint":false,"mode":"nominal","jobs_completed":2880,"backlog":0,"offered_work":2919628.8,"throttle_steps":0,"makespan_s":979.53,"simulated_board_j":6294.123159,"peak_c":81.307,"job_p99_s":609.65,"point_mae_c":0.483719,"reference_mae_c":0.643218,"forecast_points":29235,"correction_queries":0,"queries":36274,"ceiling_moves":32,"frequency_transitions":40,"control_cpu_s":0.008612}
+{"pattern":4,"seed":7,"joint":false,"mode":"cooperative","jobs_completed":2880,"backlog":0,"offered_work":2919628.8,"throttle_steps":0,"makespan_s":979.55,"simulated_board_j":6293.863896,"peak_c":81.307,"job_p99_s":609.67,"point_mae_c":0.48656,"reference_mae_c":0.646034,"forecast_points":29235,"correction_queries":1,"queries":36309,"ceiling_moves":32,"frequency_transitions":40,"control_cpu_s":0.008985}
+{"pattern":4,"seed":99,"joint":false,"mode":"nominal","jobs_completed":2880,"backlog":0,"offered_work":2919628.8,"throttle_steps":0,"makespan_s":981.83,"simulated_board_j":6300.32949,"peak_c":81.307,"job_p99_s":611.96,"point_mae_c":0.484815,"reference_mae_c":0.641113,"forecast_points":29304,"correction_queries":0,"queries":36617,"ceiling_moves":28,"frequency_transitions":38,"control_cpu_s":0.008705}
+{"pattern":4,"seed":99,"joint":false,"mode":"cooperative","jobs_completed":2880,"backlog":0,"offered_work":2919628.8,"throttle_steps":0,"makespan_s":981.9,"simulated_board_j":6300.200034,"peak_c":81.307,"job_p99_s":612.02,"point_mae_c":0.482736,"reference_mae_c":0.639256,"forecast_points":29304,"correction_queries":1,"queries":36652,"ceiling_moves":28,"frequency_transitions":38,"control_cpu_s":0.008887}
+{"pattern":0,"seed":42,"joint":true,"mode":"nominal","jobs_completed":2880,"backlog":0,"offered_work":1636992.0,"throttle_steps":0,"makespan_s":418.79,"simulated_board_j":3046.446255,"peak_c":82.23,"job_p99_s":57.07,"point_mae_c":0.631586,"reference_mae_c":0.92619,"forecast_points":12411,"correction_queries":0,"queries":14238,"ceiling_moves":9,"frequency_transitions":3805,"control_cpu_s":0.003428}
+{"pattern":0,"seed":42,"joint":true,"mode":"cooperative","jobs_completed":2880,"backlog":0,"offered_work":1636992.0,"throttle_steps":0,"makespan_s":418.79,"simulated_board_j":3046.446255,"peak_c":82.23,"job_p99_s":57.07,"point_mae_c":0.631586,"reference_mae_c":0.92619,"forecast_points":12411,"correction_queries":0,"queries":14238,"ceiling_moves":9,"frequency_transitions":3805,"control_cpu_s":0.003683}
+{"pattern":0,"seed":7,"joint":true,"mode":"nominal","jobs_completed":2880,"backlog":0,"offered_work":1636992.0,"throttle_steps":0,"makespan_s":418.22,"simulated_board_j":3046.942437,"peak_c":82.23,"job_p99_s":56.5,"point_mae_c":0.614085,"reference_mae_c":0.917913,"forecast_points":12396,"correction_queries":0,"queries":14225,"ceiling_moves":9,"frequency_transitions":3819,"control_cpu_s":0.003401}
+{"pattern":0,"seed":7,"joint":true,"mode":"cooperative","jobs_completed":2880,"backlog":0,"offered_work":1636992.0,"throttle_steps":0,"makespan_s":418.22,"simulated_board_j":3046.942437,"peak_c":82.23,"job_p99_s":56.5,"point_mae_c":0.614085,"reference_mae_c":0.917913,"forecast_points":12396,"correction_queries":0,"queries":14225,"ceiling_moves":9,"frequency_transitions":3819,"control_cpu_s":0.003767}
+{"pattern":0,"seed":99,"joint":true,"mode":"nominal","jobs_completed":2880,"backlog":0,"offered_work":1636992.0,"throttle_steps":0,"makespan_s":417.74,"simulated_board_j":3045.880971,"peak_c":82.23,"job_p99_s":56.02,"point_mae_c":0.635798,"reference_mae_c":0.921273,"forecast_points":12381,"correction_queries":0,"queries":14210,"ceiling_moves":9,"frequency_transitions":3821,"control_cpu_s":0.003582}
+{"pattern":0,"seed":99,"joint":true,"mode":"cooperative","jobs_completed":2880,"backlog":0,"offered_work":1636992.0,"throttle_steps":0,"makespan_s":417.74,"simulated_board_j":3045.880971,"peak_c":82.23,"job_p99_s":56.02,"point_mae_c":0.635798,"reference_mae_c":0.921273,"forecast_points":12381,"correction_queries":0,"queries":14210,"ceiling_moves":9,"frequency_transitions":3821,"control_cpu_s":0.003655}
+{"pattern":1,"seed":42,"joint":true,"mode":"nominal","jobs_completed":2880,"backlog":0,"offered_work":1548288.0,"throttle_steps":0,"makespan_s":538.75,"simulated_board_j":3492.476423,"peak_c":82.23,"job_p99_s":178.5,"point_mae_c":0.85818,"reference_mae_c":1.035502,"forecast_points":16011,"correction_queries":0,"queries":19311,"ceiling_moves":10,"frequency_transitions":1829,"control_cpu_s":0.004728}
+{"pattern":1,"seed":42,"joint":true,"mode":"cooperative","jobs_completed":2880,"backlog":0,"offered_work":1548288.0,"throttle_steps":0,"makespan_s":538.75,"simulated_board_j":3492.476423,"peak_c":82.23,"job_p99_s":178.5,"point_mae_c":0.85818,"reference_mae_c":1.035502,"forecast_points":16011,"correction_queries":0,"queries":19311,"ceiling_moves":10,"frequency_transitions":1829,"control_cpu_s":0.004976}
+{"pattern":1,"seed":7,"joint":true,"mode":"nominal","jobs_completed":2880,"backlog":0,"offered_work":1548288.0,"throttle_steps":0,"makespan_s":538.75,"simulated_board_j":3497.10333,"peak_c":82.23,"job_p99_s":178.5,"point_mae_c":0.727766,"reference_mae_c":1.009315,"forecast_points":16011,"correction_queries":0,"queries":19312,"ceiling_moves":10,"frequency_transitions":1854,"control_cpu_s":0.004852}
+{"pattern":1,"seed":7,"joint":true,"mode":"cooperative","jobs_completed":2880,"backlog":0,"offered_work":1548288.0,"throttle_steps":0,"makespan_s":538.75,"simulated_board_j":3497.10333,"peak_c":82.23,"job_p99_s":178.5,"point_mae_c":0.727766,"reference_mae_c":1.009315,"forecast_points":16011,"correction_queries":0,"queries":19312,"ceiling_moves":10,"frequency_transitions":1854,"control_cpu_s":0.004843}
+{"pattern":1,"seed":99,"joint":true,"mode":"nominal","jobs_completed":2880,"backlog":0,"offered_work":1548288.0,"throttle_steps":0,"makespan_s":538.75,"simulated_board_j":3492.806164,"peak_c":82.23,"job_p99_s":178.5,"point_mae_c":0.739498,"reference_mae_c":1.012688,"forecast_points":16011,"correction_queries":0,"queries":19312,"ceiling_moves":10,"frequency_transitions":1829,"control_cpu_s":0.004624}
+{"pattern":1,"seed":99,"joint":true,"mode":"cooperative","jobs_completed":2880,"backlog":0,"offered_work":1548288.0,"throttle_steps":0,"makespan_s":538.75,"simulated_board_j":3492.806164,"peak_c":82.23,"job_p99_s":178.5,"point_mae_c":0.739498,"reference_mae_c":1.012688,"forecast_points":16011,"correction_queries":0,"queries":19312,"ceiling_moves":10,"frequency_transitions":1829,"control_cpu_s":0.004845}
+{"pattern":2,"seed":42,"joint":true,"mode":"nominal","jobs_completed":2880,"backlog":0,"offered_work":1251792.0,"throttle_steps":0,"makespan_s":381.49,"simulated_board_j":2672.639652,"peak_c":79.461,"job_p99_s":21.48,"point_mae_c":0.591575,"reference_mae_c":0.884357,"forecast_points":11292,"correction_queries":0,"queries":13125,"ceiling_moves":5,"frequency_transitions":8114,"control_cpu_s":0.003232}
+{"pattern":2,"seed":42,"joint":true,"mode":"cooperative","jobs_completed":2880,"backlog":0,"offered_work":1251792.0,"throttle_steps":0,"makespan_s":381.49,"simulated_board_j":2672.639652,"peak_c":79.461,"job_p99_s":21.48,"point_mae_c":0.591575,"reference_mae_c":0.884357,"forecast_points":11292,"correction_queries":0,"queries":13125,"ceiling_moves":5,"frequency_transitions":8114,"control_cpu_s":0.003394}
+{"pattern":2,"seed":7,"joint":true,"mode":"nominal","jobs_completed":2880,"backlog":0,"offered_work":1251792.0,"throttle_steps":0,"makespan_s":365.22,"simulated_board_j":2633.845193,"peak_c":81.307,"job_p99_s":5.21,"point_mae_c":0.552913,"reference_mae_c":0.857648,"forecast_points":10806,"correction_queries":0,"queries":12639,"ceiling_moves":5,"frequency_transitions":7936,"control_cpu_s":0.003106}
+{"pattern":2,"seed":7,"joint":true,"mode":"cooperative","jobs_completed":2880,"backlog":0,"offered_work":1251792.0,"throttle_steps":0,"makespan_s":365.22,"simulated_board_j":2633.845193,"peak_c":81.307,"job_p99_s":5.21,"point_mae_c":0.552913,"reference_mae_c":0.857648,"forecast_points":10806,"correction_queries":0,"queries":12639,"ceiling_moves":5,"frequency_transitions":7936,"control_cpu_s":0.003325}
+{"pattern":2,"seed":99,"joint":true,"mode":"nominal","jobs_completed":2880,"backlog":0,"offered_work":1251792.0,"throttle_steps":0,"makespan_s":382.51,"simulated_board_j":2645.604875,"peak_c":78.538,"job_p99_s":22.5,"point_mae_c":0.587389,"reference_mae_c":0.843609,"forecast_points":11325,"correction_queries":0,"queries":13158,"ceiling_moves":4,"frequency_transitions":7514,"control_cpu_s":0.003341}
+{"pattern":2,"seed":99,"joint":true,"mode":"cooperative","jobs_completed":2880,"backlog":0,"offered_work":1251792.0,"throttle_steps":0,"makespan_s":382.51,"simulated_board_j":2645.604875,"peak_c":78.538,"job_p99_s":22.5,"point_mae_c":0.587389,"reference_mae_c":0.843609,"forecast_points":11325,"correction_queries":0,"queries":13158,"ceiling_moves":4,"frequency_transitions":7514,"control_cpu_s":0.003442}
+{"pattern":3,"seed":42,"joint":true,"mode":"nominal","jobs_completed":2880,"backlog":0,"offered_work":1020321.008,"throttle_steps":0,"makespan_s":359.92,"simulated_board_j":2372.551126,"peak_c":76.692,"job_p99_s":1.05,"point_mae_c":0.843901,"reference_mae_c":0.930119,"forecast_points":10647,"correction_queries":0,"queries":12480,"ceiling_moves":1,"frequency_transitions":7779,"control_cpu_s":0.003024}
+{"pattern":3,"seed":42,"joint":true,"mode":"cooperative","jobs_completed":2880,"backlog":0,"offered_work":1020321.008,"throttle_steps":0,"makespan_s":359.92,"simulated_board_j":2372.551126,"peak_c":76.692,"job_p99_s":1.05,"point_mae_c":0.843901,"reference_mae_c":0.930119,"forecast_points":10647,"correction_queries":0,"queries":12480,"ceiling_moves":1,"frequency_transitions":7779,"control_cpu_s":0.003225}
+{"pattern":3,"seed":7,"joint":true,"mode":"nominal","jobs_completed":2880,"backlog":0,"offered_work":1020321.008,"throttle_steps":0,"makespan_s":359.92,"simulated_board_j":2372.551126,"peak_c":77.615,"job_p99_s":1.05,"point_mae_c":0.850204,"reference_mae_c":0.923388,"forecast_points":10647,"correction_queries":0,"queries":12480,"ceiling_moves":1,"frequency_transitions":7779,"control_cpu_s":0.002987}
+{"pattern":3,"seed":7,"joint":true,"mode":"cooperative","jobs_completed":2880,"backlog":0,"offered_work":1020321.008,"throttle_steps":0,"makespan_s":359.92,"simulated_board_j":2372.551126,"peak_c":77.615,"job_p99_s":1.05,"point_mae_c":0.850204,"reference_mae_c":0.923388,"forecast_points":10647,"correction_queries":0,"queries":12480,"ceiling_moves":1,"frequency_transitions":7779,"control_cpu_s":0.003229}
+{"pattern":3,"seed":99,"joint":true,"mode":"nominal","jobs_completed":2880,"backlog":0,"offered_work":1020321.008,"throttle_steps":0,"makespan_s":359.92,"simulated_board_j":2372.551126,"peak_c":76.692,"job_p99_s":1.05,"point_mae_c":0.859807,"reference_mae_c":0.928173,"forecast_points":10647,"correction_queries":0,"queries":12480,"ceiling_moves":1,"frequency_transitions":7779,"control_cpu_s":0.003079}
+{"pattern":3,"seed":99,"joint":true,"mode":"cooperative","jobs_completed":2880,"backlog":0,"offered_work":1020321.008,"throttle_steps":0,"makespan_s":359.92,"simulated_board_j":2372.551126,"peak_c":76.692,"job_p99_s":1.05,"point_mae_c":0.859807,"reference_mae_c":0.928173,"forecast_points":10647,"correction_queries":0,"queries":12480,"ceiling_moves":1,"frequency_transitions":7779,"control_cpu_s":0.003307}
+{"pattern":4,"seed":42,"joint":true,"mode":"nominal","jobs_completed":2880,"backlog":0,"offered_work":2919628.8,"throttle_steps":0,"makespan_s":1009.87,"simulated_board_j":6372.262955,"peak_c":82.23,"job_p99_s":639.99,"point_mae_c":0.515443,"reference_mae_c":0.640917,"forecast_points":30144,"correction_queries":0,"queries":39322,"ceiling_moves":11,"frequency_transitions":30,"control_cpu_s":0.009039}
+{"pattern":4,"seed":42,"joint":true,"mode":"cooperative","jobs_completed":2880,"backlog":0,"offered_work":2919628.8,"throttle_steps":0,"makespan_s":1009.87,"simulated_board_j":6372.262955,"peak_c":82.23,"job_p99_s":639.99,"point_mae_c":0.515443,"reference_mae_c":0.640917,"forecast_points":30144,"correction_queries":0,"queries":39322,"ceiling_moves":11,"frequency_transitions":30,"control_cpu_s":0.009519}
+{"pattern":4,"seed":7,"joint":true,"mode":"nominal","jobs_completed":2880,"backlog":0,"offered_work":2919628.8,"throttle_steps":0,"makespan_s":1008.94,"simulated_board_j":6348.257503,"peak_c":82.23,"job_p99_s":639.06,"point_mae_c":0.503787,"reference_mae_c":0.630852,"forecast_points":30117,"correction_queries":0,"queries":39294,"ceiling_moves":15,"frequency_transitions":31,"control_cpu_s":0.008985}
+{"pattern":4,"seed":7,"joint":true,"mode":"cooperative","jobs_completed":2880,"backlog":0,"offered_work":2919628.8,"throttle_steps":0,"makespan_s":1008.94,"simulated_board_j":6348.257503,"peak_c":82.23,"job_p99_s":639.06,"point_mae_c":0.503787,"reference_mae_c":0.630852,"forecast_points":30117,"correction_queries":0,"queries":39294,"ceiling_moves":15,"frequency_transitions":31,"control_cpu_s":0.00944}
+{"pattern":4,"seed":99,"joint":true,"mode":"nominal","jobs_completed":2880,"backlog":0,"offered_work":2919628.8,"throttle_steps":0,"makespan_s":988.27,"simulated_board_j":6315.551787,"peak_c":82.23,"job_p99_s":618.39,"point_mae_c":0.515453,"reference_mae_c":0.644973,"forecast_points":29496,"correction_queries":0,"queries":38676,"ceiling_moves":10,"frequency_transitions":27,"control_cpu_s":0.008785}
+{"pattern":4,"seed":99,"joint":true,"mode":"cooperative","jobs_completed":2880,"backlog":0,"offered_work":2919628.8,"throttle_steps":0,"makespan_s":988.27,"simulated_board_j":6315.551787,"peak_c":82.23,"job_p99_s":618.39,"point_mae_c":0.515453,"reference_mae_c":0.644973,"forecast_points":29496,"correction_queries":0,"queries":38676,"ceiling_moves":10,"frequency_transitions":27,"control_cpu_s":0.009106}
diff --git a/results/automatic-discovery/first-conflict-failure.log b/results/automatic-discovery/first-conflict-failure.log
new file mode 100644
index 0000000..e7fe5dd
--- /dev/null
+++ b/results/automatic-discovery/first-conflict-failure.log
@@ -0,0 +1,65 @@
+clang-18 -Ivendor -Isrc -Isrc/shared -Ibuild -I/home/ubuntu/lertick-pr/build/deps/install/include -DLERTICK_VERSION=\"source\" -O2 -g -target bpf -mcpu=v3 -Wall -Wno-compare-distinct-pointer-types -D__TARGET_ARCH_arm64 -Ivendor/bpf-compat -I/usr/include/aarch64-linux-gnu -c src/bpf/scx_lertick.bpf.c -o build/scx_lertick.bpf.o
+In file included from src/bpf/scx_lertick.bpf.c:23:
+In file included from vendor/scx/common.bpf.h:21:
+build/vmlinux.h:69147:2: warning: declaration does not declare anything [-Wmissing-declarations]
+ 69147 | struct aes_enckey;
+ | ^
+build/vmlinux.h:85401:3: warning: declaration does not declare anything [-Wmissing-declarations]
+ 85401 | struct ns_tree;
+ | ^
+build/vmlinux.h:108771:2: warning: declaration does not declare anything [-Wmissing-declarations]
+ 108771 | struct __filename_head;
+ | ^
+build/vmlinux.h:152894:2: warning: declaration does not declare anything [-Wmissing-declarations]
+ 152894 | union pipe_index;
+ | ^
+build/vmlinux.h:171167:4: warning: declaration does not declare anything [-Wmissing-declarations]
+ 171167 | struct freelist_counters;
+ | ^
+5 warnings generated.
+bpftool gen skeleton build/scx_lertick.bpf.o name scx_lertick > build/scx_lertick.bpf.skel.h.tmp
+mv build/scx_lertick.bpf.skel.h.tmp build/scx_lertick.bpf.skel.h
+cc -Ivendor -Isrc -Isrc/shared -Ibuild -I/home/ubuntu/lertick-pr/build/deps/install/include -DLERTICK_VERSION=\"source\" -Isrc/userspace -O2 -g -std=gnu11 -Wall -Wextra src/userspace/scx_lertick.c src/userspace/lerwatt_plugin.c src/userspace/runtime.c -o build/scx_lertick -L/home/ubuntu/lertick-pr/build/deps/install/lib -lbpf -lelf -lzstd -pthread -lz -lelf -lz
+In file included from vendor/scx/common.h:74,
+ from src/userspace/scx_lertick.c:28:
+vendor/scx/compat.h: In function ‘__COMPAT_read_enum’:
+vendor/scx/compat.h:44:31: warning: comparison of integer expressions of different signedness: ‘int’ and ‘__u32’ {aka ‘unsigned int’} [-Wsign-compare]
+ 44 | for (i = 0; i < BTF_INFO_VLEN(t->info); i++) {
+ | ^
+vendor/scx/compat.h:55:31: warning: comparison of integer expressions of different signedness: ‘int’ and ‘__u32’ {aka ‘unsigned int’} [-Wsign-compare]
+ 55 | for (i = 0; i < BTF_INFO_VLEN(t->info); i++) {
+ | ^
+vendor/scx/compat.h: In function ‘__COMPAT_struct_has_field’:
+vendor/scx/compat.h:99:23: warning: comparison of integer expressions of different signedness: ‘int’ and ‘__u32’ {aka ‘unsigned int’} [-Wsign-compare]
+ 99 | for (i = 0; i < BTF_INFO_VLEN(t->info); i++) {
+ | ^
+build/test_lerwatt_plugin
+{"test":"lerwatt_plugin","status":"passed"}
+python3 tests/check_integration_abi.py
+Integration ABI 2 golden check passed (56 values).
+systemd: installed helper enabled an unlimited validated worker; absent peer reports waiting
+systemd: active off/on preserved scheduling and boot intent, and replaced shared maps
+systemd: stop/update/rollback preserved enabled-but-stopped intent and private integration setting
+systemd: uninstall disabled/detached/removed the validation service; configuration retained
+{"result": "passed", "checks": {"install_does_not_start_or_enable": true, "enabled_unlimited_ready_with_optional_peer_absent": true, "active_integration_toggle_restarts_only_owned_service": true, "stop_update_rollback_preserve_configuration_and_intent": true, "uninstall_disables_detaches_removes_and_retains_config": true}, "environment": {"os_release": "PRETTY_NAME=\"Ubuntu 24.04.5 LTS\"\nNAME=\"Ubuntu\"\nVERSION_ID=\"24.04\"\nVERSION=\"24.04.5 LTS (Noble Numbat)\"\nVERSION_CODENAME=noble\nID=ubuntu\nID_LIKE=debian\nHOME_URL=\"https://www.ubuntu.com/\"\nSUPPORT_URL=\"https://help.ubuntu.com/\"\nBUG_REPORT_URL=\"https://bugs.launchpad.net/ubuntu/\"\nPRIVACY_POLICY_URL=\"https://www.ubuntu.com/legal/terms-and-policies/privacy-policy\"\nUBUNTU_CODENAME=noble\nLOGO=ubuntu-logo\n", "architecture": "aarch64", "kernel": "7.0.0-38-generic", "systemd": "systemd 255 (255.4-1ubuntu8.18)", "elapsed_seconds": 4.76}, "scope": "real sched_ext attachment and PID 1 lifecycle in an opted-in VM; no physical power controls or long hardware run"}
+integration: Lerwatt-first startup connected; BPF consumed contracts
+integration: online-to-EWMA scheduler change reconnected while Lerwatt stayed running
+integration: frozen demand expired and resumed without stale control
+integration: temporal-to-adaptive predictor change preserved Lertick PID and session
+integration: reverse startup order connected
+Traceback (most recent call last):
+ File "/home/ubuntu/lertick-auto-discovery/tests/test_power_integration.py", line 292, in main
+ assert row["lerwatt"] is False
+ ^^^^^^^^^^^^^^^^^^^^^^^
+AssertionError
+
+During handling of the above exception, another exception occurred:
+
+Traceback (most recent call last):
+ File "/home/ubuntu/lertick-auto-discovery/tests/test_power_integration.py", line 343, in
+ main()
+ File "/home/ubuntu/lertick-auto-discovery/tests/test_power_integration.py", line 331, in main
+ PINS.rmdir()
+ File "/usr/lib/python3.12/pathlib.py", line 1353, in rmdir
+ os.rmdir(self)
+FileNotFoundError: [Errno 2] No such file or directory: '/sys/fs/bpf/lertick'
diff --git a/results/automatic-discovery/first-service-fixture-failure.log b/results/automatic-discovery/first-service-fixture-failure.log
new file mode 100644
index 0000000..9604830
--- /dev/null
+++ b/results/automatic-discovery/first-service-fixture-failure.log
@@ -0,0 +1,3 @@
+Traceback (most recent call last):
+ File "", line 113, in
+AssertionError: (0, 'Lerwatt integration configured: auto\n', '')
diff --git a/results/automatic-discovery/initial-live-pair.log b/results/automatic-discovery/initial-live-pair.log
new file mode 100644
index 0000000..1616686
--- /dev/null
+++ b/results/automatic-discovery/initial-live-pair.log
@@ -0,0 +1,19 @@
+test_deploy: plan/check/verify, indefinite/finite defaults, signals, trust paths, maintenance, staging and build fingerprints passed (stubs only)
+Integration ABI 2 golden check passed (56 values); peer compiled ABI matches.
+systemd: install enabled an unlimited ready worker (nominal)
+systemd: stopped worker was recovered and restarted
+systemd: missing sensor restored controls; returning sensor recovered service
+systemd: uninstall stopped, disabled and restored the validation service
+{"result": "passed", "checks": {"installation_prediction_mode": "nominal", "optional_coordination_waits_independently": true, "enabled_unlimited_ready": true, "watchdog_restores_and_restarts": true, "sensor_failure_recovers": true, "uninstall_stops_disables_restores": true}, "hardware": "temporary file fixture"}
+systemd: installed helper enabled an unlimited validated worker; absent peer reports waiting
+systemd: active off/on preserved scheduling and boot intent, and replaced shared maps
+systemd: stop/update/rollback preserved enabled-but-stopped intent and private integration setting
+systemd: uninstall disabled/detached/removed the validation service; configuration retained
+{"result": "passed", "checks": {"install_does_not_start_or_enable": true, "enabled_unlimited_ready_with_optional_peer_absent": true, "active_integration_toggle_restarts_only_owned_service": true, "stop_update_rollback_preserve_configuration_and_intent": true, "uninstall_disables_detaches_removes_and_retains_config": true}, "environment": {"os_release": "PRETTY_NAME=\"Ubuntu 24.04.5 LTS\"\nNAME=\"Ubuntu\"\nVERSION_ID=\"24.04\"\nVERSION=\"24.04.5 LTS (Noble Numbat)\"\nVERSION_CODENAME=noble\nID=ubuntu\nID_LIKE=debian\nHOME_URL=\"https://www.ubuntu.com/\"\nSUPPORT_URL=\"https://help.ubuntu.com/\"\nBUG_REPORT_URL=\"https://bugs.launchpad.net/ubuntu/\"\nPRIVACY_POLICY_URL=\"https://www.ubuntu.com/legal/terms-and-policies/privacy-policy\"\nUBUNTU_CODENAME=noble\nLOGO=ubuntu-logo\n", "architecture": "aarch64", "kernel": "7.0.0-38-generic", "systemd": "systemd 255 (255.4-1ubuntu8.18)", "elapsed_seconds": 4.99}, "scope": "real sched_ext attachment and PID 1 lifecycle in an opted-in VM; no physical power controls or long hardware run"}
+integration: Lerwatt-first startup connected; BPF consumed contracts
+integration: online-to-EWMA scheduler change reconnected while Lerwatt stayed running
+integration: frozen demand expired and resumed without stale control
+integration: temporal-to-adaptive predictor change preserved Lertick PID and session
+integration: reverse startup order connected
+{"environment": {"kernel": "7.0.0-38-generic", "architecture": "aarch64", "os_release": "PRETTY_NAME=\"Ubuntu 24.04.5 LTS\"\nNAME=\"Ubuntu\"\nVERSION_ID=\"24.04\"\nVERSION=\"24.04.5 LTS (Noble Numbat)\"\nVERSION_CODENAME=noble\nID=ubuntu\nID_LIKE=debian\nHOME_URL=\"https://www.ubuntu.com/\"\nSUPPORT_URL=\"https://help.ubuntu.com/\"\nBUG_REPORT_URL=\"https://bugs.launchpad.net/ubuntu/\"\nPRIVACY_POLICY_URL=\"https://www.ubuntu.com/legal/terms-and-policies/privacy-policy\"\nUBUNTU_CODENAME=noble\nLOGO=ubuntu-logo\n", "systemd": "systemd 255 (255.4-1ubuntu8.18)"}, "hardware": "temporary RK3588 simulated CPU-control files; real sched_ext/BPF", "checks": {"lerwatt_first_continuous_connection": true, "lertick_restart_new_session_reconnects": true, "independent_scheduler_policy_change": true, "frozen_demand_expires_and_recovers": true, "lerwatt_restart_restores_expires_reconnects": true, "independent_power_predictor_change": true, "lertick_first_connection": true, "final_control_restore_scheduler_fallback_pin_cleanup": true}, "result": "passed"}
+integration artifacts: /var/tmp/lertick-power-integration-qkldozcs
diff --git a/results/automatic-discovery/linux-build-and-tests.log b/results/automatic-discovery/linux-build-and-tests.log
new file mode 100644
index 0000000..8d8e2ac
--- /dev/null
+++ b/results/automatic-discovery/linux-build-and-tests.log
@@ -0,0 +1,559 @@
+mkdir -p build
+cc -DLERWATT_VERSION=\"source\" -O2 -g -std=c11 -D_GNU_SOURCE -Wall -Wextra -Wshadow -Werror -Iinclude -Isrc -o build/lerwattd src/hal_fs.c src/hal_discover.c src/hal_sample.c src/hal_actuate.c src/controller.c src/learn.c src/forecast.c src/temporal.c src/predict.c src/contract.c src/notify.c src/checkpoint.c src/lerwattd.c -lm -lrt
+cc -O2 -g -std=c11 -D_GNU_SOURCE -Wall -Wextra -Wshadow -Werror -Iinclude -Isrc -Isim -o build/lerwatt-sim sim/platform_rk3588.c sim/model.c sim/abi.c sim/load.c src/contract.c sim/lerwatt_sim.c -lm -lrt
+cc -O2 -g -std=c11 -D_GNU_SOURCE -Wall -Wextra -Wshadow -Werror -Iinclude -Isrc -Isim -o build/test_model tests/test_model.c sim/platform_rk3588.c sim/model.c -lm -lrt
+cc -O2 -g -std=c11 -D_GNU_SOURCE -Wall -Wextra -Wshadow -Werror -Iinclude -Isrc -o build/test_learn tests/test_learn.c src/learn.c -lm -lrt
+cc -O2 -g -std=c11 -D_GNU_SOURCE -Wall -Wextra -Wshadow -Werror -Iinclude -Isrc -Isim -o build/test_forecast tests/test_forecast.c sim/platform_rk3588.c sim/model.c src/learn.c src/forecast.c -lm -lrt
+cc -O2 -g -std=c11 -D_GNU_SOURCE -Wall -Wextra -Wshadow -Werror -Iinclude -Isrc -o build/test_forecast_feedback tests/test_forecast_feedback.c src/forecast.c -lm -lrt
+cc -O2 -g -std=c11 -D_GNU_SOURCE -Wall -Wextra -Wshadow -Werror -Iinclude -Isrc -o build/test_temporal tests/test_temporal.c src/temporal.c -lm -lrt
+cc -O2 -g -std=c11 -D_GNU_SOURCE -Wall -Wextra -Wshadow -Werror -Iinclude -Isrc -Isim -o build/test_control_forecast tests/test_control_forecast.c sim/platform_rk3588.c sim/model.c src/controller.c src/learn.c src/forecast.c src/temporal.c src/predict.c -lm -lrt
+cc -O2 -g -std=c11 -D_GNU_SOURCE -Wall -Wextra -Wshadow -Werror -Iinclude -Isrc -o build/test_controller tests/test_controller.c src/controller.c src/learn.c src/forecast.c src/temporal.c src/predict.c -lm -lrt
+cc -O2 -g -std=c11 -D_GNU_SOURCE -Wall -Wextra -Wshadow -Werror -Iinclude -Isrc -o build/test_predict tests/test_predict.c src/controller.c src/learn.c src/forecast.c src/temporal.c src/predict.c -lm -lrt
+cc -O2 -g -std=c11 -D_GNU_SOURCE -Wall -Wextra -Wshadow -Werror -Iinclude -Isrc -Isim -o build/test_cooperative_loop tests/test_cooperative_loop.c sim/platform_rk3588.c sim/model.c src/controller.c src/learn.c src/forecast.c src/temporal.c src/predict.c -lm -lrt
+cc -O2 -g -std=c11 -D_GNU_SOURCE -Wall -Wextra -Wshadow -Werror -Iinclude -Isrc -Isim -o build/test_closed_loop tests/test_closed_loop.c sim/platform_rk3588.c sim/model.c src/controller.c src/learn.c src/forecast.c src/temporal.c src/predict.c -lm -lrt
+build/test_model
+full load, passive cooling, no throttling: die 77.7 C, big 84.9 C, little 81.7 C, CPU heat 6.42 W, board 10.86 W
+bare board, full load, step_wise on: big zone 85.9 C, state 4, 4454 throttle steps
+idle, fan: board 2.93 W, die 25.9 C
+test_model: all checks passed
+build/test_learn
+test_learn: a=0.0199 c=-0.00836 ambient=24.6 after 197 samples
+test_learn: all checks passed
+build/test_forecast
+{"case":"steps-passive-seed7","seed":7,"replicate":0,"horizon_s":5,"samples":26853,"package_mae_c":0.4728,"package_persistence_mae_c":0.4346,"hot_mae_c":0.5406,"hot_persistence_mae_c":0.4779,"hot_rmse_c":0.7595,"constant_package_mae_c":0.4728,"constant_hot_mae_c":0.5406,"adaptive_package_mae_c":0.4727,"adaptive_hot_mae_c":0.4686,"adaptive_hot_rmse_c":0.7206,"hot_persistence_rmse_c":0.8249,"all_samples":26853,"all_adaptive_hot_mae_c":0.4686,"all_persistence_hot_mae_c":0.4779,"all_adaptive_hot_rmse_c":0.7206,"selected":[144,2365,0,24344]}
+{"case":"burst2-bare-seed99","seed":99,"replicate":0,"horizon_s":5,"samples":26853,"package_mae_c":0.3319,"package_persistence_mae_c":0.3130,"hot_mae_c":1.4259,"hot_persistence_mae_c":1.8220,"hot_rmse_c":1.6772,"constant_package_mae_c":1.5748,"constant_hot_mae_c":3.3919,"adaptive_package_mae_c":0.3677,"adaptive_hot_mae_c":0.9361,"adaptive_hot_rmse_c":1.1324,"hot_persistence_rmse_c":2.1463,"all_samples":26853,"all_adaptive_hot_mae_c":0.9361,"all_persistence_hot_mae_c":1.8220,"all_adaptive_hot_rmse_c":1.1324,"selected":[0,0,0,26853]}
+{"case":"burst03-fan-35C","seed":7,"replicate":0,"horizon_s":5,"samples":26853,"package_mae_c":0.2463,"package_persistence_mae_c":0.2492,"hot_mae_c":0.5163,"hot_persistence_mae_c":0.5151,"hot_rmse_c":0.5778,"constant_package_mae_c":0.5311,"constant_hot_mae_c":2.2938,"adaptive_package_mae_c":0.2549,"adaptive_hot_mae_c":0.4514,"adaptive_hot_rmse_c":0.5352,"hot_persistence_rmse_c":0.7082,"all_samples":26853,"all_adaptive_hot_mae_c":0.4514,"all_persistence_hot_mae_c":0.5151,"all_adaptive_hot_rmse_c":0.5352,"selected":[0,0,0,26853]}
+{"case":"steps-bare-15C-seed13579","seed":13579,"replicate":0,"horizon_s":5,"samples":26853,"package_mae_c":0.4837,"package_persistence_mae_c":0.6210,"hot_mae_c":0.7202,"hot_persistence_mae_c":0.6895,"hot_rmse_c":1.0453,"constant_package_mae_c":0.4369,"constant_hot_mae_c":0.6020,"adaptive_package_mae_c":0.4313,"adaptive_hot_mae_c":0.5242,"adaptive_hot_rmse_c":0.7347,"hot_persistence_rmse_c":0.9608,"all_samples":26853,"all_adaptive_hot_mae_c":0.5242,"all_persistence_hot_mae_c":0.6895,"all_adaptive_hot_rmse_c":0.7347,"selected":[27,4330,68,22428]}
+{"case":"burst2-passive-35C-seed2026","seed":2026,"replicate":0,"horizon_s":5,"samples":26853,"package_mae_c":0.3827,"package_persistence_mae_c":0.3626,"hot_mae_c":1.3909,"hot_persistence_mae_c":1.8265,"hot_rmse_c":1.6349,"constant_package_mae_c":0.6469,"constant_hot_mae_c":2.1709,"adaptive_package_mae_c":0.3923,"adaptive_hot_mae_c":0.9087,"adaptive_hot_rmse_c":1.1156,"hot_persistence_rmse_c":2.1613,"all_samples":26853,"all_adaptive_hot_mae_c":0.9087,"all_persistence_hot_mae_c":1.8265,"all_adaptive_hot_rmse_c":1.1156,"selected":[0,0,0,26853]}
+{"case":"burst03-bare-seed2718","seed":2718,"replicate":0,"horizon_s":5,"samples":26853,"package_mae_c":0.3565,"package_persistence_mae_c":0.3453,"hot_mae_c":0.4766,"hot_persistence_mae_c":0.4366,"hot_rmse_c":0.5959,"constant_package_mae_c":1.3834,"constant_hot_mae_c":3.4102,"adaptive_package_mae_c":0.3630,"adaptive_hot_mae_c":0.4160,"adaptive_hot_rmse_c":0.5317,"hot_persistence_rmse_c":0.6725,"all_samples":26853,"all_adaptive_hot_mae_c":0.4160,"all_persistence_hot_mae_c":0.4366,"all_adaptive_hot_rmse_c":0.5317,"selected":[0,0,0,26853]}
+{"case":"ramp-passive-seed424242","seed":424242,"replicate":0,"horizon_s":5,"samples":26853,"package_mae_c":0.4322,"package_persistence_mae_c":0.3828,"hot_mae_c":0.4888,"hot_persistence_mae_c":0.4618,"hot_rmse_c":0.6015,"constant_package_mae_c":0.4322,"constant_hot_mae_c":0.4888,"adaptive_package_mae_c":0.4322,"adaptive_hot_mae_c":0.4411,"adaptive_hot_rmse_c":0.5537,"hot_persistence_rmse_c":0.6893,"all_samples":26853,"all_adaptive_hot_mae_c":0.4411,"all_persistence_hot_mae_c":0.4618,"all_adaptive_hot_rmse_c":0.5537,"selected":[0,1111,0,25742]}
+{"case":"alternating-clusters-bare-seed619","seed":619,"replicate":0,"horizon_s":5,"samples":26853,"package_mae_c":0.4327,"package_persistence_mae_c":0.3646,"hot_mae_c":0.6787,"hot_persistence_mae_c":0.6369,"hot_rmse_c":1.2525,"constant_package_mae_c":0.4327,"constant_hot_mae_c":0.6787,"adaptive_package_mae_c":0.4325,"adaptive_hot_mae_c":0.6507,"adaptive_hot_rmse_c":1.2485,"hot_persistence_rmse_c":1.3378,"all_samples":26853,"all_adaptive_hot_mae_c":0.6507,"all_persistence_hot_mae_c":0.6369,"all_adaptive_hot_rmse_c":1.2485,"selected":[161,5767,0,20925]}
+{"case":"regime-changes-bare-seed701","seed":701,"replicate":0,"horizon_s":5,"samples":26853,"package_mae_c":0.3936,"package_persistence_mae_c":0.3692,"hot_mae_c":0.8939,"hot_persistence_mae_c":0.9262,"hot_rmse_c":1.1863,"constant_package_mae_c":1.2214,"constant_hot_mae_c":2.6325,"adaptive_package_mae_c":0.4009,"adaptive_hot_mae_c":0.6540,"adaptive_hot_rmse_c":0.8674,"hot_persistence_rmse_c":1.3907,"all_samples":26853,"all_adaptive_hot_mae_c":0.6540,"all_persistence_hot_mae_c":0.9262,"all_adaptive_hot_rmse_c":0.8674,"selected":[1639,0,2138,23076]}
+{"case":"duty20-bare-35C-seed883","seed":883,"replicate":0,"horizon_s":5,"samples":26853,"package_mae_c":0.6055,"package_persistence_mae_c":0.5665,"hot_mae_c":1.3540,"hot_persistence_mae_c":2.0748,"hot_rmse_c":1.6954,"constant_package_mae_c":1.2501,"constant_hot_mae_c":3.4094,"adaptive_package_mae_c":0.5702,"adaptive_hot_mae_c":1.3540,"adaptive_hot_rmse_c":1.6954,"hot_persistence_rmse_c":2.5945,"all_samples":26853,"all_adaptive_hot_mae_c":1.3540,"all_persistence_hot_mae_c":2.0748,"all_adaptive_hot_rmse_c":1.6954,"selected":[0,0,26853,0]}
+{"case":"random-steps-fan-15C-seed9959","seed":9959,"replicate":0,"horizon_s":5,"samples":26853,"package_mae_c":0.4525,"package_persistence_mae_c":0.3864,"hot_mae_c":0.5930,"hot_persistence_mae_c":0.5537,"hot_rmse_c":0.8690,"constant_package_mae_c":0.4525,"constant_hot_mae_c":0.5930,"adaptive_package_mae_c":0.4525,"adaptive_hot_mae_c":0.5475,"adaptive_hot_rmse_c":0.8364,"hot_persistence_rmse_c":0.9289,"all_samples":26853,"all_adaptive_hot_mae_c":0.5475,"all_persistence_hot_mae_c":0.5537,"all_adaptive_hot_rmse_c":0.8364,"selected":[84,854,0,25915]}
+{"case":"burst45-passive-seed1701","seed":1701,"replicate":0,"horizon_s":5,"samples":26853,"package_mae_c":0.5226,"package_persistence_mae_c":0.5004,"hot_mae_c":0.9437,"hot_persistence_mae_c":1.0449,"hot_rmse_c":1.7919,"constant_package_mae_c":0.5226,"constant_hot_mae_c":0.9437,"adaptive_package_mae_c":0.5233,"adaptive_hot_mae_c":0.9452,"adaptive_hot_rmse_c":1.7643,"hot_persistence_rmse_c":1.8246,"all_samples":26853,"all_adaptive_hot_mae_c":0.9452,"all_persistence_hot_mae_c":1.0449,"all_adaptive_hot_rmse_c":1.7643,"selected":[1705,1689,0,23459]}
+{"case":"varying-period-passive-30C-seed104729","seed":104729,"replicate":0,"horizon_s":5,"samples":26853,"package_mae_c":0.3767,"package_persistence_mae_c":0.3537,"hot_mae_c":1.3838,"hot_persistence_mae_c":1.7906,"hot_rmse_c":1.7003,"constant_package_mae_c":0.5736,"constant_hot_mae_c":3.4865,"adaptive_package_mae_c":0.3800,"adaptive_hot_mae_c":1.1864,"adaptive_hot_rmse_c":1.5538,"hot_persistence_rmse_c":2.4416,"all_samples":26853,"all_adaptive_hot_mae_c":1.1864,"all_persistence_hot_mae_c":1.7906,"all_adaptive_hot_rmse_c":1.5538,"selected":[0,0,7003,19850]}
+{"case":"asymmetric-steps-bare-20C-seed130363","seed":130363,"replicate":0,"horizon_s":5,"samples":26853,"package_mae_c":0.5082,"package_persistence_mae_c":0.6260,"hot_mae_c":1.0907,"hot_persistence_mae_c":0.7791,"hot_rmse_c":1.6013,"constant_package_mae_c":0.4712,"constant_hot_mae_c":0.6758,"adaptive_package_mae_c":0.4735,"adaptive_hot_mae_c":0.6159,"adaptive_hot_rmse_c":0.9496,"hot_persistence_rmse_c":1.1528,"all_samples":26853,"all_adaptive_hot_mae_c":0.6159,"all_persistence_hot_mae_c":0.7791,"all_adaptive_hot_rmse_c":0.9496,"selected":[103,4607,133,22010]}
+{"case":"random-fast-bare-35C-seed155921","seed":155921,"replicate":0,"horizon_s":5,"samples":26853,"package_mae_c":0.5461,"package_persistence_mae_c":0.5120,"hot_mae_c":1.2352,"hot_persistence_mae_c":0.7033,"hot_rmse_c":1.5878,"constant_package_mae_c":0.4951,"constant_hot_mae_c":0.8580,"adaptive_package_mae_c":0.4958,"adaptive_hot_mae_c":0.6963,"adaptive_hot_rmse_c":1.1118,"hot_persistence_rmse_c":1.1176,"all_samples":26853,"all_adaptive_hot_mae_c":0.6963,"all_persistence_hot_mae_c":0.7033,"all_adaptive_hot_rmse_c":1.1118,"selected":[14217,453,97,12086]}
+{"case":"sparse-bursts-fan-seed196613","seed":196613,"replicate":0,"horizon_s":5,"samples":23424,"package_mae_c":0.3096,"package_persistence_mae_c":0.2773,"hot_mae_c":1.0422,"hot_persistence_mae_c":1.2247,"hot_rmse_c":1.6409,"constant_package_mae_c":0.3575,"constant_hot_mae_c":1.3411,"adaptive_package_mae_c":0.3067,"adaptive_hot_mae_c":1.0425,"adaptive_hot_rmse_c":1.6410,"hot_persistence_rmse_c":1.9114,"all_samples":26853,"all_adaptive_hot_mae_c":1.0603,"all_persistence_hot_mae_c":1.2192,"all_adaptive_hot_rmse_c":1.6710,"selected":[0,0,23353,71]}
+{"case":"sine-passive-15C-seed262147","seed":262147,"replicate":0,"horizon_s":5,"samples":26853,"package_mae_c":0.4638,"package_persistence_mae_c":0.4143,"hot_mae_c":0.6726,"hot_persistence_mae_c":0.7397,"hot_rmse_c":0.8534,"constant_package_mae_c":0.4638,"constant_hot_mae_c":0.6726,"adaptive_package_mae_c":0.4640,"adaptive_hot_mae_c":0.6853,"adaptive_hot_rmse_c":0.8690,"hot_persistence_rmse_c":0.9938,"all_samples":26853,"all_adaptive_hot_mae_c":0.6853,"all_persistence_hot_mae_c":0.7397,"all_adaptive_hot_rmse_c":0.8690,"selected":[250,15359,0,11244]}
+{"case":"cooling-change-35C-seed327673","seed":327673,"replicate":0,"horizon_s":5,"samples":26853,"package_mae_c":0.4290,"package_persistence_mae_c":0.3782,"hot_mae_c":0.5225,"hot_persistence_mae_c":0.4122,"hot_rmse_c":0.6474,"constant_package_mae_c":0.4290,"constant_hot_mae_c":0.5225,"adaptive_package_mae_c":0.3997,"adaptive_hot_mae_c":0.4438,"adaptive_hot_rmse_c":0.5866,"hot_persistence_rmse_c":0.6427,"all_samples":26853,"all_adaptive_hot_mae_c":0.4438,"all_persistence_hot_mae_c":0.4122,"all_adaptive_hot_rmse_c":0.5866,"selected":[2483,38,0,24332]}
+{"case":"noisy-burst-bare-seed393241","seed":393241,"replicate":0,"horizon_s":5,"samples":26853,"package_mae_c":0.6844,"package_persistence_mae_c":0.6813,"hot_mae_c":1.5235,"hot_persistence_mae_c":1.8569,"hot_rmse_c":1.8195,"constant_package_mae_c":1.6232,"constant_hot_mae_c":3.4374,"adaptive_package_mae_c":0.7090,"adaptive_hot_mae_c":1.0777,"adaptive_hot_rmse_c":1.3329,"hot_persistence_rmse_c":2.2316,"all_samples":26853,"all_adaptive_hot_mae_c":1.0777,"all_persistence_hot_mae_c":1.8569,"all_adaptive_hot_rmse_c":1.3329,"selected":[0,0,0,26853]}
+{"case":"duty2of7-bare-seed458879","seed":458879,"replicate":0,"horizon_s":5,"samples":26853,"package_mae_c":0.4752,"package_persistence_mae_c":0.4459,"hot_mae_c":1.2071,"hot_persistence_mae_c":2.0813,"hot_rmse_c":1.5079,"constant_package_mae_c":1.2470,"constant_hot_mae_c":4.2069,"adaptive_package_mae_c":0.4727,"adaptive_hot_mae_c":1.2071,"adaptive_hot_rmse_c":1.5079,"hot_persistence_rmse_c":2.6306,"all_samples":26853,"all_adaptive_hot_mae_c":1.2071,"all_persistence_hot_mae_c":2.0813,"all_adaptive_hot_rmse_c":1.5079,"selected":[0,0,26853,0]}
+build/test_forecast_feedback
+test_forecast_feedback: causality, delayed labels, fallback and clock checks passed
+build/test_temporal
+test_temporal: delayed labels, causal history, reference fallback and bounds passed (7344 bytes/cluster)
+python3 tests/test_runtime_forecast.py
+.....
+----------------------------------------------------------------------
+Ran 5 tests in 0.007s
+
+OK
+python3 tests/test_runtime_summary.py
+..
+----------------------------------------------------------------------
+Ran 2 tests in 0.002s
+
+OK
+python3 tests/test_cooperative_summary.py
+...
+----------------------------------------------------------------------
+Ran 3 tests in 0.000s
+
+OK
+build/test_control_forecast --temporal --integrated --development
+{"case":"steps-passive","replicate":0,"seed":7001,"samples":26853,"persistence_mae_c":0.486501,"current_mae_c":0.484831,"candidate_mae_c":0.484831,"persistence_rmse_c":0.840648,"current_rmse_c":0.741659,"candidate_rmse_c":0.741659,"current_model_ready":26853,"candidate_model_ready":26853,"peak_c":73.0,"throttle_steps":0,"served":0.801200}
+{"case":"burst2-bare","replicate":0,"seed":7003,"samples":26853,"persistence_mae_c":1.680119,"current_mae_c":0.944647,"candidate_mae_c":0.747381,"persistence_rmse_c":2.020321,"current_rmse_c":1.165410,"candidate_rmse_c":0.965414,"current_model_ready":26840,"candidate_model_ready":26853,"peak_c":82.2,"throttle_steps":0,"served":0.663236}
+{"case":"burst03-fan","replicate":0,"seed":7013,"samples":26853,"persistence_mae_c":0.506751,"current_mae_c":0.449531,"candidate_mae_c":0.449531,"persistence_rmse_c":0.701677,"current_rmse_c":0.533216,"candidate_rmse_c":0.533216,"current_model_ready":26853,"candidate_model_ready":26853,"peak_c":47.2,"throttle_steps":0,"served":0.580275}
+{"case":"asymmetric-bare","replicate":0,"seed":7019,"samples":26853,"persistence_mae_c":0.727812,"current_mae_c":0.597914,"candidate_mae_c":0.597914,"persistence_rmse_c":1.097105,"current_rmse_c":0.902890,"candidate_rmse_c":0.902890,"current_model_ready":26853,"candidate_model_ready":26853,"peak_c":82.2,"throttle_steps":0,"served":0.755197}
+{"case":"sine-passive","replicate":0,"seed":7027,"samples":26853,"persistence_mae_c":0.743890,"current_mae_c":0.663892,"candidate_mae_c":0.663892,"persistence_rmse_c":0.999014,"current_rmse_c":0.850244,"candidate_rmse_c":0.850244,"current_model_ready":26853,"candidate_model_ready":26853,"peak_c":55.5,"throttle_steps":0,"served":0.792567}
+{"case":"regime-changes-bare","replicate":0,"seed":7039,"samples":26853,"persistence_mae_c":0.874370,"current_mae_c":0.644139,"candidate_mae_c":0.587251,"persistence_rmse_c":1.316337,"current_rmse_c":0.872167,"candidate_rmse_c":0.801227,"current_model_ready":26849,"candidate_model_ready":26853,"peak_c":82.2,"throttle_steps":0,"served":0.618728}
+build/test_controller
+test_controller: fallback, joint allocation and raw-guard checks passed
+build/test_predict
+control influence: original 2400000, cooperative 1800000, raw guard 1200000 kHz
+test_predict: action support, delayed learning, pure queries, backend replacement and MPC influence passed (14368 bytes/predictor)
+build/test_closed_loop
+| scenario | controller | peak C | s >85C | kernel | ceil | freq | mean W | served |
+|------------------------|------------|--------|---------|--------|--------|----------|---------|---------|
+| full load, passive | off | 84.1 | 0.0 | 0 | 0 | 16 | 10.61 | 69.38% |
+| full load, passive | rule | 81.3 | 0.0 | 0 | 2 | 18 | 10.48 | 68.43% |
+| full load, passive | learned | 81.3 | 0.0 | 0 | 2 | 18 | 10.48 | 68.38% |
+| big-core load, passive | off | 72.1 | 0.0 | 0 | 0 | 12 | 8.76 | 99.99% |
+| big-core load, passive | rule | 72.1 | 0.0 | 0 | 0 | 12 | 8.76 | 99.99% |
+| big-core load, passive | learned | 72.1 | 0.0 | 0 | 0 | 12 | 8.76 | 99.99% |
+| full load, bare board | off | 94.8 | 2293.3 | 5228 | 0 | 10462 | 8.17 | 53.44% |
+| full load, bare board | rule | 81.3 | 0.0 | 0 | 11 | 27 | 7.49 | 51.04% |
+| full load, bare board | learned | 81.3 | 0.0 | 0 | 14 | 30 | 7.48 | 51.08% |
+| burst load, bare board | off | 85.9 | 11.5 | 93 | 0 | 6381 | 6.88 | 68.85% |
+| burst load, bare board | rule | 82.2 | 0.0 | 0 | 42 | 5839 | 6.64 | 66.43% |
+| burst load, bare board | learned | 82.2 | 0.0 | 0 | 34 | 5873 | 6.66 | 66.52% |
+
+| held-out case | rule: peak / moves / served | learned: peak / moves / served |
+|------------------------|-----------------------------|--------------------------------|
+| full passive, seed 7 | 81.3 / 2 / 68.44% | 81.3 / 4 / 68.35% |
+| full bare, seed 7 | 81.3 / 10 / 51.04% | 81.3 / 16 / 51.09% |
+| burst bare, seed 7 | 82.2 / 39 / 66.51% | 82.2 / 27 / 66.68% |
+| burst bare, seed 99 | 82.2 / 38 / 66.62% | 82.2 / 34 / 66.49% |
+| full passive, 35 C | 82.2 / 41 / 62.60% | 81.3 / 11 / 62.95% |
+| full bare, 35 C | 82.2 / 21 / 44.77% | 81.3 / 12 / 44.38% |
+| burst bare, 35 C | 82.2 / 151 / 59.24% | 82.2 / 13 / 60.25% |
+| full bare, 15 C | 81.3 / 32 / 55.73% | 79.5 / 10 / 55.22% |
+| full passive, warm 60 | 81.3 / 2 / 67.18% | 81.3 / 6 / 67.04% |
+| full bare, warm 70 | 82.2 / 51 / 48.98% | 81.3 / 13 / 49.23% |
+| burst bare, warm 70 | 82.2 / 61 / 65.23% | 82.2 / 58 / 65.27% |
+| 10 s bursts, bare | 82.2 / 110 / 66.05% | 81.3 / 27 / 65.87% |
+| 300 ms bursts, bare | 69.3 / 0 / 58.03% | 69.3 / 0 / 58.03% |
+| 10 s bursts, passive | 60.1 / 0 / 70.46% | 60.1 / 0 / 70.46% |
+| load steps, bare | 82.2 / 31 / 69.42% | 81.3 / 30 / 69.83% |
+| load steps, passive | 69.3 / 0 / 79.25% | 69.3 / 0 / 79.25% |
+| one cluster, bare | 76.7 / 0 / 99.99% | 76.7 / 0 / 99.99% |
+| full, fan | 52.7 / 0 / 69.38% | 52.7 / 0 / 69.38% |
+| burst, fan, 35 C | 52.7 / 0 / 68.92% | 52.7 / 0 / 68.92% |
+
+peak C: hottest passive zone after 300 s. kernel: step_wise throttle steps. ceil: Lerwatt ceiling moves. served: share of demanded work done.
+hot start cooling=0 policy=0 seed=42 full: peak 83.2 C, 0 throttle steps
+hot start cooling=0 policy=0 seed=42 burst: peak 83.2 C, 0 throttle steps
+hot start cooling=0 policy=0 seed=7 full: peak 83.2 C, 0 throttle steps
+hot start cooling=0 policy=0 seed=7 burst: peak 83.2 C, 0 throttle steps
+hot start cooling=0 policy=0 seed=99 full: peak 83.2 C, 0 throttle steps
+hot start cooling=0 policy=0 seed=99 burst: peak 83.2 C, 0 throttle steps
+hot start cooling=0 policy=1 seed=42 full: peak 83.2 C, 0 throttle steps
+hot start cooling=0 policy=1 seed=42 burst: peak 83.2 C, 0 throttle steps
+hot start cooling=0 policy=1 seed=7 full: peak 83.2 C, 0 throttle steps
+hot start cooling=0 policy=1 seed=7 burst: peak 83.2 C, 0 throttle steps
+hot start cooling=0 policy=1 seed=99 full: peak 83.2 C, 0 throttle steps
+hot start cooling=0 policy=1 seed=99 burst: peak 83.2 C, 0 throttle steps
+hot start cooling=1 policy=0 seed=42 full: peak 82.2 C, 0 throttle steps
+hot start cooling=1 policy=0 seed=42 burst: peak 82.2 C, 0 throttle steps
+hot start cooling=1 policy=0 seed=7 full: peak 83.2 C, 0 throttle steps
+hot start cooling=1 policy=0 seed=7 burst: peak 83.2 C, 0 throttle steps
+hot start cooling=1 policy=0 seed=99 full: peak 83.2 C, 0 throttle steps
+hot start cooling=1 policy=0 seed=99 burst: peak 83.2 C, 0 throttle steps
+hot start cooling=1 policy=1 seed=42 full: peak 82.2 C, 0 throttle steps
+hot start cooling=1 policy=1 seed=42 burst: peak 82.2 C, 0 throttle steps
+hot start cooling=1 policy=1 seed=7 full: peak 83.2 C, 0 throttle steps
+hot start cooling=1 policy=1 seed=7 burst: peak 83.2 C, 0 throttle steps
+hot start cooling=1 policy=1 seed=99 full: peak 83.2 C, 0 throttle steps
+hot start cooling=1 policy=1 seed=99 burst: peak 83.2 C, 0 throttle steps
+hot start cooling=2 policy=0 seed=42 full: peak 83.2 C, 0 throttle steps
+hot start cooling=2 policy=0 seed=42 burst: peak 83.2 C, 0 throttle steps
+hot start cooling=2 policy=0 seed=7 full: peak 83.2 C, 0 throttle steps
+hot start cooling=2 policy=0 seed=7 burst: peak 83.2 C, 0 throttle steps
+hot start cooling=2 policy=0 seed=99 full: peak 83.2 C, 0 throttle steps
+hot start cooling=2 policy=0 seed=99 burst: peak 83.2 C, 0 throttle steps
+hot start cooling=2 policy=1 seed=42 full: peak 83.2 C, 0 throttle steps
+hot start cooling=2 policy=1 seed=42 burst: peak 83.2 C, 0 throttle steps
+hot start cooling=2 policy=1 seed=7 full: peak 83.2 C, 0 throttle steps
+hot start cooling=2 policy=1 seed=7 burst: peak 83.2 C, 0 throttle steps
+hot start cooling=2 policy=1 seed=99 full: peak 83.2 C, 0 throttle steps
+hot start cooling=2 policy=1 seed=99 burst: peak 83.2 C, 0 throttle steps
+test_closed_loop: all checks passed
+build/test_closed_loop --cross-cluster
+| scenario | controller | peak C | s >85C | kernel | ceil | freq | mean W | served |
+|------------------------|------------|--------|---------|--------|--------|----------|---------|---------|
+| full load, passive | off | 84.1 | 0.0 | 0 | 0 | 16 | 10.61 | 69.38% |
+| full load, passive | rule | 81.3 | 0.0 | 0 | 2 | 18 | 10.48 | 68.43% |
+| full load, passive | learned | 81.3 | 0.0 | 0 | 5 | 21 | 10.43 | 68.44% |
+| big-core load, passive | off | 72.1 | 0.0 | 0 | 0 | 12 | 8.76 | 99.99% |
+| big-core load, passive | rule | 72.1 | 0.0 | 0 | 0 | 12 | 8.76 | 99.99% |
+| big-core load, passive | learned | 72.1 | 0.0 | 0 | 1 | 12 | 8.76 | 99.99% |
+| full load, bare board | off | 94.8 | 2293.3 | 5228 | 0 | 10462 | 8.17 | 53.44% |
+| full load, bare board | rule | 81.3 | 0.0 | 0 | 11 | 27 | 7.49 | 51.04% |
+| full load, bare board | learned | 81.3 | 0.0 | 0 | 8 | 24 | 7.44 | 50.89% |
+| burst load, bare board | off | 85.9 | 11.5 | 93 | 0 | 6381 | 6.88 | 68.85% |
+| burst load, bare board | rule | 82.2 | 0.0 | 0 | 42 | 5839 | 6.64 | 66.43% |
+| burst load, bare board | learned | 79.5 | 0.0 | 0 | 8 | 5702 | 6.54 | 65.56% |
+
+| held-out case | rule: peak / moves / served | learned: peak / moves / served |
+|------------------------|-----------------------------|--------------------------------|
+| full passive, seed 7 | 81.3 / 2 / 68.44% | 81.3 / 2 / 68.19% |
+| full bare, seed 7 | 81.3 / 10 / 51.04% | 81.3 / 10 / 50.89% |
+| burst bare, seed 7 | 82.2 / 39 / 66.51% | 79.5 / 9 / 65.96% |
+| burst bare, seed 99 | 82.2 / 38 / 66.62% | 79.5 / 8 / 65.75% |
+| full passive, 35 C | 82.2 / 41 / 62.60% | 81.3 / 7 / 62.97% |
+| full bare, 35 C | 82.2 / 21 / 44.77% | 81.3 / 20 / 44.37% |
+| burst bare, 35 C | 82.2 / 151 / 59.24% | 79.5 / 12 / 58.95% |
+| full bare, 15 C | 81.3 / 32 / 55.73% | 80.4 / 6 / 55.13% |
+| full passive, warm 60 | 81.3 / 2 / 67.18% | 81.3 / 6 / 66.85% |
+| full bare, warm 70 | 82.2 / 51 / 48.98% | 82.2 / 21 / 47.98% |
+| burst bare, warm 70 | 82.2 / 61 / 65.23% | 81.3 / 15 / 64.76% |
+| 10 s bursts, bare | 82.2 / 110 / 66.05% | 77.6 / 8 / 64.50% |
+| 300 ms bursts, bare | 69.3 / 0 / 58.03% | 69.3 / 0 / 58.03% |
+| 10 s bursts, passive | 60.1 / 0 / 70.46% | 60.1 / 0 / 70.46% |
+| load steps, bare | 82.2 / 31 / 69.42% | 81.3 / 15 / 68.30% |
+| load steps, passive | 69.3 / 0 / 79.25% | 61.0 / 18 / 73.56% |
+| one cluster, bare | 76.7 / 0 / 99.99% | 76.7 / 2 / 99.99% |
+| full, fan | 52.7 / 0 / 69.38% | 52.7 / 0 / 69.38% |
+| burst, fan, 35 C | 52.7 / 0 / 68.92% | 52.7 / 0 / 68.92% |
+
+peak C: hottest passive zone after 300 s. kernel: step_wise throttle steps. ceil: Lerwatt ceiling moves. served: share of demanded work done.
+hot start cooling=0 policy=0 seed=42 full: peak 83.2 C, 0 throttle steps
+hot start cooling=0 policy=0 seed=42 burst: peak 83.2 C, 0 throttle steps
+hot start cooling=0 policy=0 seed=7 full: peak 83.2 C, 0 throttle steps
+hot start cooling=0 policy=0 seed=7 burst: peak 83.2 C, 0 throttle steps
+hot start cooling=0 policy=0 seed=99 full: peak 83.2 C, 0 throttle steps
+hot start cooling=0 policy=0 seed=99 burst: peak 83.2 C, 0 throttle steps
+hot start cooling=0 policy=1 seed=42 full: peak 83.2 C, 0 throttle steps
+hot start cooling=0 policy=1 seed=42 burst: peak 83.2 C, 0 throttle steps
+hot start cooling=0 policy=1 seed=7 full: peak 83.2 C, 0 throttle steps
+hot start cooling=0 policy=1 seed=7 burst: peak 83.2 C, 0 throttle steps
+hot start cooling=0 policy=1 seed=99 full: peak 83.2 C, 0 throttle steps
+hot start cooling=0 policy=1 seed=99 burst: peak 83.2 C, 0 throttle steps
+hot start cooling=1 policy=0 seed=42 full: peak 82.2 C, 0 throttle steps
+hot start cooling=1 policy=0 seed=42 burst: peak 82.2 C, 0 throttle steps
+hot start cooling=1 policy=0 seed=7 full: peak 83.2 C, 0 throttle steps
+hot start cooling=1 policy=0 seed=7 burst: peak 83.2 C, 0 throttle steps
+hot start cooling=1 policy=0 seed=99 full: peak 83.2 C, 0 throttle steps
+hot start cooling=1 policy=0 seed=99 burst: peak 83.2 C, 0 throttle steps
+hot start cooling=1 policy=1 seed=42 full: peak 82.2 C, 0 throttle steps
+hot start cooling=1 policy=1 seed=42 burst: peak 82.2 C, 0 throttle steps
+hot start cooling=1 policy=1 seed=7 full: peak 83.2 C, 0 throttle steps
+hot start cooling=1 policy=1 seed=7 burst: peak 83.2 C, 0 throttle steps
+hot start cooling=1 policy=1 seed=99 full: peak 83.2 C, 0 throttle steps
+hot start cooling=1 policy=1 seed=99 burst: peak 83.2 C, 0 throttle steps
+hot start cooling=2 policy=0 seed=42 full: peak 83.2 C, 0 throttle steps
+hot start cooling=2 policy=0 seed=42 burst: peak 83.2 C, 0 throttle steps
+hot start cooling=2 policy=0 seed=7 full: peak 83.2 C, 0 throttle steps
+hot start cooling=2 policy=0 seed=7 burst: peak 83.2 C, 0 throttle steps
+hot start cooling=2 policy=0 seed=99 full: peak 83.2 C, 0 throttle steps
+hot start cooling=2 policy=0 seed=99 burst: peak 83.2 C, 0 throttle steps
+hot start cooling=2 policy=1 seed=42 full: peak 83.2 C, 0 throttle steps
+hot start cooling=2 policy=1 seed=42 burst: peak 83.2 C, 0 throttle steps
+hot start cooling=2 policy=1 seed=7 full: peak 83.2 C, 0 throttle steps
+hot start cooling=2 policy=1 seed=7 burst: peak 83.2 C, 0 throttle steps
+hot start cooling=2 policy=1 seed=99 full: peak 83.2 C, 0 throttle steps
+hot start cooling=2 policy=1 seed=99 burst: peak 83.2 C, 0 throttle steps
+test_closed_loop: all checks passed
+build/test_closed_loop --cooperative
+| scenario | controller | peak C | s >85C | kernel | ceil | freq | mean W | served |
+|------------------------|------------|--------|---------|--------|--------|----------|---------|---------|
+| full load, passive | off | 84.1 | 0.0 | 0 | 0 | 16 | 10.61 | 69.38% |
+| full load, passive | rule | 81.3 | 0.0 | 0 | 2 | 18 | 10.48 | 68.43% |
+| full load, passive | learned | 81.3 | 0.0 | 0 | 2 | 18 | 10.48 | 68.38% |
+| big-core load, passive | off | 72.1 | 0.0 | 0 | 0 | 12 | 8.76 | 99.99% |
+| big-core load, passive | rule | 72.1 | 0.0 | 0 | 0 | 12 | 8.76 | 99.99% |
+| big-core load, passive | learned | 72.1 | 0.0 | 0 | 0 | 12 | 8.76 | 99.99% |
+| full load, bare board | off | 94.8 | 2293.3 | 5228 | 0 | 10462 | 8.17 | 53.44% |
+| full load, bare board | rule | 81.3 | 0.0 | 0 | 11 | 27 | 7.49 | 51.04% |
+| full load, bare board | learned | 81.3 | 0.0 | 0 | 10 | 26 | 7.49 | 51.19% |
+| burst load, bare board | off | 85.9 | 11.5 | 93 | 0 | 6381 | 6.88 | 68.85% |
+| burst load, bare board | rule | 82.2 | 0.0 | 0 | 42 | 5839 | 6.64 | 66.43% |
+| burst load, bare board | learned | 82.2 | 0.0 | 0 | 34 | 5873 | 6.66 | 66.52% |
+
+| held-out case | rule: peak / moves / served | learned: peak / moves / served |
+|------------------------|-----------------------------|--------------------------------|
+| full passive, seed 7 | 81.3 / 2 / 68.44% | 81.3 / 4 / 68.35% |
+| full bare, seed 7 | 81.3 / 10 / 51.04% | 81.3 / 10 / 51.20% |
+| burst bare, seed 7 | 82.2 / 39 / 66.51% | 82.2 / 27 / 66.68% |
+| burst bare, seed 99 | 82.2 / 38 / 66.62% | 82.2 / 34 / 66.49% |
+| full passive, 35 C | 82.2 / 41 / 62.60% | 81.3 / 11 / 62.95% |
+| full bare, 35 C | 82.2 / 21 / 44.77% | 81.3 / 12 / 44.38% |
+| burst bare, 35 C | 82.2 / 151 / 59.24% | 82.2 / 13 / 60.25% |
+| full bare, 15 C | 81.3 / 32 / 55.73% | 79.5 / 10 / 55.22% |
+| full passive, warm 60 | 81.3 / 2 / 67.18% | 81.3 / 6 / 67.04% |
+| full bare, warm 70 | 82.2 / 51 / 48.98% | 81.3 / 13 / 49.23% |
+| burst bare, warm 70 | 82.2 / 61 / 65.23% | 82.2 / 58 / 65.27% |
+| 10 s bursts, bare | 82.2 / 110 / 66.05% | 81.3 / 27 / 65.87% |
+| 300 ms bursts, bare | 69.3 / 0 / 58.03% | 69.3 / 0 / 58.03% |
+| 10 s bursts, passive | 60.1 / 0 / 70.46% | 60.1 / 0 / 70.46% |
+| load steps, bare | 82.2 / 31 / 69.42% | 81.3 / 30 / 69.83% |
+| load steps, passive | 69.3 / 0 / 79.25% | 69.3 / 0 / 79.25% |
+| one cluster, bare | 76.7 / 0 / 99.99% | 76.7 / 0 / 99.99% |
+| full, fan | 52.7 / 0 / 69.38% | 52.7 / 0 / 69.38% |
+| burst, fan, 35 C | 52.7 / 0 / 68.92% | 52.7 / 0 / 68.92% |
+
+peak C: hottest passive zone after 300 s. kernel: step_wise throttle steps. ceil: Lerwatt ceiling moves. served: share of demanded work done.
+hot start cooling=0 policy=0 seed=42 full: peak 83.2 C, 0 throttle steps
+hot start cooling=0 policy=0 seed=42 burst: peak 83.2 C, 0 throttle steps
+hot start cooling=0 policy=0 seed=7 full: peak 83.2 C, 0 throttle steps
+hot start cooling=0 policy=0 seed=7 burst: peak 83.2 C, 0 throttle steps
+hot start cooling=0 policy=0 seed=99 full: peak 83.2 C, 0 throttle steps
+hot start cooling=0 policy=0 seed=99 burst: peak 83.2 C, 0 throttle steps
+hot start cooling=0 policy=1 seed=42 full: peak 83.2 C, 0 throttle steps
+hot start cooling=0 policy=1 seed=42 burst: peak 83.2 C, 0 throttle steps
+hot start cooling=0 policy=1 seed=7 full: peak 83.2 C, 0 throttle steps
+hot start cooling=0 policy=1 seed=7 burst: peak 83.2 C, 0 throttle steps
+hot start cooling=0 policy=1 seed=99 full: peak 83.2 C, 0 throttle steps
+hot start cooling=0 policy=1 seed=99 burst: peak 83.2 C, 0 throttle steps
+hot start cooling=1 policy=0 seed=42 full: peak 82.2 C, 0 throttle steps
+hot start cooling=1 policy=0 seed=42 burst: peak 82.2 C, 0 throttle steps
+hot start cooling=1 policy=0 seed=7 full: peak 83.2 C, 0 throttle steps
+hot start cooling=1 policy=0 seed=7 burst: peak 83.2 C, 0 throttle steps
+hot start cooling=1 policy=0 seed=99 full: peak 83.2 C, 0 throttle steps
+hot start cooling=1 policy=0 seed=99 burst: peak 83.2 C, 0 throttle steps
+hot start cooling=1 policy=1 seed=42 full: peak 82.2 C, 0 throttle steps
+hot start cooling=1 policy=1 seed=42 burst: peak 82.2 C, 0 throttle steps
+hot start cooling=1 policy=1 seed=7 full: peak 83.2 C, 0 throttle steps
+hot start cooling=1 policy=1 seed=7 burst: peak 83.2 C, 0 throttle steps
+hot start cooling=1 policy=1 seed=99 full: peak 83.2 C, 0 throttle steps
+hot start cooling=1 policy=1 seed=99 burst: peak 83.2 C, 0 throttle steps
+hot start cooling=2 policy=0 seed=42 full: peak 83.2 C, 0 throttle steps
+hot start cooling=2 policy=0 seed=42 burst: peak 83.2 C, 0 throttle steps
+hot start cooling=2 policy=0 seed=7 full: peak 83.2 C, 0 throttle steps
+hot start cooling=2 policy=0 seed=7 burst: peak 83.2 C, 0 throttle steps
+hot start cooling=2 policy=0 seed=99 full: peak 83.2 C, 0 throttle steps
+hot start cooling=2 policy=0 seed=99 burst: peak 83.2 C, 0 throttle steps
+hot start cooling=2 policy=1 seed=42 full: peak 83.2 C, 0 throttle steps
+hot start cooling=2 policy=1 seed=42 burst: peak 83.2 C, 0 throttle steps
+hot start cooling=2 policy=1 seed=7 full: peak 83.2 C, 0 throttle steps
+hot start cooling=2 policy=1 seed=7 burst: peak 83.2 C, 0 throttle steps
+hot start cooling=2 policy=1 seed=99 full: peak 83.2 C, 0 throttle steps
+hot start cooling=2 policy=1 seed=99 burst: peak 83.2 C, 0 throttle steps
+test_closed_loop: all checks passed
+build/test_closed_loop --cooperative --cross-cluster
+| scenario | controller | peak C | s >85C | kernel | ceil | freq | mean W | served |
+|------------------------|------------|--------|---------|--------|--------|----------|---------|---------|
+| full load, passive | off | 84.1 | 0.0 | 0 | 0 | 16 | 10.61 | 69.38% |
+| full load, passive | rule | 81.3 | 0.0 | 0 | 2 | 18 | 10.48 | 68.43% |
+| full load, passive | learned | 81.3 | 0.0 | 0 | 5 | 21 | 10.43 | 68.44% |
+| big-core load, passive | off | 72.1 | 0.0 | 0 | 0 | 12 | 8.76 | 99.99% |
+| big-core load, passive | rule | 72.1 | 0.0 | 0 | 0 | 12 | 8.76 | 99.99% |
+| big-core load, passive | learned | 72.1 | 0.0 | 0 | 1 | 12 | 8.76 | 99.99% |
+| full load, bare board | off | 94.8 | 2293.3 | 5228 | 0 | 10462 | 8.17 | 53.44% |
+| full load, bare board | rule | 81.3 | 0.0 | 0 | 11 | 27 | 7.49 | 51.04% |
+| full load, bare board | learned | 81.3 | 0.0 | 0 | 8 | 24 | 7.44 | 50.89% |
+| burst load, bare board | off | 85.9 | 11.5 | 93 | 0 | 6381 | 6.88 | 68.85% |
+| burst load, bare board | rule | 82.2 | 0.0 | 0 | 42 | 5839 | 6.64 | 66.43% |
+| burst load, bare board | learned | 79.5 | 0.0 | 0 | 8 | 5702 | 6.54 | 65.56% |
+
+| held-out case | rule: peak / moves / served | learned: peak / moves / served |
+|------------------------|-----------------------------|--------------------------------|
+| full passive, seed 7 | 81.3 / 2 / 68.44% | 81.3 / 2 / 68.19% |
+| full bare, seed 7 | 81.3 / 10 / 51.04% | 81.3 / 10 / 50.89% |
+| burst bare, seed 7 | 82.2 / 39 / 66.51% | 79.5 / 9 / 65.96% |
+| burst bare, seed 99 | 82.2 / 38 / 66.62% | 79.5 / 8 / 65.75% |
+| full passive, 35 C | 82.2 / 41 / 62.60% | 81.3 / 7 / 62.97% |
+| full bare, 35 C | 82.2 / 21 / 44.77% | 81.3 / 20 / 44.37% |
+| burst bare, 35 C | 82.2 / 151 / 59.24% | 79.5 / 12 / 58.95% |
+| full bare, 15 C | 81.3 / 32 / 55.73% | 80.4 / 6 / 55.13% |
+| full passive, warm 60 | 81.3 / 2 / 67.18% | 81.3 / 6 / 66.85% |
+| full bare, warm 70 | 82.2 / 51 / 48.98% | 82.2 / 21 / 47.98% |
+| burst bare, warm 70 | 82.2 / 61 / 65.23% | 81.3 / 15 / 64.76% |
+| 10 s bursts, bare | 82.2 / 110 / 66.05% | 77.6 / 8 / 64.50% |
+| 300 ms bursts, bare | 69.3 / 0 / 58.03% | 69.3 / 0 / 58.03% |
+| 10 s bursts, passive | 60.1 / 0 / 70.46% | 60.1 / 0 / 70.46% |
+| load steps, bare | 82.2 / 31 / 69.42% | 81.3 / 15 / 68.30% |
+| load steps, passive | 69.3 / 0 / 79.25% | 61.0 / 18 / 73.56% |
+| one cluster, bare | 76.7 / 0 / 99.99% | 76.7 / 2 / 99.99% |
+| full, fan | 52.7 / 0 / 69.38% | 52.7 / 0 / 69.38% |
+| burst, fan, 35 C | 52.7 / 0 / 68.92% | 52.7 / 0 / 68.92% |
+
+peak C: hottest passive zone after 300 s. kernel: step_wise throttle steps. ceil: Lerwatt ceiling moves. served: share of demanded work done.
+hot start cooling=0 policy=0 seed=42 full: peak 83.2 C, 0 throttle steps
+hot start cooling=0 policy=0 seed=42 burst: peak 83.2 C, 0 throttle steps
+hot start cooling=0 policy=0 seed=7 full: peak 83.2 C, 0 throttle steps
+hot start cooling=0 policy=0 seed=7 burst: peak 83.2 C, 0 throttle steps
+hot start cooling=0 policy=0 seed=99 full: peak 83.2 C, 0 throttle steps
+hot start cooling=0 policy=0 seed=99 burst: peak 83.2 C, 0 throttle steps
+hot start cooling=0 policy=1 seed=42 full: peak 83.2 C, 0 throttle steps
+hot start cooling=0 policy=1 seed=42 burst: peak 83.2 C, 0 throttle steps
+hot start cooling=0 policy=1 seed=7 full: peak 83.2 C, 0 throttle steps
+hot start cooling=0 policy=1 seed=7 burst: peak 83.2 C, 0 throttle steps
+hot start cooling=0 policy=1 seed=99 full: peak 83.2 C, 0 throttle steps
+hot start cooling=0 policy=1 seed=99 burst: peak 83.2 C, 0 throttle steps
+hot start cooling=1 policy=0 seed=42 full: peak 82.2 C, 0 throttle steps
+hot start cooling=1 policy=0 seed=42 burst: peak 82.2 C, 0 throttle steps
+hot start cooling=1 policy=0 seed=7 full: peak 83.2 C, 0 throttle steps
+hot start cooling=1 policy=0 seed=7 burst: peak 83.2 C, 0 throttle steps
+hot start cooling=1 policy=0 seed=99 full: peak 83.2 C, 0 throttle steps
+hot start cooling=1 policy=0 seed=99 burst: peak 83.2 C, 0 throttle steps
+hot start cooling=1 policy=1 seed=42 full: peak 82.2 C, 0 throttle steps
+hot start cooling=1 policy=1 seed=42 burst: peak 82.2 C, 0 throttle steps
+hot start cooling=1 policy=1 seed=7 full: peak 83.2 C, 0 throttle steps
+hot start cooling=1 policy=1 seed=7 burst: peak 83.2 C, 0 throttle steps
+hot start cooling=1 policy=1 seed=99 full: peak 83.2 C, 0 throttle steps
+hot start cooling=1 policy=1 seed=99 burst: peak 83.2 C, 0 throttle steps
+hot start cooling=2 policy=0 seed=42 full: peak 83.2 C, 0 throttle steps
+hot start cooling=2 policy=0 seed=42 burst: peak 83.2 C, 0 throttle steps
+hot start cooling=2 policy=0 seed=7 full: peak 83.2 C, 0 throttle steps
+hot start cooling=2 policy=0 seed=7 burst: peak 83.2 C, 0 throttle steps
+hot start cooling=2 policy=0 seed=99 full: peak 83.2 C, 0 throttle steps
+hot start cooling=2 policy=0 seed=99 burst: peak 83.2 C, 0 throttle steps
+hot start cooling=2 policy=1 seed=42 full: peak 83.2 C, 0 throttle steps
+hot start cooling=2 policy=1 seed=42 burst: peak 83.2 C, 0 throttle steps
+hot start cooling=2 policy=1 seed=7 full: peak 83.2 C, 0 throttle steps
+hot start cooling=2 policy=1 seed=7 burst: peak 83.2 C, 0 throttle steps
+hot start cooling=2 policy=1 seed=99 full: peak 83.2 C, 0 throttle steps
+hot start cooling=2 policy=1 seed=99 burst: peak 83.2 C, 0 throttle steps
+test_closed_loop: all checks passed
+build/test_cooperative_loop --full
+{"pattern":0,"seed":42,"joint":false,"mode":"nominal","jobs_completed":2880,"backlog":0,"offered_work":1636992.000000,"throttle_steps":0,"makespan_s":405.220,"simulated_board_j":3064.103829,"peak_c":81.307,"job_p99_s":45.290,"point_mae_c":0.618066,"reference_mae_c":0.934517,"forecast_points":12006,"correction_queries":0,"queries":15565,"ceiling_moves":15,"frequency_transitions":5032,"control_cpu_s":0.003446}
+{"pattern":0,"seed":42,"joint":false,"mode":"cooperative","jobs_completed":2880,"backlog":0,"offered_work":1636992.000000,"throttle_steps":0,"makespan_s":405.220,"simulated_board_j":3064.103829,"peak_c":81.307,"job_p99_s":45.290,"point_mae_c":0.618066,"reference_mae_c":0.934517,"forecast_points":12006,"correction_queries":0,"queries":15565,"ceiling_moves":15,"frequency_transitions":5032,"control_cpu_s":0.003817}
+{"pattern":0,"seed":7,"joint":false,"mode":"nominal","jobs_completed":2880,"backlog":0,"offered_work":1636992.000000,"throttle_steps":0,"makespan_s":419.520,"simulated_board_j":3140.746146,"peak_c":81.307,"job_p99_s":59.980,"point_mae_c":0.608290,"reference_mae_c":0.908556,"forecast_points":12435,"correction_queries":0,"queries":14279,"ceiling_moves":18,"frequency_transitions":5389,"control_cpu_s":0.003588}
+{"pattern":0,"seed":7,"joint":false,"mode":"cooperative","jobs_completed":2880,"backlog":0,"offered_work":1636992.000000,"throttle_steps":0,"makespan_s":419.520,"simulated_board_j":3140.746146,"peak_c":81.307,"job_p99_s":59.980,"point_mae_c":0.608290,"reference_mae_c":0.908556,"forecast_points":12435,"correction_queries":0,"queries":14279,"ceiling_moves":18,"frequency_transitions":5389,"control_cpu_s":0.003681}
+{"pattern":0,"seed":99,"joint":false,"mode":"nominal","jobs_completed":2880,"backlog":0,"offered_work":1636992.000000,"throttle_steps":0,"makespan_s":402.760,"simulated_board_j":3056.050846,"peak_c":81.307,"job_p99_s":42.820,"point_mae_c":0.598512,"reference_mae_c":0.901395,"forecast_points":11931,"correction_queries":0,"queries":15553,"ceiling_moves":15,"frequency_transitions":5019,"control_cpu_s":0.003332}
+{"pattern":0,"seed":99,"joint":false,"mode":"cooperative","jobs_completed":2880,"backlog":0,"offered_work":1636992.000000,"throttle_steps":0,"makespan_s":402.760,"simulated_board_j":3056.050846,"peak_c":81.307,"job_p99_s":42.820,"point_mae_c":0.598512,"reference_mae_c":0.901395,"forecast_points":11931,"correction_queries":0,"queries":15553,"ceiling_moves":15,"frequency_transitions":5019,"control_cpu_s":0.003601}
+{"pattern":1,"seed":42,"joint":false,"mode":"nominal","jobs_completed":2880,"backlog":0,"offered_work":1548288.000000,"throttle_steps":0,"makespan_s":503.040,"simulated_board_j":3412.501516,"peak_c":82.230,"job_p99_s":142.790,"point_mae_c":0.709844,"reference_mae_c":1.032735,"forecast_points":14940,"correction_queries":0,"queries":16479,"ceiling_moves":15,"frequency_transitions":1587,"control_cpu_s":0.004321}
+{"pattern":1,"seed":42,"joint":false,"mode":"cooperative","jobs_completed":2880,"backlog":0,"offered_work":1548288.000000,"throttle_steps":0,"makespan_s":503.040,"simulated_board_j":3412.501516,"peak_c":82.230,"job_p99_s":142.790,"point_mae_c":0.709844,"reference_mae_c":1.032735,"forecast_points":14940,"correction_queries":0,"queries":16479,"ceiling_moves":15,"frequency_transitions":1587,"control_cpu_s":0.004525}
+{"pattern":1,"seed":7,"joint":false,"mode":"nominal","jobs_completed":2880,"backlog":0,"offered_work":1548288.000000,"throttle_steps":0,"makespan_s":500.660,"simulated_board_j":3394.311558,"peak_c":82.230,"job_p99_s":140.410,"point_mae_c":0.719791,"reference_mae_c":1.044086,"forecast_points":14868,"correction_queries":0,"queries":15913,"ceiling_moves":17,"frequency_transitions":1514,"control_cpu_s":0.004368}
+{"pattern":1,"seed":7,"joint":false,"mode":"cooperative","jobs_completed":2880,"backlog":0,"offered_work":1548288.000000,"throttle_steps":0,"makespan_s":500.660,"simulated_board_j":3394.311558,"peak_c":82.230,"job_p99_s":140.410,"point_mae_c":0.719791,"reference_mae_c":1.044086,"forecast_points":14868,"correction_queries":0,"queries":15913,"ceiling_moves":17,"frequency_transitions":1514,"control_cpu_s":0.004452}
+{"pattern":1,"seed":99,"joint":false,"mode":"nominal","jobs_completed":2880,"backlog":0,"offered_work":1548288.000000,"throttle_steps":0,"makespan_s":497.110,"simulated_board_j":3383.829286,"peak_c":82.230,"job_p99_s":136.860,"point_mae_c":0.718125,"reference_mae_c":1.028734,"forecast_points":14763,"correction_queries":0,"queries":15518,"ceiling_moves":16,"frequency_transitions":1443,"control_cpu_s":0.004362}
+{"pattern":1,"seed":99,"joint":false,"mode":"cooperative","jobs_completed":2880,"backlog":0,"offered_work":1548288.000000,"throttle_steps":0,"makespan_s":497.110,"simulated_board_j":3383.829286,"peak_c":82.230,"job_p99_s":136.860,"point_mae_c":0.718125,"reference_mae_c":1.028734,"forecast_points":14763,"correction_queries":0,"queries":15518,"ceiling_moves":16,"frequency_transitions":1443,"control_cpu_s":0.004480}
+{"pattern":2,"seed":42,"joint":false,"mode":"nominal","jobs_completed":2880,"backlog":0,"offered_work":1251792.000000,"throttle_steps":0,"makespan_s":360.640,"simulated_board_j":2638.146268,"peak_c":81.307,"job_p99_s":1.590,"point_mae_c":0.531840,"reference_mae_c":0.859365,"forecast_points":10668,"correction_queries":0,"queries":10209,"ceiling_moves":6,"frequency_transitions":8519,"control_cpu_s":0.003051}
+{"pattern":2,"seed":42,"joint":false,"mode":"cooperative","jobs_completed":2880,"backlog":0,"offered_work":1251792.000000,"throttle_steps":0,"makespan_s":360.640,"simulated_board_j":2638.146268,"peak_c":81.307,"job_p99_s":1.590,"point_mae_c":0.531840,"reference_mae_c":0.859365,"forecast_points":10668,"correction_queries":0,"queries":10209,"ceiling_moves":6,"frequency_transitions":8519,"control_cpu_s":0.003251}
+{"pattern":2,"seed":7,"joint":false,"mode":"nominal","jobs_completed":2880,"backlog":0,"offered_work":1251792.000000,"throttle_steps":0,"makespan_s":360.630,"simulated_board_j":2630.933970,"peak_c":81.307,"job_p99_s":1.580,"point_mae_c":0.519062,"reference_mae_c":0.849117,"forecast_points":10668,"correction_queries":0,"queries":10066,"ceiling_moves":7,"frequency_transitions":8387,"control_cpu_s":0.003041}
+{"pattern":2,"seed":7,"joint":false,"mode":"cooperative","jobs_completed":2880,"backlog":0,"offered_work":1251792.000000,"throttle_steps":0,"makespan_s":360.630,"simulated_board_j":2630.933970,"peak_c":81.307,"job_p99_s":1.580,"point_mae_c":0.519062,"reference_mae_c":0.849117,"forecast_points":10668,"correction_queries":0,"queries":10066,"ceiling_moves":7,"frequency_transitions":8387,"control_cpu_s":0.003235}
+{"pattern":2,"seed":99,"joint":false,"mode":"nominal","jobs_completed":2880,"backlog":0,"offered_work":1251792.000000,"throttle_steps":0,"makespan_s":360.640,"simulated_board_j":2626.024997,"peak_c":81.307,"job_p99_s":1.590,"point_mae_c":0.536754,"reference_mae_c":0.852969,"forecast_points":10668,"correction_queries":0,"queries":9965,"ceiling_moves":9,"frequency_transitions":8378,"control_cpu_s":0.003089}
+{"pattern":2,"seed":99,"joint":false,"mode":"cooperative","jobs_completed":2880,"backlog":0,"offered_work":1251792.000000,"throttle_steps":0,"makespan_s":360.640,"simulated_board_j":2626.024997,"peak_c":81.307,"job_p99_s":1.590,"point_mae_c":0.536754,"reference_mae_c":0.852969,"forecast_points":10668,"correction_queries":0,"queries":9965,"ceiling_moves":9,"frequency_transitions":8378,"control_cpu_s":0.003234}
+{"pattern":3,"seed":42,"joint":false,"mode":"nominal","jobs_completed":2880,"backlog":0,"offered_work":1020321.008000,"throttle_steps":0,"makespan_s":359.870,"simulated_board_j":2372.536090,"peak_c":76.692,"job_p99_s":1.050,"point_mae_c":0.843208,"reference_mae_c":0.929450,"forecast_points":10644,"correction_queries":0,"queries":8067,"ceiling_moves":0,"frequency_transitions":7829,"control_cpu_s":0.002996}
+{"pattern":3,"seed":42,"joint":false,"mode":"cooperative","jobs_completed":2880,"backlog":0,"offered_work":1020321.008000,"throttle_steps":0,"makespan_s":359.870,"simulated_board_j":2372.536090,"peak_c":76.692,"job_p99_s":1.050,"point_mae_c":0.843208,"reference_mae_c":0.929450,"forecast_points":10644,"correction_queries":0,"queries":8067,"ceiling_moves":0,"frequency_transitions":7829,"control_cpu_s":0.003215}
+{"pattern":3,"seed":7,"joint":false,"mode":"nominal","jobs_completed":2880,"backlog":0,"offered_work":1020321.008000,"throttle_steps":0,"makespan_s":359.870,"simulated_board_j":2372.536090,"peak_c":77.615,"job_p99_s":1.050,"point_mae_c":0.850526,"reference_mae_c":0.923730,"forecast_points":10644,"correction_queries":0,"queries":8067,"ceiling_moves":0,"frequency_transitions":7829,"control_cpu_s":0.002960}
+{"pattern":3,"seed":7,"joint":false,"mode":"cooperative","jobs_completed":2880,"backlog":0,"offered_work":1020321.008000,"throttle_steps":0,"makespan_s":359.870,"simulated_board_j":2372.536090,"peak_c":77.615,"job_p99_s":1.050,"point_mae_c":0.850526,"reference_mae_c":0.923730,"forecast_points":10644,"correction_queries":0,"queries":8067,"ceiling_moves":0,"frequency_transitions":7829,"control_cpu_s":0.003194}
+{"pattern":3,"seed":99,"joint":false,"mode":"nominal","jobs_completed":2880,"backlog":0,"offered_work":1020321.008000,"throttle_steps":0,"makespan_s":359.870,"simulated_board_j":2372.536090,"peak_c":77.615,"job_p99_s":1.050,"point_mae_c":0.860971,"reference_mae_c":0.929357,"forecast_points":10644,"correction_queries":0,"queries":8067,"ceiling_moves":0,"frequency_transitions":7829,"control_cpu_s":0.003067}
+{"pattern":3,"seed":99,"joint":false,"mode":"cooperative","jobs_completed":2880,"backlog":0,"offered_work":1020321.008000,"throttle_steps":0,"makespan_s":359.870,"simulated_board_j":2372.536090,"peak_c":77.615,"job_p99_s":1.050,"point_mae_c":0.860971,"reference_mae_c":0.929357,"forecast_points":10644,"correction_queries":0,"queries":8067,"ceiling_moves":0,"frequency_transitions":7829,"control_cpu_s":0.003214}
+{"pattern":4,"seed":42,"joint":false,"mode":"nominal","jobs_completed":2880,"backlog":0,"offered_work":2919628.800000,"throttle_steps":0,"makespan_s":980.400,"simulated_board_j":6295.971093,"peak_c":81.307,"job_p99_s":610.530,"point_mae_c":0.478139,"reference_mae_c":0.640429,"forecast_points":29259,"correction_queries":0,"queries":35913,"ceiling_moves":36,"frequency_transitions":42,"control_cpu_s":0.008729}
+{"pattern":4,"seed":42,"joint":false,"mode":"cooperative","jobs_completed":2880,"backlog":0,"offered_work":2919628.800000,"throttle_steps":0,"makespan_s":980.400,"simulated_board_j":6295.971093,"peak_c":81.307,"job_p99_s":610.530,"point_mae_c":0.478139,"reference_mae_c":0.640429,"forecast_points":29259,"correction_queries":0,"queries":35913,"ceiling_moves":36,"frequency_transitions":42,"control_cpu_s":0.008973}
+{"pattern":4,"seed":7,"joint":false,"mode":"nominal","jobs_completed":2880,"backlog":0,"offered_work":2919628.800000,"throttle_steps":0,"makespan_s":979.530,"simulated_board_j":6294.123159,"peak_c":81.307,"job_p99_s":609.650,"point_mae_c":0.483719,"reference_mae_c":0.643218,"forecast_points":29235,"correction_queries":0,"queries":36274,"ceiling_moves":32,"frequency_transitions":40,"control_cpu_s":0.008612}
+{"pattern":4,"seed":7,"joint":false,"mode":"cooperative","jobs_completed":2880,"backlog":0,"offered_work":2919628.800000,"throttle_steps":0,"makespan_s":979.550,"simulated_board_j":6293.863896,"peak_c":81.307,"job_p99_s":609.670,"point_mae_c":0.486560,"reference_mae_c":0.646034,"forecast_points":29235,"correction_queries":1,"queries":36309,"ceiling_moves":32,"frequency_transitions":40,"control_cpu_s":0.008985}
+{"pattern":4,"seed":99,"joint":false,"mode":"nominal","jobs_completed":2880,"backlog":0,"offered_work":2919628.800000,"throttle_steps":0,"makespan_s":981.830,"simulated_board_j":6300.329490,"peak_c":81.307,"job_p99_s":611.960,"point_mae_c":0.484815,"reference_mae_c":0.641113,"forecast_points":29304,"correction_queries":0,"queries":36617,"ceiling_moves":28,"frequency_transitions":38,"control_cpu_s":0.008705}
+{"pattern":4,"seed":99,"joint":false,"mode":"cooperative","jobs_completed":2880,"backlog":0,"offered_work":2919628.800000,"throttle_steps":0,"makespan_s":981.900,"simulated_board_j":6300.200034,"peak_c":81.307,"job_p99_s":612.020,"point_mae_c":0.482736,"reference_mae_c":0.639256,"forecast_points":29304,"correction_queries":1,"queries":36652,"ceiling_moves":28,"frequency_transitions":38,"control_cpu_s":0.008887}
+{"pattern":0,"seed":42,"joint":true,"mode":"nominal","jobs_completed":2880,"backlog":0,"offered_work":1636992.000000,"throttle_steps":0,"makespan_s":418.790,"simulated_board_j":3046.446255,"peak_c":82.230,"job_p99_s":57.070,"point_mae_c":0.631586,"reference_mae_c":0.926190,"forecast_points":12411,"correction_queries":0,"queries":14238,"ceiling_moves":9,"frequency_transitions":3805,"control_cpu_s":0.003428}
+{"pattern":0,"seed":42,"joint":true,"mode":"cooperative","jobs_completed":2880,"backlog":0,"offered_work":1636992.000000,"throttle_steps":0,"makespan_s":418.790,"simulated_board_j":3046.446255,"peak_c":82.230,"job_p99_s":57.070,"point_mae_c":0.631586,"reference_mae_c":0.926190,"forecast_points":12411,"correction_queries":0,"queries":14238,"ceiling_moves":9,"frequency_transitions":3805,"control_cpu_s":0.003683}
+{"pattern":0,"seed":7,"joint":true,"mode":"nominal","jobs_completed":2880,"backlog":0,"offered_work":1636992.000000,"throttle_steps":0,"makespan_s":418.220,"simulated_board_j":3046.942437,"peak_c":82.230,"job_p99_s":56.500,"point_mae_c":0.614085,"reference_mae_c":0.917913,"forecast_points":12396,"correction_queries":0,"queries":14225,"ceiling_moves":9,"frequency_transitions":3819,"control_cpu_s":0.003401}
+{"pattern":0,"seed":7,"joint":true,"mode":"cooperative","jobs_completed":2880,"backlog":0,"offered_work":1636992.000000,"throttle_steps":0,"makespan_s":418.220,"simulated_board_j":3046.942437,"peak_c":82.230,"job_p99_s":56.500,"point_mae_c":0.614085,"reference_mae_c":0.917913,"forecast_points":12396,"correction_queries":0,"queries":14225,"ceiling_moves":9,"frequency_transitions":3819,"control_cpu_s":0.003767}
+{"pattern":0,"seed":99,"joint":true,"mode":"nominal","jobs_completed":2880,"backlog":0,"offered_work":1636992.000000,"throttle_steps":0,"makespan_s":417.740,"simulated_board_j":3045.880971,"peak_c":82.230,"job_p99_s":56.020,"point_mae_c":0.635798,"reference_mae_c":0.921273,"forecast_points":12381,"correction_queries":0,"queries":14210,"ceiling_moves":9,"frequency_transitions":3821,"control_cpu_s":0.003582}
+{"pattern":0,"seed":99,"joint":true,"mode":"cooperative","jobs_completed":2880,"backlog":0,"offered_work":1636992.000000,"throttle_steps":0,"makespan_s":417.740,"simulated_board_j":3045.880971,"peak_c":82.230,"job_p99_s":56.020,"point_mae_c":0.635798,"reference_mae_c":0.921273,"forecast_points":12381,"correction_queries":0,"queries":14210,"ceiling_moves":9,"frequency_transitions":3821,"control_cpu_s":0.003655}
+{"pattern":1,"seed":42,"joint":true,"mode":"nominal","jobs_completed":2880,"backlog":0,"offered_work":1548288.000000,"throttle_steps":0,"makespan_s":538.750,"simulated_board_j":3492.476423,"peak_c":82.230,"job_p99_s":178.500,"point_mae_c":0.858180,"reference_mae_c":1.035502,"forecast_points":16011,"correction_queries":0,"queries":19311,"ceiling_moves":10,"frequency_transitions":1829,"control_cpu_s":0.004728}
+{"pattern":1,"seed":42,"joint":true,"mode":"cooperative","jobs_completed":2880,"backlog":0,"offered_work":1548288.000000,"throttle_steps":0,"makespan_s":538.750,"simulated_board_j":3492.476423,"peak_c":82.230,"job_p99_s":178.500,"point_mae_c":0.858180,"reference_mae_c":1.035502,"forecast_points":16011,"correction_queries":0,"queries":19311,"ceiling_moves":10,"frequency_transitions":1829,"control_cpu_s":0.004976}
+{"pattern":1,"seed":7,"joint":true,"mode":"nominal","jobs_completed":2880,"backlog":0,"offered_work":1548288.000000,"throttle_steps":0,"makespan_s":538.750,"simulated_board_j":3497.103330,"peak_c":82.230,"job_p99_s":178.500,"point_mae_c":0.727766,"reference_mae_c":1.009315,"forecast_points":16011,"correction_queries":0,"queries":19312,"ceiling_moves":10,"frequency_transitions":1854,"control_cpu_s":0.004852}
+{"pattern":1,"seed":7,"joint":true,"mode":"cooperative","jobs_completed":2880,"backlog":0,"offered_work":1548288.000000,"throttle_steps":0,"makespan_s":538.750,"simulated_board_j":3497.103330,"peak_c":82.230,"job_p99_s":178.500,"point_mae_c":0.727766,"reference_mae_c":1.009315,"forecast_points":16011,"correction_queries":0,"queries":19312,"ceiling_moves":10,"frequency_transitions":1854,"control_cpu_s":0.004843}
+{"pattern":1,"seed":99,"joint":true,"mode":"nominal","jobs_completed":2880,"backlog":0,"offered_work":1548288.000000,"throttle_steps":0,"makespan_s":538.750,"simulated_board_j":3492.806164,"peak_c":82.230,"job_p99_s":178.500,"point_mae_c":0.739498,"reference_mae_c":1.012688,"forecast_points":16011,"correction_queries":0,"queries":19312,"ceiling_moves":10,"frequency_transitions":1829,"control_cpu_s":0.004624}
+{"pattern":1,"seed":99,"joint":true,"mode":"cooperative","jobs_completed":2880,"backlog":0,"offered_work":1548288.000000,"throttle_steps":0,"makespan_s":538.750,"simulated_board_j":3492.806164,"peak_c":82.230,"job_p99_s":178.500,"point_mae_c":0.739498,"reference_mae_c":1.012688,"forecast_points":16011,"correction_queries":0,"queries":19312,"ceiling_moves":10,"frequency_transitions":1829,"control_cpu_s":0.004845}
+{"pattern":2,"seed":42,"joint":true,"mode":"nominal","jobs_completed":2880,"backlog":0,"offered_work":1251792.000000,"throttle_steps":0,"makespan_s":381.490,"simulated_board_j":2672.639652,"peak_c":79.461,"job_p99_s":21.480,"point_mae_c":0.591575,"reference_mae_c":0.884357,"forecast_points":11292,"correction_queries":0,"queries":13125,"ceiling_moves":5,"frequency_transitions":8114,"control_cpu_s":0.003232}
+{"pattern":2,"seed":42,"joint":true,"mode":"cooperative","jobs_completed":2880,"backlog":0,"offered_work":1251792.000000,"throttle_steps":0,"makespan_s":381.490,"simulated_board_j":2672.639652,"peak_c":79.461,"job_p99_s":21.480,"point_mae_c":0.591575,"reference_mae_c":0.884357,"forecast_points":11292,"correction_queries":0,"queries":13125,"ceiling_moves":5,"frequency_transitions":8114,"control_cpu_s":0.003394}
+{"pattern":2,"seed":7,"joint":true,"mode":"nominal","jobs_completed":2880,"backlog":0,"offered_work":1251792.000000,"throttle_steps":0,"makespan_s":365.220,"simulated_board_j":2633.845193,"peak_c":81.307,"job_p99_s":5.210,"point_mae_c":0.552913,"reference_mae_c":0.857648,"forecast_points":10806,"correction_queries":0,"queries":12639,"ceiling_moves":5,"frequency_transitions":7936,"control_cpu_s":0.003106}
+{"pattern":2,"seed":7,"joint":true,"mode":"cooperative","jobs_completed":2880,"backlog":0,"offered_work":1251792.000000,"throttle_steps":0,"makespan_s":365.220,"simulated_board_j":2633.845193,"peak_c":81.307,"job_p99_s":5.210,"point_mae_c":0.552913,"reference_mae_c":0.857648,"forecast_points":10806,"correction_queries":0,"queries":12639,"ceiling_moves":5,"frequency_transitions":7936,"control_cpu_s":0.003325}
+{"pattern":2,"seed":99,"joint":true,"mode":"nominal","jobs_completed":2880,"backlog":0,"offered_work":1251792.000000,"throttle_steps":0,"makespan_s":382.510,"simulated_board_j":2645.604875,"peak_c":78.538,"job_p99_s":22.500,"point_mae_c":0.587389,"reference_mae_c":0.843609,"forecast_points":11325,"correction_queries":0,"queries":13158,"ceiling_moves":4,"frequency_transitions":7514,"control_cpu_s":0.003341}
+{"pattern":2,"seed":99,"joint":true,"mode":"cooperative","jobs_completed":2880,"backlog":0,"offered_work":1251792.000000,"throttle_steps":0,"makespan_s":382.510,"simulated_board_j":2645.604875,"peak_c":78.538,"job_p99_s":22.500,"point_mae_c":0.587389,"reference_mae_c":0.843609,"forecast_points":11325,"correction_queries":0,"queries":13158,"ceiling_moves":4,"frequency_transitions":7514,"control_cpu_s":0.003442}
+{"pattern":3,"seed":42,"joint":true,"mode":"nominal","jobs_completed":2880,"backlog":0,"offered_work":1020321.008000,"throttle_steps":0,"makespan_s":359.920,"simulated_board_j":2372.551126,"peak_c":76.692,"job_p99_s":1.050,"point_mae_c":0.843901,"reference_mae_c":0.930119,"forecast_points":10647,"correction_queries":0,"queries":12480,"ceiling_moves":1,"frequency_transitions":7779,"control_cpu_s":0.003024}
+{"pattern":3,"seed":42,"joint":true,"mode":"cooperative","jobs_completed":2880,"backlog":0,"offered_work":1020321.008000,"throttle_steps":0,"makespan_s":359.920,"simulated_board_j":2372.551126,"peak_c":76.692,"job_p99_s":1.050,"point_mae_c":0.843901,"reference_mae_c":0.930119,"forecast_points":10647,"correction_queries":0,"queries":12480,"ceiling_moves":1,"frequency_transitions":7779,"control_cpu_s":0.003225}
+{"pattern":3,"seed":7,"joint":true,"mode":"nominal","jobs_completed":2880,"backlog":0,"offered_work":1020321.008000,"throttle_steps":0,"makespan_s":359.920,"simulated_board_j":2372.551126,"peak_c":77.615,"job_p99_s":1.050,"point_mae_c":0.850204,"reference_mae_c":0.923388,"forecast_points":10647,"correction_queries":0,"queries":12480,"ceiling_moves":1,"frequency_transitions":7779,"control_cpu_s":0.002987}
+{"pattern":3,"seed":7,"joint":true,"mode":"cooperative","jobs_completed":2880,"backlog":0,"offered_work":1020321.008000,"throttle_steps":0,"makespan_s":359.920,"simulated_board_j":2372.551126,"peak_c":77.615,"job_p99_s":1.050,"point_mae_c":0.850204,"reference_mae_c":0.923388,"forecast_points":10647,"correction_queries":0,"queries":12480,"ceiling_moves":1,"frequency_transitions":7779,"control_cpu_s":0.003229}
+{"pattern":3,"seed":99,"joint":true,"mode":"nominal","jobs_completed":2880,"backlog":0,"offered_work":1020321.008000,"throttle_steps":0,"makespan_s":359.920,"simulated_board_j":2372.551126,"peak_c":76.692,"job_p99_s":1.050,"point_mae_c":0.859807,"reference_mae_c":0.928173,"forecast_points":10647,"correction_queries":0,"queries":12480,"ceiling_moves":1,"frequency_transitions":7779,"control_cpu_s":0.003079}
+{"pattern":3,"seed":99,"joint":true,"mode":"cooperative","jobs_completed":2880,"backlog":0,"offered_work":1020321.008000,"throttle_steps":0,"makespan_s":359.920,"simulated_board_j":2372.551126,"peak_c":76.692,"job_p99_s":1.050,"point_mae_c":0.859807,"reference_mae_c":0.928173,"forecast_points":10647,"correction_queries":0,"queries":12480,"ceiling_moves":1,"frequency_transitions":7779,"control_cpu_s":0.003307}
+{"pattern":4,"seed":42,"joint":true,"mode":"nominal","jobs_completed":2880,"backlog":0,"offered_work":2919628.800000,"throttle_steps":0,"makespan_s":1009.870,"simulated_board_j":6372.262955,"peak_c":82.230,"job_p99_s":639.990,"point_mae_c":0.515443,"reference_mae_c":0.640917,"forecast_points":30144,"correction_queries":0,"queries":39322,"ceiling_moves":11,"frequency_transitions":30,"control_cpu_s":0.009039}
+{"pattern":4,"seed":42,"joint":true,"mode":"cooperative","jobs_completed":2880,"backlog":0,"offered_work":2919628.800000,"throttle_steps":0,"makespan_s":1009.870,"simulated_board_j":6372.262955,"peak_c":82.230,"job_p99_s":639.990,"point_mae_c":0.515443,"reference_mae_c":0.640917,"forecast_points":30144,"correction_queries":0,"queries":39322,"ceiling_moves":11,"frequency_transitions":30,"control_cpu_s":0.009519}
+{"pattern":4,"seed":7,"joint":true,"mode":"nominal","jobs_completed":2880,"backlog":0,"offered_work":2919628.800000,"throttle_steps":0,"makespan_s":1008.940,"simulated_board_j":6348.257503,"peak_c":82.230,"job_p99_s":639.060,"point_mae_c":0.503787,"reference_mae_c":0.630852,"forecast_points":30117,"correction_queries":0,"queries":39294,"ceiling_moves":15,"frequency_transitions":31,"control_cpu_s":0.008985}
+{"pattern":4,"seed":7,"joint":true,"mode":"cooperative","jobs_completed":2880,"backlog":0,"offered_work":2919628.800000,"throttle_steps":0,"makespan_s":1008.940,"simulated_board_j":6348.257503,"peak_c":82.230,"job_p99_s":639.060,"point_mae_c":0.503787,"reference_mae_c":0.630852,"forecast_points":30117,"correction_queries":0,"queries":39294,"ceiling_moves":15,"frequency_transitions":31,"control_cpu_s":0.009440}
+{"pattern":4,"seed":99,"joint":true,"mode":"nominal","jobs_completed":2880,"backlog":0,"offered_work":2919628.800000,"throttle_steps":0,"makespan_s":988.270,"simulated_board_j":6315.551787,"peak_c":82.230,"job_p99_s":618.390,"point_mae_c":0.515453,"reference_mae_c":0.644973,"forecast_points":29496,"correction_queries":0,"queries":38676,"ceiling_moves":10,"frequency_transitions":27,"control_cpu_s":0.008785}
+{"pattern":4,"seed":99,"joint":true,"mode":"cooperative","jobs_completed":2880,"backlog":0,"offered_work":2919628.800000,"throttle_steps":0,"makespan_s":988.270,"simulated_board_j":6315.551787,"peak_c":82.230,"job_p99_s":618.390,"point_mae_c":0.515453,"reference_mae_c":0.644973,"forecast_points":29496,"correction_queries":0,"queries":38676,"ceiling_moves":10,"frequency_transitions":27,"control_cpu_s":0.009106}
+cc -DLERWATT_VERSION=\"source\" -O2 -g -std=c11 -D_GNU_SOURCE -Wall -Wextra -Wshadow -Werror -Iinclude -Isrc -DLW_NO_BPF -o build/lerwattd-standalone src/hal_fs.c src/hal_discover.c src/hal_sample.c src/hal_actuate.c src/controller.c src/learn.c src/forecast.c src/temporal.c src/predict.c src/contract.c src/notify.c src/checkpoint.c src/lerwattd.c -lm -lrt
+cc -O2 -g -std=c11 -D_GNU_SOURCE -Wall -Wextra -Wshadow -Werror -Iinclude -Isrc -o build/test_runtime_fs tests/test_runtime_fs.c src/hal_fs.c src/hal_actuate.c -lm -lrt
+cc -O2 -g -std=c11 -D_GNU_SOURCE -Wall -Wextra -Wshadow -Werror -Iinclude -Isrc -o build/test_discovery tests/test_discovery.c src/hal_discover.c src/hal_sample.c src/hal_fs.c -lm -lrt
+cc -O2 -g -std=c11 -D_GNU_SOURCE -Wall -Wextra -Wshadow -Werror -Iinclude -Isrc -o build/test_notify tests/test_notify.c src/notify.c -lm -lrt
+cc -O2 -g -std=c11 -D_GNU_SOURCE -Wall -Wextra -Wshadow -Werror -Iinclude -Isrc -o build/test_checkpoint tests/test_checkpoint.c src/checkpoint.c -lm -lrt
+cc -O2 -g -std=c11 -D_GNU_SOURCE -Wall -Wextra -Wshadow -Werror -Iinclude -Isrc -DLW_NO_BPF -o build/test_contract tests/test_contract.c src/contract.c -lm -lrt
+python3 tests/check_integration_abi.py
+Integration ABI 2 golden check passed (56 values).
+build/test_runtime_fs
+lerwatt: cannot journal /tmp/lerwatt-runtime-WTCNkb/sys/devices/system/cpu/cpufreq/policy0/scaling_max_freq (Is a directory); not writing it
+lerwatt: write /tmp/lerwatt-runtime-WTCNkb/sys/devices/system/cpu/cpufreq/policy0/scaling_max_freq <- 1500 failed: Is a directory
+lerwatt: restore failed: /tmp/lerwatt-runtime-WTCNkb/sys/devices/system/cpu/cpufreq/policy0/scaling_max_freq <- 2000
+test_runtime_fs: ok (strict reads, lock lifetime, journal faults, restore validation)
+build/test_discovery
+test_discovery: topology, frequency, sensor and read-only capability fixtures passed
+build/test_notify
+test_notify: environment validation, readiness, progress heartbeats and socket failure passed
+build/test_checkpoint
+test_checkpoint: learned-only roundtrip, identity/schema/age validation and private atomic files passed
+build/test_contract
+test_contract: ABI2 identity, independent peer versions, bounded freshness and recovery passed
+python3 tests/test_daemon_deployment.py build/lerwattd-standalone
+daemon deployment: CLI, read-only checks, 16-policy range control, locking, recovery and sensor loss passed
+sh tests/test_deploy.sh
+test_deploy: all checks passed (fixtures only; no host service or CPU changes)
+tests/e2e.sh build
+e2e: ok - joint planning requires the learned policy
+e2e: ok - unknown point forecaster is rejected before touching a system
+e2e: ok - prediction mode and unsupported combinations are rejected before discovery
+e2e: ok - no hardware and no simulator -> clear error
+e2e: ok - simulator up, policy4 scaling_max_freq = 2400000
+e2e: ok - live root rejects a second simulator, including --force
+e2e: ok - auto-detected the simulator
+e2e: ok - lerwattd capped big0 down to 1200 MHz while the board ran hot
+e2e: ok - clean exit restored every knob and removed the journal
+e2e: ok - joint-policy startup falls back before training and restores knobs
+e2e: ok - cooperative cold start keeps fallback, valid telemetry and restoration
+e2e: ok - kill -9 then --restore recovered scaling_max_freq = 2400000
+e2e: ok - simulator removed its marker on exit
+e2e: ok - same root restarts after SIGTERM, duration expiry and SIGINT; data preserved
+e2e: ok - unidentified non-empty roots remain protected
+e2e: all checks passed
+mkdir -p build
+bpftool btf dump file /sys/kernel/btf/vmlinux format c > build/vmlinux.h.tmp
+mv build/vmlinux.h.tmp build/vmlinux.h
+clang-18 -Ivendor -Isrc -Isrc/shared -Ibuild -I/home/ubuntu/lertick-pr/build/deps/install/include -DLERTICK_VERSION=\"source\" -O2 -g -target bpf -mcpu=v3 -Wall -Wno-compare-distinct-pointer-types -D__TARGET_ARCH_arm64 -Ivendor/bpf-compat -I/usr/include/aarch64-linux-gnu -c src/bpf/scx_lertick.bpf.c -o build/scx_lertick.bpf.o
+In file included from src/bpf/scx_lertick.bpf.c:23:
+In file included from vendor/scx/common.bpf.h:21:
+build/vmlinux.h:69147:2: warning: declaration does not declare anything [-Wmissing-declarations]
+ 69147 | struct aes_enckey;
+ | ^
+build/vmlinux.h:85401:3: warning: declaration does not declare anything [-Wmissing-declarations]
+ 85401 | struct ns_tree;
+ | ^
+build/vmlinux.h:108771:2: warning: declaration does not declare anything [-Wmissing-declarations]
+ 108771 | struct __filename_head;
+ | ^
+build/vmlinux.h:152894:2: warning: declaration does not declare anything [-Wmissing-declarations]
+ 152894 | union pipe_index;
+ | ^
+build/vmlinux.h:171167:4: warning: declaration does not declare anything [-Wmissing-declarations]
+ 171167 | struct freelist_counters;
+ | ^
+5 warnings generated.
+bpftool gen skeleton build/scx_lertick.bpf.o name scx_lertick > build/scx_lertick.bpf.skel.h.tmp
+mv build/scx_lertick.bpf.skel.h.tmp build/scx_lertick.bpf.skel.h
+cc -Ivendor -Isrc -Isrc/shared -Ibuild -I/home/ubuntu/lertick-pr/build/deps/install/include -DLERTICK_VERSION=\"source\" -Isrc/userspace -O2 -g -std=gnu11 -Wall -Wextra src/userspace/scx_lertick.c src/userspace/lerwatt_plugin.c src/userspace/runtime.c -o build/scx_lertick -L/home/ubuntu/lertick-pr/build/deps/install/lib -lbpf -lelf -lzstd -pthread -lz -lelf -lz
+In file included from vendor/scx/common.h:74,
+ from src/userspace/scx_lertick.c:28:
+vendor/scx/compat.h: In function ‘__COMPAT_read_enum’:
+vendor/scx/compat.h:44:31: warning: comparison of integer expressions of different signedness: ‘int’ and ‘__u32’ {aka ‘unsigned int’} [-Wsign-compare]
+ 44 | for (i = 0; i < BTF_INFO_VLEN(t->info); i++) {
+ | ^
+vendor/scx/compat.h:55:31: warning: comparison of integer expressions of different signedness: ‘int’ and ‘__u32’ {aka ‘unsigned int’} [-Wsign-compare]
+ 55 | for (i = 0; i < BTF_INFO_VLEN(t->info); i++) {
+ | ^
+vendor/scx/compat.h: In function ‘__COMPAT_struct_has_field’:
+vendor/scx/compat.h:99:23: warning: comparison of integer expressions of different signedness: ‘int’ and ‘__u32’ {aka ‘unsigned int’} [-Wsign-compare]
+ 99 | for (i = 0; i < BTF_INFO_VLEN(t->info); i++) {
+ | ^
+cc -O2 -g -std=gnu11 -Wall -Wextra benchmarks/burst_workload.c -o build/burst_workload -pthread -lm
+cc -O2 -g -std=gnu11 -Wall -Wextra benchmarks/phase2_workload.c -o build/phase2_workload -pthread -lm
+cc -O2 -g -std=gnu11 -Wall -Wextra tests/lifecycle_workload.c -o build/lifecycle_workload -pthread -lm
+cc -Isrc/userspace -O2 -std=gnu11 -Wall -Wextra -Werror tests/test_loader_runtime.c src/userspace/runtime.c -o build/test_loader_runtime
+python3 tests/check_integration_abi.py
+Integration ABI 2 golden check passed (56 values).
+build/test_loader_runtime
+loader runtime: private output, lock ownership, idle-state guard, readiness notify and maintenance lifetime passed
+sh tests/test_deploy.sh
+test_deploy: plan/check/verify, indefinite/finite defaults, signals, trust paths, maintenance, staging and build fingerprints passed (stubs only)
+sh tests/test_service.sh
+test_service: notify readiness, long-running intent, immutable bundles, active verification, atomic update/rollback, foreign refusal and default fallback passed (fixtures only)
+python3 tests/test_report.py
+.....
+----------------------------------------------------------------------
+Ran 5 tests in 0.111s
+
+OK
+cc -Isrc/shared -O2 -std=c11 -Wall -Wextra -Werror tests/test_model.c -o build/test_model
+build/test_model
+{"test":"synthetic_holdout","samples":1000,"model_squared_error":563987,"fixed_squared_error":156938088319}
+{"test":"fractional_carry","oracle_trials":100000,"model_bytes":48}
+{"test":"model","status":"passed","oracle_trials":200121}
+cc -Isrc/shared -O2 -std=c11 -Wall -Wextra -Werror tests/test_accounting.c -o build/test_accounting
+build/test_accounting
+accounting: 202000 oracle cases and callback-order traces passed
+cc -Ibenchmarks -O2 -std=c11 -Wall -Wextra -Werror tests/test_burst_profiles.c -o build/test_burst_profiles
+build/test_burst_profiles
+burst profiles: phase boundaries, deterministic seeds, groups and u64 bounds passed
+cc -Isrc/shared -O2 -std=c11 -Wall -Wextra -Werror tests/test_cpuperf.c -o build/test_cpuperf -lm
+build/test_cpuperf
+{"test":"cpuperf","status":"passed"}
+cc -Ivendor -Isrc/shared -Isrc/userspace -O2 -std=gnu11 -Wall -Wextra -Werror tests/test_lerwatt_plugin.c src/userspace/lerwatt_plugin.c -o build/test_lerwatt_plugin
+build/test_lerwatt_plugin
+{"test":"lerwatt_plugin","status":"passed"}
+python3 tests/check_integration_abi.py
+Integration ABI 2 golden check passed (56 values).
+Integration ABI 2 golden check passed (56 values); peer compiled ABI matches.
diff --git a/results/automatic-discovery/linux-service-fixture-followup.log b/results/automatic-discovery/linux-service-fixture-followup.log
new file mode 100644
index 0000000..5ce3978
--- /dev/null
+++ b/results/automatic-discovery/linux-service-fixture-followup.log
@@ -0,0 +1 @@
+test_service: notify readiness, long-running intent, immutable bundles, active verification, atomic update/rollback, foreign refusal and default fallback passed (fixtures only)
diff --git a/results/automatic-discovery/old-argv-fixture-failure.log b/results/automatic-discovery/old-argv-fixture-failure.log
new file mode 100644
index 0000000..c3f72f6
--- /dev/null
+++ b/results/automatic-discovery/old-argv-fixture-failure.log
@@ -0,0 +1,3 @@
+Traceback (most recent call last):
+ File "", line 132, in
+AssertionError
diff --git a/results/automatic-discovery/portable-foreground-fixtures.log b/results/automatic-discovery/portable-foreground-fixtures.log
new file mode 100644
index 0000000..13c7c6a
--- /dev/null
+++ b/results/automatic-discovery/portable-foreground-fixtures.log
@@ -0,0 +1 @@
+test_deploy: plan/check/verify, indefinite/finite defaults, signals, trust paths, maintenance, staging and build fingerprints passed (stubs only)
diff --git a/results/automatic-discovery/portable-lerwatt-fixtures.log b/results/automatic-discovery/portable-lerwatt-fixtures.log
new file mode 100644
index 0000000..4de7d6e
--- /dev/null
+++ b/results/automatic-discovery/portable-lerwatt-fixtures.log
@@ -0,0 +1 @@
+test_deploy: all checks passed (fixtures only; no host service or CPU changes)
diff --git a/results/automatic-discovery/portable-service-fixtures.log b/results/automatic-discovery/portable-service-fixtures.log
new file mode 100644
index 0000000..5ce3978
--- /dev/null
+++ b/results/automatic-discovery/portable-service-fixtures.log
@@ -0,0 +1 @@
+test_service: notify readiness, long-running intent, immutable bundles, active verification, atomic update/rollback, foreign refusal and default fallback passed (fixtures only)
diff --git a/results/automatic-discovery/provenance.json b/results/automatic-discovery/provenance.json
new file mode 100644
index 0000000..dcb0e20
--- /dev/null
+++ b/results/automatic-discovery/provenance.json
@@ -0,0 +1,25 @@
+{
+ "baseline": "5ce8a3c",
+ "recorded_utc": "2026-11-02T17:42:56.174813+00:00",
+ "evidence_sha256": {
+ "README.md": "16244409dbb330d16aaff3258208449d10bc166956f50789a82141c9b4cfa372",
+ "ci-musl-failure.log": "874be5f8d076d922147fa1f077d3a509aa86569f9711f2e3c4573717fbf3fd88",
+ "control-summary.json": "9e74762e49ce9b221e9eb8abbf5c0e071c6987e778471322dd2e7bb214ea23a5",
+ "final-live-pair.log": "8f5620e709a63a1f19e1d05dd872c03df0d01a2459c788dc8dd79e60ff0f5569",
+ "finite-jobs.jsonl": "3c7858b7ed51b69280e1f8c17520de4ab49770ac6c095c3e6607d83c9c818d68",
+ "first-conflict-failure.log": "4c10ef76306ed10a7a5a5adb6b81544d0393c9a7ff3076b6798135276dfa4952",
+ "first-service-fixture-failure.log": "7dbf8da6d7f711e7a857a175952aa6db5a5bfb77553d1a26a71887a919551bae",
+ "initial-live-pair.log": "349de732553cf7ba0e5a646cc4a67d8e89d2b2d767867f48262bd60b3117f43f",
+ "linux-build-and-tests.log": "e68934912ec73ea884be85a809ffcd8847722a073eea263f276ddebbd9720c6f",
+ "linux-service-fixture-followup.log": "32cd2b425beeb3a6b31bce24481160da326f08074870792fc495126f20552e87",
+ "old-argv-fixture-failure.log": "5b322409080f5178a0927b39a6b8bdb8a5a52f3889fad3b963c4e280e0795d22",
+ "portable-foreground-fixtures.log": "77be90f6d97ebc8c186d51b7a5a2676de4b3a74f87533c6a5b56a7f2e84b0312",
+ "portable-lerwatt-fixtures.log": "565b6cb0d04952400ec611f7841d379cba9312824c77f59df96655019453c259",
+ "portable-service-fixtures.log": "32cd2b425beeb3a6b31bce24481160da326f08074870792fc495126f20552e87",
+ "raw-archive.json": "d9a669aa9e8672ae89b34300ebd5f2a2d2f359be00eb8a9d64230704427d9b4e",
+ "result.json": "be36cefbd3fe7f1f9aecb89d0476aea9102a4b4385989f3ee045404e2da1782b",
+ "source-identity.json": "6d18331720dc8c6a1122849a1220f6c3384a582bbc3c52a78f3b93a0b8324697",
+ "status-fixture-failure.log": "40a51e64775d76029e3fa691ff6b70ebf72b430d5e20df640ad4b5b3aa533d33"
+ },
+ "no_energy_or_model_accuracy_improvement_claim": true
+}
diff --git a/results/automatic-discovery/raw-archive.json b/results/automatic-discovery/raw-archive.json
new file mode 100644
index 0000000..0eb7a17
--- /dev/null
+++ b/results/automatic-discovery/raw-archive.json
@@ -0,0 +1,6 @@
+{
+ "path": "results/local/automatic-discovery-final-pair.tgz",
+ "bytes": 17179,
+ "sha256": "c22a84c84f612944659bd6890214df4479d96d263f9cd4110b0262acc46df83e",
+ "vm_path": "/var/tmp/lertick-power-integration-s07845od"
+}
diff --git a/results/automatic-discovery/result.json b/results/automatic-discovery/result.json
new file mode 100644
index 0000000..4c875de
--- /dev/null
+++ b/results/automatic-discovery/result.json
@@ -0,0 +1,21 @@
+{
+ "environment": {
+ "kernel": "7.0.0-38-generic",
+ "architecture": "aarch64",
+ "os_release": "PRETTY_NAME=\"Ubuntu 24.04.5 LTS\"\nNAME=\"Ubuntu\"\nVERSION_ID=\"24.04\"\nVERSION=\"24.04.5 LTS (Noble Numbat)\"\nVERSION_CODENAME=noble\nID=ubuntu\nID_LIKE=debian\nHOME_URL=\"https://www.ubuntu.com/\"\nSUPPORT_URL=\"https://help.ubuntu.com/\"\nBUG_REPORT_URL=\"https://bugs.launchpad.net/ubuntu/\"\nPRIVACY_POLICY_URL=\"https://www.ubuntu.com/legal/terms-and-policies/privacy-policy\"\nUBUNTU_CODENAME=noble\nLOGO=ubuntu-logo\n",
+ "systemd": "systemd 255 (255.4-1ubuntu8.18)"
+ },
+ "hardware": "temporary RK3588 simulated CPU-control files; real sched_ext/BPF",
+ "checks": {
+ "lerwatt_first_continuous_connection": true,
+ "lertick_restart_new_session_reconnects": true,
+ "independent_scheduler_policy_change": true,
+ "frozen_demand_expires_and_recovers": true,
+ "lerwatt_restart_restores_expires_reconnects": true,
+ "independent_power_predictor_change": true,
+ "lertick_first_connection": true,
+ "final_control_restore_scheduler_fallback_pin_cleanup": true,
+ "foreign_pin_preserved_auto_falls_back_without_advice": true
+ },
+ "result": "passed"
+}
diff --git a/results/automatic-discovery/source-identity.json b/results/automatic-discovery/source-identity.json
new file mode 100644
index 0000000..5a57b84
--- /dev/null
+++ b/results/automatic-discovery/source-identity.json
@@ -0,0 +1,121 @@
+{
+ "files": {
+ "lerwatt-auto-discovery": {
+ "Makefile": "4c6335c1cfd4df4bcb87ef18aede229bd0096e5b6d8cf500136a8c5ae3a73a2f",
+ "deploy.sh": "eac0bb15c94e729ed5ce6be2c2da9006ff01c0fe29c7854e0a3f57c03e979009",
+ "docs/INTEGRATION.md": "3c40ef4796400a2d352fd9ec41e072911dd07c6c6bac035f6acac418492fba5d",
+ "include/lerwatt/contract.h": "b19b030600cf7b365e0b182fe3655aab2d5dd6cf8aa6b6c589196251a64b6eed",
+ "include/lerwatt/controller.h": "b415e5e60425aa783c9c90ffa5d532621eba8289e91212b4c74c0ee2d013b5eb",
+ "include/lerwatt/forecast.h": "0cf89c0c5dfd7b888e99cefaa08fb55802fa96741e5cddd75011716e57bbf905",
+ "include/lerwatt/hal.h": "4ad85de2f9d9185a07cdf1aed1b98084ba7483c2d53f75926bb830d3fe904d66",
+ "include/lerwatt/learn.h": "5ffdc676d148948038605b577cb90864e304ce2def61cee027a9fb6d6677127a",
+ "include/lerwatt/predict.h": "49aafa0f808ae47c86e8efb1400522852d230d7ad7db4b35ce2b7a57722d748b",
+ "include/lerwatt/temporal.h": "e8f4f47e4c639604672e62e38be616cf6871d2c1406cdbcca9559f4cbe9c4c27",
+ "sim/abi.c": "5a50ab25a37775a6eada1decf626e96907c3e545fef21f36cf8bf8ccf2017628",
+ "sim/abi.h": "71e38aae1903acb40ca810d4ab6cc62b670494e54cc7428f2957b6f75f594aaf",
+ "sim/lerwatt_sim.c": "2db38f27215c3a272481a7745bf2c6a036742daa1280330cf4c7bf25de76d5a2",
+ "sim/load.c": "4e5f7aef689abd8f195835afb514da99f35f6c3bb9f83e53eb3b31551f5959c9",
+ "sim/load.h": "3666e213c868a6b6f0f14a23ecf0f1d38c53ae75452f8d623eb390fceb4b5a68",
+ "sim/model.c": "a96f3c6dd7be9ab3a5abbaaf99fa277d4174bb397604ce9c4d643263ffaed781",
+ "sim/platform_rk3588.c": "a89646c4f61623381d4348fff0052fb7a2bd47eb5dd94c13e6b1e4f67112bfff",
+ "sim/sim.h": "dd344c137f690920935f5b140075b7bd5cb9b7afb9d58aa60121201c03150ec8",
+ "src/checkpoint.c": "f320d349c5853c2ddb3676550273875f38cde8e40cb53be7bb5c3da65d23df6b",
+ "src/checkpoint.h": "bfda811ca6cbbfee8721de836170687b249345a1913c50b5de0c7e8569ee45e8",
+ "src/contract.c": "1ef5ea601b922c9788e9cf0a91d411c8ce327d27ea2c36f76007c7ea88122a64",
+ "src/contract.h": "8a4e105d3691513615220538cc83b92f441a916bc5ac8a8db37d403cc2d0de75",
+ "src/controller.c": "2fb3116ce7d9d73bdc04b7dc0cdfa213b4ff4cd889500f3724b117a1d8087563",
+ "src/forecast.c": "21265a764056780555e32f03de4f4d2db0a82ed16557ec5e855b1ef7405c815e",
+ "src/hal_actuate.c": "c1deefb466ab085a4858241a785edb3e2a415363d241a31a1329e7071ff5db93",
+ "src/hal_discover.c": "c4ffe3b8e42199c23760aa39a039cb2abbc881e98d09d9199e13dcc8f2cc26f5",
+ "src/hal_fs.c": "c783cb23cd9e372755bd8175029538d7739f154e042190bd6de3e58b53c78761",
+ "src/hal_sample.c": "fa97c3330044c3df140b317c7b188800be7ab6796a088573340cfbacff64ffc7",
+ "src/learn.c": "8a5fbfc6c93f48f3e40a7abf1b2259c3b1070cdf58bc3a14079fce6402162d64",
+ "src/lerwattd.c": "1502d8f4664f7d4768baecce5ad631a4b9d3e4c282878d06e0f4af539229688c",
+ "src/notify.c": "0207aa96e1a8d9e7c3e7985eab465bcb76fe5fed7bbec88b7d4f047138fbc319",
+ "src/notify.h": "d5f83fa2bf09c383d60b95dae5aff9cd7a420633d6fd70056e64185641830b06",
+ "src/predict.c": "3927893b3a848bb0b7f1f240de52b53632b118a94287f58659782e00a2b5bec7",
+ "src/runtime.h": "d6abadcfa2b80f107c632fd8708009546045c487b4df4d31a4da65663f7d06f3",
+ "src/temporal.c": "5a73c46bb8c16857ecaaa1c8725ed54bd3a2033f86ff5bb3d1982e77632d3f64",
+ "tests/check_integration_abi.py": "eb209f6d5d68972c5fdfa436987455f3273f9a36218e1fe0d8ea4e6f9006f60b",
+ "tests/e2e.sh": "4d6df818d2115599f9cbb64b36d14969c12277fc47f2b8df674ca697781702ff",
+ "tests/summarize_cooperative.py": "5ce8546144bc92cbf531cef4a6f4bf39100fc6514eaf535a2e5acb1e2da24cff",
+ "tests/summarize_runtime_forecast.py": "c6b967a11735a21294df66d837525bdf6b7b19c5ed0a43df37635f9fc0e867a6",
+ "tests/test_checkpoint.c": "3e73f7b862a9a5c1f03f177bc2435b94a2ab0af729bbea594af7cb71c2f39e41",
+ "tests/test_closed_loop.c": "c9531b8f4bbf26fbe004f2cd0b4dc5b8d7e3e908dd292601d9b5842d1fe37903",
+ "tests/test_contract.c": "11fc26d35355837b3d68bdd35a75724de9826c6491f12254fe5bdb07478a253b",
+ "tests/test_control_forecast.c": "9e6d67bba8d19020c31ede5379c3a1358ba9e592fd45ed5a450e74a36875a1fb",
+ "tests/test_controller.c": "ae7141c015e40bc80234a0fce610fe9804abbb73abe2c4518e31a51f73736c63",
+ "tests/test_cooperative_loop.c": "31b36e3cdbf0f120a07a5dab597e0be55134b2bfdcf1dd6b0b97ecebc808a1ab",
+ "tests/test_cooperative_summary.py": "454cac9d844cde5a6b95fbf48eaf937daa9dcaaacb050b7993625b075e920033",
+ "tests/test_daemon_deployment.py": "1f702645739f9ce226447b159f774959071f25a769675c7080f89484d5990f48",
+ "tests/test_deploy.sh": "ca1b99717cfe99042e615a12a5a5a36a6d2872713c03bf6a96fb76bf426d897f",
+ "tests/test_discovery.c": "69ab7d958ffe385a933d768a9827d9ea5c27f6c6365dd56b333d8e958fa74982",
+ "tests/test_forecast.c": "b1fc49c84cdd73da2b8736e1395064dead7d2b69f5f162ad9b30fcd0c443066a",
+ "tests/test_forecast_feedback.c": "79c6b3b455f23e4aecbf872eefe7c648bf87c5fe92e09363ab41c4014fe02e09",
+ "tests/test_learn.c": "ddd490c1951e4d7531e23f98c46b585ee804c1957b38832da69d32a40abcf794",
+ "tests/test_model.c": "6095171e0de9d5b47d9ae958e4524854f40ed5f9c6826d11250ca31dfac174d8",
+ "tests/test_notify.c": "3f46f1f849a6021560b435789a2819d1a34cf851a2b9b96c6796c560cb56b180",
+ "tests/test_predict.c": "c4cff46982f6d5f0b7e5743c1ddefcb1b0039515aac322758869436726fbd80e",
+ "tests/test_runtime_forecast.py": "a92fef248162a50ed0766354b096a135c18886de7334dd94ea1da82fedea75ca",
+ "tests/test_runtime_fs.c": "e571d36b4ba303361ddeda4fa6b4e681fa4b69efda7b7694a8b3afe3093a0591",
+ "tests/test_runtime_summary.py": "4f3fbfe2d0a0b5812de6e6e08d43656d70f273f00a8661773d66a5180fb4fac6",
+ "tests/test_service_systemd.py": "c3f966f4c7c477e9391e1e49f20bf20a391071921d353b9760def36e5d3b7155",
+ "tests/test_temporal.c": "d43751dc32f2aedd044e01688df30979cc533e7ffea399e268c361b58f754742",
+ "tests/verify_nominal_reference.py": "62e0b651841420709b93c738bc57e319485bb39911042f2a5fe0556aa9b247a9",
+ "tests/verify_runtime_forecast.py": "55dfb4c9d411ece1b122fdb7ff811e5259d9924235ec6b5f62f8e21a3d7b706e"
+ },
+ "lertick-auto-discovery": {
+ "Makefile": "402b0057d13999b9c913405842f4afad33bf84bf104377ebdfe1b658adbeded1",
+ "deploy.sh": "9c6c84a193464c0b970cdfbcf9bde78ee97e59500ea79ba86295fd0d3591a5f7",
+ "docs/INTEGRATION.md": "3c40ef4796400a2d352fd9ec41e072911dd07c6c6bac035f6acac418492fba5d",
+ "scripts/build_libbpf.sh": "e1c3df3765aadf0d0c3e0e7c5d59a132e71b326741d72799df358d63cab7fe75",
+ "scripts/collect_report.py": "77a95132e7588e739d244a25d3c4bd2e21ff67c5f3fe8d51b23b984c618c000a",
+ "scripts/fetch_reference.sh": "73568483432f84dbfe1d73a0e19256229fb92c4539500518b1558338692c31ad",
+ "scripts/native_build.sh": "6de422e54ed387ddc2bc9253eab0749adeed55b6a981441cc0740e912c54a8b3",
+ "scripts/preflight.sh": "e4b80a418b3a033300e7d9dd5654ab9a4cb8ba3b61eec05a99cb4ba8c6caaf46",
+ "scripts/qemu_vm.py": "37d1413f99071f116f6abcbb9766208b0f999569f9720df483f68fc5a8d540cb",
+ "scripts/service_manager.sh": "3381491e6ff0983408dff79c2ba1fbe042a93d466685a1b560e200e78acb2b42",
+ "src/README.md": "c3488beec824932044a9e949ff21515e7b5d249c912233fc8fe2e9ea5295a442",
+ "src/bpf/scx_lertick.bpf.c": "ebd8dd4f7042374239cd0195bc17201d97b648b1bc5a2abe1ae9d4f494f57cfd",
+ "src/shared/accounting.h": "9abb0b777743d7fa3aef775a693a35030c59748614235d9c0b2c00e196329033",
+ "src/shared/cpuperf.h": "b48a4c97c5e93aeddc5a08a2e29b56e5805baab52d2f61cb834b6a16c4e9bf35",
+ "src/shared/lertick.h": "346f35187c607e9d40cb4689a7cb81f68ff5df360c11eb769d0a25544cf89007",
+ "src/shared/model.h": "9ce54cb889d29d2532c4ea465c728143e2cb44dfd43dd27c32b24d3641a3c639",
+ "src/userspace/lerwatt_plugin.c": "4c05727eacc86314b9f211fee02f4aff69ba8415c23e17ecb73a40bb2ba93320",
+ "src/userspace/lerwatt_plugin.h": "44afb720bd73de0f892c6fb4af33e8b4ddd1e8f29a509a0eac27427b77fc2696",
+ "src/userspace/runtime.c": "2e199ed93fd1a3b3a5de740514b3ffa54134c44943a05b6d1dec99984ae0d672",
+ "src/userspace/runtime.h": "8cf5589583e1ebfd50a4baff32a01bc773d11a69ea52947c23a30a10c757e655",
+ "src/userspace/scx_lertick.c": "e35c014b5b88c254138076517e97839404b3be349568dc6a2376d34cd3705287",
+ "templates/lertick.service.in": "cf3b7809a40bbc8d7171f26aceceb29eaad1b7a8eff515ac3e46a7f113572498",
+ "tests/README.md": "231c9a0bb03e10482610c7a68a1c6cfd5afe4fbb99cea1621737b0d265214112",
+ "tests/check_docs.py": "cb991bf25f025d30393c0ccfa4b3b12b5e2b02f319fa55efe515caac0e1071cd",
+ "tests/check_integration_abi.py": "eb209f6d5d68972c5fdfa436987455f3273f9a36218e1fe0d8ea4e6f9006f60b",
+ "tests/lifecycle_workload.c": "a9cb9946dd064b3ec5ff97aa821db73d52e817d2956ec0e9f877c3fcc55923d0",
+ "tests/test_accounting.c": "6b149b1d3cc3038e65368d9c1d7115221252e6481061a9c82d59e4a2e01338bc",
+ "tests/test_burst_profiles.c": "5629ccd030fae7b95fc19ea77fe6e1db9c32e044e5d3094eb388ceef7efff0c7",
+ "tests/test_compact_confirmation.py": "a286e5287ac8fbbe9d04517dfe8313deae1aa99641516643062ce3a378f9bbac",
+ "tests/test_confirmation.py": "d32ad987399ec6e07fd119d5e59d80daf96a7b3ed14d7fa1d186c94ced77a79c",
+ "tests/test_cpuperf.c": "c580affc977e2149ab30b7990a8397cf7a2a19e8110b654477798b4f0aa40acd",
+ "tests/test_deploy.sh": "87e55667cdad6b60df954e14fe760ad4810a5844db7ab9b1b4293ff5ab0e15a1",
+ "tests/test_lerwatt_plugin.c": "7f8b8f83b43085c41314c245579d40fd2e3db57cd130ef780d1c00e147b65e43",
+ "tests/test_loader_deployment.py": "b010fa9d40d137676d82a6b3bf555b8e77cd8e8c61d0386fcef986f7addf288b",
+ "tests/test_loader_runtime.c": "1065d4495e2ebf6c98039a0599a1ac063b30bd138461b2565dea80bd94200076",
+ "tests/test_model.c": "d7f5acc458083757c03fc3eb8cba2f3d8e7a44623a059a82f1f09af2010ae40d",
+ "tests/test_online_utility.py": "c9676a2fa618910196b3826820860accbc8026abc4544afb1519bf72dd0a99fd",
+ "tests/test_power_integration.py": "197d6bfa589b825af08282d5c4aef01c390b36b434ce5fc257890264f6b427f8",
+ "tests/test_report.py": "4464a43a6723e095d05947c258029a7fa89028939a0e85b68b6dec12cab4f801",
+ "tests/test_service.sh": "6b836cbc8009b7f5642f5aee6871e4eb75d74b60f4cb152a09604d64fa1acd1c",
+ "tests/test_service_systemd.py": "6854fd0b358a56f5e24d4698496feac409a331c010274f03699891c92495fd49",
+ "tests/validate_linux.py": "3a7b9fbf94ad417cedfb57f81218bfa0df3ba47ede61380327da6f7d5d84cffc",
+ "tests/validate_phase2.py": "db695af5508f66e73dca1aee87c617df47675fc53b02acdc715b659b2936f4e2",
+ "tests/validate_watchdog_flags.py": "3d874d394eee6ae8273285e96546dbc902a937b8bc1371eea3f2c9fd95737797",
+ "vendor/lerwatt/contract.h": "b19b030600cf7b365e0b182fe3655aab2d5dd6cf8aa6b6c589196251a64b6eed"
+ }
+ },
+ "sched_ext": "disabled",
+ "vcpus": 8,
+ "archive_retained": true,
+ "recorded_utc": "2026-11-02T17:29:36.558451+00:00",
+ "fixture_followup_utc": "2026-11-02T17:42:56.173831+00:00",
+ "fixture_followup": "Only test transaction budget/timeout diagnostics changed after live tests; the matching revision passed the full Linux service fixture."
+}
diff --git a/results/automatic-discovery/status-fixture-failure.log b/results/automatic-discovery/status-fixture-failure.log
new file mode 100644
index 0000000..a5224cd
--- /dev/null
+++ b/results/automatic-discovery/status-fixture-failure.log
@@ -0,0 +1,38 @@
+cc -Ivendor -Isrc -Isrc/shared -Ibuild -I/home/ubuntu/lertick-pr/build/deps/install/include -DLERTICK_VERSION=\"source\" -Isrc/userspace -O2 -g -std=gnu11 -Wall -Wextra src/userspace/scx_lertick.c src/userspace/lerwatt_plugin.c src/userspace/runtime.c -o build/scx_lertick -L/home/ubuntu/lertick-pr/build/deps/install/lib -lbpf -lelf -lzstd -pthread -lz -lelf -lz
+In file included from vendor/scx/common.h:74,
+ from src/userspace/scx_lertick.c:28:
+vendor/scx/compat.h: In function ‘__COMPAT_read_enum’:
+vendor/scx/compat.h:44:31: warning: comparison of integer expressions of different signedness: ‘int’ and ‘__u32’ {aka ‘unsigned int’} [-Wsign-compare]
+ 44 | for (i = 0; i < BTF_INFO_VLEN(t->info); i++) {
+ | ^
+vendor/scx/compat.h:55:31: warning: comparison of integer expressions of different signedness: ‘int’ and ‘__u32’ {aka ‘unsigned int’} [-Wsign-compare]
+ 55 | for (i = 0; i < BTF_INFO_VLEN(t->info); i++) {
+ | ^
+vendor/scx/compat.h: In function ‘__COMPAT_struct_has_field’:
+vendor/scx/compat.h:99:23: warning: comparison of integer expressions of different signedness: ‘int’ and ‘__u32’ {aka ‘unsigned int’} [-Wsign-compare]
+ 99 | for (i = 0; i < BTF_INFO_VLEN(t->info); i++) {
+ | ^
+cc -Ivendor -Isrc/shared -Isrc/userspace -O2 -std=gnu11 -Wall -Wextra -Werror tests/test_lerwatt_plugin.c src/userspace/lerwatt_plugin.c -o build/test_lerwatt_plugin
+build/test_lerwatt_plugin
+{"test":"lerwatt_plugin","status":"passed"}
+python3 tests/check_integration_abi.py
+Integration ABI 2 golden check passed (56 values).
+systemd: installed helper enabled an unlimited validated worker; absent peer reports waiting
+systemd: active off/on preserved scheduling and boot intent, and replaced shared maps
+systemd: stop/update/rollback preserved enabled-but-stopped intent and private integration setting
+systemd: uninstall disabled/detached/removed the validation service; configuration retained
+{"result": "passed", "checks": {"install_does_not_start_or_enable": true, "enabled_unlimited_ready_with_optional_peer_absent": true, "active_integration_toggle_restarts_only_owned_service": true, "stop_update_rollback_preserve_configuration_and_intent": true, "uninstall_disables_detaches_removes_and_retains_config": true}, "environment": {"os_release": "PRETTY_NAME=\"Ubuntu 24.04.5 LTS\"\nNAME=\"Ubuntu\"\nVERSION_ID=\"24.04\"\nVERSION=\"24.04.5 LTS (Noble Numbat)\"\nVERSION_CODENAME=noble\nID=ubuntu\nID_LIKE=debian\nHOME_URL=\"https://www.ubuntu.com/\"\nSUPPORT_URL=\"https://help.ubuntu.com/\"\nBUG_REPORT_URL=\"https://bugs.launchpad.net/ubuntu/\"\nPRIVACY_POLICY_URL=\"https://www.ubuntu.com/legal/terms-and-policies/privacy-policy\"\nUBUNTU_CODENAME=noble\nLOGO=ubuntu-logo\n", "architecture": "aarch64", "kernel": "7.0.0-38-generic", "systemd": "systemd 255 (255.4-1ubuntu8.18)", "elapsed_seconds": 4.76}, "scope": "real sched_ext attachment and PID 1 lifecycle in an opted-in VM; no physical power controls or long hardware run"}
+integration: Lerwatt-first startup connected; BPF consumed contracts
+integration: online-to-EWMA scheduler change reconnected while Lerwatt stayed running
+integration: frozen demand expired and resumed without stale control
+integration: temporal-to-adaptive predictor change preserved Lertick PID and session
+integration: reverse startup order connected
+integration artifacts: /var/tmp/lertick-power-integration-p7i4ko5f
+Traceback (most recent call last):
+ File "/home/ubuntu/lertick-auto-discovery/tests/test_power_integration.py", line 343, in
+ main()
+ File "/home/ubuntu/lertick-auto-discovery/tests/test_power_integration.py", line 293, in main
+ wait_for(lambda: link(unit, "off"), "failed auto transport did not report independent operation")
+ File "/home/ubuntu/lertick-auto-discovery/tests/test_power_integration.py", line 48, in wait_for
+ raise AssertionError(message)
+AssertionError: failed auto transport did not report independent operation
diff --git a/scripts/service_manager.sh b/scripts/service_manager.sh
index c15d147..73f4e51 100755
--- a/scripts/service_manager.sh
+++ b/scripts/service_manager.sh
@@ -131,7 +131,7 @@ $report_hash"
}
integration_read()
{
- integration_value=off
+ integration_value=auto
trusted_path "$config_dir"
[ -e "$config_dir" ] || return 0
[ "$(stat -c '%u %a' "$config_dir")" = '0 700' ] || fail 'integration directory must be root-owned mode 0700'
@@ -142,17 +142,17 @@ integration_read()
integration_seen=no
while IFS= read -r config_line || [ -n "$config_line" ]; do
case $config_line in
- lerwatt=on|lerwatt=off)
+ lerwatt=auto|lerwatt=on|lerwatt=off)
[ "$integration_seen" = no ] || fail 'duplicate integration setting'
integration_value=${config_line#lerwatt=}; integration_seen=yes ;;
- *) fail 'invalid integration configuration; expected lerwatt=on or lerwatt=off' ;;
+ *) fail 'invalid integration configuration; expected lerwatt=auto, lerwatt=on or lerwatt=off' ;;
esac
done < "$config"
[ "$integration_seen" = yes ] || fail 'empty integration configuration'
}
integration_write()
{
- case $1 in on|off) ;; *) fail 'invalid integration value' ;; esac
+ case $1 in auto|on|off) ;; *) fail 'invalid integration value' ;; esac
trusted_path "$config_dir"; trusted_path "$config"
mkdir -p "$config_dir"; chmod 700 "$config_dir"
integration_tmp=$(mktemp "$config_dir/.integration.XXXXXX")
@@ -164,6 +164,8 @@ verify_integration()
{
if [ "$2" = on ]; then
"$1" --verify --mode online --track-all --cpuperf auto --summary-only --lerwatt >/dev/null
+ elif [ "$2" = auto ]; then
+ "$1" --verify --mode online --track-all --cpuperf auto --summary-only --lerwatt-auto >/dev/null
else
"$1" --verify --mode online --track-all --cpuperf auto --summary-only >/dev/null
fi
@@ -179,6 +181,8 @@ if [ "$command" = service-run ]; then
[ -n "$current" ] && validate_release "$current" || fail 'installed release validation failed; no scheduler attached'
if [ "$integration_value" = on ]; then
exec "$release/scx_lertick" --mode online --duration 0 --track-all --cpuperf auto --summary-only --sample-ms 60000 --notify --lerwatt
+ elif [ "$integration_value" = auto ]; then
+ exec "$release/scx_lertick" --mode online --duration 0 --track-all --cpuperf auto --summary-only --sample-ms 60000 --notify --lerwatt-auto
fi
exec "$release/scx_lertick" --mode online --duration 0 --track-all --cpuperf auto --summary-only --sample-ms 60000 --notify
fi
@@ -281,7 +285,7 @@ case $command in
fail 'internal uninstall dispatch failed' ;;
integration)
require_service
- case ${bundle:-status} in on|off|status) ;; *) fail 'integration accepts on, off or status' ;; esac
+ case ${bundle:-status} in auto|on|off|status) ;; *) fail 'integration accepts auto, on, off or status' ;; esac
integration_read
if [ "${bundle:-status}" = status ]; then printf 'Lerwatt integration configured: %s\n' "$integration_value"; exit 0; fi
old_integration=$integration_value
diff --git a/src/bpf/scx_lertick.bpf.c b/src/bpf/scx_lertick.bpf.c
index f590e29..adaed16 100644
--- a/src/bpf/scx_lertick.bpf.c
+++ b/src/bpf/scx_lertick.bpf.c
@@ -46,6 +46,9 @@ const volatile bool model_update_carry;
const volatile u32 cpuperf_mode = LERTICK_CPUPERF_MAX;
/* Optional Lerwatt plug-in: read lw_contract, keep demand counters. */
const volatile bool lerwatt_enabled;
+/* Loader can disable advice after a best-effort publication failure, before
+ * attach. Keep this writable: rodata is frozen when BPF is loaded. */
+bool lerwatt_runtime_disabled;
const volatile u64 lerwatt_session_id;
const volatile u64 cluster_topology_id[LW_CONTRACT_MAX_CLUSTERS];
const volatile u32 nr_cpus = 1;
@@ -214,7 +217,7 @@ static __always_inline bool contract_get(s32 cpu, u64 now, struct lertick_contra
out->price_milli = 0;
out->floor_cap = 0;
out->ceiling_cap = 0;
- if (!lerwatt_enabled || idx >= LERTICK_MAX_CPUS)
+ if (!lerwatt_enabled || READ_ONCE(lerwatt_runtime_disabled) || idx >= LERTICK_MAX_CPUS)
return false;
cluster = cpu_cluster[idx];
if (cluster >= LW_CONTRACT_MAX_CLUSTERS)
diff --git a/src/userspace/lerwatt_plugin.c b/src/userspace/lerwatt_plugin.c
index 5e02ec5..ee6dbad 100644
--- a/src/userspace/lerwatt_plugin.c
+++ b/src/userspace/lerwatt_plugin.c
@@ -356,8 +356,8 @@ void ltlw_publish(struct lt_demand_rec *slot, const struct lt_demand_rec *val)
__atomic_store_n(&slot->seq, seq + 2, __ATOMIC_RELEASE);
}
-int ltlw_pin_prepare(const char *dir, const char *const *pins, int npins,
- char *err, size_t errlen)
+static int pin_prepare(const char *dir, const char *const *pins, int npins,
+ char *err, size_t errlen, bool remove_stale)
{
char parent[PATH_MAX];
struct statfs fs;
@@ -406,7 +406,7 @@ int ltlw_pin_prepare(const char *dir, const char *const *pins, int npins,
close(fd);
return -1;
}
- if (fchmod(fd, 0700)) {
+ if (remove_stale && fchmod(fd, 0700)) {
snprintf(err, errlen, "cannot protect BPF pin directory %s: %s", dir, strerror(errno));
close(fd);
return -1;
@@ -422,6 +422,10 @@ int ltlw_pin_prepare(const char *dir, const char *const *pins, int npins,
snprintf(err, errlen, "refusing unexpected stale pin %s", pins[i]);
close(fd);
return -1;
+ } else if (!remove_stale) {
+ snprintf(err, errlen, "automatic discovery keeps existing pin %s unchanged", pins[i]);
+ close(fd);
+ return -1;
} else if (!unlinkat(fd, name, 0) || errno == ENOENT) {
continue;
}
@@ -435,3 +439,15 @@ int ltlw_pin_prepare(const char *dir, const char *const *pins, int npins,
close(fd);
return 0;
}
+
+int ltlw_pin_prepare(const char *dir, const char *const *pins, int npins,
+ char *err, size_t errlen)
+{
+ return pin_prepare(dir, pins, npins, err, errlen, true);
+}
+
+int ltlw_pin_prepare_auto(const char *dir, const char *const *pins, int npins,
+ char *err, size_t errlen)
+{
+ return pin_prepare(dir, pins, npins, err, errlen, false);
+}
diff --git a/src/userspace/lerwatt_plugin.h b/src/userspace/lerwatt_plugin.h
index 78808ed..4d4126f 100644
--- a/src/userspace/lerwatt_plugin.h
+++ b/src/userspace/lerwatt_plugin.h
@@ -74,5 +74,8 @@ void ltlw_publish(struct lt_demand_rec *slot, const struct lt_demand_rec *val);
*/
int ltlw_pin_prepare(const char *dir, const char *const *pins, int npins,
char *err, size_t errlen);
+/* Best-effort discovery never unlinks or changes permissions on existing pins. */
+int ltlw_pin_prepare_auto(const char *dir, const char *const *pins, int npins,
+ char *err, size_t errlen);
#endif /* LERTICK_LERWATT_PLUGIN_H */
diff --git a/src/userspace/scx_lertick.c b/src/userspace/scx_lertick.c
index 720c452..eae26ca 100644
--- a/src/userspace/scx_lertick.c
+++ b/src/userspace/scx_lertick.c
@@ -459,7 +459,8 @@ static void usage(FILE *out, const char *argv0)
" --cpuperf auto|util|max CPU frequency requests to schedutil (default auto:\n"
" util when a policy uses schedutil or with --lerwatt;\n"
" max keeps the kernel default, every CPU at 1024)\n"
- " --lerwatt Attach the optional Lerwatt plug-in: pin contract\n"
+ " --lerwatt-auto Discover optional power advice; failures keep scheduling\n"
+ " --lerwatt Strict optional Lerwatt plug-in: pin contract\n"
" maps under " LW_CONTRACT_PIN_DIR ", publish demand\n"
" --fault-stall --stall-tgid PID --partial --dangerous-vm-test --duration SEC\n"
" QEMU-only opted-task watchdog injection\n"
@@ -530,7 +531,7 @@ int main(int argc, char **argv)
/* CPU frequency requests and the optional Lerwatt plug-in. */
const char *cpuperf_arg = "auto", *cpuperf_reason = "explicit";
unsigned cpuperf = LERTICK_CPUPERF_MAX;
- bool lerwatt = false, cpu_report;
+ bool lerwatt = false, lerwatt_auto = false, cpu_report;
unsigned pin_mask = 0;
bool model_init_ewma = false;
bool model_update_carry = false;
@@ -543,7 +544,7 @@ int main(int argc, char **argv)
int ncpus;
enum { OPT_CHECK = 1000, OPT_SAMPLE, OPT_TRACK, OPT_FIXED, OPT_MIN, OPT_MAX,
OPT_PARTIAL, OPT_FAULT, OPT_STALL, OPT_DANGER, OPT_EXPECT, OPT_PROFILE, OPT_ALL,
- OPT_CPUPERF, OPT_LERWATT, OPT_MODEL_INIT, OPT_MODEL_UPDATE,
+ OPT_CPUPERF, OPT_LERWATT, OPT_LERWATT_AUTO, OPT_MODEL_INIT, OPT_MODEL_UPDATE,
OPT_VERIFY, OPT_MAINTENANCE, OPT_VERSION, OPT_SUMMARY, OPT_FULL_STATS, OPT_NOTIFY };
static const struct option opts[] = {
{ "mode", required_argument, NULL, 'm' }, { "duration", required_argument, NULL, 'd' },
@@ -563,6 +564,7 @@ int main(int argc, char **argv)
{ "fault-stall", no_argument, NULL, OPT_FAULT }, { "stall-tgid", required_argument, NULL, OPT_STALL },
{ "dangerous-vm-test", no_argument, NULL, OPT_DANGER }, { "expect-watchdog", no_argument, NULL, OPT_EXPECT },
{ "cpuperf", required_argument, NULL, OPT_CPUPERF }, { "lerwatt", no_argument, NULL, OPT_LERWATT },
+ { "lerwatt-auto", no_argument, NULL, OPT_LERWATT_AUTO },
{ "model-init", required_argument, NULL, OPT_MODEL_INIT },
{ "model-update", required_argument, NULL, OPT_MODEL_UPDATE },
{ NULL, 0, NULL, 0 },
@@ -610,7 +612,8 @@ int main(int argc, char **argv)
return 2;
}
break;
- case OPT_LERWATT: lerwatt = true; break;
+ case OPT_LERWATT: lerwatt = true; lerwatt_auto = false; break;
+ case OPT_LERWATT_AUTO: lerwatt = true; lerwatt_auto = true; break;
case OPT_MODEL_INIT:
if (strcmp(optarg, "bias") && strcmp(optarg, "ewma")) {
usage(stderr, argv[0]);
@@ -707,7 +710,7 @@ int main(int argc, char **argv)
cpuperf = LERTICK_CPUPERF_UTIL;
} else if (!strcmp(cpuperf_arg, "max")) {
cpuperf = LERTICK_CPUPERF_MAX;
- } else if (lerwatt) {
+ } else if (lerwatt && !lerwatt_auto) {
cpuperf = LERTICK_CPUPERF_UTIL;
cpuperf_reason = "lerwatt";
} else if (ltlw_schedutil_in_use("")) {
@@ -719,13 +722,13 @@ int main(int argc, char **argv)
cpu_report = cpuperf == LERTICK_CPUPERF_UTIL || lerwatt;
if (lerwatt && ltlw_discover(&topo, ncpus, "", NULL)) {
fprintf(stderr, "Lerwatt plug-in: cannot discover CPU clusters.\n");
- status = 1;
- goto cleanup;
+ if (!lerwatt_auto) { status = 1; goto cleanup; }
+ lerwatt = false;
}
if (lerwatt && new_power_session(&topo.session_id)) {
fprintf(stderr, "Lerwatt plug-in: cannot obtain a fresh attachment identity.\n");
- status = 1;
- goto cleanup;
+ if (!lerwatt_auto) { status = 1; goto cleanup; }
+ lerwatt = false;
}
if (lerwatt && topo.npolicies > LW_CONTRACT_MAX_CLUSTERS)
fprintf(stderr, "Lerwatt plug-in: %d cpufreq policies; CPUs past the first %d have no contract.\n",
@@ -833,7 +836,9 @@ int main(int argc, char **argv)
cpu_buf = calloc((size_t)ncpus, sizeof(*cpu_buf));
demand_len = (sizeof(struct lt_demand_rec) * LW_CONTRACT_MAX_CLUSTERS +
(size_t)page - 1) / (size_t)page * (size_t)page;
- ok = cpu_buf && !ltlw_pin_prepare(LW_CONTRACT_PIN_DIR, pins, 2, err, sizeof(err));
+ ok = cpu_buf && !(lerwatt_auto ?
+ ltlw_pin_prepare_auto(LW_CONTRACT_PIN_DIR, pins, 2, err, sizeof(err)) :
+ ltlw_pin_prepare(LW_CONTRACT_PIN_DIR, pins, 2, err, sizeof(err)));
if (ok) {
if (bpf_map__pin(skel->maps.lw_contract, LW_CONTRACT_PIN_LW)) {
snprintf(err, sizeof(err), "cannot pin contract maps: %s", strerror(errno));
@@ -858,8 +863,14 @@ int main(int argc, char **argv)
}
if (!ok) {
fprintf(stderr, "Lerwatt plug-in: %s\n", err);
- status = 1;
- goto cleanup;
+ if (!lerwatt_auto) { status = 1; goto cleanup; }
+ /* Disable BPF consumption too: even a briefly exposed partial
+ * mapping must not affect independent scheduling after failure. */
+ skel->bss->lerwatt_runtime_disabled = true;
+ lerwatt_detach(skel, pin_mask, demand_mem, demand_len);
+ pin_mask = 0; demand_mem = NULL; demand_len = 0;
+ lerwatt = false;
+ fprintf(stderr, "Lerwatt auto discovery unavailable; scheduling independently.\n");
}
}
if (exit_req)
diff --git a/tests/test_deploy.sh b/tests/test_deploy.sh
index d197daf..a6c55cf 100644
--- a/tests/test_deploy.sh
+++ b/tests/test_deploy.sh
@@ -134,7 +134,7 @@ with tempfile.TemporaryDirectory(prefix='lertick-deploy-fixtures-') as directory
quoted="telemetry a'b.jsonl"
result=invoke('plan','--stats-json',quoted)
command_line=result.stdout.splitlines()[1]
- assert shlex.split(command_line)==['sudo',str(tree/'build/scx_lertick'),'--mode','online','--duration','0','--track-all','--cpuperf','auto','--summary-only','--sample-ms','60000','--stats-json',quoted]
+ assert shlex.split(command_line)==['sudo',str(tree/'build/scx_lertick'),'--mode','online','--duration','0','--track-all','--cpuperf','auto','--lerwatt-auto','--summary-only','--sample-ms','60000','--stats-json',quoted]
assert calls()==[], 'plan invoked build, sudo or loader'
for args in [('run','--fault-stall'),('run','--partial'),('run','--cpuperf','max'),('run','--duration','-1'),
('run','--duration','3601'),('run','--sample-ms','99'),('check','--mode','online'),('plan','--mode','invalid')]:
@@ -149,8 +149,8 @@ with tempfile.TemporaryDirectory(prefix='lertick-deploy-fixtures-') as directory
clear();result=invoke('run')
assert '"event"' not in result.stdout
attached=[x for x in calls() if x['tool']=='loader' and '--track-all' in x['args']]
- assert attached and attached[-1]['args']==['--mode','online','--duration','0','--track-all','--cpuperf','auto','--summary-only','--sample-ms','60000']
- clear();invoke('trial');assert [x for x in calls() if x['tool']=='loader' and '--track-all' in x['args']][-1]['args']==['--mode','online','--duration','30','--track-all','--cpuperf','auto']
+ assert attached and attached[-1]['args']==['--mode','online','--duration','0','--track-all','--cpuperf','auto','--lerwatt-auto','--summary-only','--sample-ms','60000']
+ clear();invoke('trial');assert [x for x in calls() if x['tool']=='loader' and '--track-all' in x['args']][-1]['args']==['--mode','online','--duration','30','--track-all','--cpuperf','auto','--lerwatt-auto']
result=invoke('run','--stats-json','-');assert '"event":"registered"' in result.stdout
invoke('run',extra={'STUB_LEAVE_ENABLED':'1'},ok=False);state.write_text('disabled')
for file in [work/'child-started',work/'child-done']:file.unlink(missing_ok=True)
diff --git a/tests/test_power_integration.py b/tests/test_power_integration.py
index 378d316..92e0e09 100644
--- a/tests/test_power_integration.py
+++ b/tests/test_power_integration.py
@@ -112,6 +112,9 @@ def main():
simroot = work / "simulated-board"
owned_units, owned_processes, streams = [], [], []
lwunit = ltunit = None
+ foreign_pin = PINS / "lw_contract"
+ foreign_owned = False
+ pin_dir_created = False
result = {"environment": {"kernel": platform.release(), "architecture": platform.machine(),
"os_release": Path("/etc/os-release").read_text(),
"systemd": call("systemd", "--version").stdout.splitlines()[0]},
@@ -143,7 +146,7 @@ def start_lerwatt(forecast="temporal"):
def start_lertick(mode="online"):
assert STATE.read_text().strip() == "disabled", "refusing another scheduler"
path = work / f"lertick-{len(owned_units)}.jsonl"
- unit = start_unit("lertick", [loader, "--mode", mode, "--track-all", "--lerwatt",
+ unit = start_unit("lertick", [loader, "--mode", mode, "--track-all", "--lerwatt-auto",
"--cpuperf", "util", "--duration", "0", "--notify",
"--sample-ms", "100", "--summary-only", "--stats-json", path])
row = wait_for(lambda: next((r for r in records(path) if r.get("event") == "registered"), None),
@@ -271,6 +274,36 @@ def restored():
wait_for(lambda: STATE.read_text().strip() == "disabled" and not any(PINS.glob("*")),
"final scheduling state or pins were not restored")
result["checks"]["final_control_restore_scheduler_fallback_pin_cleanup"] = True
+
+ # Own one deliberately conflicting pin. Auto discovery must preserve it
+ # and still attach independently, with advice consumption disabled.
+ pin_dir_created = not PINS.exists()
+ PINS.mkdir(mode=0o700, exist_ok=True)
+ call("bpftool", "map", "create", foreign_pin, "type", "array", "key", "4",
+ "value", "8", "entries", "1", "name", "ltlw_autotest")
+ foreign_owned = True
+ foreign_before = call("bpftool", "-j", "map", "show", "pinned", foreign_pin).stdout
+ path = work / "conflicting-pin.jsonl"
+ unit = start_unit("lertick-auto-conflict", [loader, "--mode", "online", "--track-all",
+ "--lerwatt-auto", "--duration", "0", "--notify",
+ "--sample-ms", "100", "--summary-only", "--stats-json", path])
+ row = wait_for(lambda: next((r for r in records(path) if r.get("event") == "registered"), None),
+ "auto-conflict registration missing")
+ assert row["lerwatt"] is False
+ wait_for(lambda: link(unit, "off"), "failed auto transport did not report independent operation")
+ wait_for(lambda: any(r.get("phase") == "periodic" for r in records(path)),
+ "independent scheduler did not emit a runtime snapshot")
+ assert call("bpftool", "-j", "map", "show", "pinned", foreign_pin).stdout == foreign_before
+ assert applied(path) == 0
+ stop_unit(unit)
+ wait_for(lambda: STATE.read_text().strip() == "disabled", "auto-conflict scheduler did not detach")
+ foreign_pin.unlink()
+ foreign_owned = False
+ if pin_dir_created and PINS.exists():
+ PINS.rmdir()
+ pin_dir_created = False
+ result["checks"]["foreign_pin_preserved_auto_falls_back_without_advice"] = True
+ print("integration: conflicting pin preserved; auto mode scheduled independently", flush=True)
result["result"] = "passed"
(work / "result.json").write_text(json.dumps(result, indent=2) + "\n")
print(json.dumps(result), flush=True)
@@ -294,6 +327,10 @@ def restored():
process.wait(timeout=5)
except (OSError, subprocess.SubprocessError) as error:
cleanup_errors.append(str(error))
+ if foreign_owned:
+ foreign_pin.unlink(missing_ok=True)
+ if pin_dir_created and PINS.exists():
+ PINS.rmdir()
for stream in streams:
stream.close()
if args.keep_results or result.get("result") != "passed" or cleanup_errors:
diff --git a/tests/test_service.sh b/tests/test_service.sh
index 5d0b486..8e175ab 100755
--- a/tests/test_service.sh
+++ b/tests/test_service.sh
@@ -98,7 +98,12 @@ with tempfile.TemporaryDirectory(prefix='lertick-service-fixtures-') as td:
def calls():return [json.loads(x) for x in log.read_text().splitlines()]
def clear():log.write_text('')
def run(cmd,ok=True,extra=None,bundle=None):
- p=subprocess.run([str(tree/'scripts/service_manager.sh'),str(prefix),cmd,str(bundle or tree)],env=dict(env,**(extra or {})),capture_output=True,text=True,timeout=15)
+ # Transactions launch many trust/hash stub processes. Slow musl/container
+ # runners need a fixture budget, not the worker's production watchdog budget.
+ try:
+ p=subprocess.run([str(tree/'scripts/service_manager.sh'),str(prefix),cmd,str(bundle or tree)],env=dict(env,**(extra or {})),capture_output=True,text=True,timeout=45)
+ except subprocess.TimeoutExpired as error:
+ raise AssertionError(('fixture transaction timed out',cmd,calls()[-12:])) from error
assert (p.returncode==0)==ok,(cmd,p.returncode,p.stdout,p.stderr)
return p
def marker(name):return (prefix/'lib/lertick'/name).read_text().strip()
@@ -111,11 +116,16 @@ with tempfile.TemporaryDirectory(prefix='lertick-service-fixtures-') as td:
for part in ['Type=notify','NotifyAccess=main','Restart=no',str(prefix)+'/libexec/lertick/service_manager.sh '+str(prefix)+' service-run','NoNewPrivileges=yes','LogRateLimitBurst=100']:
assert part in unittext,part
for file in (prefix/'lib/lertick/releases'/first).iterdir():assert not file.is_symlink() and not(file.stat().st_mode&0o222)
- assert config.read_text()=='lerwatt=off\n' and config.stat().st_mode&0o777==0o600 and configdir.stat().st_mode&0o777==0o700
- assert 'configured: off' in integration('status').stdout
+ assert config.read_text()=='lerwatt=auto\n' and config.stat().st_mode&0o777==0o600 and configdir.stat().st_mode&0o777==0o700
+ assert 'configured: auto' in integration('status').stdout
# Public installed helper preserves arguments across its normal sudo path.
clear();query=subprocess.run([str(prefix/'bin/lertick'),'integration'],env=dict(env,STUB_UID='1000',PREFIX=str(prefix),DESTDIR=''),capture_output=True,text=True,timeout=15)
- assert query.returncode==0 and 'configured: off' in query.stdout and any(c['tool']=='sudo' for c in calls()),(query.returncode,query.stdout,query.stderr)
+ assert query.returncode==0 and 'configured: auto' in query.stdout and any(c['tool']=='sudo' for c in calls()),(query.returncode,query.stdout,query.stderr)
+ # First enable uses automatic discovery without a separate integration command.
+ clear();run('enable')
+ automatic=[c for c in calls() if c['tool']=='loader' and '--notify' in c['args']][-1]
+ assert '--lerwatt-auto' in automatic['args'] and '--lerwatt' not in automatic['args']
+ run('disable');assert config.read_text()=='lerwatt=auto\n'
# A stopped service stays stopped; on/off is exact data and never a sourced script.
clear();integration('on');assert config.read_text()=='lerwatt=on\n' and not json.loads(service.read_text())['active']
assert not any(c['tool']=='systemctl' and c['args'][0] in ['start','stop','enable','restart'] for c in calls())
diff --git a/tests/test_service_systemd.py b/tests/test_service_systemd.py
index 358607a..9a61523 100644
--- a/tests/test_service_systemd.py
+++ b/tests/test_service_systemd.py
@@ -148,16 +148,15 @@ def map_ids():
assert property_value("ActiveState") == "inactive"
assert call("systemctl", "is-enabled", unit, check=False).stdout.strip() == "disabled"
require_default()
- assert config.read_text() == "lerwatt=off\n"
+ assert config.read_text() == "lerwatt=auto\n"
result["install_does_not_start_or_enable"] = True
- call(installed_helper, "integration", "on", env=environment)
- assert config.read_text() == "lerwatt=on\n" and property_value("ActiveState") == "inactive"
+ assert property_value("ActiveState") == "inactive"
require_default()
call(installed_helper, "enable", env=environment)
first_pid = wait_for(ready, "enabled integration service did not become ready/waiting")
command = command_line(first_pid)
- assert command[command.index(b"--duration") + 1] == b"0" and b"--lerwatt" in command
+ assert command[command.index(b"--duration") + 1] == b"0" and b"--lerwatt-auto" in command
assert "/lib/lertick/releases/" in os.readlink(f"/proc/{first_pid}/exe")
assert STATE.read_text().strip() == "enabled"
assert call("systemctl", "is-enabled", unit).stdout.strip() == "enabled"