Merge nucleic/ivory-jade-ibis-gv6u into dev
This commit is contained in:
@@ -79,12 +79,14 @@ public struct RoutingNote: Sendable, Equatable {
|
||||
|
||||
/// Purpose × level → concrete (model, effort). The routing knowledge is a hand-encoded
|
||||
/// matrix grounded in July-2026 model research (see the plan's research table and the
|
||||
/// per-cell comments): Opus 5 peaks at *medium* effort on routine coding and never exceeds
|
||||
/// `high`; GPT-5.6 Sol owns the backend lane at high+; Fable 5 appears only at Deep/Max on
|
||||
/// the lanes that genuinely reward long-horizon reasoning; Haiku/Luna only where a light
|
||||
/// model can't hurt. Every cell carries a Claude-lane and a GPT-lane candidate so a
|
||||
/// single-provider user always resolves; `IntelligenceRoutingTests` enforces the
|
||||
/// invariants (tier monotonicity, per-model caps, valid efforts for every connected set).
|
||||
/// per-cell comments): Opus 5 carries the heavy Claude lane and is allowed past `high` only
|
||||
/// at the top of the two rows that reward it (`debugging`/`review` at Max); GPT-5.6 Sol owns
|
||||
/// the backend lane at high+; Fable 5 is now reserved for the two long-horizon Max cells
|
||||
/// (`planning`, `refactor`); Luna only where a light model can't hurt. Nearly every cell
|
||||
/// carries a Claude-lane and a GPT-lane candidate so a single-provider user resolves inside
|
||||
/// its own row — `frontendImpl`/`balanced` is the one deliberate exception, and falls back
|
||||
/// to the row's Light GPT candidate. `IntelligenceRoutingTests` enforces the invariants
|
||||
/// (tier monotonicity, per-model caps, valid efforts for every connected set).
|
||||
public enum IntelligenceRouter {
|
||||
/// One ranked routing option: a SKU, its effort, and whether a ChatGPT Pro account
|
||||
/// upgrades the effort to Sol's `ultra` mode at Max level.
|
||||
@@ -267,84 +269,89 @@ public enum IntelligenceRouter {
|
||||
/// inline where a cell is deliberately *not* the obvious escalation.
|
||||
static let matrix: [PromptPurpose: [IntelligenceLevel: [Candidate]]] = [
|
||||
.planning: [
|
||||
.quick: [.init("claude-sonnet-5", "medium"), .init("gpt-5.6-terra", "medium")],
|
||||
.light: [.init("claude-opus-5", "medium"), .init("gpt-5.6-terra", "high")],
|
||||
.quick: [.init("gpt-5.6-terra", "medium"), .init("claude-sonnet-5", "medium")],
|
||||
.light: [.init("claude-sonnet-5", "medium"), .init("gpt-5.6-terra", "high")],
|
||||
.balanced: [.init("claude-opus-5", "high"), .init("gpt-5.6-sol", "high")],
|
||||
.deep: [.init("claude-fable-5", "high"), .init("gpt-5.6-sol", "xhigh")],
|
||||
// Fable is the long-horizon planning specialist; max effort is reserved for it.
|
||||
.deep: [.init("claude-opus-5", "high"), .init("gpt-5.6-sol", "xhigh")],
|
||||
// The preferred lane here is the long-horizon specialist; max effort is reserved for it.
|
||||
.max: [.init("claude-fable-5", "max"), .init("gpt-5.6-sol", "xhigh", ultraEligible: true)],
|
||||
],
|
||||
.backendImpl: [
|
||||
.quick: [.init("claude-sonnet-5", "low"), .init("gpt-5.6-luna", "medium")],
|
||||
.light: [.init("claude-sonnet-5", "medium"), .init("gpt-5.6-terra", "medium")],
|
||||
// Sol leads the backend lane from Balanced up — it rewards high+ effort on
|
||||
// implementation where Opus 5 would already be past its medium-effort peak.
|
||||
.quick: [.init("gpt-5.6-luna", "low"), .init("claude-sonnet-5", "medium")],
|
||||
.light: [.init("gpt-5.6-terra", "medium"), .init("claude-sonnet-5", "medium")],
|
||||
// Lane preference flips from Balanced up: the preferred model rewards high+ effort on
|
||||
// implementation, where the secondary would already be past its medium-effort peak.
|
||||
.balanced: [.init("gpt-5.6-sol", "high"), .init("claude-opus-5", "medium")],
|
||||
.deep: [.init("gpt-5.6-sol", "xhigh"), .init("claude-fable-5", "high")],
|
||||
.max: [.init("gpt-5.6-sol", "xhigh", ultraEligible: true), .init("claude-fable-5", "high")],
|
||||
.deep: [.init("gpt-5.6-sol", "xhigh"), .init("claude-opus-5", "high")],
|
||||
.max: [.init("gpt-5.6-sol", "xhigh", ultraEligible: true), .init("claude-opus-5", "high")],
|
||||
],
|
||||
.frontendImpl: [
|
||||
.quick: [.init("claude-sonnet-5", "low"), .init("gpt-5.6-luna", "medium")],
|
||||
.light: [.init("claude-sonnet-5", "medium"), .init("gpt-5.6-terra", "medium")],
|
||||
// Opus 5 is the consensus UI pick — at medium, its measured peak for routine work.
|
||||
.balanced: [.init("claude-opus-5", "medium"), .init("gpt-5.6-terra", "high")],
|
||||
.quick: [.init("gpt-5.6-luna", "low"), .init("claude-sonnet-5", "medium")],
|
||||
.light: [.init("gpt-5.6-terra", "medium"), .init("claude-sonnet-5", "medium")],
|
||||
// The one deliberately single-lane cell: routine UI work sits on the Claude lane at
|
||||
// both ranks (Sonnet at its peak, Opus behind it), because neither GPT-lane model
|
||||
// is worth the swap here. A Codex-only account falls back to this row's Light
|
||||
// candidate — see `everyCellCoversBothLanes`, which allowlists exactly this cell.
|
||||
.balanced: [.init("claude-sonnet-5", "medium"), .init("claude-opus-5", "high")],
|
||||
.deep: [.init("claude-opus-5", "high"), .init("gpt-5.6-sol", "high")],
|
||||
.max: [.init("claude-fable-5", "high"), .init("gpt-5.6-sol", "xhigh")],
|
||||
.max: [.init("claude-opus-5", "high"), .init("gpt-5.6-sol", "xhigh")],
|
||||
],
|
||||
.quickFix: [
|
||||
.quick: [.init("gpt-5.6-luna", "low"), .init("claude-sonnet-5", "low")],
|
||||
.light: [.init("claude-sonnet-5", "low"), .init("gpt-5.6-luna", "medium")],
|
||||
.balanced: [.init("claude-sonnet-5", "medium"), .init("gpt-5.6-terra", "medium")],
|
||||
// Deep tops out at Opus·medium (its measured peak) — a "deep" quick fix wants
|
||||
.light: [.init("gpt-5.6-luna", "low"), .init("claude-sonnet-5", "medium")],
|
||||
.balanced: [.init("gpt-5.6-terra", "medium"), .init("claude-sonnet-5", "medium")],
|
||||
// Deep tops out at the preferred model's measured peak effort — a "deep" quick fix wants
|
||||
// care, not scope; and it keeps this row within one cost tier of the heavy rows
|
||||
// a borderline classification could have landed on (the drift invariant).
|
||||
.deep: [.init("claude-opus-5", "medium"), .init("gpt-5.6-terra", "high")],
|
||||
// Even "Max" on a quick fix stops at Opus·medium — more model would only invite
|
||||
// out-of-scope refactors (the measured Opus-past-medium failure mode).
|
||||
.max: [.init("claude-opus-5", "medium"), .init("gpt-5.6-sol", "high")],
|
||||
.deep: [.init("claude-sonnet-5", "medium"), .init("gpt-5.6-terra", "high")],
|
||||
// Even "Max" on a quick fix stops at that same peak — more model would only invite
|
||||
// out-of-scope refactors (the measured past-peak failure mode).
|
||||
.max: [.init("claude-sonnet-5", "medium"), .init("gpt-5.6-terra", "high")],
|
||||
],
|
||||
.refactor: [
|
||||
.quick: [.init("claude-sonnet-5", "low"), .init("gpt-5.6-luna", "medium")],
|
||||
.quick: [.init("gpt-5.6-terra", "low"), .init("claude-sonnet-5", "medium")],
|
||||
.light: [.init("claude-sonnet-5", "medium"), .init("gpt-5.6-terra", "medium")],
|
||||
.balanced: [.init("claude-sonnet-5", "high"), .init("gpt-5.6-terra", "high")],
|
||||
.balanced: [.init("claude-opus-5", "high"), .init("gpt-5.6-sol", "high")],
|
||||
.deep: [.init("claude-opus-5", "medium"), .init("gpt-5.6-sol", "high")],
|
||||
.max: [.init("claude-fable-5", "high"), .init("gpt-5.6-sol", "xhigh")],
|
||||
],
|
||||
.debugging: [
|
||||
.quick: [.init("claude-sonnet-5", "medium"), .init("gpt-5.6-terra", "medium")],
|
||||
.light: [.init("claude-sonnet-5", "high"), .init("gpt-5.6-terra", "high")],
|
||||
.quick: [.init("gpt-5.6-terra", "medium"), .init("claude-sonnet-5", "medium")],
|
||||
.light: [.init("claude-opus-5", "high"), .init("gpt-5.6-sol", "high")],
|
||||
.balanced: [.init("claude-opus-5", "high"), .init("gpt-5.6-sol", "high")],
|
||||
.deep: [.init("claude-fable-5", "high"), .init("gpt-5.6-sol", "xhigh")],
|
||||
// Root-causing gnarly failures is the other lane that genuinely rewards Fable max.
|
||||
.max: [.init("claude-fable-5", "max"), .init("gpt-5.6-sol", "xhigh", ultraEligible: true)],
|
||||
.deep: [.init("claude-opus-5", "high"), .init("gpt-5.6-sol", "xhigh")],
|
||||
// Root-causing gnarly failures is one of the two rows where Opus earns effort past
|
||||
// its routine-coding peak — `max` here, `xhigh` on Review's top cell.
|
||||
.max: [.init("claude-opus-5", "max"), .init("gpt-5.6-sol", "xhigh", ultraEligible: true)],
|
||||
],
|
||||
.review: [
|
||||
.quick: [.init("claude-haiku-4-5", "low"), .init("gpt-5.6-luna", "low")],
|
||||
.light: [.init("claude-sonnet-5", "low"), .init("gpt-5.6-terra", "medium")],
|
||||
.quick: [.init("gpt-5.6-luna", "low"), .init("claude-sonnet-5", "low")],
|
||||
.light: [.init("gpt-5.6-terra", "low"), .init("claude-sonnet-5", "medium")],
|
||||
.balanced: [.init("gpt-5.6-sol", "medium"), .init("claude-opus-5", "high")],
|
||||
.deep: [.init("gpt-5.6-sol", "high"), .init("claude-opus-5", "high")],
|
||||
.max: [.init("gpt-5.6-sol", "high"), .init("claude-opus-5", "xhigh")],
|
||||
],
|
||||
.writing: [
|
||||
.quick: [.init("gpt-5.6-luna", "low"), .init("claude-sonnet-5", "low")],
|
||||
.light: [.init("gpt-5.6-terra", "low"), .init("claude-sonnet-5", "medium")],
|
||||
// Off the bargain tier from Balanced up: prose prompts routinely border on
|
||||
// backend/docs mixes, so the row stays within one cost tier of the implementation
|
||||
// rows a borderline classification could have meant (the drift invariant) — and
|
||||
// serious writing genuinely reads better off a heavier model, held at its
|
||||
// routine-work peak effort rather than pushed higher.
|
||||
.balanced: [.init("claude-opus-5", "medium"), .init("gpt-5.6-terra", "medium")],
|
||||
.deep: [.init("claude-opus-5", "medium"), .init("gpt-5.6-sol", "high")],
|
||||
.max: [.init("claude-opus-5", "medium"), .init("gpt-5.6-sol", "high")],
|
||||
],
|
||||
// The conservative row every failed classification lands on: mid-tier at every stop,
|
||||
// so "we couldn't tell" can never mean the specialist at max or a light model on a hard task.
|
||||
.general: [
|
||||
.quick: [.init("gpt-5.6-luna", "low"), .init("claude-sonnet-5", "medium")],
|
||||
.light: [.init("gpt-5.6-terra", "medium"), .init("claude-sonnet-5", "medium")],
|
||||
.balanced: [.init("claude-sonnet-5", "medium"), .init("gpt-5.6-terra", "high")],
|
||||
.deep: [.init("claude-opus-5", "high"), .init("gpt-5.6-sol", "high")],
|
||||
.max: [.init("claude-opus-5", "high"), .init("gpt-5.6-sol", "xhigh")],
|
||||
],
|
||||
.writing: [
|
||||
.quick: [.init("claude-haiku-4-5", "low"), .init("gpt-5.6-luna", "low")],
|
||||
.light: [.init("claude-sonnet-5", "low"), .init("gpt-5.6-luna", "medium")],
|
||||
// Sonnet/Terra (not the bargain tier) from Balanced up: prose prompts routinely
|
||||
// border on backend/docs mixes, so the row stays within one cost tier of the
|
||||
// implementation rows a borderline classification could have meant (the drift
|
||||
// invariant) — and serious writing genuinely reads better off the mid tier.
|
||||
.balanced: [.init("claude-sonnet-5", "medium"), .init("gpt-5.6-terra", "medium")],
|
||||
.deep: [.init("claude-opus-5", "medium"), .init("gpt-5.6-terra", "high")],
|
||||
.max: [.init("claude-opus-5", "medium"), .init("gpt-5.6-terra", "high")],
|
||||
],
|
||||
// The conservative row every failed classification lands on: mid-tier at every stop,
|
||||
// so "we couldn't tell" can never mean Fable-at-max or Haiku-on-a-hard-task.
|
||||
.general: [
|
||||
.quick: [.init("claude-sonnet-5", "low"), .init("gpt-5.6-luna", "medium")],
|
||||
.light: [.init("claude-sonnet-5", "medium"), .init("gpt-5.6-terra", "medium")],
|
||||
.balanced: [.init("claude-opus-5", "medium"), .init("gpt-5.6-terra", "high")],
|
||||
.deep: [.init("claude-opus-5", "high"), .init("gpt-5.6-sol", "high")],
|
||||
.max: [.init("claude-fable-5", "high"), .init("gpt-5.6-sol", "xhigh")],
|
||||
],
|
||||
]
|
||||
|
||||
/// Every SKU the matrix can produce. Kept derived from the matrix so quota-family
|
||||
|
||||
@@ -6,11 +6,14 @@ import Testing
|
||||
|
||||
/// Locks the Intelligence router's research-derived invariants: every purpose × level cell
|
||||
/// resolves to a real, supported (model, effort) for every connected-provider combination;
|
||||
/// cost never decreases as the slider rises; and the per-model rules the July-2026 research
|
||||
/// established (Opus 5 never above `high`, Fable only at Deep/Max, Haiku only on the light
|
||||
/// review/writing rows, `max` effort reserved for Fable's planning/debugging cells, `ultra`
|
||||
/// only for a Pro account at Max on Sol) hold for the whole matrix — so a future matrix edit
|
||||
/// can't silently reintroduce the misrouting failure modes the slider exists to prevent.
|
||||
/// cost never decreases as the slider rises; and the per-model rules hold for the whole
|
||||
/// matrix (Opus 5 past `high` only on the two documented top cells, Fable only on the two
|
||||
/// long-horizon Max cells, Haiku never off the light review/writing rows, `max` effort only
|
||||
/// at the top of Planning/Debugging, `ultra` only for a Pro account at Max on Sol) — so a
|
||||
/// future matrix edit can't silently reintroduce the misrouting failure modes the slider
|
||||
/// exists to prevent. Where the current matrix takes a deliberate exception to a rule, the
|
||||
/// exception is an explicit allowlist here rather than a loosened bound, so anything *else*
|
||||
/// drifting the same way still fails.
|
||||
@Suite struct IntelligenceRoutingTests {
|
||||
private let allLevels = IntelligenceLevel.allCases
|
||||
private let allPurposes = PromptPurpose.allCases
|
||||
@@ -21,15 +24,48 @@ import Testing
|
||||
|
||||
// MARK: Matrix shape
|
||||
|
||||
/// Cells that deliberately rank two models from the SAME lane. A single-provider user
|
||||
/// still resolves — `rankedCandidates` walks the lower levels of the same purpose — but
|
||||
/// they resolve out of a neighbouring cell rather than this one, so each exception is
|
||||
/// listed explicitly and checked below.
|
||||
private let singleLaneCells: [(purpose: PromptPurpose, level: IntelligenceLevel, lane: BackendID)] = [
|
||||
(.frontendImpl, .balanced, .claudeCode),
|
||||
]
|
||||
|
||||
@Test func everyCellCoversBothLanes() {
|
||||
for purpose in allPurposes {
|
||||
for level in allLevels {
|
||||
let cell = IntelligenceRouter.matrix[purpose]?[level]
|
||||
#expect(cell != nil, "missing cell \(purpose)/\(level)")
|
||||
let backends = Set((cell ?? []).compactMap { BackendID.forModel($0.sku) })
|
||||
guard
|
||||
let exception = singleLaneCells.first(
|
||||
where: { $0.purpose == purpose && $0.level == level })
|
||||
else {
|
||||
#expect(
|
||||
backends.isSuperset(of: [.claudeCode, .codex]),
|
||||
"\(purpose)/\(level) must offer a Claude and a GPT lane")
|
||||
continue
|
||||
}
|
||||
#expect(
|
||||
backends == [exception.lane],
|
||||
"\(purpose)/\(level) is allowlisted as \(exception.lane)-only; it now spans \(backends)")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// The promise the allowlist above must not break: even on a single-lane cell, a user
|
||||
/// connected to only the *other* provider still gets a route on their own lane.
|
||||
@Test func singleLaneCellsStillResolveOnTheMissingLane() {
|
||||
for exception in singleLaneCells {
|
||||
for lane in [BackendID.claudeCode, .codex] where lane != exception.lane {
|
||||
let resolution = IntelligenceRouter.route(
|
||||
purpose: exception.purpose, level: exception.level,
|
||||
connected: [lane], fallback: fallback)
|
||||
#expect(
|
||||
BackendID.forModel(resolution.model) == lane,
|
||||
"\(exception.purpose)/\(exception.level) left \(lane) without a route")
|
||||
#expect(resolution.isAvailable)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -95,30 +131,40 @@ import Testing
|
||||
|
||||
// MARK: Research-derived per-model rules
|
||||
|
||||
@Test func opusNeverExceedsHighEffort() {
|
||||
/// Opus stays at or below `high` — its measured peak band for routine work — everywhere
|
||||
/// except the top of the two rows that genuinely reward more thinking. Those two cells
|
||||
/// are named, so a third one can't appear without this test failing.
|
||||
@Test func opusExceedsHighOnlyOnTheDocumentedTopCells() {
|
||||
let allowed: [PromptPurpose: String] = [.debugging: "max", .review: "xhigh"]
|
||||
for (purpose, row) in IntelligenceRouter.matrix {
|
||||
for (level, cell) in row {
|
||||
for candidate in cell where candidate.sku.hasPrefix("claude-opus") {
|
||||
if ["low", "medium", "high"].contains(candidate.effort) { continue }
|
||||
#expect(
|
||||
["low", "medium", "high"].contains(candidate.effort),
|
||||
level == .max && allowed[purpose] == candidate.effort,
|
||||
"\(purpose)/\(level): Opus past its measured peak (\(candidate.effort))")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test func fableOnlyAtDeepAndMax() {
|
||||
/// Fable is the long-horizon specialist and now appears on exactly two cells — the Max
|
||||
/// stop of the two rows whose whole point at that level is unbounded reasoning depth.
|
||||
@Test func fableOnlyOnThePlanningAndRefactorMaxCells() {
|
||||
let allowed: Set<PromptPurpose> = [.planning, .refactor]
|
||||
for (purpose, row) in IntelligenceRouter.matrix {
|
||||
for (level, cell) in row {
|
||||
for candidate in cell where candidate.sku.hasPrefix("claude-fable") {
|
||||
#expect(
|
||||
level >= .deep,
|
||||
"\(purpose)/\(level): Fable on a low tier overpays for routine work")
|
||||
level == .max && allowed.contains(purpose),
|
||||
"\(purpose)/\(level): Fable off its two specialist cells overpays")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Haiku is currently unused by the matrix; this stays as a forward guard so a future
|
||||
/// edit can't put it anywhere it couldn't carry the work.
|
||||
@Test func haikuOnlyOnLightReviewAndWritingCells() {
|
||||
for (purpose, row) in IntelligenceRouter.matrix {
|
||||
for (level, cell) in row {
|
||||
@@ -131,13 +177,19 @@ import Testing
|
||||
}
|
||||
}
|
||||
|
||||
@Test func maxEffortReservedForFablePlanningAndDebugging() {
|
||||
/// `max` effort is the top of the ladder and stays at the top of the two rows that earn
|
||||
/// it: Planning (on the specialist) and Debugging (on Opus).
|
||||
@Test func maxEffortReservedForTheTopOfPlanningAndDebugging() {
|
||||
let allowed: [PromptPurpose: String] = [
|
||||
.planning: "claude-fable-5", .debugging: "claude-opus-5",
|
||||
]
|
||||
for (purpose, row) in IntelligenceRouter.matrix {
|
||||
for (level, cell) in row {
|
||||
for candidate in cell where candidate.effort == "max" {
|
||||
#expect(candidate.sku.hasPrefix("claude-fable"))
|
||||
#expect([PromptPurpose.planning, .debugging].contains(purpose))
|
||||
#expect(level == .max, "\(purpose)/\(level)")
|
||||
#expect(
|
||||
allowed[purpose] == candidate.sku,
|
||||
"\(purpose)/\(level): \(candidate.sku) can't carry max effort")
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -285,7 +337,7 @@ import Testing
|
||||
#expect(limits.contains(model: "claude-fable-5"))
|
||||
#expect(!limits.contains(model: "claude-opus-5"))
|
||||
let resolution = IntelligenceRouter.route(
|
||||
purpose: .planning, level: .deep,
|
||||
purpose: .planning, level: .max,
|
||||
connected: [.claudeCode, .codex], limits: limits,
|
||||
fallback: fallback)
|
||||
#expect(resolution.model == "gpt-5.6-sol")
|
||||
@@ -343,12 +395,12 @@ import Testing
|
||||
}
|
||||
|
||||
@Test func specialistPerformanceWinsDespiteLowProviderCapacity() {
|
||||
// Deep planning is a deliberately decisive Fable cell. Even a large quota imbalance
|
||||
// Max planning is a deliberately decisive Fable cell. Even a large quota imbalance
|
||||
// must not turn the capacity signal into a quality downgrade.
|
||||
let limits = IntelligenceRouter.Limits(
|
||||
providerUtilization: [.claudeCode: 95, .codex: 0])
|
||||
let resolution = IntelligenceRouter.route(
|
||||
purpose: .planning, level: .deep,
|
||||
purpose: .planning, level: .max,
|
||||
connected: [.claudeCode, .codex], limits: limits,
|
||||
fallback: fallback)
|
||||
|
||||
@@ -401,22 +453,24 @@ import Testing
|
||||
|
||||
// MARK: Mixed-intent blend
|
||||
|
||||
/// Light is the stop where Writing and Debugging still sit a tier apart on the Claude
|
||||
/// lane (Balanced puts both on the same tier, so it exercises the no-raise branch).
|
||||
@Test func mixedIntentRaisesPrimaryToSecondaryCostFloor() {
|
||||
let primary = IntelligenceRouter.route(
|
||||
purpose: .writing, level: .balanced, connected: [.claudeCode],
|
||||
purpose: .writing, level: .light, connected: [.claudeCode],
|
||||
fallback: fallback)
|
||||
let secondary = IntelligenceRouter.route(
|
||||
purpose: .debugging, level: .balanced, connected: [.claudeCode],
|
||||
purpose: .debugging, level: .light, connected: [.claudeCode],
|
||||
fallback: fallback)
|
||||
let mixed = IntelligenceRouter.routeMixed(
|
||||
primary: .writing, secondary: .debugging, level: .balanced,
|
||||
primary: .writing, secondary: .debugging, level: .light,
|
||||
connected: [.claudeCode], fallback: fallback)
|
||||
|
||||
#expect(IntelligenceRouter.costTier(of: primary.model) == 1)
|
||||
#expect(IntelligenceRouter.costTier(of: secondary.model) == 2)
|
||||
#expect(IntelligenceRouter.costTier(of: mixed.model) == 2)
|
||||
#expect(mixed.purpose == .writing)
|
||||
#expect(mixed.level == .balanced)
|
||||
#expect(mixed.level == .light)
|
||||
#expect(mixed.reason.contains("\(PromptPurpose.writing.displayName) + \(PromptPurpose.debugging.displayName)"))
|
||||
#expect(mixed.reason.contains("secondary intent's cost tier"))
|
||||
}
|
||||
@@ -454,10 +508,22 @@ import Testing
|
||||
|
||||
// MARK: Misroute damage bound
|
||||
|
||||
/// Purpose pairs allowed to drift two tiers instead of one. `quickFix` is now capped at
|
||||
/// the mid tier at *every* level — deliberately, so a "quick fix" can never turn into a
|
||||
/// scope-expanding refactor — while Debugging and Refactor climb to the top of their
|
||||
/// rows. That makes a quickFix↔heavy misclassification a two-tier move by construction.
|
||||
/// It is an allowlist rather than a loosened bound so every other pair still gets the
|
||||
/// one-tier guarantee.
|
||||
private let twoTierDriftPairs: [Set<PromptPurpose>] = [
|
||||
[.quickFix, .debugging],
|
||||
[.quickFix, .refactor],
|
||||
]
|
||||
|
||||
/// The binding accuracy invariant: even when the heuristic classifier mislabels a
|
||||
/// fixture, routing its *predicted* purpose lands within one cost tier of routing the
|
||||
/// *true* purpose, at every level and in every lane — so a misclassification can never
|
||||
/// cause the 12× overpay (or a hard task on a toy model) the feature exists to prevent.
|
||||
/// *true* purpose (two for the documented pairs above), at every level and in every
|
||||
/// lane — so a misclassification can never cause the 12× overpay (or a hard task on a
|
||||
/// toy model) the feature exists to prevent.
|
||||
@Test func fixtureMisroutesStayWithinOneCostTier() throws {
|
||||
for fixture in try PurposeFixtures.load() {
|
||||
let predicted = HeuristicPurposeClassifier.classify(fixture.prompt).purpose
|
||||
@@ -475,11 +541,12 @@ import Testing
|
||||
let drift = abs(
|
||||
IntelligenceRouter.costTier(of: routedPredicted.model)
|
||||
- IntelligenceRouter.costTier(of: routedTruth.model))
|
||||
let bound = twoTierDriftPairs.contains([predicted, truth]) ? 2 : 1
|
||||
#expect(
|
||||
drift <= 1,
|
||||
drift <= bound,
|
||||
"""
|
||||
"\(fixture.prompt)": predicted \(predicted) vs true \(truth) drifts \
|
||||
\(drift) tiers at \(level) with \(connected)
|
||||
\(drift) tiers at \(level) with \(connected) (bound \(bound))
|
||||
""")
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user