From 928edad0af27855316a28d62ebbf5589ab052473 Mon Sep 17 00:00:00 2001 From: saravanakumardb1 Date: Mon, 1 Jun 2026 02:19:38 -0700 Subject: [PATCH] feat(fleet): surface engine + agent session, editable config, timeline filter MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Backend: insights now carry engine + sessionId/sessionUrl; releaseLease promotes the reported engine onto the run (was created with the abstract engineClass, usually 'unknown'). tracker-web job detail: - Runs: show the concrete engine (insights.engine, falls back off 'unknown') and the agent session (Devin session id with a `devin --resume ` hint, or a link when a sessionUrl is present). - PromptCard: edit repo/baseBranch/verify/autoMerge (not just the prompt) while draft/queued/blocked. - Timeline: filter by event type (default collapses heartbeat runs). - Show a "no PR — needs verify / not PR mode" hint when parked in review. 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 | 203 +++++++++++++++--- .../tracker-web/src/lib/fleet-client.ts | 6 + .../src/modules/fleet/coordinator.ts | 3 + .../src/modules/fleet/types.ts | 7 + 4 files changed, 184 insertions(+), 35 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 5ca3a04c..5e4a0e16 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 @@ -45,6 +45,7 @@ export default function FleetJobDetailPage() { const [shipping, setShipping] = useState(false); const [acting, setActing] = useState(null); const [reviewing, setReviewing] = useState(false); + const [eventFilter, setEventFilter] = useState(''); const [streamMode, setStreamMode] = useState<'connecting' | 'live' | 'polling'>('connecting'); const refresh = useCallback(async () => { @@ -263,34 +264,52 @@ export default function FleetJobDetailPage() { {/* Pull request (surfaced from whichever run opened it) */} {(() => { const prRun = runs.find(r => r.prUrl); - if (!prRun?.prUrl) return null; - return ( -
- Pull Request - - {prRun.prUrl} ↗ - - {prRun.prState && ( - Pull Request + - {prRun.prState} + {prRun.prUrl} ↗ + + {prRun.prState && ( + + {prRun.prState} + + )} +
+ ); + } + // Parked in review with no PR — explain why + what to do. + if (job.repo && (job.stage === 'review' || job.stage === 'testing')) { + return ( +
+ No pull request yet.{' '} + + This run didn’t open a PR — it ran without a verify step, or the + factory wasn’t in PR mode. Set a verify command and requeue to produce one, or + Ship to promote as-is. - )} -
- ); + + ); + } + return null; })()} {/* Review gate (multi-reviewer human gate) */} @@ -337,9 +356,41 @@ export default function FleetJobDetailPage() { Polling )} + {events.length > 0 && ( + + )} {events.length === 0 ? (

No events recorded.

+ ) : eventFilter ? ( +
    + {events + .filter(e => e.type === eventFilter) + .map(e => ( +
  • + + {new Date(e.at).toLocaleTimeString()} + + {e.type} + {e.actor && by {e.actor}} +
  • + ))} +
) : (
    {groupTimelineEvents(events).map(g => @@ -432,7 +483,31 @@ export default function FleetJobDetailPage() { return ( #{r.attempt} - {r.engine} + + {ins.engine ?? (r.engine && r.engine !== 'unknown' ? r.engine : '—')} + {ins.sessionId && ( +
    + {ins.sessionUrl ? ( + + open session ↗ + + ) : ( + + session: {ins.sessionId} + + )} +
    + )} + {r.factoryId ?? '—'} {r.result ?? 'running'} @@ -563,9 +638,23 @@ 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 [editRepo, setEditRepo] = useState(job.repo ?? ''); + const [editBranch, setEditBranch] = useState(job.baseBranch ?? ''); + const [editVerify, setEditVerify] = useState(job.verify ?? ''); + const [editAutoMerge, setEditAutoMerge] = useState(!!job.autoMerge); const [busy, setBusy] = useState(null); const [err, setErr] = useState(null); + const beginEdit = () => { + setDraftBody(job.bodyMd ?? ''); + setEditRepo(job.repo ?? ''); + setEditBranch(job.baseBranch ?? ''); + setEditVerify(job.verify ?? ''); + setEditAutoMerge(!!job.autoMerge); + setErr(null); + setEditing(true); + }; + const save = async () => { if (!draftBody.trim()) { setErr('Prompt cannot be empty.'); @@ -574,7 +663,18 @@ function PromptCard({ job, onChanged }: { job: FleetJob; onChanged: () => void | setBusy('save'); setErr(null); try { - await updateDraft(job.id, { bodyMd: draftBody.trim() }); + const repo = editRepo.trim(); + await updateDraft(job.id, { + bodyMd: draftBody.trim(), + ...(repo + ? { + repo, + baseBranch: editBranch.trim() || 'main', + autoMerge: editAutoMerge, + ...(editVerify.trim() ? { verify: editVerify.trim() } : {}), + } + : {}), + }); setEditing(false); await onChanged(); } catch (e) { @@ -632,14 +732,7 @@ function PromptCard({ job, onChanged }: { job: FleetJob; onChanged: () => void |
    {editable && !editing && ( - )} @@ -660,6 +753,46 @@ function PromptCard({ job, onChanged }: { job: FleetJob; onChanged: () => void | className="w-full rounded-lg border bg-background p-3 text-sm font-mono" aria-label="Edit job prompt" /> +
    + + + + +