Skip to content

[feature] Capability-routed delegation can only route down — no quality floor #97

Description

@Jason-Vaughan

Problem / motivation

Capability-routed delegation can only route down. delegate(task="code") resolves to the
cheapest can_delegate backend advertising code, which — on any roster where the free local model
lists code — is always the local model, regardless of how hard the sub-task is.

That is correct and desirable for grunt work, and it is the behavior that makes TangleBrain cheap by
default. It becomes a limitation the moment a coordinator wants to express "this sub-task is hard,
send it somewhere better."
Today the only way to say that is to name a backend id explicitly, which
moves the routing decision out of config and into the orchestrator's prompt — the thing capability
routing exists to avoid.

Current behavior

_select_by_capability (tanglebrain/delegate.py:184-212):

candidates = [e for e in roster.delegate_targets() if e.tier in TIER_RANK and task in e.good_at]
return min(candidates, key=lambda e: TIER_RANK[e.tier])

TIER_RANK = {"local": 0, "sub": 1}, so local always wins a tie on capability. api is excluded
outright. The result: capability is a filter, and cost is the only ranking signal.

Proposed behavior

A way for the caller to express a minimum quality bar alongside the capability, so the cheapest
eligible backend is still chosen — just from a smaller eligible set. Rough shape, naming open:

delegate(prompt, task="code", min_tier="sub")

Design questions that need answers before this is buildable:

  • Is tier the right axis? tier currently encodes how it is invoked and paid for
    (local / sub / api), not how capable it is. Overloading it to mean quality conflates two
    independent things — a strong local model on good hardware may beat a weak subscription CLI. A
    separate capability-strength field may be more honest, at the cost of another thing to keep
    accurate in config.
  • Who decides a sub-task is hard? If the orchestrator asserts min_tier, the guarantee is only
    as good as the model's self-assessment, and the natural failure mode is upward drift — every
    sub-task claims to be hard and the cost floor quietly disappears. If config decides, it needs a
    signal it does not currently have.
  • What happens when nothing meets the floor? NoDelegateFit already means "handle it yourself,
    you are the most capable thing available," which is probably the right answer here too — but that
    should be a deliberate decision, not a fallthrough.

Alternatives considered

  • Name the target id explicitly. Works today, needs no change, and is the right escape hatch —
    but it hardcodes roster ids into orchestrator prompts, so swapping a backend means editing prompts
    instead of editing config.
  • Reverse the default to most-capable-first. Rejected. It inverts the project's whole premise and
    would make spend-avoided collapse silently.
  • Let the classifier score sub-tasks. Reuses machinery that already exists
    (tanglebrain/classifier.py) and takes the judgment away from the delegating model. Costs a
    classifier call per delegated sub-task, which may exceed what it saves on short ones.

Scope notes

  • This touches a ratified invariant. "Paid is last resort, never preferred" is documented at
    delegate.py:41-44 and enforced by TIER_RANK omitting api. Any floor mechanism must not become
    a path to auto-selecting a paid backend — the api exclusion should survive whatever lands here.
    Treat that as a constraint on the design, not a detail to settle during implementation.
  • Needs new code in delegate.py and a change to the delegate MCP tool schema.
  • Worth building after the coordinator-preference work: the value only shows up once a
    designated coordinator is actually fanning work out across a heterogeneous fleet, and real usage
    should inform whether the axis is tier, strength, or something else.

Additional context

Third of three gaps found while designing a coordinator-plus-fleet setup. The other two are plumbing;
this one is a genuine design decision, and deliberately filed without a recommended answer.

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or requesthelp wantedExtra attention is needed

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions