feat(tracker-web): show job prompt + PR/target config on the detail page
The fleet job detail page never rendered the prompt (bodyMd) or the repo/ verify/auto-merge/capabilities/deps config. Add a Prompt card (verbatim body, scrollable) + a target/config grid, with a read-only badge once the job leaves queued/draft (a factory may already be acting on it). Expose verify/autoMerge/ deps on the FleetJob client type. Generated with [Devin](https://cli.devin.ai/docs) Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
This commit is contained in:
parent
78c4e47460
commit
cb4f7a7606
@ -255,6 +255,9 @@ export default function FleetJobDetailPage() {
|
|||||||
<MetaCard label="Attempts" value={String(job.attempts)} />
|
<MetaCard label="Attempts" value={String(job.attempts)} />
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
|
{/* Prompt (the job body) + PR/target config */}
|
||||||
|
<PromptCard job={job} />
|
||||||
|
|
||||||
{/* Review gate (multi-reviewer human gate) */}
|
{/* Review gate (multi-reviewer human gate) */}
|
||||||
{job.stage === 'review' && (
|
{job.stage === 'review' && (
|
||||||
<ReviewGateCard
|
<ReviewGateCard
|
||||||
@ -497,6 +500,62 @@ function MetaCard({ label, value }: { label: string; value: string }) {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The job's prompt (verbatim `bodyMd`) + its PR/target config. The prompt is only
|
||||||
|
* mutable before a factory picks the job up; once it leaves `queued`/`draft` it is
|
||||||
|
* locked (a worker may already be acting on it) so it renders read-only.
|
||||||
|
*/
|
||||||
|
function PromptCard({ job }: { job: FleetJob }) {
|
||||||
|
const locked = job.stage !== 'queued' && job.stage !== 'draft';
|
||||||
|
const cfg: Array<[string, string | undefined]> = [
|
||||||
|
['Repo', job.repo],
|
||||||
|
['Base branch', job.repo ? (job.baseBranch ?? 'main') : undefined],
|
||||||
|
['Verify', job.verify],
|
||||||
|
['Auto-merge', job.repo ? (job.autoMerge ? 'yes' : 'no') : undefined],
|
||||||
|
['Capabilities', job.capabilities?.length ? job.capabilities.join(', ') : undefined],
|
||||||
|
['Deps', job.deps?.length ? job.deps.join(', ') : undefined],
|
||||||
|
['Idempotency key', job.idempotencyKey],
|
||||||
|
];
|
||||||
|
const shown = cfg.filter(([, v]) => v != null && v !== '');
|
||||||
|
return (
|
||||||
|
<section className="space-y-3" aria-label="Prompt and target">
|
||||||
|
<div className="flex items-center gap-2">
|
||||||
|
<h2 className="text-lg font-semibold">Prompt</h2>
|
||||||
|
<span
|
||||||
|
className={`rounded-full px-2 py-0.5 text-xs font-medium ${
|
||||||
|
locked
|
||||||
|
? 'bg-muted text-muted-foreground'
|
||||||
|
: 'bg-blue-500/15 text-blue-700 dark:text-blue-300'
|
||||||
|
}`}
|
||||||
|
title={
|
||||||
|
locked
|
||||||
|
? 'The job has been picked up — its prompt is locked.'
|
||||||
|
: 'Still queued — not yet picked up by a factory.'
|
||||||
|
}
|
||||||
|
>
|
||||||
|
{locked ? 'read-only — picked up' : 'queued — not yet picked up'}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<pre
|
||||||
|
className="max-h-96 overflow-auto whitespace-pre-wrap rounded-lg border bg-muted/30 p-3 text-sm font-mono"
|
||||||
|
aria-label="Job prompt body"
|
||||||
|
>
|
||||||
|
{job.bodyMd?.trim() ? job.bodyMd : 'No prompt body.'}
|
||||||
|
</pre>
|
||||||
|
{shown.length > 0 && (
|
||||||
|
<div className="grid gap-3 sm:grid-cols-2 lg:grid-cols-3">
|
||||||
|
{shown.map(([k, v]) => (
|
||||||
|
<div key={k} className="rounded-lg border p-3">
|
||||||
|
<p className="text-xs text-muted-foreground">{k}</p>
|
||||||
|
<p className="text-sm font-medium break-words font-mono">{v}</p>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</section>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
/** Compact stat chip for the per-job cost/token/time totals. */
|
/** Compact stat chip for the per-job cost/token/time totals. */
|
||||||
function Stat({ label, value }: { label: string; value: string }) {
|
function Stat({ label, value }: { label: string; value: string }) {
|
||||||
return (
|
return (
|
||||||
|
|||||||
@ -27,6 +27,12 @@ export interface FleetJob {
|
|||||||
reviewDecisions?: ReviewDecision[];
|
reviewDecisions?: ReviewDecision[];
|
||||||
repo?: string;
|
repo?: string;
|
||||||
baseBranch?: string;
|
baseBranch?: string;
|
||||||
|
/** PR mode: verify command run in the checkout before the PR opens. */
|
||||||
|
verify?: string;
|
||||||
|
/** PR mode: squash-merge the PR automatically when verify passes. */
|
||||||
|
autoMerge?: boolean;
|
||||||
|
/** Job dependencies (idempotency keys this job is gated on). */
|
||||||
|
deps?: string[];
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface FleetFactory {
|
export interface FleetFactory {
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user