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 01957990..1f0c9268 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 @@ -404,15 +404,28 @@ export default function FleetJobDetailPage() { {r.prUrl ? ( - - PR ↗ - + + + PR ↗ + + {r.prState && ( + + {r.prState} + + )} + ) : ( '—' )} diff --git a/dashboards/tracker-web/src/lib/fleet-client.ts b/dashboards/tracker-web/src/lib/fleet-client.ts index dd009f16..5e041b57 100644 --- a/dashboards/tracker-web/src/lib/fleet-client.ts +++ b/dashboards/tracker-web/src/lib/fleet-client.ts @@ -65,6 +65,7 @@ export interface FleetRun { insights: FleetRunInsights; prUrl?: string; branch?: string; + prState?: 'open' | 'merged'; } export interface FleetEvent { diff --git a/services/platform-service/src/modules/fleet/coordinator.ts b/services/platform-service/src/modules/fleet/coordinator.ts index 46765df5..6bc0d692 100644 --- a/services/platform-service/src/modules/fleet/coordinator.ts +++ b/services/platform-service/src/modules/fleet/coordinator.ts @@ -895,6 +895,7 @@ export async function releaseLease( result?: FleetRunDoc['result']; prUrl?: string; branch?: string; + prState?: FleetRunDoc['prState']; } ): Promise> { const job = await repo.getJob(jobId, productId); @@ -906,13 +907,14 @@ export async function releaseLease( if (!res.ok) return { ok: false, reason: 'conflict' }; if (stage) await repo.revUpdateJob(jobId, productId, job.rev, { stage }); // Record the factory's reported metrics + outcome + PR deliverable on the run. - if (report?.insights || report?.result || report?.prUrl || report?.branch) { + if (report?.insights || report?.result || report?.prUrl || report?.branch || report?.prState) { const runId = `${jobId}:run:${job.attempts}`; await repo.updateRun(runId, jobId, { ...(report.insights ? { insights: report.insights } : {}), ...(report.result ? { result: report.result } : {}), ...(report.prUrl ? { prUrl: report.prUrl } : {}), ...(report.branch ? { branch: report.branch } : {}), + ...(report.prState ? { prState: report.prState } : {}), endedAt: new Date().toISOString(), }); } diff --git a/services/platform-service/src/modules/fleet/routes.ts b/services/platform-service/src/modules/fleet/routes.ts index a9576afd..c26fe1f7 100644 --- a/services/platform-service/src/modules/fleet/routes.ts +++ b/services/platform-service/src/modules/fleet/routes.ts @@ -263,6 +263,7 @@ export async function fleetRoutes(app: FastifyInstance) { result: parsed.data.result, prUrl: parsed.data.prUrl, branch: parsed.data.branch, + prState: parsed.data.prState, }); if (!res.ok) { if (res.reason === 'not_found') throw new NotFoundError('Job or lease not found'); diff --git a/services/platform-service/src/modules/fleet/types.ts b/services/platform-service/src/modules/fleet/types.ts index f84add31..62332dba 100644 --- a/services/platform-service/src/modules/fleet/types.ts +++ b/services/platform-service/src/modules/fleet/types.ts @@ -227,6 +227,7 @@ export const FleetRunDocSchema = z.object({ /** PR deliverable produced by this run (§PR mode). */ prUrl: z.string().optional(), branch: z.string().optional(), + prState: z.enum(['open', 'merged']).optional(), }); export type FleetRunDoc = z.infer; @@ -432,9 +433,10 @@ export const ReleaseLeaseSchema = z.object({ // releases the lease at the end of a work unit. Recorded on the current run. insights: InsightsSchema.optional(), result: z.enum(RUN_RESULTS).optional(), - // PR deliverable (§PR mode): the pull request the run opened + its branch. + // PR deliverable (§PR mode): the pull request the run opened + its branch + state. prUrl: z.string().optional(), branch: z.string().optional(), + prState: z.enum(['open', 'merged']).optional(), }); export type ReleaseLeaseInput = z.infer;