routing: make per-role tier and effort user-configurable (config layer), persistent across plugin updates #54

Open
opened 2026-07-24 16:23:34 +00:00 by crenshawdev · 1 comment
crenshawdev commented 2026-07-24 16:23:34 +00:00 (Migrated from github.com)

Problem

Model routing is defined in two plugin-internal places: cadence-core/route-table.json (per-role tier and base_effort, plus the profile matrix) and each agent's frontmatter (effort:). Both ship inside the plugin, so a plugin update replaces them and any user edit to routing is silently overwritten.

The only update-safe customization surface is the config layers that live outside the plugin (~/.claude/cadence/config.json global, .planning/config.json per repo). Today those expose model selection only:

  • model.profile (fast / balanced / quality / auto)
  • model.auto.* (ceiling, escalation)
  • model.overrides.<role> (pin a role to a model alias)

There is no config key for per-role tier or effort. A user cannot durably change either: editing route-table.json or agent frontmatter works until the next plugin update, then reverts.

Impact

A user who wants (for example) cad-verifier routed lighter, or cad-assumptions-analyzer at a lower effort, has no persistent path. The model can be pinned; tier and effort cannot. model.overrides can only assert a model alias, and per the resolver it explicitly does not touch effort (route.mjs override block; effort stays fixed from route-table base_effort + agent frontmatter).

Desired behavior

route.mjs resolve should read per-role tier and effort from the config layer, falling back to route-table.json when unset, matching the precedence model.overrides already follows (config over shipped default). Then route-table.json stays purely our defaults, and a user's config carries their routing, surviving every update.

Sketch

  • Extend the config schema: either grow model.overrides.<role> from a bare alias string into an object { model?, tier?, effort? }, or add a sibling model.roles.<role>: { tier?, effort? }. Keep the bare-string form working for back-compat.
  • route.mjs: after resolving from the profile/tier matrix, apply config tier/effort over the route-table defaults (alongside the existing model-pin step).
  • Effort currently lives in agent frontmatter as the runtime-authoritative value, mirrored by route-table base_effort. Decide the single source of truth and reconcile so the two cannot silently disagree (nothing checks they match today).
  • Update config.schema.json + validation, add a self-verify CONTRACTS entry, and cover with tests: config tier/effort applied, absent falls back to route-table, back-compat string pin still works.

Acceptance

  • A user can set a role's tier and effort in ~/.claude/cadence/config.json (or .planning/config.json) and it persists across a plugin update.
  • route-table.json and agent frontmatter carry defaults only; nothing a user must edit to customize routing lives inside the plugin.
## Problem Model routing is defined in two plugin-internal places: `cadence-core/route-table.json` (per-role `tier` and `base_effort`, plus the profile matrix) and each agent's frontmatter (`effort:`). Both ship inside the plugin, so a plugin update replaces them and any user edit to routing is silently overwritten. The only update-safe customization surface is the config layers that live outside the plugin (`~/.claude/cadence/config.json` global, `.planning/config.json` per repo). Today those expose model selection only: - `model.profile` (fast / balanced / quality / auto) - `model.auto.*` (ceiling, escalation) - `model.overrides.<role>` (pin a role to a model alias) There is no config key for per-role `tier` or `effort`. A user cannot durably change either: editing `route-table.json` or agent frontmatter works until the next plugin update, then reverts. ## Impact A user who wants (for example) `cad-verifier` routed lighter, or `cad-assumptions-analyzer` at a lower effort, has no persistent path. The model can be pinned; tier and effort cannot. `model.overrides` can only assert a model alias, and per the resolver it explicitly does not touch effort (`route.mjs` override block; effort stays fixed from `route-table` `base_effort` + agent frontmatter). ## Desired behavior `route.mjs resolve` should read per-role `tier` and `effort` from the config layer, falling back to `route-table.json` when unset, matching the precedence `model.overrides` already follows (config over shipped default). Then `route-table.json` stays purely our defaults, and a user's config carries their routing, surviving every update. ## Sketch - Extend the config schema: either grow `model.overrides.<role>` from a bare alias string into an object `{ model?, tier?, effort? }`, or add a sibling `model.roles.<role>: { tier?, effort? }`. Keep the bare-string form working for back-compat. - `route.mjs`: after resolving from the profile/tier matrix, apply config `tier`/`effort` over the route-table defaults (alongside the existing model-pin step). - Effort currently lives in agent frontmatter as the runtime-authoritative value, mirrored by `route-table` `base_effort`. Decide the single source of truth and reconcile so the two cannot silently disagree (nothing checks they match today). - Update `config.schema.json` + validation, add a `self-verify` CONTRACTS entry, and cover with tests: config tier/effort applied, absent falls back to route-table, back-compat string pin still works. ## Acceptance - A user can set a role's `tier` and `effort` in `~/.claude/cadence/config.json` (or `.planning/config.json`) and it persists across a plugin update. - `route-table.json` and agent frontmatter carry defaults only; nothing a user must edit to customize routing lives inside the plugin.
crenshawdev commented 2026-07-28 17:41:51 +00:00 (Migrated from github.com)

Collision to settle before either is scheduled: #63 proposes deleting the tier ladder entirely and replacing model-as-lever with effort-as-lever. This issue asks to make that same tier ladder user-configurable so it survives a plugin update. Both cannot land. Whichever wins, the other closes.

Collision to settle before either is scheduled: #63 proposes deleting the tier ladder entirely and replacing model-as-lever with effort-as-lever. This issue asks to make that same tier ladder user-configurable so it survives a plugin update. Both cannot land. Whichever wins, the other closes.
Sign in to join this conversation.
No description provided.