routing: make the axis stakes, not spend - one question (what happens if this is wrong) driving model, effort, review, and verification #81
Labels
No labels
already-shipped
bug
documentation
duplicate
enhancement
good first issue
help wanted
in progress
invalid
needs-decision
proposal
question
wontfix
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set.
Reference: crenshawdev/cadence#81
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Supersedes the routing half of #63. #63 diagnosed the problem correctly and got the replacement wrong; this is the replacement. #63 stays open as the trace of how the argument got here.
What you want Cadence to do
Change the question the routing axis asks.
Today
model.profileasks how much are you willing to spend:fast,balanced,quality. That is a budget question wearing a capability costume, and on a Max subscription it is not even a question the user has.Replace it with the question they actually have, which is what happens if this is wrong:
personalproductioncriticalThat is answerable in one second by someone who has never opened
route-table.json. "How much do you want to spend" is answerable but useless. "Is this hardened work" (the earlier proposal in #63) is neither, because it asks the user to grade their own rigor rather than describe their situation.Stakes are a property of the artifact, not of the author. Cadence itself is a personal project that ships to strangers, so
personalhas to mean "nobody else runs this," not "I am the one typing."The cell value gets richer, and that is the point
The existing matrix shape is
(profile × tier) → model. The shape is right; two dimensions are correct, because roles carry different failure consequences at the same stakes level. A planner's error costs a whole phase; the plan checker is one gate.What is wrong, besides the axis label, is that a cell yields only a model. Quality is not one knob:
One question in, a bundle out. This is also why collapsing everything to a single
effortdial (as #63 proposed) is under-powered: effort cannot express "fire a blocking cross-model review," and that is exactly the knob that matters most at the top rung.The top rung is computed, not claimed
This is the strongest argument here and it is the one that only exists after the reframe.
Cadence already detects auth/authz, DB schema, money, concurrency, destructive operations, secrets/crypto, public API contracts, and untrusted-input parsing. It computes this today, for every user, in order to fire the blocking review trigger.
That list is the
criticalrung. Not "similar to" it, not "a signal that could inform" it. It is the same question, already answered by the codebase.So the top rung asserts itself from the code rather than from the user's self-assessment. Direction of control matters: detection sets a floor, the user may raise it, and lowering below a detected floor requires an explicit override that names what is being overridden. A rung that is free to claim gets claimed for everything and then means nothing.
That also settles the scope question. A project has a baseline; a phase has a surface. The user sets the baseline once, and detection raises individual phases above it. A
personalproject with one phase that writes credentials gets acriticalphase without the user having to notice.Why the default no is wrong here
Because the current question cannot express the thing users need to say. You cannot auto-set a spend level from a risk signal without it being absurd: touching auth is no reason to become willing to pay more. You can absolutely auto-set a stakes level from one. "This phase touches money, so verify harder" is a sentence the current config cannot represent at all.
Because the signal already exists and is half-wired. The risk surface is computed on every phase and used for exactly one thing. Everything else it should be driving reads from somewhere else instead. That is the same defect shape as #64 and #78 (a value resolved and then not delivered), one level up the stack.
Because the knobs are scattered across four places with no question behind them. Model comes from the profile matrix. Effort comes from agent frontmatter. Review gating comes from the triggers config. Verification depth comes from the workflow. A user who wants "be careful with this phase" has to know all four exist and edit each one. That is not a configuration surface, it is a scavenger hunt.
Because it rescues the matrix instead of deleting it. #63 proposed deleting the profile matrix, the tier ladder, and
escalate_effort_variant. Under a stakes axis the two-dimensional lookup is the correct shape and should stay. What changes is the axis label and the cell value. That is a smaller, more reversible change than deleting three mechanisms, and it keeps a structure that already works.The MANIFESTO objection, met
The stated principle is value per dispatch, not minimize spend. Stakes is a strictly better proxy for value than price is. "How much does it cost if this dispatch is wrong" is the numerator the principle was always reaching for;
fast/balanced/qualityonly ever measured the denominator. The principle is unchanged; the instrument improved.The observation that started this
Conventional wisdom is heavy model plans, cheap model executes. Flipping that specifically at the executor produced fewer iterative cycles of bug-finding in adversarial review in observed use. A looping executor burns more than the model it saved, so the cheap placement was never cheap.
Anthropic's Opus 5 guidance gives a mechanism rather than just a correlation: it is strongest on difficult multi-file work and "completes tasks rather than leaving stubs or placeholders," while the gap over prior models is smaller on easy single-turn edits. Fewer defects introduced means fewer review cycles to find them. It also predicts the effect concentrates on heavy phases and nearly vanishes on trivial ones, which is a falsifiable claim worth checking.
Under this proposal the executor's model stops being a pinned value and becomes a derived one, which is why the standalone "route the executor to opus" issue (#75) does not survive as its own change.
What it costs
A breaking config change.
model.profileenum values change with no back-compat alias. This is the v2.0.0 break and the reason this cannot ship in a minor release.Naming is load-bearing and it is the part I hold least firmly.
personal/production/criticalare a proposal, not a conclusion. Whatever they end up being, they have to describe a situation rather than grade an effort, or the ladder degrades back into self-assessment.It depends on #74. Effort is frozen per agent file on the dispatch path Cadence uses (established under #64), so varying effort per role needs per-rung agent files plus the single-sourced contract mechanism. This proposal cannot land before that one.
A richer cell value is more surface that can drift. Three stakes levels times six roles times four knobs is a lot of cells. They must be computed from a small table rather than enumerated by hand, and self-verify needs to assert every cell resolves to a valid model, a valid effort, and a real trigger.
Auto-detection that fires on the wrong surface makes a cheap phase expensive. A false positive on the risk detector now costs more than it did when it only fired a review.
What does not cost what it looks like. This is not a cost optimization and should not be argued as one. Cadence targets Max subscribers, where the allowance is a ceiling rather than a budget. It should be argued on quality or not at all.
Measurements that would validate or kill this
Why this belongs in
mainand not a forkThe wrong question is in
mainand is wrong for everyone. Every user is asked how much they want to spend; no user is asked what happens if the work is wrong.The risk detection is also in
main, computed for every user on every phase, and then used for one thing and discarded. Every fork would rediscover the same mapping from the same signals, and no fork can fix the shipped default.The escape hatches stay:
model.overrides.<role>keeps pinning a model,fablestays reachable by explicit pin, and thepersonalrung exists precisely so throwaway work does not pay for any of this.Files this touches
cadence-core/route-table.json,cadence-core/bin/route.mjsandroute.test.mjs,cadence-core/config.schema.json,cadence-core/templates/config.json,cadence-core/references/review-triggers.md,cadence-core/references/seams.md,agents/(per-rung files),skills/(role contracts, via #74),cadence-core/workflows/config.md,cadence-core/bin/self-verify.mjs,INTERNALS.md"Model routing" (a rewrite, not an edit),DESIGN.md.Related
triggers.<t>.tierdefect may resolve here rather than separately, since the tier vocabulary is one of the things being replaced.