From 6c31577cf2f69be7e67d41199afecdf2f29e5b6e Mon Sep 17 00:00:00 2001 From: saravanakumardb1 Date: Mon, 1 Jun 2026 02:30:22 -0700 Subject: [PATCH] feat(fleet): per-job engine picker (devin/claude/codex/copilot, default devin) Add an optional concrete `engine` to a job (overrides engineClass; resolved by the runner's resolve_engine where an explicit engine wins). All additive + optional, so existing engineless jobs keep falling back to the factory default. - types: FLEET_ENGINES enum; engine on SubmitJob/FleetJobDoc/UpdateDraft. - coordinator: store engine on create/supersede/updateDraft; run.engine at claim prefers job.engine, then engineClass, then 'unknown'. - tracker-web: Engine dropdown on the New-Job form (default devin) + editable on draft/queued jobs; shown in the detail config grid. Generated with [Devin](https://cli.devin.ai/docs) Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com> --- .../app/dashboard/fleet/jobs/[id]/page.tsx | 20 +++++++++++++ .../src/app/dashboard/fleet/jobs/page.tsx | 29 +++++++++++++++++-- .../tracker-web/src/lib/fleet-client.ts | 9 ++++++ .../src/modules/fleet/coordinator.test.ts | 10 +++++++ .../src/modules/fleet/coordinator.ts | 7 ++++- .../src/modules/fleet/types.ts | 9 ++++++ 6 files changed, 81 insertions(+), 3 deletions(-) diff --git a/dashboards/tracker-web/src/app/dashboard/fleet/jobs/[id]/page.tsx b/dashboards/tracker-web/src/app/dashboard/fleet/jobs/[id]/page.tsx index 5e4a0e16..6204bce2 100644 --- a/dashboards/tracker-web/src/app/dashboard/fleet/jobs/[id]/page.tsx +++ b/dashboards/tracker-web/src/app/dashboard/fleet/jobs/[id]/page.tsx @@ -21,7 +21,9 @@ import { requestReview, submitReview, subscribeJobEvents, + FLEET_ENGINES, type OperatorAction, + type FleetEngine, type FleetJob, type FleetRun, type FleetEvent, @@ -638,6 +640,7 @@ function PromptCard({ job, onChanged }: { job: FleetJob; onChanged: () => void | const editable = job.stage === 'draft' || job.stage === 'queued' || job.stage === 'blocked'; const [editing, setEditing] = useState(false); const [draftBody, setDraftBody] = useState(job.bodyMd ?? ''); + const [editEngine, setEditEngine] = useState(job.engine ?? 'devin'); const [editRepo, setEditRepo] = useState(job.repo ?? ''); const [editBranch, setEditBranch] = useState(job.baseBranch ?? ''); const [editVerify, setEditVerify] = useState(job.verify ?? ''); @@ -647,6 +650,7 @@ function PromptCard({ job, onChanged }: { job: FleetJob; onChanged: () => void | const beginEdit = () => { setDraftBody(job.bodyMd ?? ''); + setEditEngine(job.engine ?? 'devin'); setEditRepo(job.repo ?? ''); setEditBranch(job.baseBranch ?? ''); setEditVerify(job.verify ?? ''); @@ -666,6 +670,7 @@ function PromptCard({ job, onChanged }: { job: FleetJob; onChanged: () => void | const repo = editRepo.trim(); await updateDraft(job.id, { bodyMd: draftBody.trim(), + engine: editEngine, ...(repo ? { repo, @@ -698,6 +703,7 @@ function PromptCard({ job, onChanged }: { job: FleetJob; onChanged: () => void | }; const cfg: Array<[string, string | undefined]> = [ + ['Engine', job.engine], ['Repo', job.repo], ['Base branch', job.repo ? (job.baseBranch ?? 'main') : undefined], ['Verify', job.verify], @@ -754,6 +760,20 @@ function PromptCard({ job, onChanged }: { job: FleetJob; onChanged: () => void | aria-label="Edit job prompt" />
+
+
+ + +