diff --git a/apps/x/apps/renderer/src/components/track-modal.tsx b/apps/x/apps/renderer/src/components/track-modal.tsx
index 8e261977..a4c0b512 100644
--- a/apps/x/apps/renderer/src/components/track-modal.tsx
+++ b/apps/x/apps/renderer/src/components/track-modal.tsx
@@ -156,6 +156,8 @@ export function TrackModal() {
const lastRunAt = track?.lastRunAt ?? ''
const lastRunId = track?.lastRunId ?? ''
const lastRunSummary = track?.lastRunSummary ?? ''
+ const model = track?.model ?? ''
+ const provider = track?.provider ?? ''
const scheduleSummary = useMemo(() => summarizeSchedule(schedule), [schedule])
const triggerType: 'scheduled' | 'event' | 'manual' =
schedule ? 'scheduled' : eventMatchCriteria ? 'event' : 'manual'
@@ -393,6 +395,12 @@ export function TrackModal() {
Track ID{trackId}
File{detail.filePath}
Status{active ? 'Active' : 'Paused'}
+ {model && (<>
+ Model{model}
+ >)}
+ {provider && (<>
+ Provider{provider}
+ >)}
{lastRunAt && (<>
Last run{formatDateTime(lastRunAt)}
>)}
diff --git a/apps/x/packages/core/src/application/assistant/skills/tracks/skill.ts b/apps/x/packages/core/src/application/assistant/skills/tracks/skill.ts
index ff345acf..17521806 100644
--- a/apps/x/packages/core/src/application/assistant/skills/tracks/skill.ts
+++ b/apps/x/packages/core/src/application/assistant/skills/tracks/skill.ts
@@ -87,6 +87,23 @@ ${schemaYaml}
**Runtime-managed fields — never write these yourself:** ` + "`" + `lastRunAt` + "`" + `, ` + "`" + `lastRunId` + "`" + `, ` + "`" + `lastRunSummary` + "`" + `.
+## Do Not Set ` + "`" + `model` + "`" + ` or ` + "`" + `provider` + "`" + ` (almost always)
+
+The schema includes optional ` + "`" + `model` + "`" + ` and ` + "`" + `provider` + "`" + ` fields. **Omit them.** A user-configurable global default already picks the right model and provider for tracks; setting per-track values bypasses that and is almost always wrong.
+
+The only time these belong on a track:
+
+- The user **explicitly** named a model or provider for *this specific track* in their request ("use Claude Opus for this one", "force this track onto OpenAI"). Quote the user's wording back when confirming.
+
+Things that are **not** reasons to set these:
+
+- "Tracks should be fast" / "I want a small model" — that's a global preference, not a per-track one. Leave it; the global default exists.
+- "This track is complex" — write a clearer instruction; don't reach for a different model.
+- "Just to be safe" / "in case it matters" — this is the antipattern. Leave them out.
+- The user changed their main chat model — that has nothing to do with tracks. Leave them out.
+
+When in doubt: omit both fields. Never volunteer them. Never include them in a starter template you suggest. If you find yourself adding them as a sensible default, stop — you're wrong.
+
## Choosing a trackId
- Kebab-case, short, descriptive: ` + "`" + `chicago-time` + "`" + `, ` + "`" + `sfo-weather` + "`" + `, ` + "`" + `hn-top5` + "`" + `, ` + "`" + `btc-usd` + "`" + `.
diff --git a/apps/x/packages/core/src/knowledge/track/runner.ts b/apps/x/packages/core/src/knowledge/track/runner.ts
index 35f7e7ac..1eec3da1 100644
--- a/apps/x/packages/core/src/knowledge/track/runner.ts
+++ b/apps/x/packages/core/src/knowledge/track/runner.ts
@@ -102,8 +102,15 @@ export async function triggerTrackUpdate(
const contentBefore = track.content;
- // Emit start event — runId is set after agent run is created
- const agentRun = await createRun({ agentId: 'track-run', model: await getTrackBlockModel() });
+ // Per-track model/provider overrides win when set; otherwise fall back
+ // to the configured trackBlockModel default and the run-creation
+ // provider default (signed-in: rowboat; BYOK: active provider).
+ const model = track.track.model ?? await getTrackBlockModel();
+ const agentRun = await createRun({
+ agentId: 'track-run',
+ model,
+ ...(track.track.provider ? { provider: track.track.provider } : {}),
+ });
// Set lastRunAt and lastRunId immediately (before agent executes) so
// the scheduler's next poll won't re-trigger this track.
diff --git a/apps/x/packages/shared/src/track-block.ts b/apps/x/packages/shared/src/track-block.ts
index c9e738b7..6d9ce3af 100644
--- a/apps/x/packages/shared/src/track-block.ts
+++ b/apps/x/packages/shared/src/track-block.ts
@@ -25,6 +25,8 @@ export const TrackBlockSchema = z.object({
eventMatchCriteria: z.string().optional().describe('When set, this track participates in event-based triggering. Describe what kinds of events should consider this track for an update (e.g. "Emails about Q3 planning"). Omit to disable event triggers — the track will only run on schedule or manually.'),
active: z.boolean().default(true).describe('Set false to pause without deleting'),
schedule: TrackScheduleSchema.optional(),
+ model: z.string().optional().describe('ADVANCED — leave unset. Per-track LLM model override (e.g. "anthropic/claude-sonnet-4.6"). Only set when the user explicitly asked for a specific model for THIS track. The global default already picks a tuned model for tracks; overriding usually makes things worse, not better.'),
+ provider: z.string().optional().describe('ADVANCED — leave unset. Per-track provider name override (e.g. "openai", "anthropic"). Only set when the user explicitly asked for a specific provider for THIS track. Almost always omitted; the global default flows through correctly.'),
lastRunAt: z.string().optional().describe('Runtime-managed — never write this yourself'),
lastRunId: z.string().optional().describe('Runtime-managed — never write this yourself'),
lastRunSummary: z.string().optional().describe('Runtime-managed — never write this yourself'),