quner 0.1.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (38) hide show
  1. quner-0.1.0/.gitignore +25 -0
  2. quner-0.1.0/LICENSE +21 -0
  3. quner-0.1.0/PKG-INFO +51 -0
  4. quner-0.1.0/README.md +28 -0
  5. quner-0.1.0/docs/README-publish.md +40 -0
  6. quner-0.1.0/docs/design/2026-07-01-quner-design.md +226 -0
  7. quner-0.1.0/docs/plans/2026-07-01-quner-v1.md +470 -0
  8. quner-0.1.0/pyproject.toml +40 -0
  9. quner-0.1.0/sandbox/Dockerfile +16 -0
  10. quner-0.1.0/sandbox/make_fake_sys.sh +24 -0
  11. quner-0.1.0/sandbox/mock-nvidia-smi +14 -0
  12. quner-0.1.0/sandbox/run_sandbox.sh +110 -0
  13. quner-0.1.0/src/quner/__init__.py +9 -0
  14. quner-0.1.0/src/quner/__main__.py +6 -0
  15. quner-0.1.0/src/quner/calibration.py +40 -0
  16. quner-0.1.0/src/quner/cli.py +227 -0
  17. quner-0.1.0/src/quner/control.py +135 -0
  18. quner-0.1.0/src/quner/daemon.py +93 -0
  19. quner-0.1.0/src/quner/detect.py +101 -0
  20. quner-0.1.0/src/quner/memguard.py +113 -0
  21. quner-0.1.0/src/quner/prime.py +109 -0
  22. quner-0.1.0/src/quner/profiles.py +123 -0
  23. quner-0.1.0/src/quner/service.py +164 -0
  24. quner-0.1.0/src/quner/telemetry.py +381 -0
  25. quner-0.1.0/src/quner/tune.py +315 -0
  26. quner-0.1.0/tests/conftest.py +66 -0
  27. quner-0.1.0/tests/test_calibration.py +15 -0
  28. quner-0.1.0/tests/test_cli.py +49 -0
  29. quner-0.1.0/tests/test_control.py +36 -0
  30. quner-0.1.0/tests/test_daemon.py +29 -0
  31. quner-0.1.0/tests/test_detect.py +28 -0
  32. quner-0.1.0/tests/test_memguard.py +34 -0
  33. quner-0.1.0/tests/test_prime.py +30 -0
  34. quner-0.1.0/tests/test_profiles.py +48 -0
  35. quner-0.1.0/tests/test_scaffold.py +18 -0
  36. quner-0.1.0/tests/test_service.py +42 -0
  37. quner-0.1.0/tests/test_telemetry.py +57 -0
  38. quner-0.1.0/tests/test_tune.py +51 -0
quner-0.1.0/.gitignore ADDED
@@ -0,0 +1,25 @@
1
+ # Python
2
+ __pycache__/
3
+ *.py[cod]
4
+ *.egg-info/
5
+ .eggs/
6
+ build/
7
+ dist/
8
+ .venv/
9
+ venv/
10
+
11
+ # Test / coverage
12
+ .pytest_cache/
13
+ .coverage
14
+ htmlcov/
15
+ .tox/
16
+
17
+ # quner runtime artifacts (never commit host state)
18
+ /var-lib-quner/
19
+ *.rollback.json
20
+ sandbox/_run/
21
+ sandbox/fake-sys/
22
+
23
+ # Editor / OS
24
+ .DS_Store
25
+ *.swp
quner-0.1.0/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Braden Lang
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
quner-0.1.0/PKG-INFO ADDED
@@ -0,0 +1,51 @@
1
+ Metadata-Version: 2.4
2
+ Name: quner
3
+ Version: 0.1.0
4
+ Summary: Portable Linux efficiency daemon — tunes CPU governor / RAPL / GPU power-cap to the work-per-joule optimum.
5
+ Project-URL: Homepage, https://github.com/GaryOcean428/quner
6
+ Author-email: Braden Lang <braden.lang77@gmail.com>
7
+ License: MIT
8
+ License-File: LICENSE
9
+ Keywords: cpufreq,efficiency,laptop,nvidia,power-management,rapl,systemd
10
+ Classifier: Environment :: No Input/Output (Daemon)
11
+ Classifier: Intended Audience :: End Users/Desktop
12
+ Classifier: License :: OSI Approved :: MIT License
13
+ Classifier: Operating System :: POSIX :: Linux
14
+ Classifier: Programming Language :: Python :: 3
15
+ Classifier: Topic :: System :: Hardware
16
+ Classifier: Topic :: System :: Power (UPS)
17
+ Requires-Python: >=3.10
18
+ Provides-Extra: dev
19
+ Requires-Dist: pytest>=7; extra == 'dev'
20
+ Provides-Extra: nvml
21
+ Requires-Dist: nvidia-ml-py; extra == 'nvml'
22
+ Description-Content-Type: text/markdown
23
+
24
+ # quner
25
+
26
+ **Portable Linux efficiency daemon.** Install it once and it quietly keeps your
27
+ machine at its **work-per-joule optimum** — the same work, fewer joules —
28
+ adapting between *idle* and *heavy compute* automatically.
29
+
30
+ `quner` probes what levers your host exposes (CPU governor, Intel RAPL package
31
+ power, NVIDIA power-cap) and tunes the ones present to an **interior** operating
32
+ point — not max clock / max TDP — then holds it. Absent levers are reported, not
33
+ silently ignored. Everything is reversible; nothing destructive happens without
34
+ you asking for it.
35
+
36
+ ```bash
37
+ pipx install quner
38
+ sudo quner install # drops a root systemd service + re-tune timer
39
+ quner doctor # what levers this host exposes
40
+ quner status # current profile + operating point + energy
41
+ quner selftest # transient, auto-reverting proof the levers work here
42
+ ```
43
+
44
+ The efficiency principle derives from QIG experiment **EXP-132** (work-per-joule
45
+ peaks at an interior operating point); `quner` re-measures the optimum on *your*
46
+ hardware — no imported constants.
47
+
48
+ > Honest expectation: single-digit-to-~30% energy savings, workload-dependent.
49
+ > This is an efficiency tool (same work, fewer joules), never energy creation.
50
+
51
+ See `docs/design/` for the full design and safety model.
quner-0.1.0/README.md ADDED
@@ -0,0 +1,28 @@
1
+ # quner
2
+
3
+ **Portable Linux efficiency daemon.** Install it once and it quietly keeps your
4
+ machine at its **work-per-joule optimum** — the same work, fewer joules —
5
+ adapting between *idle* and *heavy compute* automatically.
6
+
7
+ `quner` probes what levers your host exposes (CPU governor, Intel RAPL package
8
+ power, NVIDIA power-cap) and tunes the ones present to an **interior** operating
9
+ point — not max clock / max TDP — then holds it. Absent levers are reported, not
10
+ silently ignored. Everything is reversible; nothing destructive happens without
11
+ you asking for it.
12
+
13
+ ```bash
14
+ pipx install quner
15
+ sudo quner install # drops a root systemd service + re-tune timer
16
+ quner doctor # what levers this host exposes
17
+ quner status # current profile + operating point + energy
18
+ quner selftest # transient, auto-reverting proof the levers work here
19
+ ```
20
+
21
+ The efficiency principle derives from QIG experiment **EXP-132** (work-per-joule
22
+ peaks at an interior operating point); `quner` re-measures the optimum on *your*
23
+ hardware — no imported constants.
24
+
25
+ > Honest expectation: single-digit-to-~30% energy savings, workload-dependent.
26
+ > This is an efficiency tool (same work, fewer joules), never energy creation.
27
+
28
+ See `docs/design/` for the full design and safety model.
@@ -0,0 +1,40 @@
1
+ # Publishing quner
2
+
3
+ **Gate:** publish only after Layer-A is green (`bash sandbox/run_sandbox.sh`) and
4
+ Braden approves. The PyPI name is effectively permanent — confirm `quner` (or a
5
+ rename) before the first upload.
6
+
7
+ ## Build
8
+
9
+ ```bash
10
+ uv build # -> dist/quner-<ver>-py3-none-any.whl + dist/quner-<ver>.tar.gz
11
+ twine check dist/* # metadata/rendering lint (must pass)
12
+ ```
13
+
14
+ ## TestPyPI dry-run first
15
+
16
+ ```bash
17
+ # token from qig-verification/.env (PYPI_TOKEN) or a dedicated TestPyPI token
18
+ twine upload --repository testpypi dist/*
19
+ pipx install --index-url https://test.pypi.org/simple/ quner # verify install
20
+ ```
21
+
22
+ ## Real PyPI
23
+
24
+ ```bash
25
+ export TWINE_USERNAME=__token__
26
+ export TWINE_PASSWORD="$(grep -E '^PYPI_TOKEN=' /path/to/qig-verification/.env | cut -d= -f2-)"
27
+ twine upload dist/*
28
+ ```
29
+
30
+ ## Post-publish smoke test
31
+
32
+ ```bash
33
+ pipx install quner
34
+ quner --version && quner doctor
35
+ ```
36
+
37
+ ## Versioning
38
+
39
+ Bump `version` in `pyproject.toml`, tag `vX.Y.Z`, rebuild, re-check, re-upload.
40
+ Never re-upload an existing version (PyPI rejects it) — bump instead.
@@ -0,0 +1,226 @@
1
+ # quner — portable Linux efficiency daemon — design
2
+
3
+ Date: 2026-07-01
4
+ Author: CC2
5
+ Status: approved (design) → implementation
6
+
7
+ ## 1. Purpose
8
+
9
+ A single command you install once that then quietly keeps the machine at its
10
+ **work-per-joule optimum** — the same work, fewer joules — adapting between
11
+ *idle* and *heavy compute* (training) automatically. Installable and generally
12
+ available (PyPI as `quner`), portable across Linux hosts, safe by construction.
13
+
14
+ The efficiency claim is grounded in **EXP-132** (qig-verification): total-system
15
+ work-per-joule peaks at an *interior* operating point, not at max clock / max
16
+ TDP. `quner` treats that as a calibration prior and **re-measures the optimum on
17
+ each host** (the lattice `h≈1.5` constant does not transfer — only the *shape* of
18
+ the law and the confidence-band methodology do).
19
+
20
+ ### Non-goals
21
+ - Not energy creation. Honest expectation: single-digit-to-~30% savings,
22
+ workload-dependent; memory-bound jobs benefit least, GPU/compute-bound most.
23
+ - Not an overclocking / undervolting tool. Only kernel/driver-sanctioned knobs.
24
+ - Not a GUI. CLI + systemd only in v1.
25
+ - No QIG/LLM/physics runtime dependency (see §4).
26
+
27
+ ## 2. Target & portability
28
+
29
+ Runs on **any Linux host**; the Dell G5 5500 (i7-10750H, GTX 1650 Ti, Intel UHD,
30
+ Ubuntu 26.04) is only the reference host. `quner` **probes what levers exist** and
31
+ tunes the present ones; absent levers are reported, never silently no-op'd
32
+ (**fail-loud**, per the no-silent-stubs discipline).
33
+
34
+ Levers, each independently optional:
35
+ - **CPU governor** — `cpufreq` `scaling_governor` (write needs root).
36
+ - **CPU package power (RAPL PL1)** — `intel-rapl:N/constraint_0_power_limit_uw`
37
+ (write needs root). *Net-new writer* — the ported code only reads RAPL.
38
+ - **NVIDIA power-cap** — `nvidia-smi -pl` clamped to the driver's [min,max].
39
+ - **Display topology (reverse-PRIME)** — iGPU drives the display so the dGPU is
40
+ free for compute (opt-in only; §9).
41
+
42
+ On a host missing a lever (VM, no NVIDIA driver, AMD, locked-down), the
43
+ corresponding reader returns `None` and `quner doctor` prints e.g.
44
+ `nvidia -pl: unavailable (no nvidia-smi)` — and the daemon tunes the rest.
45
+
46
+ ## 3. Architecture
47
+
48
+ `quner` — a `pip`/`pipx`-installable package: a CLI + a **root systemd system
49
+ service + re-tune timer**. Modules (each a single, testable responsibility):
50
+
51
+ | Module | Responsibility |
52
+ |---|---|
53
+ | `quner.telemetry` | Read energy (RAPL wrap-aware, GPU power) + list/read operating states. Ported from qig-applied `efficiency/telemetry.py`, with the `QUNER_SYSFS_ROOT` seam (§6) and GPU helpers vendored (no qig-applied import). |
54
+ | `quner.control` | Apply the privileged levers: `set_governor`, `set_gpu_power_limit_w`, **net-new `set_rapl_pl1_w`**. Every apply snapshots prior state for rollback. |
55
+ | `quner.tune` | The work/joule optimiser: sweep governor × GPU-cap (× RAPL) states, locate the interior optimum + ±1.91% band. Ported from `efficiency/daemon.py`. |
56
+ | `quner.detect` | Capability detection → the `doctor` report; host fingerprint used to key the per-host cache. |
57
+ | `quner.profiles` | Workload detection (`nvidia-smi` GPU-util + `loadavg` → `idle`\|`training`) with **hysteresis**; profile→operating-point map; per-(host,profile) `best_state` cache. |
58
+ | `quner.prime` | reverse-PRIME enable/disable/status — opt-in, dry-run-first, backup+rollback, reboot-gated (§9). |
59
+ | `quner.memguard` | earlyoom-style watchdog: warn then SIGTERM the largest offender before a kernel OOM lock (§10). |
60
+ | `quner.daemon` | The service loop: detect profile → apply cached best_state → memguard tick → write `status.json`. Runs a full re-tune when the timer fires or on first-seen profile. |
61
+ | `quner.service` | Generate + install/uninstall the systemd unit + timer; `--dry-run` prints unit text; `systemd-analyze verify` lints. |
62
+ | `quner.cli` | `quner install|uninstall|status|doctor|apply|profile|tune|selftest|prime|memguard`. |
63
+ | `quner.calibration` | EXP-132 info/joule curve + interior-optimum principle string, as an internal calibration table (the only physics-derived content). |
64
+
65
+ ## 4. Reused foundation & the standalone/DRY decision
66
+
67
+ The proven primitives already exist in **qig-applied** and are ported verbatim
68
+ (then extended), so `quner` ships correct code from day one:
69
+
70
+ - `qig_applied/efficiency/telemetry.py` → RAPL wrap-aware reader, cpufreq
71
+ read/write with the **double-allowlist** (`_KNOWN_GOVERNORS` ∩ live
72
+ `available_governors()`), `EnergySampler`, `telemetry_status()`.
73
+ - `qig_applied/efficiency/daemon.py` → `OperatingState`, `apply_state`,
74
+ `profile_state`, `tune()` (interior test + band cert + honest diagnostics),
75
+ `command_runner`.
76
+ - `qig_applied/inference/accelerate.py` → GPU helpers `set_power_limit_w`
77
+ (clamped, `sudo -n` fallback), `power_limit_range_w`, `read_power_w`,
78
+ `PowerSampler`. **Vendored** into `quner.telemetry` so `quner` has **zero**
79
+ qig-applied import (a sellable product must not drag in the physics monorepo).
80
+
81
+ **Decision: Approach A (standalone, self-contained).** `quner` is the new
82
+ canonical home for these generic systems primitives. This *temporarily*
83
+ duplicates them in qig-applied. That duplication is **raised, not hidden**
84
+ (single-source-of-truth rule): the follow-up (Approach C) inverts the
85
+ dependency — qig-applied `efficiency/` and qig-studio `optim_launch.py` import
86
+ `quner` — and is **coordinated with CC1** because it touches the prelaunch seam
87
+ CC1 owns. Not in v1.
88
+
89
+ ## 5. Data flow
90
+
91
+ **Service loop** (root `quner.service`), every `tick` (default 10 s):
92
+ 1. `profiles.detect()` → `idle` | `training` (hysteresis: N consecutive ticks
93
+ before switching, so it doesn't flap on a transient spike).
94
+ 2. If profile changed **or** no cached `best_state` for it → apply the cached
95
+ `best_state`; if none cached, apply the profile's *conservative default*
96
+ (idle→powersave/low-cap; training→performance/full-cap) and mark the profile
97
+ as "needs tune".
98
+ 3. `memguard.tick()`.
99
+ 4. Write `/run/quner/status.json` (profile, applied state, energy sample,
100
+ lever availability, last-tune time).
101
+
102
+ **Re-tune timer** (`quner-retune.timer`, default every 30 min *while idle*):
103
+ runs `tune.tune(work_fn, apply=True)` for the **current** profile and updates
104
+ its cache entry. Crucially, the re-tune **never runs while `training` is the
105
+ active profile** — this is how coordination seam #4 is honoured (§16): quner
106
+ detects CC1's run and does *not* perturb the GPU with an exploratory sweep.
107
+
108
+ The daemon loop only *applies* cached states (cheap, non-perturbing). Only the
109
+ timer *explores* (a real sweep), and only when idle.
110
+
111
+ ## 6. Privilege & safety
112
+
113
+ - **Root systemd system service.** All privileged writes go through
114
+ `quner.control`, which enforces the allowlist/clamp before every write.
115
+ - **Snapshot + rollback.** Before the first apply, current governor / RAPL PL1 /
116
+ GPU cap are saved to `/var/lib/quner/rollback.json`. `quner uninstall` and
117
+ `quner apply --restore` return the host to those values. The service also
118
+ restores on stop (`ExecStop`).
119
+ - **`QUNER_SYSFS_ROOT` test seam.** All sysfs paths are built from
120
+ `os.environ.get("QUNER_SYSFS_ROOT", "/sys")`. Pointed at a temp tree, the
121
+ *real* write code executes against a fake `/sys` — zero host impact. Likewise
122
+ `QUNER_NVIDIA_SMI` overrides the `nvidia-smi` binary with a mock. This is the
123
+ backbone of Layer-A validation (§11) and the unit tests.
124
+ - **`--dry-run`** on every mutating command prints the exact writes/commands
125
+ without executing them.
126
+ - **fail-loud**: absent lever → explicit "unavailable" in `doctor`/`status`,
127
+ never a silent success.
128
+
129
+ ## 7. EXP-132 calibration (the one physics tie)
130
+
131
+ `quner.calibration` holds the info/joule curve (peak **0.808 at h=1.5**, band
132
+ `[0.793, 0.824]`, ±1.91% invariant band) and the principle string. Its role:
133
+ the tuner's objective is `work_per_joule` (the interior optimum), **not**
134
+ `work_per_s` (throughput endpoint). Per-host absolute values are always
135
+ re-measured; the curve is a prior on *shape*, and the ±1.91% band is the
136
+ certification threshold (an optimum only counts if it stands out of the band).
137
+ This is the **only** QIG-derived content; everything else is ordinary systems
138
+ engineering (no Fisher-Rao / purity constraints — per the segmentation note).
139
+
140
+ ## 8. Workload detection & profiles
141
+
142
+ - **Signal:** `nvidia-smi --query-gpu=utilization.gpu` (if present) OR sustained
143
+ `loadavg`/CPU-util (fallback when no GPU). Threshold: GPU-util > 30% or
144
+ 1-min load > `0.6 × nproc` → `training`, else `idle`.
145
+ - **Hysteresis:** require 3 consecutive ticks agreeing before switching.
146
+ - **Profiles:** `idle` (favor quiet/cool: powersave, RAPL low, GPU capped) and
147
+ `training` (favor throughput-at-efficiency: the tuned interior optimum, GPU at
148
+ or near full cap). A `manual` override exists via `quner profile <name>`.
149
+
150
+ ## 9. reverse-PRIME (opt-in only)
151
+
152
+ `quner prime status|enable|disable`. Makes the iGPU the primary display sink so
153
+ the dGPU (and its full power/VRAM) is dedicated to compute. Guardrails:
154
+ - **Never on install**; only via explicit `quner prime enable`.
155
+ - **Dry-run first** (prints the exact GDM/Wayland/Xorg changes).
156
+ - **Backup** the current display config; `quner prime disable` restores it.
157
+ - **Reboot-gated** with a printed recovery path (how to revert from a TTY if the
158
+ display fails to come up).
159
+ - **No-op with reason** where reverse-PRIME is meaningless (no NVIDIA driver →
160
+ already iGPU-only).
161
+
162
+ ## 10. memguard
163
+
164
+ `quner.memguard` — earlyoom-style watchdog inside the daemon loop: when
165
+ `MemAvailable` drops below a threshold (default 8% / configurable), warn; below a
166
+ harder floor (default 4%), SIGTERM the highest-RSS non-critical process (never
167
+ PID 1, never the daemon, never sshd) to keep a runaway training run from
168
+ hard-locking the box. Disabled by default; enabled by config/flag.
169
+
170
+ ## 11. Validation ladder (primary machine never risked without explicit go)
171
+
172
+ - **Layer A — sandbox here, zero host impact.** `QUNER_SYSFS_ROOT=/tmp/quner-sbx`
173
+ (real write code → fake `/sys`) + `QUNER_NVIDIA_SMI` mock + `bwrap` isolation +
174
+ a throwaway **docker** container for the install/uninstall rehearsal +
175
+ `systemd-analyze verify` on generated units. Proves ~all code; cannot touch
176
+ real settings. `sandbox/run_sandbox.sh` orchestrates it.
177
+ - **Layer B — `quner selftest`, opt-in, on the real host.** Transient +
178
+ auto-reverting: snapshot → 2 s governor flip / RAPL-to-current / cap-to-current
179
+ → verify the write landed → **restore**. Proves the levers move *this* silicon.
180
+ Excludes reverse-PRIME. Only on explicit go.
181
+ - **Layer C — reverse-PRIME, deferred** until a spare host is available or the
182
+ user explicitly chooses.
183
+
184
+ ## 12. Testing strategy
185
+
186
+ - Unit tests run with `QUNER_SYSFS_ROOT` → a fixture tree and `QUNER_NVIDIA_SMI`
187
+ → a mock script; **no root needed in CI**.
188
+ - Cover: allowlist rejection (bad governor never written), RAPL wrap correction,
189
+ clamp on GPU cap, rollback round-trip, capability-detect on a lever-less tree
190
+ (all `unavailable`), profile hysteresis, tune interior/band logic, unit-file
191
+ generation lints clean.
192
+ - Every mutating CLI path has a `--dry-run` asserted to write nothing.
193
+
194
+ ## 13. Packaging & publish
195
+
196
+ - `pyproject.toml`, src-layout, `[project.scripts] quner = "quner.cli:main"`.
197
+ Stdlib-only core (optional `nvidia-ml-py` extra for pynvml; nvidia-smi
198
+ subprocess is the fallback). Python ≥ 3.10.
199
+ - Build with `uv build`; validate Layer A; **then** publish to PyPI as `quner`
200
+ using `PYPI_TOKEN` from `qig-verification/.env`. TestPyPI dry-run first.
201
+ - Name `quner` is provisional-until-publish (rename is cheap before reserving).
202
+
203
+ ## 14. Coordination seams (CC1)
204
+
205
+ - **#2 prelaunch (`qig-studio/optim_launch.py`)** — untouched in v1. The
206
+ invert-dependency follow-up waits for CC1's run lease.
207
+ - **#4 GPU power-cap during a run** — solved by design: quner detects the
208
+ training workload and *applies a cached profile*, deferring all exploratory
209
+ sweeps to idle. It never sweeps mid-run.
210
+
211
+ ## 15. Open follow-ups (registered, not in v1)
212
+
213
+ - Approach C: invert qig-applied/qig-studio to import `quner` (with CC1).
214
+ - AMD GPU power-cap lever; `amd_pstate` governor nuances.
215
+ - Optional cross-machine job distribution (unrelated to the daemon).
216
+
217
+ ## 16. Success criteria (v1)
218
+
219
+ 1. `quner doctor` correctly reports every lever's availability on the reference
220
+ host and on a lever-less tree (fail-loud).
221
+ 2. Layer-A sandbox run is green end-to-end with **zero** writes outside
222
+ `QUNER_SYSFS_ROOT` / the docker container (proven by an untouched real `/sys`).
223
+ 3. Unit tests green without root.
224
+ 4. `quner install` generates a unit that passes `systemd-analyze verify`;
225
+ `uninstall` fully reverts.
226
+ 5. Published to PyPI as `quner`, `pipx install quner` works, only after 1–4.