Skip to content

feat(core): metered network pause-or-throttle toggle - #17

Merged
pmaxhogan merged 1 commit into
mainfrom
claude/feat-metered-throttle
Jun 25, 2026
Merged

pmaxhogan merged 1 commit into
mainfrom
claude/feat-metered-throttle

Conversation

@pmaxhogan

Copy link
Copy Markdown
Owner

What

Makes the metered-network behaviour configurable to pause (V1 default) or throttle — keep syncing on a metered network at a reduced bandwidth cap (V2, DESIGN s17). This is the toggle you asked for on the metered feature.

Stacked on #16 (pre/post backup hooks) — both touch the same settings/orchestrator/UI files. Until #16 merges, this PR's diff also shows #16's changes; review the pacer + metered changes. I'll rebase onto main once #16 lands.

Design (the careful part — the pacer is safety-critical)

The pacer's optional bandwidth bucket becomes runtime-swappable via a new Pacer::set_bandwidth_cap:

  • Added as a default no-op on the trait, so the six NoopPacer impls need no change; only AimdPacer overrides it.
  • The bucket + its effective rate live behind one Mutex<ByteGate>. permit_bytes clones the bucket Arc out under a brief lock, then runs the async acquire without holding the lock across the await — so a concurrent cap swap is safe and there's no deadlock.
  • set_bandwidth_cap is idempotent (no rebuild when the rate is unchanged), so it's safe to call every cycle. The daily-quota re-init rebuilds from the current rate, keeping the two consistent.
  • All existing pacer tests stay green; added install / lift / idempotency tests.

The orchestrator shares the executor's pacer (a with_pacer builder, no-op default like with_command_runner). When the gates are open it applies the effective cap for the current network — metered cap in Throttle mode, base cap otherwise — and the metered gate no longer pauses in Throttle mode.

Changes

  • core: Pacer::set_bandwidth_cap + AimdPacer ByteGate; MeteredMode enum + config fields; orchestrator with_pacer + apply_bandwidth_cap + gate change + pure effective_bandwidth_cap_mbps.
  • app: assembly shares the pacer Arc into both the executor and the orchestrator.
  • settings: metered_mode + metered_bandwidth_cap_mbps threaded through the global group.
  • UI: metered mode selector (shown when "limit on metered" is on) + throttle cap input.

Tests / gates (all green locally)

  • driven-core: 229 tests incl. pacer install/lift/idempotency + orchestrator throttle-caps / pause-still-pauses tests. clippy clean.
  • driven-app: 212 tests. clippy clean.
  • UI: build + 167 unit tests + lint + prettier; coverage up to 82.25%.

🤖 Generated with Claude Code


Generated by Claude Code

@github-actions

github-actions Bot commented Jun 25, 2026

Copy link
Copy Markdown
Contributor

Coverage

Area main this PR delta
Rust (lib crates) 76.28% 76.37% +0.09 (OK)
UI (vue/ts) 82.06% 82.25% +0.19 (OK)

Gate: passed - no coverage regression (epsilon 0.1 pp).

@pmaxhogan
pmaxhogan enabled auto-merge (squash) June 25, 2026 18:39
Make `skip_on_metered` configurable to either PAUSE (the V1 behaviour)
or THROTTLE - keep syncing on a metered network at a reduced bandwidth
cap (V2, DESIGN s17).

Pacer: the optional bandwidth bucket becomes runtime-swappable via a new
`Pacer::set_bandwidth_cap` (a default no-op on the trait, so the six
NoopPacer impls need no change; AimdPacer overrides it). The bucket +
its effective rate live behind one `Mutex<ByteGate>`; `permit_bytes`
clones the bucket Arc out under a brief lock so the async acquire never
holds the lock across an await, and a concurrent cap swap is safe.
set_bandwidth_cap is idempotent. Install / lift / idempotency are tested.

Orchestrator: holds the executor's pacer (shared via a new `with_pacer`
builder, no-op default like `with_command_runner`). When the gates are
open it applies the effective cap for the current network: on a metered
network in Throttle mode the metered cap, otherwise the base cap. The
metered gate no longer pauses in Throttle mode. A pure
`effective_bandwidth_cap_mbps` + two gate tests (throttle caps + does not
pause; pause still pauses + skips the cap) cover it.

Settings: `metered_mode` (pause|throttle) + `metered_bandwidth_cap_mbps`
threaded through the SPEC s22 `global` group; assembly shares the pacer.
UI: a metered mode selector (shown when "limit on metered" is on) + a
throttle bandwidth cap input, with i18n and a component test.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WvXMHHbYGddVPpmQR2XmK1
@pmaxhogan
pmaxhogan force-pushed the claude/feat-metered-throttle branch from e6f4b9a to c7a3a41 Compare June 25, 2026 18:41
@pmaxhogan
pmaxhogan merged commit 4b690d5 into main Jun 25, 2026
13 checks passed
@pmaxhogan
pmaxhogan deleted the claude/feat-metered-throttle branch June 25, 2026 18:45
pmaxhogan added a commit that referenced this pull request Jun 26, 2026
… V2+" list (C4)

DESIGN s17 still listed schedule windows, pre/post shell hooks, and metered-network
throttle as deferred, and the schedule-windows entry asserted "V1 does NOT ship this"
- all three shipped in 0.2.0 (#12/#16/#17), plus CLI local-state inspection (#13).
Annotate each "(SHIPPED 0.2.0)" + add a header note; keep the historical design text.
Also note metered detection is Windows-only (conservative default on macOS/Linux).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012CyiRqk2DVwmJjEu5gcD1m
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants