fix(power): exempt charge-limit oneshot from start rate limiting
All checks were successful
Check / eval (push) Successful in 3m9s
All checks were successful
Check / eval (push) Successful in 3m9s
Round 1 of #101's V3 failed on TuringMachine (2026-07-13 18:16): a SPACED dock/AC event storm — each run finishing before the next event — never coalesces, so 5 successful sub-second starts inside systemd's default 10s window tripped start-limit-hit and marked the unit failed (doctor badge) although every run succeeded. The write is idempotent and cheap; rate limiting it only manufactures false failures, so the unit sets StartLimitIntervalSec=0. checks.battery-charge-limit now asserts the exemption and runs 8 sequential starts expecting a clean, non-failed state; the original mid-run coalescing phase is unchanged. Verified: V2 — the check passes under KVM (exit 0). V0 flake check green. V3 round 2 re-queued in HARDWARE-QUEUE. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@@ -11,20 +11,20 @@ the **T14s** (webcam case).
|
||||
|
||||
## Any machine (dev box is fine)
|
||||
|
||||
- [ ] **#101 charge-limit service survives USB-C power-event bursts** —
|
||||
after updating/rebuilding to the commit carrying this entry, clear the
|
||||
old failure with `sudo systemctl reset-failed
|
||||
nomarchy-battery-charge-limit.service`. Plug and unplug the powered
|
||||
USB-C dock twice, allowing five seconds after the last transition.
|
||||
**Pass:** `systemctl show nomarchy-battery-charge-limit.service -p
|
||||
ActiveState -p Result` reports `ActiveState=inactive` and
|
||||
`Result=success`; `systemctl is-failed
|
||||
nomarchy-battery-charge-limit.service` is false; BAT0's
|
||||
`charge_control_end_threshold` still matches the configured/live limit;
|
||||
and `journalctl -b -u nomarchy-battery-charge-limit.service` contains
|
||||
no new `SIGTERM`, `start-limit-hit`, or `Failed with result` lines after
|
||||
the rebuild. Capture that journal plus `udevadm monitor
|
||||
--subsystem-match=power_supply` if it fails.
|
||||
- [ ] **#101 charge-limit service survives USB-C power-event bursts**
|
||||
(round 2) — round 1 FAILED on TuringMachine 2026-07-13 18:16: a
|
||||
*spaced* storm (each run finished before the next AC event) landed 5
|
||||
successful starts in 10s → `start-limit-hit`, unit marked failed,
|
||||
doctor badge — although every run succeeded. Fixed by exempting the
|
||||
unit from start rate limiting (`StartLimitIntervalSec=0`) + a
|
||||
spaced-storm guard in `checks.battery-charge-limit` (the commit
|
||||
carrying this entry). **Re-check:** after pulling/rebuilding this
|
||||
commit, `sudo systemctl reset-failed nomarchy-battery-charge-limit
|
||||
.service`, then plug/unplug the powered USB-C dock several times in
|
||||
quick succession. **Pass:** `systemctl is-failed
|
||||
nomarchy-battery-charge-limit.service` stays false, `Result=success`,
|
||||
BAT0's `charge_control_end_threshold` still matches the limit, and the
|
||||
journal has no new `start-limit-hit`/`Failed with result` lines.
|
||||
- [ ] **Docking recovery round 4 (#100, closed-lid BenQ sequence)** — after
|
||||
updating to the commit carrying this entry, run `nomarchy-home` and
|
||||
relogin. With the lid open, put visible windows on at least workspaces
|
||||
|
||||
@@ -19,6 +19,21 @@ Template:
|
||||
|
||||
---
|
||||
|
||||
## 2026-07-13 — charge-limit start-limit-hit on spaced AC storms (this commit)
|
||||
- **Task:** Live V3 failure of #101 (TuringMachine doctor badge, 18:16): a
|
||||
spaced dock/AC event storm → 5 successful starts in 10s →
|
||||
`start-limit-hit`, unit marked failed though every run succeeded.
|
||||
- **Did:** `unitConfig.StartLimitIntervalSec = 0` on
|
||||
`nomarchy-battery-charge-limit` (idempotent sub-second write — rate
|
||||
limiting only manufactures false failures); extended
|
||||
`checks.battery-charge-limit` with the exemption assert + an 8× sequential
|
||||
start loop and a clean-state check.
|
||||
- **Verified:** V2 — the check passes under KVM (exit 0: exemption asserted,
|
||||
8 spaced starts, not failed, no start-limit-hit; original coalescing
|
||||
phase still green). V0 flake check green.
|
||||
- **Pending:** V3 round 2 re-queued in HARDWARE-QUEUE (#101 entry updated).
|
||||
- **Next suggestion:** land the in-flight #93/#99/#107 evidence run.
|
||||
|
||||
## 2026-07-13 — #102 Nextcloud Desktop opt-in in the template (this commit)
|
||||
- **Task:** BACKLOG #102 — document upstream HM `services.nextcloud-client`
|
||||
as a downstream-template opt-in; no Nomarchy wrapper.
|
||||
|
||||
23
flake.nix
23
flake.nix
@@ -805,6 +805,29 @@
|
||||
)
|
||||
assert "start-limit-hit" not in service_log, service_log
|
||||
assert "Failed with result" not in service_log, service_log
|
||||
|
||||
# SPACED storm (T14s, 2026-07-13): when each run finishes
|
||||
# before the next AC event arrives nothing coalesces, and 5
|
||||
# successful starts inside systemd's default 10s window mark
|
||||
# the unit failed (start-limit-hit) although every run
|
||||
# succeeded. The unit must stay exempt from start rate
|
||||
# limiting — assert the exemption, then prove repeated
|
||||
# back-to-back starts leave a clean state.
|
||||
machine.succeed(
|
||||
'test "$(systemctl show -p StartLimitIntervalUSec --value '
|
||||
'nomarchy-battery-charge-limit.service)" = 0'
|
||||
)
|
||||
machine.succeed(
|
||||
"for i in $(seq 1 8); do "
|
||||
"systemctl start nomarchy-battery-charge-limit.service; done"
|
||||
)
|
||||
machine.fail(
|
||||
"systemctl is-failed --quiet nomarchy-battery-charge-limit.service"
|
||||
)
|
||||
service_log = machine.succeed(
|
||||
"journalctl -b -u nomarchy-battery-charge-limit.service --no-pager"
|
||||
)
|
||||
assert "start-limit-hit" not in service_log, service_log
|
||||
'';
|
||||
};
|
||||
|
||||
|
||||
@@ -64,6 +64,14 @@ in
|
||||
description = "Apply battery charge end threshold from state or config";
|
||||
wantedBy = [ "multi-user.target" ];
|
||||
path = [ pkgs.jq pkgs.coreutils ];
|
||||
# A sustained dock/AC event storm can land SPACED starts — each run
|
||||
# finishes (~1s) before the next event, so nothing coalesces and 5
|
||||
# successful starts in 10s trip systemd's default start limit: the
|
||||
# unit is marked failed (start-limit-hit) although every run
|
||||
# succeeded (T14s, 2026-07-13; the #101 coalescing only covers
|
||||
# events that arrive DURING a run). The write is idempotent and
|
||||
# sub-second — exempt it from rate limiting.
|
||||
unitConfig.StartLimitIntervalSec = 0;
|
||||
serviceConfig = {
|
||||
Type = "oneshot";
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user