feat: one number per lane — the cap — and the autoscaler is the mechanism (4295)
CI and images / lint (push) Successful in 4s
CI and images / extension-version (push) Successful in 4s
CI and images / frontend-build (push) Successful in 24s
CI and images / integration (push) Failing after 24s
CI and images / backend-lint-and-test (push) Failing after 34s
CI and images / sign-extension (push) Skipped
CI and images / build-web (push) Skipped
CI and images / smoke-web (push) Skipped
CI and images / promote (push) Skipped
CI and images / build-agent (push) Skipped

Operator, 2026-09-23: *"auto should be always on, not a setting, so that idle
instances quiet down when not running. the number that is visible and
something the user can tweak and manage should be the cap itself the number of
running workers is handled by the autoscaling function which is always on."*

They are right, and the reason it was not built this way is worth stating: the
manual dial came first (steps 2-4) and the autoscaler came last (step 7), as
an opt-in BESIDE a control that already existed. Nothing ever asked whether
the dial should still exist once something could move it automatically. Each
step was defensible; the result was three operator settings over one number.

## `slots`, `enabled` and `autoscale` are gone

`slots` was a MEASUREMENT wearing a preference's clothes. How many workers a
lane runs is read live and moved every minute; storing it meant the operator
had to keep two numbers in agreement and the autoscaler had to be told it was
allowed to touch one of them.

`autoscale` gated the mechanism behind a choice, so a lane nobody opted in
never gave its workers back — which is why an idle instance never quieted
down.

`enabled` is derived: a cap of zero means no consumers. "Off" and "may use no
workers" were two spellings of one fact, stored separately, free to disagree.

## Two sweeps become one

`reconcile_lanes_sync` drove the pool to the stored `slots`; `autoscale_lanes_
sync` moved it away from that same number; and most of step 7's hardest
reasoning — a stored value that is a FLOOR, a target of `max(stored, current)`
— existed only to stop them fighting. Delete the stored number and the problem
is not solved, it is absent.

`size_lanes_sync` runs every minute and owns both consumers and pool size. It
also subsumes what the reconcile was for: a worker restarted at its ENV
concurrency is corrected on the next tick rather than after five.

Growth is immediate, shrink is one worker per tick. Deliberately asymmetric —
"always on" is only pleasant if the ramp keeps up, and +1/minute would take
four minutes to answer a burst. Being one worker too large for a minute costs
a sleeping process; being too small costs work not happening. For ML the
asymmetry matters most: every new slot reloads a multi-GB model, so the slow
shrink is what stops a quiet patch from paying that cost again a minute later.

## The caps ship at one, and zero for ML

Per the operator. Conservative on purpose — and a conservative default nobody
knows how to raise is just a slow product, which is the other half of what
they asked for:

    "there needs to be something that tells the user to bump those numbers to
     improve processing rate or they'd never know the controls exist."

So a lane running everything its cap allows while work piles up says so, in
its own row, with the headroom named: *"4,060 waiting and all 1 worker busy.
Raise the cap to run more at once — this machine allows up to 7."*

It fires only when raising the cap would actually help. Not when the lane is
keeping up, not when the sizing pass has room it has not taken, and not at the
machine ceiling — where "raise the cap" is advice nobody can take.

## Migration 0105 rewrites the caps rather than carrying them

The old defaults (4/2/2/1) bounded a manual control and were loose because
moving within them was the ordinary act. The number now means "the most
workers this lane may use", which is a different promise; carrying the old
figure over would quadruple the worker lane on every existing install at the
moment this deploys.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LVjrnpQjRgHdvq95rASoiR
This commit is contained in:
2026-09-23 12:48:22 -04:00
co-authored by Claude Opus 5
parent abe16aa382
commit 445164c852
16 changed files with 1110 additions and 1271 deletions
+120 -171
View File
@@ -47,237 +47,186 @@ async def test_get_lists_every_lane_with_its_ceiling(client, no_live_workers):
assert set(by_name) == {"worker", "scheduler", "maintenance_long", "ml"}
for lane in body["lanes"]:
assert lane["ceiling"] >= 0
assert lane["slots"] <= lane["slots_cap"]
assert lane["slots_cap"] <= lane["ceiling"]
# Nothing is running, so live state must say so rather than report
# zeroes that read like a healthy idle lane.
assert lane["live"]["present"] is False
@pytest.mark.asyncio
async def test_enabled_is_derived_from_the_cap_and_never_stored(
client, no_live_workers,
):
"""The reshape of 2026-09-23. "Off" and "may use no workers" were two
spellings of one fact, stored separately and free to disagree."""
body = await (await client.get("/api/system/workers")).get_json()
for lane in body["lanes"]:
assert lane["enabled"] == (lane["slots_cap"] > 0), lane["name"]
@pytest.mark.asyncio
async def test_ml_ships_off(client, no_live_workers):
"""Rule 164's carve-out and the weak-hardware default in one row: enabling
the lane is what triggers the SigLIP download, so a fresh install must not
find it on."""
"""Rule 164's carve-out and the weak-hardware default in one row: raising
the cap is what triggers the SigLIP download, so a fresh install must not
find it above zero."""
body = await (await client.get("/api/system/workers")).get_json()
ml = next(lane for lane in body["lanes"] if lane["name"] == "ml")
assert ml["slots_cap"] == 0
assert ml["enabled"] is False
assert ml["slots"] == 0
@pytest.mark.asyncio
async def test_the_other_lanes_ship_at_one(client, no_live_workers):
"""Operator: *"the cap defaults should be 1 and 0 for the ml-worker."*"""
body = await (await client.get("/api/system/workers")).get_json()
caps = {lane["name"]: lane["slots_cap"] for lane in body["lanes"]}
assert caps == {
"worker": 1, "scheduler": 1, "maintenance_long": 1, "ml": 0,
}
# --- the persist / push split ------------------------------------------------
@pytest.mark.asyncio
async def test_a_value_is_stored_even_when_it_cannot_be_pushed(
client, db, no_live_workers
async def test_a_cap_is_stored_even_when_it_cannot_be_pushed(
client, db, no_live_workers,
):
"""THE test for this step. `pool_grow` is not durable and the lane is not
answering, so the push fails — and the setting must survive anyway, or a
UI that saved while a worker was restarting would silently lose it
(lesson #4202). 200 with `applied: false`, not an error.
"""
resp = await client.post("/api/system/workers/worker", json={"slots": 3})
"""Nothing is answering, so the live push fails. That is NOT a failed
setting: the value is saved and the sizing pass carries it within a
minute (lesson #4202 — a live change that does not survive, with nothing
saying so)."""
resp = await client.post("/api/system/workers/worker", json={"slots_cap": 3})
assert resp.status_code == 200
body = await resp.get_json()
assert body["slots"] == 3
assert body["slots_cap"] == 3
assert body["applied"] is False
assert "not running" in body["apply_error"]
assert (await _lane_row(db, "worker")).slots == 3
assert (await _lane_row(db, "worker")).slots_cap == 3
# --- the cap is the switch ---------------------------------------------------
@pytest.mark.asyncio
async def test_a_partial_update_leaves_the_other_fields_alone(
client, db, no_live_workers
async def test_a_cap_of_zero_turns_the_lane_off(client, db, no_live_workers):
await client.post("/api/system/workers/worker", json={"slots_cap": 0})
row = await _lane_row(db, "worker")
assert row.slots_cap == 0
body = await (await client.get("/api/system/workers")).get_json()
worker = next(lane for lane in body["lanes"] if lane["name"] == "worker")
assert worker["enabled"] is False
@pytest.mark.asyncio
async def test_raising_it_off_zero_turns_the_lane_on(client, db, no_live_workers):
await client.post("/api/system/workers/ml", json={"slots_cap": 1})
assert (await _lane_row(db, "ml")).slots_cap == 1
body = await (await client.get("/api/system/workers")).get_json()
ml = next(lane for lane in body["lanes"] if lane["name"] == "ml")
assert ml["enabled"] is True
@pytest.mark.asyncio
async def test_the_model_fetch_fires_on_the_transition_not_on_every_write(
client, db, no_live_workers, monkeypatch,
):
"""The stepper sends `{"slots": n}` without restating a cap it did not
touch."""
before = await _lane_row(db, "worker")
original_cap = before.slots_cap
"""Raising the cap off zero downloads SigLIP, once. A second nudge of the
same dial must not re-enqueue a multi-GB download — and the trigger must
be the TRANSITION rather than "a field was sent", which is what it tested
before the UI stopped sending `enabled` at all."""
monkeypatch.setattr(
wc, "set_lane_enabled_sync", lambda lane, enabled, live=None: (True, None),
)
monkeypatch.setattr(
wc, "set_lane_slots_sync", lambda lane, target, live=None: (True, None),
)
fired = []
monkeypatch.setattr(wc, "_enqueue_model_fetch", lambda: fired.append(1) or True)
await client.post("/api/system/workers/worker", json={"slots": 2})
first = await (await client.post(
"/api/system/workers/ml", json={"slots_cap": 1},
)).get_json()
second = await (await client.post(
"/api/system/workers/ml", json={"slots_cap": 2},
)).get_json()
await db.refresh(before)
assert before.slots == 2
assert before.slots_cap == original_cap
assert first["fetching_models"] is True
assert second["fetching_models"] is False
assert fired == [1]
# --- what is refused ---------------------------------------------------------
@pytest.mark.asyncio
async def test_slots_above_the_cap_are_refused_and_nothing_is_stored(
client, db, no_live_workers
):
row = await _lane_row(db, "worker")
resp = await client.post(
"/api/system/workers/worker", json={"slots": row.slots_cap + 1},
)
assert resp.status_code == 400
body = await resp.get_json()
assert body["error"] == "refused"
# The sentence the UI shows. A greyed control with no reason reads as a bug.
assert "cap" in body["detail"]
await db.refresh(row)
assert row.slots <= row.slots_cap
@pytest.mark.asyncio
async def test_a_cap_above_the_derived_ceiling_is_refused(
client, db, no_live_workers
client, db, no_live_workers,
):
"""The operator's cap is theirs to set, but not past what the container
can hold — the whole point of deriving a ceiling rather than typing one."""
"""The ceiling is the machine's, not the operator's, and it is the one
bound they cannot lower themselves past. The detail is written to be read
by a person — a refused control with no reason reads as a bug."""
before = (await _lane_row(db, "ml")).slots_cap
resp = await client.post(
"/api/system/workers/ml", json={"slots_cap": 10_000},
)
assert resp.status_code == 400
body = await resp.get_json()
assert body["error"] == "refused"
assert "container can hold" in body["detail"]
await _refreshed(db, "ml", before)
@pytest.mark.asyncio
async def test_a_boolean_is_not_accepted_as_a_slot_count(client, no_live_workers):
"""`True` is an int in Python. Coerced, it would silently set one slot —
a control that appears to work and does something nobody asked for."""
resp = await client.post("/api/system/workers/worker", json={"slots": True})
async def test_a_negative_cap_is_refused(client, db, no_live_workers):
resp = await client.post("/api/system/workers/worker", json={"slots_cap": -1})
assert resp.status_code == 400
body = await resp.get_json()
assert body["error"] == "invalid_body"
@pytest.mark.asyncio
async def test_a_boolean_is_not_accepted_as_a_cap(client, no_live_workers):
"""`True` is an int in Python. Reading it as a cap of 1 would be a control
that appears to work and sets something nobody asked for."""
resp = await client.post("/api/system/workers/worker", json={"slots_cap": True})
assert resp.status_code == 400
@pytest.mark.asyncio
async def test_the_retired_fields_are_no_longer_accepted(client, no_live_workers):
"""`slots`, `enabled` and `autoscale` are gone. A client still sending one
must be told, not silently ignored — a POST that returns 200 having
changed nothing is the worst of the three outcomes."""
for field in ("slots", "enabled", "autoscale"):
resp = await client.post(
"/api/system/workers/worker", json={field: 2},
)
assert resp.status_code == 400, field
@pytest.mark.asyncio
async def test_an_unknown_lane_is_refused_and_names_the_known_ones(
client, no_live_workers
client, no_live_workers,
):
resp = await client.post("/api/system/workers/nonsense", json={"slots": 1})
resp = await client.post("/api/system/workers/nope", json={"slots_cap": 1})
assert resp.status_code == 400
body = await resp.get_json()
assert body["error"] == "unknown_lane"
assert "worker" in body["known"]
@pytest.mark.asyncio
async def test_an_empty_body_is_refused_rather_than_treated_as_a_no_op(
client, no_live_workers
client, no_live_workers,
):
"""A POST that changes nothing and returns 200 is indistinguishable from
one that worked, which is how a broken UI control goes unnoticed."""
resp = await client.post("/api/system/workers/worker", json={})
assert resp.status_code == 400
body = await resp.get_json()
assert body["error"] == "invalid_body"
# --- the dial is the switch --------------------------------------------------
#
# Operator, 2026-09-23: *"almost all of it always needs to run there's only one
# optional piece and it is killed by moving the 'cap' to zero."* So `enabled`
# is derived from the number rather than being a second control the operator
# has to keep in agreement with it. It stays on the API — these assert that it
# still does, because it is the mechanism the reconcile and the healthcheck
# read.
@pytest.mark.asyncio
async def test_dialling_a_lane_to_zero_turns_it_off(client, db, no_live_workers):
await client.post("/api/system/workers/worker", json={"slots": 0})
row = await _lane_row(db, "worker")
assert row.slots == 0
assert row.enabled is False
@pytest.mark.asyncio
async def test_dialling_it_back_up_turns_it_on(client, db, no_live_workers):
await client.post("/api/system/workers/ml", json={"slots": 1})
row = await _lane_row(db, "ml")
assert row.slots == 1
assert row.enabled is True, "the lane the operator just asked for work from"
@pytest.mark.asyncio
async def test_an_explicit_enabled_still_wins(client, db, no_live_workers):
"""The field is not removed, only derived when absent. Something that
genuinely wants a lane holding its process with consumers cancelled — a
drain before a restart — must still be able to say so without having to
destroy the operator's slot count to express it."""
await client.post(
"/api/system/workers/worker", json={"slots": 3, "enabled": False},
)
row = await _lane_row(db, "worker")
assert (row.slots, row.enabled) == (3, False)
@pytest.mark.asyncio
async def test_a_cap_only_write_does_not_decide_the_switch(
client, db, no_live_workers,
):
"""Only the SLOTS dial derives it. A cap is a ceiling, not a request for
work, and letting it flip the lane would make raising a ceiling start
something."""
before = await _lane_row(db, "ml")
assert before.enabled is False
await client.post("/api/system/workers/ml", json={"slots_cap": 1})
await db.refresh(before)
assert (before.slots, before.enabled) == (0, False)
@pytest.mark.asyncio
async def test_the_model_fetch_fires_on_the_transition_not_on_the_field(
client, db, no_live_workers, monkeypatch,
):
"""The trap the derivation set, caught here rather than in production.
The fetch used to be conditioned on `enabled is True` — the FIELD having
been sent. The UI no longer sends it at all, so the download that makes
the ML lane usable would simply never have fired, and the lane would have
come on and sat there consuming a queue it had no model for.
"""
fired = []
monkeypatch.setattr(wc, "_enqueue_model_fetch", lambda: fired.append(1) or True)
monkeypatch.setattr(
wc, "set_lane_enabled_sync", lambda lane, enabled, live=None: (True, None),
)
monkeypatch.setattr(
wc, "set_lane_slots_sync", lambda lane, target, live=None: (True, None),
)
body = await (await client.post(
"/api/system/workers/ml", json={"slots": 1},
)).get_json()
assert body["fetching_models"] is True
assert fired == [1]
@pytest.mark.asyncio
async def test_it_does_not_fire_again_on_a_lane_already_running(
client, db, no_live_workers, monkeypatch,
):
"""The other half. A second nudge of the dial on a lane that is already on
must not re-enqueue a multi-GB download."""
monkeypatch.setattr(
wc, "set_lane_enabled_sync", lambda lane, enabled, live=None: (True, None),
)
monkeypatch.setattr(
wc, "set_lane_slots_sync", lambda lane, target, live=None: (True, None),
)
await client.post("/api/system/workers/ml", json={"slots": 1})
fired = []
monkeypatch.setattr(wc, "_enqueue_model_fetch", lambda: fired.append(1) or True)
body = await (await client.post(
"/api/system/workers/ml", json={"slots": 1},
)).get_json()
assert body["fetching_models"] is False
assert fired == []
async def _refreshed(db, name: str, expected: int) -> None:
row = await _lane_row(db, name)
await db.refresh(row)
assert row.slots_cap == expected, "a refused write must store nothing"