feat(pi-workflow): add human-readable run labels

This commit is contained in:
jb
2026-08-25 21:22:08 -04:00
parent a1d0b1415c
commit 54a7921227
17 changed files with 385 additions and 52 deletions
+24 -11
View File
@@ -21,6 +21,7 @@ import {
executeHostDecision,
type ContextTransitionRuntime,
} from "./context-transition.ts";
import { deriveRunLabels } from "./run-label.ts";
import {
readVerifiedPlanArtifact,
readVerifiedReportArtifact,
@@ -88,6 +89,7 @@ export class FocusedWorkflowDashboard implements Component {
private readonly runs: WorkflowRun[];
private readonly theme: DashboardTheme;
private readonly done: (result: string | null) => void;
private readonly labels: string[];
private selected = 0;
private detail = false;
@@ -100,6 +102,7 @@ export class FocusedWorkflowDashboard implements Component {
this.runs = runs;
this.theme = theme;
this.done = done;
this.labels = deriveRunLabels(runs);
const initial = initialRunId
? runs.findIndex((run) => run.runId === initialRunId)
: -1;
@@ -150,7 +153,7 @@ export class FocusedWorkflowDashboard implements Component {
for (const [visibleIndex, run] of visibleRuns.entries()) {
const index = windowStart + visibleIndex;
const prefix = index === this.selected ? "> " : " ";
const name = run.featureName ?? run.request.slice(0, 24);
const name = this.labels[index]!;
const status = this.theme.fg(
statusColor(run.status),
run.status.toUpperCase().padEnd(9),
@@ -171,7 +174,7 @@ export class FocusedWorkflowDashboard implements Component {
const run = this.runs[this.selected]!;
const gate = selectedGate(run);
lines.push("", this.theme.fg("dim", "─".repeat(Math.max(1, width))));
lines.push(this.theme.bold(run.featureName ?? run.request));
lines.push(this.theme.bold(this.labels[this.selected]!));
if (run.workspace.branch) lines.push(this.theme.fg("muted", run.workspace.branch));
lines.push("", `${this.theme.bold("Progress")} ${stageLine(run, this.theme)}`);
if (gate) {
@@ -202,10 +205,10 @@ export class FocusedWorkflowDashboard implements Component {
invalidate() {}
}
function runSummary(run: WorkflowRun) {
function runSummary(run: WorkflowRun, label: string) {
const gate = selectedGate(run);
return [
`${run.runId} · ${run.status} · revision ${run.revision}`,
`${label} · ${run.status} · revision ${run.revision}`,
`Stage: ${activeStage(run)}`,
...(gate ? [`Gate: ${gate.kind} (${gate.id})`] : []),
...(run.workspace.branch ? [`Branch: ${run.workspace.branch}`] : []),
@@ -443,6 +446,7 @@ async function actionSheet(
runtime: ContextTransitionRuntime,
ctx: ExtensionCommandContext,
run: WorkflowRun,
label: string,
) {
const actions = [
"Inspect run",
@@ -461,8 +465,8 @@ async function actionSheet(
: []),
...(run.workspace.root ? ["Show worktree path"] : []),
];
const action = await ctx.ui.select(run.featureName ?? run.runId, actions);
if (action === "Inspect run") ctx.ui.notify(runSummary(run), "info");
const action = await ctx.ui.select(label, actions);
if (action === "Inspect run") ctx.ui.notify(runSummary(run, label), "info");
else if (action === "Open plan") {
const plan = await readVerifiedPlanArtifact(run.planArtifact!);
await ctx.ui.editor("Workflow plan", plan.slice(0, 32 * 1024));
@@ -495,11 +499,11 @@ async function actionSheet(
decision.strategy === "smart-compact"
) {
ctx.ui.notify(
`Workflow ${run.runId} is ready for smart_compact. Ask the assistant to invoke it and report the Gate-bound outcome through pi_workflow.`,
`Workflow ${label} is ready for smart_compact. Ask the assistant to invoke it and report the Gate-bound outcome through pi_workflow.`,
"info",
);
} else {
ctx.ui.notify(`Workflow ${run.runId}: ${result.status}`, "info");
ctx.ui.notify(`Workflow ${label}: ${result.status}`, "info");
}
}
}
@@ -520,12 +524,20 @@ export async function openWorkflowDashboard(
const runs = (await runtime.store.listRuns())
.filter((run) => !sessionId || run.parentSessionIds.includes(sessionId))
.sort((left, right) => right.revision - left.revision);
if (initialRunId && !runs.some((run) => run.runId === initialRunId)) {
ctx.ui.notify(
`Workflow Run ${initialRunId} was not found for this session.`,
"error",
);
return {};
}
const labels = deriveRunLabels(runs);
if (!ctx.hasUI || ctx.mode !== "tui") {
const visibleRuns = runs.slice(0, 20);
const omitted = runs.length - visibleRuns.length;
ctx.ui.notify(
runs.length > 0
? `${visibleRuns.map(runSummary).join("\n\n")}${
? `${visibleRuns.map((run, index) => runSummary(run, labels[index]!)).join("\n\n")}${
omitted > 0 ? `\n\n[${omitted} older runs omitted]` : ""
}`
: "No Workflow Runs.",
@@ -552,6 +564,7 @@ export async function openWorkflowDashboard(
},
);
if (!selected) return {};
const run = runs.find((candidate) => candidate.runId === selected);
return run ? actionSheet(pi, runtime, ctx, run) : {};
const index = runs.findIndex((candidate) => candidate.runId === selected);
const run = runs[index];
return run ? actionSheet(pi, runtime, ctx, run, labels[index]!) : {};
}