Skip to content

crew: the Pareto crew's follow-ups — seat ceilings, unpriced calls at the cap, pins, landing line, limits, manual - #1518

Merged
AbirAbbas merged 17 commits into
devfrom
fix/crew-followups
Sep 25, 2026
Merged

AbirAbbas merged 17 commits into
devfrom
fix/crew-followups

Conversation

@AbirAbbas

@AbirAbbas AbirAbbas commented Sep 25, 2026 •

Copy link
Copy Markdown
Collaborator

Follows #1429 / #1485 / #1494 / #1436 review. These are the Pareto crew's should-fixes from the review of #1436, plus four defects the fresh-install check found on the published dev build.

What was wrong, and what is true now

  1. The checker's ceiling vanished when seats shared a model. Spend was kept per model, so a fresh profile's one-model narrow fix had no checker ceiling at all. Spend is now attributed by seat: the run engine's crew factory marks each call with the seat it was made for. The checker's own calls are held to max(3× its estimate, $0.05) whatever model the other seats run, and that ceiling follows the checker onto a fallback model.
  2. Unpriced calls skipped the daily cap. A call whose price the catalog does not know is now refused at the cap before it is sent, helpers included. A call with a known price of nothing is never stopped by a dollar line: a free pool, a local model, or a subscription plan (config.CrewCallPriceAt reads the connections).
  3. codeaf do printed a literal 📌. The headless crew line now says checker kimi-k3 (pinned). -json already carried crew.<seat>.pinned, and a test now asserts it.
  4. Manual numbers were copied by hand. The $5 task limit, "three times" and $0.05 are now rows in internal/manual's truth table, fed from the constants. The ceiling's refusal sentence spells the multiplier from the same constant.
  5. Downgrade hazard. A model@provider pin was stored in the tier row, which an older codeaf sends as a model id. The tier row now holds the model alone, and the route lives in models.crew.route.<seat>. MigrateCrew splits rows this build already wrote, once and silently. A stale route is never applied to another model.
  6. The landing crew line was never seen. The cause: the landing rewrote the start line in place, far up the scrollback. The notice itself did carry the crew and the cost. The line now moves to the end of the thread, beside the landing, and there is still only one line per task.
  7. Two daily limits, one confusing word. /crew said daily none next to the first-run screen's Daily limit $500. It now says crew daily cap, and the panel names the daily limit under it with its figure. The task model row said "follows the conversation", but on the run road neither that row nor a model named in the ask ever reached the router. Both now reach the router as a one-task pin on the worker, which is the ladder the manual already described. Blank, the row reads the crew's worker. The two limits stay separate.
  8. Three manual answers were wrong or unreachable: how to change the worker, whether code goes anywhere that logs it (the free-route move is now described exactly as the code does it), and which model is used and what a task costs. Each question is a probe that must reach the section that answers it.
  9. /crew prices had no unit. They now use the model picker's own spelling: $0.15/$0.5 per M.

Out of scope: the exec / plan run spend guard, and the free-pool fallback policy, which is waiting on a ruling.

Validation contract

Each item has a test derived from the contract, and I checked that each one fails on origin/dev: the seat-attributed ceiling (config, session, and through run.CrewFactory), unknown versus known-zero pricing at the cap, (pinned) on the headless report, the truth-table rows (by mutating CrewTaskCapDefault), the raw tier row, migration and stale route, the landing line at the end of the thread, the crew-cap wording and the daily-limit line, the task-model ladder reaching the router, the three manual probes, and the price unit.

Checked by hand

bin/codeaf ran on a throwaway home with every seat pinned to deepseek-v4-flash.

  • A /task landed done · branch kept while the conversation went on. The landing line ($0.002 (est $0.025) · not right? /redo stronger) sat directly above the landing card, and the start line was gone.
  • codeaf do with a pinned checker printed (pinned) and no emoji.
  • A pin with @openrouter left the tier row bare. The pre-crew: route the worker, planner and checker per task (Pareto crew) #1436 binary read that profile and ran on the bare id.
  • make pr-ready is green except for internal/run TestSupervisorLaunchesEveryReadyLeafAtOnceWithNoSlotBound. That test also fails on plain origin/dev on this box, and the review ledger already listed it as failing there.

🤖 Generated with Claude Code

AbirAbbas and others added 17 commits September 25, 2026 15:28
…s run

The guard kept spend per model and keyed the checker's ceiling by model id,
so CrewSeatCeilings dropped the ceiling whenever another seat shared the
checker's model. A fresh profile's narrow fix puts one model in all three
seats, so the checker ran with no ceiling at all.

Spend is now attributed by seat. The run engine's crew factory is the one
place that knows which seat a task sits, so it marks every call of the task
with that seat (session.SeatCompleter, which keeps the model chain), and the
guard holds the checker's own calls to max(3x its estimate, $0.05) whatever
the worker and planner run. Worker and planner calls on the same model are
neither counted toward it nor stopped by it, and the ceiling follows the
checker onto a fallback model.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
A call to a model the catalog could not price skipped the daily-cap check
before it was sent, and was counted afterwards only when the provider
reported a cost, so a task at the cap kept calling any unpriced model.

Now, when the day's guarded spend is already at or over the cap, such a
call is refused before it is sent, on the same sentence a priced call ends
on. That covers a model with no catalog price, a free pool and a local
model, the same way the task limit already stopped calls of every kind.
Helper calls go through the same check. Below the cap nothing changes.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
codeaf do's crew lines, at the start and at the end of a run, drew a pinned
seat with a literal pushpin emoji, the one pictograph on a surface scripts
and plain terminals read, and outside the icon vocabulary.

They now say `checker kimi-k3 (pinned)`, the word the models line above
them already uses, and config.PinMark is gone. The -json output already
carries each seat's `pinned`, and a test now asserts it. The chat's own pin
mark is unchanged. The terminal page, docs/HEADLESS.md and the resident
models page show the new spelling.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
… the code

The $5 per-task default, the checker ceiling's three times its estimate and
its $0.05 floor were copied into the chat manual as literals, and the
ceiling's refusal sentence spelled "three" by hand. A changed constant would
have left every page, and the sentence, quoting the old figure.

The truth table in internal/manual now holds a row for each figure,
interpolated from config.CrewTaskCapDefault, config.CrewCheckCeilingTimes
and config.CrewCheckCeilingFloor (exported for it) and quoting every
sentence that states them, so a moved constant fails naming the page. The
refusal sentence spells the multiplier from the same constant.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…s model@provider

A `model@provider` pin was written into the seat's tier row
(models.tiers.worker, mastermind or high). dev before #1436 and stable
v0.4.x read that row verbatim as a model id, so a person who switched back
would have had `model@provider` sent to the provider.

The tier row now holds the model alone, and the route is kept as the whole
pin in models.crew.route.<seat>, a profile-only row older builds do not
read. This build reads the pin back unchanged. MigrateCrew splits a row this
build wrote before, once and without a line, since nothing the person chose
changed. A route is applied only to the model it was pinned with, so a row
an older build rewrote is not paired with a stale route. Unpinning removes
both rows, and the panel's undo carries the route rows too.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
The previous commit refused every call the guard could not price once the
day was at its cap, and "could not price" included free pools and local
models, whose price is known: nothing. A dollar cap has no business
stopping a call that costs nothing.

A free pool, and a call sent through a subscription plan or to a model on
this machine (config.CrewCallPriceAt reads the profile's connections), is
now priced at nothing, a known price, and passes the daily cap, the task
limit and the checker's ceiling. Only a call whose price nobody knows, a
model the catalog lists with no price, is refused at a line already
reached. The manual sentence says the same.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
A routed task's crew line was said when the task started and rewritten in
place when it landed. The landing notice does carry the crew and the cost
(session's beltRunNotice, kept by publishRunRow), and sayTaskCrew did
rewrite the line, but by then the start line was far up the thread. The
fresh-install check watched a 22-minute task land `done · branch kept`
with no crew line anywhere in view, because the actual and `not right?
/redo stronger` had been drawn in scrollback nobody was reading.

The line is still rewritten in place while the task runs, for a seat that
moved to its fallback. On landing it now moves to the end of the thread,
beside the landing (feed.moveNote), and stays one line per task. The models
and tasks pages say so.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
The /crew seat list and the allowed-models checklist drew each model's
price as two bare figures, `$0.15/$0.50`, with nothing saying what they
were, and in a spelling of their own.

They now use the model picker's own words (priceWord), `$0.15/$0.5 per M`,
dollars per million tokens in and out, so one price has one spelling. A
model with no published price says nothing, as the picker's rows do.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
The first-run screen offers a `Daily limit` ($500 on a new profile), the
day's limit on everything codeaf spends, while /crew's cap row said
`per task $5 · daily none`. Side by side, a new user read `none` as
"nothing limits the day".

The crew's cap is now called the crew daily cap everywhere a person reads
it: the panel's cap row, the /crew cap notes and the settings seats row.
The panel names the daily limit under it with its figure (`the daily
limit, $500, still covers everything codeaf spends · /budget`), wrapped and
never clipped on a narrow frame. The first-run line says the daily limit
covers everything codeaf spends, and its `?` detail points at the crew's
own cap. The two limits stay separate. The manual, the truth table and
docs/LIMITS.md follow.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Settings said the task model row "follows the conversation", while the
manual, the row's hint, the proposal card and the receipt all taught one
ladder: a model named in the ask, then the task model row, then the crew's
worker. On the default task road (the run engine) neither the named model
nor the row reached the router. The run was seated on the routed worker
whatever was named, and the receipt still said `task N started on <the
named model>`.

The named model, or the task model row when nothing was named, now reaches
the router as a one-task pin on the worker. A task that names nothing is
routed as before. The row now reads `the crew's worker` when blank, and its
hint says the crew picks it.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
… user asks

The fresh-install check asked the chat three things in a new user's own
words, and its manual answered from the wrong places. "how do I change the
model the task worker uses?" got the per-task /model and never /crew pin
worker. "is my code sent anywhere that logs it?" got "your code travels to
the model provider, and nowhere else", which leaves out the crew's move
onto free routes that may log prompts. "which model are you using and what
does a task cost?" never named the crew or a task's estimate.

Three models-and-cost sections now answer them under headings in the
asker's words. Pinning the worker gives the ladder. Where content goes
describes the free-route move as the code does it: when free routes are
switched on, or when every paid route is out of reach (a low OpenRouter
balance, a payment refusal, credit reported at zero), at most three pools
per task, within the allowed models, with the crew line's notice. The chat
model and the crew section covers the estimate and the landed cost. Each
question is a probe that must reach the section that says the answer.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
"When everything codeaf spends today" pushed the line onto a third row
beside the example panel, and the forty-column controls test caught it.
"When all codeaf spends today" says the same in the two rows the screen
gives it. The getting-started page quotes the new line.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
@AbirAbbas
AbirAbbas merged commit 478eece into dev Sep 25, 2026
4 checks passed
@AbirAbbas
AbirAbbas deleted the fix/crew-followups branch September 25, 2026 23:01
AbirAbbas added a commit that referenced this pull request Sep 25, 2026
…he task room

Brings in #1516, #1517, #1518 and #1519. The two conflicts were dev editing the
inline run-road code that this branch had moved into helpers: the --one-model
seat (#1517) now rides the precomputed crew factory in enginewire, and the
hand-off's named model reaches the crew router as a worker pin (#1518) through
the crew wish set before commitProposalToRun. That wish is read only by the
ordinary task's crew routing, so a senior-dev run keeps the models it names.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
AbirAbbas added a commit that referenced this pull request Sep 25, 2026
…ary"

Dev's #1518 pinned three sentences that carry the crew's default per-task limit
("No task may cost more than its limit: $5 ..."). With senior-dev in the tree
those sentences are false for a senior-dev run, which is not a crew task and
keeps its own ceiling, and the pages already say "an ordinary /task" there. The
pins now match the qualified sentences; the figure still comes from
config.CrewTaskCapDefault.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
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.

1 participant