mirror of
https://github.com/Twigpine/zero.git
synced 2026-11-11 13:11:30 -10:00
Admission, scheduler and worker cap in ONE commit, because splitting them the
way the plan suggested would have shipped a regression: relaxing max_workers
while the executor stayed sequential means a plan asking for 8 silently runs
with 1, which is exactly what the old rejection existed to prevent.
ONE SCHEDULER, NOT TWO. With a single worker it walks plan.Order() one task at
a time, applying the same checks in the same order and producing the same
report — so the entire existing suite, written against the sequential executor,
IS the equivalence oracle. It passed unchanged except for the one test that
encoded "max_workers must be 1". A second executor would have been easier to
write and impossible to keep in step; a duplicated rule drifts, and a duplicated
executor drifts faster.
THE WALK IS WHAT PRESERVES ORDER. Rather than keeping a ready SET and picking
from it — which would let task 7 start before task 3 because its dependencies
happened to resolve first — the scheduler walks the validated topological order
and, for each task in turn, waits until that task's dependencies have resolved
AND a worker is free. With one worker the wait is "until the previous task
finished", which is today exactly.
TWO NUMBERS, BOTH REPORTED. max_workers is what the plan asked for; the machine
may carry less. A plan that asked for sixteen and ran six has not been given
sixteen, so the report carries Workers and WorkersRequested rather than letting
the request stand as if honoured. Out of range is REJECTED, not trimmed, for the
same reason the old rule rejected: a trimmed number is one nobody can reason
about afterwards.
THE TUI STOPS ATTRIBUTING RATHER THAN ATTRIBUTING WRONGLY. Child progress events
carry no task identity — the loop's callback holds only the parent's tool-call
id — so with two tasks in flight there is nothing to route them by. Pointing
every child's output at whichever task started last would show one task's tool
calls under another's name. A card that says nothing is honest; a card that says
something false is not. Per-task progress needs the child's identity threaded
through the callback, and that is its own change.
Eight mutations, all caught, including the two that would be invisible in
production: dependencies ignored, and one worker overlapping anyway.
Race detector clean at -count=10.
MEASURED IN THE REAL BINARY, same plan, same build — a five-task diamond:
max_workers=1 WALL 40.0s
max_workers=4 WALL 27.0s
Note what this spends: MaxSpeedup was defined as the theoretical ceiling
measured FROM a sequential run, and it was the evidence the decision to build
this rested on. Once plans run parallel that instrument no longer measures a
ceiling. The 1.67x it reports for this diamond still matches the observed
40→27; on plans that are mostly chain it will not.