Design thesis
Two claims about agent work, and why both need a hierarchy to be true.
Most agent tooling makes one of two bets: that a bigger context window solves coordination, or that a swarm of identical agents will average out to something correct. dev3d makes neither. It makes two claims, and then builds an organisation chart to make them true.
Agent work should be priced per task
A one-line intake summary and an architecture review are not the same purchase. Billing them at the same rate is how an agent workflow acquires a reputation for being expensive without anyone being able to say which part was.
So price is not a reporting field in dev3d. It is a routing input. Every turn is priced before it is taken, against a policy that says how much model this piece of work deserves, and the decision that follows is recorded with its reasoning. That is the whole of the first claim, and it has three consequences worth stating plainly: cost becomes a design variable rather than an invoice, quality becomes something you can choose to buy for specific turns, and the interesting question stops being "how much did the agent cost" and becomes "was that turn worth a frontier model".
The tier band is where a role's price lives
A role carries a ModelPolicy, and the policy is richer than a preferred model name. The real fields, from the shared contracts package:
| Field | What it means |
|---|---|
defaultTier | The tier this role normally runs at. |
byTaskClass | Per-task-class overrides, for example a developer at standard who is strong at coding. |
minTier | A hard floor. No posture, hint or escalation goes below it. |
maxTier | A hard ceiling. No posture, hint or escalation goes above it. |
escalateAtComplexity | The complexity, 0 to 1, at which this turn is escalated. |
escalateTo | The tier to escalate to when that threshold is crossed. |
maxOutputTokens | An output cap per model response. A turn loops over several round trips, so a turn's total can exceed it. |
pin | A boolean, not a model. When true the router honours the task-class mapping exactly, skips the posture and never escalates. It does not name a model, despite the name inviting that reading. |
preferredModelId | Pin this role to one concrete model, inside the band. A model the bounds exclude is reported in the routing reason rather than obeyed. |
Those last two are worth separating explicitly, because they are the pair people confuse. pin switches off the two steps that would move the target tier; preferredModelId names a concrete model and is a preference subject to a bounds check. A policy may carry either, both, or neither, and the only field that pins a model is the second. The full field reference is on Model policies and task classes.
The tiers, cheapest first, are nano, small, standard, strong and max. Five bands is coarse on purpose: a band answers "how much model does this work deserve" without naming a vendor, which is what keeps a policy portable across a catalog that changes every few weeks.
The shipped company uses the band rather than the model. Every role gets its policy from one helper that sets minTier: 'small' and maxTier: 'max'; researcher is the single role that overrides the floor to nano, because reading prior art is work a small model can do. Output caps are 4096 tokens by default and 3072 on ui-designer and platform-engineer. The escalation ladder is graduated rather than uniform, which is itself an argument about price: the chief executive escalates to max only at 0.75 complexity, the chief technology officer at 0.70, leads and the platform engineer at 0.65, and the four engineers at 0.60. ui-designer and researcher do not escalate at all.
The decision, in the order the router actually makes it
This is where the previous version of this page was wrong, and the correction matters more than the original claim did. Routing is not "filter to the tier band, then take the cheapest model in it". The real sequence is:
- Providers without a key never contribute candidates. Filtering starts before the router sees a catalog.
- Capability filtering happens next, and it is not tier-based. Candidates are dropped for being explicitly excluded, for lacking tool calling, for lacking vision, and for a context window smaller than the request needs.
- The posture moves the target tier.
cheapmoves it topolicy.minTier;balancedleaves it at the task-class override or the default;qualitymoves it one tier up, not to the strongest model in the band. A run with under five cents left is forced tocheapwhatever the posture says. - Escalation is applied separately, from
escalateAtComplexityandescalateTo. - The target is clamped into the policy's own band, and the clamp is noted in the reasoning when it fires.
- A walk order is built: tiers a plugin rule asked for, then the target tier, then every tier above it weakest-first, then every tier below it strongest-first. Affinity falls off along that walk:
tierAffinity = 1 − 0.15 × position. - A weighted score decides.
score = 0.45 · fitness + 0.20 · quality + 0.35 · tierAffinity
− costPressure · relativeCost − reliabilityPenalty + hintBonus
Cost pressure is the posture speaking: 0.30 at cheap, 0.12 at balanced, 0.03 at quality. It falls to zero when nothing in the pool has a quality rating, because a cost term with nothing to weigh it against would just always pick the cheapest model. Ties break on blended cost, then on id, so the same catalog and the same policy always produce the same choice.
The important consequence is the one that a "filter then sort by price" description hides: the target tier is a preference with weight, not a gate. A model that fits the work better at a different tier can still win.
A real decision, captured from a run
The reasoning is not a summary written after the fact. It is the router's own clause list for that turn, joined with em dashes, and it is stored on the turn. This is one turn from a scripted-provider run made while auditing this documentation:
task 'intake' maps to small under this role's policy — chose Qwen 2.5 Coder 7B (local)
(local/local/qwen2.5-coder-7b-instruct) — fitness 1.00 for intake, quality 0.75,
tier affinity 1.00 at walk position 0, score 0.990 — quality from learned
Read as a sequence of admissions: which policy rule produced the target; which model was chosen and through which provider; the three scored terms and the winner's total; and where the quality opinion came from. Alongside it, the router records every candidate it considered and the specific reason each lost:
lmstudio/qwen2.5-coder-14b[small] scored 0.838 against the chosen 0.990
openrouter/meta-llama/llama-3.3-70b-instruct[strong] scored 0.749 against the chosen 0.990
deepseek-flash[nano] scored 0.367 against the chosen 0.990
The model names in those strings come from the catalog configured on the machine that ran it, so treat them as an example rather than a fixture. The shape is the contract. Other recorded reasons are negative and just as specific: lacks tool calling, lacks vision, context window too small, excluded, a pin is in force for this role, tier is not on this policy's walk.
And here is the correction in miniature. In the same run, the researcher turn had a policy target of standard and was served by a strong model at walk position 1, because that model's fitness for the research task class was 0.99 against the standard-tier alternatives' lower scores. No escalation fired; the researcher has no escalation threshold. The score simply preferred a better-fitting model one tier above the policy tier. Anyone who read the earlier description of routing as a hard band filter would have predicted the wrong answer.
Escalation is a separate threshold on a deterministic estimate
Complexity is a heuristic and the source says so: it only has to be ordered correctly, so that adding a button does not burn a frontier model and untangling a concurrency bug does not get a nano one. It is deterministic, and it rounds to two decimals so the reason string stays readable.
- A per-stage baseline starts everything off, from 0.15 for intake and 0.20 for the final report up to 0.60 for the technical plan and integration.
- Length adds a little, and saturates: at most 0.15, on a log curve, so a very long brief is harder but not four times harder.
- Named hard problems add more: up to 0.22 from a fixed list that includes concurrency, race conditions, deadlocks, migrations, backward compatibility, rollback, transactions and cache invalidation.
- Explicit triviality pulls it down by up to 0.15 for signals like typo, rename, label or lint — but only when no hard signal is present.
- Revision passes, file involvement and file count add the rest (up to 0.12, 0.08 and 0.10 respectively), and the role's seniority shifts the estimate by at most 0.03.
Every shipped escalation threshold sits at or above the stage baselines, so escalation is a response to the text of a turn rather than to its stage: a build turn, baseline 0.50, has to look genuinely harder than a build before a frontend engineer's 0.60 threshold is crossed.
The postures are a runtime control, and so is the answer
There are exactly three postures: cheap, balanced and quality, with balanced as the default. Switching one is a console command rather than a reconfiguration, and the hard bounds mean a cheap posture still cannot downgrade a role below its floor. One honest caveat: the routing environment variable sets the installation default only. The engine resolves the posture as the floor's own setting first and the installation default second, so a floor that has been given a posture overrides the environment.
The payoff is that the question "was that turn worth a frontier model" has an answer rather than an opinion. The decision and its reasoning are attached to the turn record, published as a routing event on the wire, and summarised onto the employee as their last route. In the office you click a person and the panel shows the model, the tier, the provider and the full reason string for their most recent turn.
What it cannot tell you: the prices the router reasons about are curated estimates, good enough to order candidates and not billing truth. And the rejection reasons only cover candidates the router actually considered — a model that was never in the catalog leaves no trace, because it was never a candidate.
Agent work should be visible as physical work
The alternative is a chat log: a single scrolling column where a thirteen-person company collapses into one voice, and where the only signal about progress is whether text is still arriving. In dev3d the interface is the organisation chart, and it is not decoration on top of a queue. It is how the hierarchy stays legible.
Status is colour, and there are seven statuses
An employee in the office is a person at a desk, and their state is carried by colour, an emissive tint and body language at the same time: a ring on the floor when the employee is selected, a chest lamp plus a wash over the body, and a pose. The seven real statuses, with the labels and colours the interface actually uses:
| Status | Label in the interface | Colour |
|---|---|---|
offline | offline | #64748b, greyed and flat |
idle | idle | #38bdf8, a gentle bob |
thinking | thinking | #a78bfa, hand to chin |
working | working | #a3e635, typing |
talking | in a meeting | #f0abfc, turned and gesturing |
blocked | needs approval | #fbbf24, an amber pulse |
error | error | #f87171 |
Two corrections to the earlier prose, both of which change what you should expect to see. First, queued, acting and done are not employee statuses at all: queued and done are run states, used for badge tones and filters, and acting appears nowhere in the repository. Second, talking exists as a status with a label and a colour, but no code path in the server currently sets it. A debate renders as speeches in the transcript and the feed; it does not paint anybody pink. Both of these are the kind of thing a screenshot will not tell you and the type union will.
A debate is a sequence of speeches attributed to people
What is real, and still the distinctive part, is attribution. A debate stage has each participant speak once per round, and every speech is emitted as its own event carrying the speaker, the audience and the text. The facilitator then rules and emits a closing speech, and that verdict becomes the stage's summary. In the console each speech is one line in the activity feed, addressed from one named employee to the others, and the feed has a filter for exactly this: talk.
So a debate reads as a sequence of positions held by people who can be held to them, with the transcript to check. That is the claim. The claim it does not support is locomotion: the earlier version of this page said a debate is people walking to the meeting room. They do not walk. The only two things in the whole system that emit a movement event are hiring someone and moving someone's seat, and avatars are repositioned to their seat directly, with no walk cycle and no pathfinding. The interface is honest about the work, not about the journey.
Clicking a person
Clicking an avatar in the office, or a row in the org chart, opens that employee's panel, and the panel is the argument in miniature:
- What they are doing right now, as a sentence — for example editing a named file — plus their seat, their room and their current turn id.
- The routing decision behind their last turn: model, tier, provider, and the full reason string.
- Lifetime totals: turns, tokens, spend, and the skills currently pulled into context.
- Their recent turns, each showing the purpose the employee was given, the tier they ran on, the turn's status, its duration, its cost and the first line of what it produced.
- A direct thread, because you can talk to one employee outside any pipeline.
"What were they asked" is the purpose line, and "what did they do" is one click away in the transcript. A chat log gives you the second and loses the first.
Why both claims need a hierarchy
A flat swarm can be cheap or it can be correct, and it usually manages neither, because nothing in it is accountable for a decision. Pricing work per task is only a policy if something owns the plan that decides which tasks exist. Making work visible is only informative if the people you are watching have distinct jobs.
What is accountable, concretely
The shipped company is 13 roles across 8 departments: executive, design, research, technology, frontend, backend, platform and quality. The shape is not a headcount target. It is the smallest org chart in which someone can be paid to disagree with someone else.
The product pipeline shows how the accountability is distributed across a single run: the chief executive owns understanding the brief, planning the workstreams and the final report; the research stage belongs to the researcher and is the one optional stage in the shipped pipelines; the four leads and the chief technology officer debate the approach and then converge in a decision workshop; the technical plan belongs to the technology role alone; four engineers build in parallel; a review stage runs as a loop in which the review chair is deliberately not the producer of the work; and quality verifies independently of everyone who built anything.
That is what makes "spend a frontier call here and a nano call there" a policy instead of a hope. Somebody owns the objective, somebody owns the plan, and each employee is accountable for a bounded piece of it — which is also what makes the price of each piece a decision somebody can defend.
What would falsify the thesis
- If routing reasons were empty, generic or missing from the turn record, the first claim would be marketing rather than a mechanism. They are recorded per turn, per candidate, and surfaced in the interface.
- If every turn cost about the same, the tiers would be doing no work. They are visibly not: the shipped policies span the full five tiers.
- If the office showed only a busy or idle flag, the second claim would collapse into a progress bar. Seven statuses, seats, rooms and per-person transcripts are what make it an interface rather than an animation.
- If the org chart were decorative — if a run ignored which role owned which stage — the hierarchy would be a costume. The pipeline's stages name their owners by role id, and the engine refuses to start a pipeline the floor cannot staff.
The rest of this section is the audit of those claims: Architecture decisions for the three decisions that hold the shape together, Verification for what has actually been proven about all of it, and Known gaps for the places where the claims are thinner than they sound.
Linked from
Did this page answer your question?