Description
POST /control/load accepts volume_multiplier 0 (validateState range 0-100) and Engine.SetVolumeMultiplier stores math.Float64bits(0), which Engine.VolumeMultiplier (internal/shape/shape.go) reads as unset and returns 1.0. The control store also normalises a persisted 0 back to 1.0 on reload (internal/control/control.go). An operator who sets 0 to pause load gets full volume. Either reject 0 at validation or represent unset separately so 0 means zero volume; document the chosen meaning.
Acceptance Criteria
- #1 volume_multiplier 0 either is rejected with a 400 naming the valid range or produces zero-scaled volume, never 1.0x
- #2 A persisted value round-trips across restart with the same meaning
- #3 docs/control-plane.md states the meaning of 0
Definition of Done
- #1 just check (fmt-check, lint, gen-check, env-check, docs-check, test, race, hygiene, ui-check, compose-check, helm-test, lab-check, signal-fidelity)
- #2 just gen (only if a blueprint field, construct/workload config struct, or a skill under plugins/synthkit/skills/ changed)
- #3 just dump — inventory diffed against signals/
Implementation Notes
loop50 (2026-10-18): held for an owner decision, not started. Reject 0 with a 400 (root recommendation) or make 0 mean zero volume; see the loop50 closeout.
Owner ruling (2026-10-18): an explicit 0 means zero volume (pause), not a 400. Represent unset separately from 0 so a persisted 0 round-trips as zero; define and document how a 0-volume blueprint reads on readiness and blueprint_emitting.
loop52 BATCH8: landed 94ed16b. Owner rulings: true pause; readiness/blueprint_emitting unchanged. Two guarded reviews (rv147 FAIL M1/M2 fixed; rv147b FAIL on k8s_profiling) then split at the convergence tripwire: k8s_profiling pause and test trim -> SKT-0167; ai_agent -> SKT-0163. Shape-valued spec gauges read 0 while paused (for owner ratification). Nonzero output byte-identical (wire hashes). CodeRabbit 20 files 0; gate gaterun 20261022T051836Z-synthkit-e43e1ae1 exit 0 on 5d7d4fb.