Merge nucleic/dapper-breezy-heron-eqsl into dev

This commit is contained in:
2026-07-30 16:13:23 -07:00
parent 45d57cac69
commit 8b8917e4d7
7 changed files with 389 additions and 26 deletions
+76 -2
View File
@@ -189,8 +189,70 @@ enum ModelCatalog {
static let fallbackModel = "claude-opus-5"
static let fallbackEffort = EffortLadder.fallbackEffort
static let fallbackSupervisorModel = "claude-fable-5"
static let fallbackOrchestraWorkerModel = "claude-sonnet-5"
// MARK: - Orchestra supervisor & worker selection
/// One "Supervisor model" choice: a model paired with the effort it conducts at. Orchestra used
/// to pin every supervisor to `xhigh`; the two levels make that selectable without adding a
/// second control, since the only supervisor efforts worth offering are the top two.
///
/// The list is deliberately short. A supervisor plans, delegates, and synthesizes long-horizon
/// reasoning work so it is offered only the two models that lead that lane (Fable 5 on the
/// Claude side, GPT-5.6 Sol on the GPT side), one per provider, rather than the whole catalog.
struct SupervisorChoice: Hashable, Identifiable {
let model: String
/// The API effort this choice conducts at `xhigh` for Deep, `max` for Max.
let effort: String
/// The level's product name, shown in parentheses after the model.
let levelName: String
var id: String { "\(model)|\(effort)" }
var displayName: String { "\(ModelCatalog.displayName(model)) (\(levelName))" }
}
/// The supervisor choices, Claude lane first (matching `models` order).
static let supervisorChoices: [SupervisorChoice] = [
SupervisorChoice(model: "claude-fable-5", effort: "xhigh", levelName: "Deep"),
SupervisorChoice(model: "claude-fable-5", effort: "max", levelName: "Max"),
SupervisorChoice(model: "gpt-5.6-sol", effort: "xhigh", levelName: "Deep"),
SupervisorChoice(model: "gpt-5.6-sol", effort: "max", levelName: "Max"),
]
/// Fable 5 at `xhigh` the model Orchestra already defaulted to, at the effort it already ran,
/// so an untouched install behaves exactly as before.
static let fallbackSupervisorChoice = supervisorChoices[0]
static let fallbackSupervisorModel = fallbackSupervisorChoice.model
static let fallbackSupervisorEffort = fallbackSupervisorChoice.effort
/// The choice matching a stored (model, effort) pair. Falls back to the default for anything
/// unrecognized including a model stored before the list was shortened (an install that had
/// picked Opus 5 as its supervisor lands on Fable 5 (Deep) rather than on an empty picker).
static func supervisorChoice(model: String?, effort: String?) -> SupervisorChoice {
if let exact = supervisorChoices.first(where: { $0.model == model && $0.effort == effort }) {
return exact
}
// A known model whose stored effort is no longer offered keeps the model, at Deep.
if let sameModel = supervisorChoices.first(where: { $0.model == model }) { return sameModel }
return fallbackSupervisorChoice
}
/// The **Intelligent** worker option: classify each worker's task and let the Intelligence
/// router pick the model for it, rather than pinning one SKU for the whole fan-out. Stored as
/// `OrchestrationMode.intelligentWorkerModel`, which is not a real SKU and is resolved to a
/// concrete model at spawn (`AppStore.orchestraWorkerSelection`).
static let intelligentWorkerModel = OrchestrationMode.intelligentWorkerModel
/// Named for the routing it does, not for "the app picks" "Auto" is already taken by the
/// single reasoning mode the ACP agents expose (see `effortDisplayName`) and by Auto-approve.
static let intelligentWorkerDisplayName = "Intelligent"
static let intelligentWorkerBlurb =
"Classifies each worker's task and routes it to the model that suits it, so a quick fix "
+ "runs cheap and a hard subtask runs strong."
/// Workers default to Intelligent: a fan-out's subtasks vary far more in difficulty than one
/// chat's turns do, so per-task routing is a better default than any single pinned SKU.
static let fallbackOrchestraWorkerModel = intelligentWorkerModel
/// How many Orchestra workers the supervisor may run at once by default. The fan-out
/// multiplies token usage, so this bounds a single supervisor's concurrent workers rather
/// than letting an over-eager fan-out spawn an unlimited wave. `0` means "unlimited".
@@ -268,6 +330,9 @@ enum ModelCatalog {
static let defaultModelKey = "nucleic.defaultModel"
static let defaultEffortKey = "nucleic.defaultEffort"
static let defaultSupervisorModelKey = "nucleic.defaultSupervisorModel"
/// The supervisor's effort the "(Deep)" / "(Max)" half of the supervisor choice. Kept as its
/// own key rather than folded into the model string so both halves stay independently readable.
static let defaultSupervisorEffortKey = "nucleic.defaultSupervisorEffort"
static let defaultOrchestraWorkerModelKey = "nucleic.defaultOrchestraWorkerModel"
static let defaultOrchestraMaxWorkersKey = "nucleic.defaultOrchestraMaxConcurrentWorkers"
static let defaultAutoKey = "nucleic.defaultAuto"
@@ -291,6 +356,15 @@ enum ModelCatalog {
static var storedDefaultSupervisorModel: String {
UserDefaults.standard.string(forKey: defaultSupervisorModelKey) ?? fallbackSupervisorModel
}
static var storedDefaultSupervisorEffort: String {
UserDefaults.standard.string(forKey: defaultSupervisorEffortKey) ?? fallbackSupervisorEffort
}
/// The stored supervisor choice, normalized through ``supervisorChoice(model:effort:)`` so a
/// value from before the list was shortened resolves to a real entry.
static var storedSupervisorChoice: SupervisorChoice {
supervisorChoice(
model: storedDefaultSupervisorModel, effort: storedDefaultSupervisorEffort)
}
static var storedDefaultOrchestraWorkerModel: String {
UserDefaults.standard.string(forKey: defaultOrchestraWorkerModelKey) ?? fallbackOrchestraWorkerModel
}
+5 -1
View File
@@ -179,9 +179,13 @@ struct NucleicApp: App {
store.chatDockBounceHook = { DockBounce.shared.setPending($0) }
store.defaultModel = ModelCatalog.storedDefaultModel
store.defaultEffort = ModelCatalog.storedDefaultEffort
store.defaultSupervisorModel = ModelCatalog.storedDefaultSupervisorModel
// Normalized through `storedSupervisorChoice` so a supervisor stored before the list was
// shortened (or with an effort no longer offered) launches on a real entry.
store.defaultSupervisorModel = ModelCatalog.storedSupervisorChoice.model
store.defaultSupervisorEffort = ModelCatalog.storedSupervisorChoice.effort
store.defaultOrchestraWorkerModel = ModelCatalog.storedDefaultOrchestraWorkerModel
store.defaultOrchestraMaxConcurrentWorkers = ModelCatalog.storedDefaultOrchestraMaxWorkers
store.defaultIntelligenceLevel = ModelCatalog.storedDefaultIntelligenceLevel
store.defaultAuto = ModelCatalog.storedDefaultAuto
store.defaultAutoShip = ModelCatalog.storedDefaultAutoShip
store.summarizeToolCalls = TranscriptDisplay.storedSummarizeToolCalls
+41 -9
View File
@@ -2392,6 +2392,7 @@ private struct AgentsSettingsTab: View {
@AppStorage(ModelCatalog.defaultModelKey) private var defaultModel = ModelCatalog.fallbackModel
@AppStorage(ModelCatalog.defaultEffortKey) private var defaultEffort = ModelCatalog.fallbackEffort
@AppStorage(ModelCatalog.defaultSupervisorModelKey) private var defaultSupervisorModel = ModelCatalog.fallbackSupervisorModel
@AppStorage(ModelCatalog.defaultSupervisorEffortKey) private var defaultSupervisorEffort = ModelCatalog.fallbackSupervisorEffort
@AppStorage(ModelCatalog.defaultOrchestraWorkerModelKey) private var defaultOrchestraWorkerModel = ModelCatalog.fallbackOrchestraWorkerModel
@AppStorage(ModelCatalog.defaultOrchestraMaxWorkersKey) private var defaultOrchestraMaxWorkers = ModelCatalog.fallbackOrchestraMaxWorkers
@AppStorage(ModelCatalog.defaultAutoKey) private var defaultAuto = false
@@ -2491,20 +2492,31 @@ private struct AgentsSettingsTab: View {
}
Section("Orchestra") {
Picker("Supervisor model", selection: $defaultSupervisorModel) {
ForEach(ModelCatalog.models, id: \.self) { sku in
let badge = ModelCatalog.contextBadge(for: sku).map { Text(" \($0)").foregroundColor(.secondary) } ?? Text("")
Text("\(ModelCatalog.displayName(sku))\(badge)")
.tag(sku)
// Model + effort in one control: the supervisor conducts (plan, delegate,
// synthesize), so only the two long-horizon leaders are offered, each at the top
// two efforts. The two halves persist to their own keys see `supervisorBinding`.
Picker("Supervisor model", selection: supervisorBinding) {
ForEach(ModelCatalog.supervisorChoices) { choice in
Text(choice.displayName).tag(choice)
}
}
.help("The model that plans the work and drives the workers. Deep runs at xhigh effort; Max runs at max.")
Picker("Worker model", selection: $defaultOrchestraWorkerModel) {
ForEach(ModelCatalog.models, id: \.self) { sku in
let badge = ModelCatalog.contextBadge(for: sku).map { Text(" \($0)").foregroundColor(.secondary) } ?? Text("")
Text("\(ModelCatalog.displayName(sku))\(badge)")
.tag(sku)
// Intelligent first, above the fixed SKUs it chooses among.
Text(ModelCatalog.intelligentWorkerDisplayName)
.tag(ModelCatalog.intelligentWorkerModel)
Divider()
// Providers set apart by a separator, as in the composer's model menu.
ForEach(Array(ModelCatalog.modelGroups.enumerated()), id: \.offset) { index, group in
if index > 0 { Divider() }
ForEach(group, id: \.self) { sku in
let badge = ModelCatalog.contextBadge(for: sku).map { Text(" \($0)").foregroundColor(.secondary) } ?? Text("")
Text("\(ModelCatalog.displayName(sku))\(badge)")
.tag(sku)
}
}
}
.help(ModelCatalog.intelligentWorkerBlurb)
Picker("Max concurrent workers", selection: $defaultOrchestraMaxWorkers) {
ForEach(ModelCatalog.orchestraMaxWorkerChoices, id: \.self) { count in
Text(count <= 0 ? "Unlimited" : "\(count)").tag(count)
@@ -2597,6 +2609,7 @@ private struct AgentsSettingsTab: View {
}
.onChange(of: defaultEffort) { _, _ in pushDefaults() }
.onChange(of: defaultSupervisorModel) { _, _ in pushDefaults() }
.onChange(of: defaultSupervisorEffort) { _, _ in pushDefaults() }
.onChange(of: defaultOrchestraWorkerModel) { _, _ in pushDefaults() }
.onChange(of: defaultOrchestraMaxWorkers) { _, _ in pushDefaults() }
.onChange(of: defaultAuto) { _, _ in pushDefaults() }
@@ -2656,12 +2669,31 @@ private struct AgentsSettingsTab: View {
? "*" : ExternalAgentIntegrationSettings.encodedModels(models)
}
/// The supervisor picker's selection, projected over the two stored halves (model + effort) so
/// the control is one row while each half keeps its own well-named key. Reads through
/// `supervisorChoice(model:effort:)`, so a value stored before the list was shortened resolves
/// to a real entry instead of leaving the picker blank.
private var supervisorBinding: Binding<ModelCatalog.SupervisorChoice> {
Binding(
get: {
ModelCatalog.supervisorChoice(
model: defaultSupervisorModel, effort: defaultSupervisorEffort)
},
set: { choice in
defaultSupervisorModel = choice.model
defaultSupervisorEffort = choice.effort
})
}
private func pushDefaults() {
store.defaultModel = defaultModel
store.defaultEffort = defaultEffort
store.defaultSupervisorModel = defaultSupervisorModel
store.defaultSupervisorEffort = defaultSupervisorEffort
store.defaultOrchestraWorkerModel = defaultOrchestraWorkerModel
store.defaultOrchestraMaxConcurrentWorkers = defaultOrchestraMaxWorkers
store.defaultIntelligenceLevel =
IntelligenceLevel(rawValue: defaultIntelligenceLevel) ?? .fallback
store.defaultAuto = defaultAuto
}
+69 -5
View File
@@ -774,7 +774,20 @@ public final class AppStore: ConflictArbiter {
public var defaultModel: String?
public var defaultEffort: String?
public var defaultSupervisorModel: String?
/// The effort an Orchestra supervisor runs at the "(Deep)" / "(Max)" half of the supervisor
/// choice in Settings. Stored on the session as a qualified sentinel
/// (`OrchestrationMode.qualifiedEffortSentinel`) so the mode stays Orchestra while the
/// underlying API effort varies. `nil` keeps the historical fixed `xhigh`.
public var defaultSupervisorEffort: String?
/// The worker model for Orchestra fan-out a SKU, or
/// `OrchestrationMode.intelligentWorkerModel` for **Intelligent**, which classifies each
/// worker's task and routes it (see `orchestraWorkerSelection`).
public var defaultOrchestraWorkerModel: String?
/// The Intelligence level routed workers are resolved at (set by the app from Settings). Level
/// is the ambition/budget knob the classifier's purpose is combined with Orchestra's own
/// thoroughness lives in the supervisor and the fan-out, so workers route at the user's
/// ordinary default rather than being forced to the top of the ladder.
public var defaultIntelligenceLevel: IntelligenceLevel = .fallback
/// Cap on how many Orchestra workers one supervisor may run concurrently (set by the app from
/// Settings). `<= 0` means unlimited. Enforced by `spawnOrchestraSubagent`, which parks a
/// worker's `nucleic_subagent` call until a slot frees, so an over-eager fan-out queues rather
@@ -7010,8 +7023,13 @@ public final class AppStore: ConflictArbiter {
// supervisor instead. Gated to Control projects, where Orchestra actually runs
// (`orchestraActive`); the supervisor default falls back to the ordinary model if unset.
let resolvedModel: String?
// The supervisor's *effort* is configurable alongside its model ("Fable 5 (Max)"), so an
// Orchestra chat is stored with the qualified sentinel rather than the bare one. `xhigh`
// still stores bare, so the default selection produces the same value it always did.
var resolvedEffort = effort
if OrchestrationMode.isOrchestra(effort ?? defaultEffort), project.isNucleicControlled {
resolvedModel = defaultSupervisorModel ?? model ?? defaultModel
resolvedEffort = OrchestrationMode.qualifiedEffortSentinel(defaultSupervisorEffort)
} else {
resolvedModel = model ?? defaultModel
}
@@ -7094,7 +7112,7 @@ public final class AppStore: ConflictArbiter {
parentSessionID: nvrsionBranch != nil ? nil : parentSession?.id,
spawnedBySessionID: spawnedBy,
rootSpawnedBySessionID: spawnedByRoot,
model: resolvedModel, effort: effort ?? defaultEffort,
model: resolvedModel, effort: resolvedEffort ?? defaultEffort,
routedPurpose: routing?.purpose.rawValue,
routedLevel: routing?.level.rawValue,
routedReason: routing?.reason,
@@ -7290,6 +7308,42 @@ public final class AppStore: ConflictArbiter {
return created
}
/// The model + effort one worker should run at, and the routing provenance when the Intelligence
/// router chose them.
///
/// With a SKU pinned in Settings, every worker runs it. With **Intelligent**, the worker's own
/// task is classified (`HeuristicPurposeClassifier` deterministic and sub-millisecond, so it's
/// safe on this synchronous spawn path, unlike the model-backed layers a composer can await)
/// and `IntelligenceRouter` picks the model and effort that suit
/// *that* subtask: a typo fix lands on a cheap model, a backend feature on a strong one. That is
/// the whole point of the fan-out's cost story the expensive model supervises, and each worker
/// costs what its own task deserves.
///
/// `fallbackEffort` is what a pinned-SKU worker runs at (Orchestra's `xhigh` for a supervised
/// worker, the parent's effort for a blocking subagent); a routed worker uses the router's effort
/// instead. Always returns a concrete SKU the Intelligent sentinel must never reach a backend.
func orchestraWorkerSelection(
task: String, prompt: String, parent: Session, fallbackEffort: String?
) -> (model: String?, effort: String?, routing: RoutingNote?) {
guard OrchestrationMode.isIntelligentWorkerModel(defaultOrchestraWorkerModel) else {
return (defaultOrchestraWorkerModel ?? parent.model, fallbackEffort, nil)
}
// The short label plus the full instructions: the label carries the supervisor's own summary
// of the subtask, which is often the clearest statement of what the work *is*.
let verdict = HeuristicPurposeClassifier.classify([task, prompt].joined(separator: "\n"))
let resolution = IntelligenceRouter.route(
purpose: verdict.purpose,
level: defaultIntelligenceLevel,
connected: connectedProviders,
limits: intelligenceRoutingLimits,
codexPro: isCodexPro,
fallback: (
parent.model ?? defaultModel ?? OrchestrationMode.intelligentWorkerFallbackModel,
fallbackEffort ?? EffortLadder.fallbackEffort
))
return (resolution.model, resolution.effort, RoutingNote(resolution))
}
/// Spawn one Orchestra worker as a first-class Nucleic session **non-blocking**. It creates the
/// worker session (so the supervisor gets a `worker_id` to track immediately), registers it with
/// the supervisor's coordinator, and hands the actual run to a detached task; it does NOT wait for
@@ -7308,7 +7362,10 @@ public final class AppStore: ConflictArbiter {
guard !prompt.isEmpty else {
return .denied(message: "Worker prompt is required.")
}
let workerModel = defaultOrchestraWorkerModel ?? parent.model
let selection = orchestraWorkerSelection(
task: request.task, prompt: prompt, parent: parent,
fallbackEffort: OrchestrationMode.resolvedOrchestraEffort)
let workerModel = selection.model
let title = Self.orchestraWorkerTitle(for: request.task)
let coordinator = orchestraCoordinator(for: parent.id)
let workerID: SessionID
@@ -7318,7 +7375,9 @@ public final class AppStore: ConflictArbiter {
// immediately with a real `worker_id`.
workerID = try await createSession(
in: project, title: title, prompt: prompt,
model: workerModel, effort: OrchestrationMode.resolvedOrchestraEffort,
model: workerModel,
effort: selection.effort ?? OrchestrationMode.resolvedOrchestraEffort,
routing: selection.routing,
useWorktree: !project.nvrsionActive, auto: true, autoShip: false,
spawnedBy: parent.id, startRun: false)
} catch {
@@ -7467,13 +7526,18 @@ public final class AppStore: ConflictArbiter {
guard !prompt.isEmpty else {
return .denied(message: "Subagent prompt is required.")
}
let workerModel = defaultOrchestraWorkerModel ?? parent.model
// Same worker-model resolution as the Orchestra spawn, so an Intelligent selection routes a
// blocking subagent too and, critically, so the sentinel is never passed through as a SKU.
let selection = orchestraWorkerSelection(
task: request.task, prompt: prompt, parent: parent, fallbackEffort: parent.effort)
let workerModel = selection.model
let title = Self.orchestraWorkerTitle(for: request.task)
let workerID: SessionID
do {
workerID = try await createSession(
in: project, title: title, prompt: prompt,
model: workerModel, effort: parent.effort,
model: workerModel, effort: selection.effort ?? parent.effort,
routing: selection.routing,
useWorktree: !project.nvrsionActive, auto: true, autoShip: false,
spawnedBy: parent.id, startRun: false)
} catch {
+70 -9
View File
@@ -26,15 +26,50 @@ public enum OrchestrationMode {
/// name keeps resolving to the mode rather than being sent to a backend verbatim.
public static let legacyEffortSentinel = "ultracode"
/// The real effort level orchestra runs at. The xhigh ceiling is the most thorough the
/// API documents; orchestra adds the fan-out consent on top, not a hidden higher level.
/// The effort orchestra runs at when the sentinel carries no qualifier the level every
/// Orchestra session used before the supervisor's effort became selectable, so an unqualified
/// (or legacy) stored selection keeps behaving exactly as it did.
public static let resolvedOrchestraEffort = "xhigh"
/// Separates the sentinel from the supervisor's chosen effort: `"orchestra:max"`. The mode is
/// still Orchestra only the underlying API effort differs so the qualifier rides on the
/// sentinel rather than becoming a second `Session` column: every existing reader keeps
/// working (`isOrchestra` is true, the UI still shows "Orchestra"), and the choice persists
/// and transfers with the session for free. `xhigh` is stored bare, so the common case
/// produces exactly the string it always did.
public static let effortQualifierSeparator: Character = ":"
/// The value to store in `Session.effort` for a supervisor running at `effort`. Falls back to
/// the bare sentinel for a nil/unknown effort or for `xhigh` (the unqualified default).
public static func qualifiedEffortSentinel(_ effort: String?) -> String {
guard let normalized = effort?.trimmingCharacters(in: .whitespacesAndNewlines).lowercased(),
normalized != resolvedOrchestraEffort,
EffortLadder.efforts.contains(normalized) || normalized == EffortLadder.proEffort
else { return effortSentinel }
return "\(effortSentinel)\(effortQualifierSeparator)\(normalized)"
}
/// The sentinel and its optional effort qualifier, or nil when `effort` isn't an Orchestra
/// selection at all. Case-insensitive and whitespace-tolerant, like the raw comparison it
/// replaced.
private static func parse(_ effort: String?) -> (base: String, qualifier: String?)? {
guard let normalized = effort?.trimmingCharacters(in: .whitespacesAndNewlines).lowercased(),
!normalized.isEmpty
else { return nil }
let parts = normalized.split(
separator: effortQualifierSeparator, maxSplits: 1, omittingEmptySubsequences: false)
let base = String(parts[0])
guard base == effortSentinel || base == legacyEffortSentinel else { return nil }
let qualifier = parts.count > 1 ? String(parts[1]) : nil
return (base, (qualifier?.isEmpty ?? true) ? nil : qualifier)
}
/// Whether `effort` selects orchestra (case-insensitive, whitespace-tolerant). Accepts the
/// legacy "ultracode" token too, so the rename can't strand an already-stored selection.
/// legacy "ultracode" token too, so the rename can't strand an already-stored selection, and
/// the qualified form (`"orchestra:max"`), so choosing a supervisor effort never reads as
/// "not Orchestra" which would drop the mode's tools and prompt.
public static func isOrchestra(_ effort: String?) -> Bool {
let normalized = effort?.trimmingCharacters(in: .whitespacesAndNewlines).lowercased()
return normalized == effortSentinel || normalized == legacyEffortSentinel
parse(effort) != nil
}
/// Which side of an Orchestra session a run is: the user-started chat is the `supervisor` (it
@@ -47,11 +82,37 @@ public enum OrchestrationMode {
case worker
}
/// The effort actually handed to a backend: `orchestra` resolves to `xhigh`; everything
/// else passes through untouched. One-way the resolved value never flows back into
/// `Session.effort`, so the UI keeps showing "Orchestra".
/// The effort actually handed to a backend: an Orchestra selection resolves to its qualifier
/// (`"orchestra:max"` `max`) or to `xhigh` when unqualified; everything else passes through
/// untouched. One-way the resolved value never flows back into `Session.effort`, so the UI
/// keeps showing "Orchestra". An unrecognized qualifier resolves to `xhigh` rather than being
/// forwarded, so a hand-edited or future-versioned store value can't reach a backend as a
/// bogus `--effort`.
public static func resolvedEffort(_ effort: String?) -> String? {
isOrchestra(effort) ? resolvedOrchestraEffort : effort
guard let parsed = parse(effort) else { return effort }
guard let qualifier = parsed.qualifier,
EffortLadder.efforts.contains(qualifier) || qualifier == EffortLadder.proEffort
else { return resolvedOrchestraEffort }
return qualifier
}
// MARK: - Worker model selection
/// The sentinel stored in the worker-model setting for **Intelligent**: instead of pinning one
/// SKU for every worker, classify each worker's task and let `IntelligenceRouter` pick the model
/// (and effort) that suits it. Deliberately not a real SKU `BackendID.forModel` doesn't know
/// it, so it must be resolved to a concrete model before any spawn (see
/// `AppStore.orchestraWorkerSelection`); it must never reach a backend as `--model`.
public static let intelligentWorkerModel = "nucleic-intelligent"
/// The model a routed worker falls back to when the router can't resolve one no provider
/// probed yet at launch, or every candidate is quota-blocked. Matches the fixed worker default
/// Orchestra shipped with, so the fallback is the behavior users already knew.
public static let intelligentWorkerFallbackModel = "claude-sonnet-5"
/// Whether the worker-model setting selects Intelligent (classifier-chosen) rather than a SKU.
public static func isIntelligentWorkerModel(_ model: String?) -> Bool {
model?.trimmingCharacters(in: .whitespacesAndNewlines).lowercased() == intelligentWorkerModel
}
/// The standing prompt appended for the **supervisor** while Orchestra is on. It frames the
@@ -252,6 +252,81 @@ struct OrchestraWorkerSlotTests {
#expect(store.orchestraWorkersRunning == 0) // the slot came back
}
// MARK: - Worker model selection (Intelligent)
/// With **Intelligent** selected, each worker is routed from its own task rather than pinned to
/// one SKU and, load-bearing, the sentinel is resolved to a real model. Passing it through as
/// `--model` would hand the backend a SKU no provider knows.
@Test func intelligentWorkerModelRoutesPerTaskAndNeverLeaksTheSentinel() async throws {
let repo = try await GitTestRepo()
defer { repo.cleanup() }
let store = makeStore(repo: repo) { _ in ScriptedBackend { _, _ in } }
store.defaultOrchestraWorkerModel = OrchestrationMode.intelligentWorkerModel
// Both lanes connected, so routing can pick either provider's candidate.
let project = try #require(
await store.addProject(name: "demo", rootPath: repo.root, defaultBranch: "main"))
let parentID = try await store.createSession(in: project, title: "supervisor", prompt: "")
let parent = try #require(await store.liveSnapshot(parentID)).session
// A one-line typo fix and a substantial backend task must not resolve to the same thing on a
// per-task router that difference IS the feature.
let quick = store.orchestraWorkerSelection(
task: "fix typo", prompt: "Fix the typo in the README heading.",
parent: parent, fallbackEffort: OrchestrationMode.resolvedOrchestraEffort)
let heavy = store.orchestraWorkerSelection(
task: "build the API",
prompt: "Implement the server endpoint, its data model, and the algorithm behind it.",
parent: parent, fallbackEffort: OrchestrationMode.resolvedOrchestraEffort)
for selection in [quick, heavy] {
let model = try #require(selection.model)
#expect(model != OrchestrationMode.intelligentWorkerModel)
#expect(BackendID.forModel(model) != nil) // a real, runnable SKU
#expect(selection.routing != nil) // provenance recorded for "why this model"
}
#expect(quick.routing?.purpose == .quickFix)
#expect(heavy.routing?.purpose == .backendImpl)
// A pinned SKU is passed straight through, with the caller's effort and no routing note.
store.defaultOrchestraWorkerModel = "claude-haiku-4-5"
let pinned = store.orchestraWorkerSelection(
task: "fix typo", prompt: "Fix the typo.", parent: parent, fallbackEffort: "xhigh")
#expect(pinned.model == "claude-haiku-4-5")
#expect(pinned.effort == "xhigh")
#expect(pinned.routing == nil)
}
/// A spawned worker actually runs the routed model the selection reaches the created session,
/// not just the return value.
@Test func spawnedWorkerSessionUsesTheRoutedModel() async throws {
let repo = try await GitTestRepo()
defer { repo.cleanup() }
let store = makeStore(repo: repo) { _ in ScriptedBackend { _, _ in } }
store.defaultOrchestraWorkerModel = OrchestrationMode.intelligentWorkerModel
let project = try #require(
await store.addProject(name: "demo", rootPath: repo.root, defaultBranch: "main"))
let parentID = try await store.createSession(in: project, title: "supervisor", prompt: "")
let parent = try #require(await store.liveSnapshot(parentID)).session
let result = await store.spawnOrchestraSubagent(
OrchestraSubagentRequest(task: "fix typo", prompt: "Fix the typo in the heading."),
parent: parent, project: project)
guard case .spawned(let workerID, _, let model) = result else {
Issue.record("expected .spawned, got \(result)")
return
}
let reported = try #require(model)
#expect(reported != OrchestrationMode.intelligentWorkerModel)
let worker = try #require(await store.liveSnapshot(workerID)).session
#expect(worker.model == reported)
// Routing provenance rides onto the session, so its info panel can say why this model.
#expect(worker.routedPurpose == PromptPurpose.quickFix.rawValue)
#expect(worker.routedReason?.isEmpty == false)
await store.teardownOrchestra(supervisor: parentID)
}
// MARK: - Plan delegation
/// Submitting a plan spawns a real worker session for EVERY task in EVERY batch at once batches
@@ -27,6 +27,59 @@ struct OrchestrationModeTests {
#expect(OrchestrationMode.resolvedEffort("Orchestra") == "xhigh")
}
// MARK: - Supervisor effort qualifier
/// The supervisor's effort rides on the sentinel ("orchestra:max"). The load-bearing part is that
/// it still reads as Orchestra a qualified value that failed `isOrchestra` would silently drop
/// the mode's tools and standing prompt.
@Test func qualifiedSentinelStaysOrchestraAndCarriesItsEffort() {
#expect(OrchestrationMode.isOrchestra("orchestra:max"))
#expect(OrchestrationMode.isOrchestra(" Orchestra:MAX ")) // trimmed, case-insensitive
#expect(OrchestrationMode.resolvedEffort("orchestra:max") == "max")
#expect(OrchestrationMode.resolvedEffort("orchestra:xhigh") == "xhigh")
// The legacy sentinel takes a qualifier too, so an old session that gets re-saved is fine.
#expect(OrchestrationMode.isOrchestra("ultracode:max"))
#expect(OrchestrationMode.resolvedEffort("ultracode:max") == "max")
}
/// `xhigh` what Orchestra always ran at stores bare, so the default selection produces
/// exactly the value it always did and nothing downstream sees a new shape.
@Test func defaultSupervisorEffortStoresTheBareSentinel() {
#expect(OrchestrationMode.qualifiedEffortSentinel("xhigh") == "orchestra")
#expect(OrchestrationMode.qualifiedEffortSentinel(nil) == "orchestra")
#expect(OrchestrationMode.qualifiedEffortSentinel("max") == "orchestra:max")
// Round-trips: what we store resolves back to what was chosen.
for effort in ["low", "medium", "high", "xhigh", "max"] {
let stored = OrchestrationMode.qualifiedEffortSentinel(effort)
#expect(OrchestrationMode.isOrchestra(stored))
#expect(OrchestrationMode.resolvedEffort(stored) == effort)
}
}
/// A qualifier that isn't a real effort resolves to xhigh rather than being forwarded a
/// hand-edited or future-versioned store value must never reach a backend as a bogus `--effort`.
@Test func unknownQualifierFallsBackToXhigh() {
#expect(OrchestrationMode.resolvedEffort("orchestra:bogus") == "xhigh")
#expect(OrchestrationMode.resolvedEffort("orchestra:") == "xhigh")
#expect(OrchestrationMode.qualifiedEffortSentinel("bogus") == "orchestra")
// And a non-Orchestra effort that merely contains a colon is left alone.
#expect(OrchestrationMode.isOrchestra("high:max") == false)
#expect(OrchestrationMode.resolvedEffort("high:max") == "high:max")
}
// MARK: - Intelligent worker sentinel
/// The Intelligent sentinel is not a SKU: it must be recognizable, and it must not be mistaken
/// for a model (it is resolved to a real one before any spawn).
@Test func intelligentWorkerSentinelIsRecognizedAndIsNotASKU() {
#expect(OrchestrationMode.isIntelligentWorkerModel(OrchestrationMode.intelligentWorkerModel))
#expect(OrchestrationMode.isIntelligentWorkerModel(" Nucleic-Intelligent "))
#expect(OrchestrationMode.isIntelligentWorkerModel("claude-sonnet-5") == false)
#expect(OrchestrationMode.isIntelligentWorkerModel(nil) == false)
// No provider claims it which is exactly why it must never be passed through as `--model`.
#expect(BackendID.forModel(OrchestrationMode.intelligentWorkerModel) == nil)
}
@Test func passesThroughRealEffortLevels() {
for level in ["low", "medium", "high", "xhigh", "max"] {
#expect(OrchestrationMode.resolvedEffort(level) == level)