Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
45 changes: 45 additions & 0 deletions src/workflow.ts
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,12 @@ async function processIssue(
startedAt: new Date().toISOString(),
updatedAt: new Date().toISOString(),
} satisfies RunState);
issueLogger.info(
buildIssueJobLogFields(runState, runState.stage, {
resumed: existing !== null,
}),
"Taking issue job",
);

try {
await executeIssue(config, linear, runState);
Expand Down Expand Up @@ -179,6 +185,30 @@ export async function sleep(ms: number): Promise<void> {
await new Promise((resolve) => setTimeout(resolve, ms));
}

export interface IssueJobLogFields {
projectId: string;
issueKey: string;
issueId: string;
issueTitle: string;
stage: string;
resumed?: true;
}

export function buildIssueJobLogFields(
state: RunState,
stage: string,
options?: { resumed?: boolean },
): IssueJobLogFields {
return {
projectId: state.projectId,
issueKey: state.issue.key,
issueId: state.issue.id,
issueTitle: state.issue.title,
stage,
...(options?.resumed ? { resumed: true as const } : {}),
};
}

async function executeIssue(
config: ResolvedProjectConfig,
linear: LinearClient,
Expand All @@ -196,6 +226,7 @@ async function executeIssue(
}

if (state.stage === "planning") {
logger.info(buildIssueJobLogFields(state, "planning"), "Planning issue");
const prompt = await buildPlanPrompt(config.skills.plan, state.issue);
const result = await runPlanSession(config, prompt);
state.codexSessionId = result.sessionId ?? state.codexSessionId;
Expand All @@ -207,12 +238,17 @@ async function executeIssue(
state.issue.id,
buildPlanComment(state.issue.key, state.planSummary),
);
logger.info(buildIssueJobLogFields(state, "planning"), "Plan completed");
}

if (state.stage === "implementing") {
if (!state.codexSessionId) {
throw new Error("Missing codex session id for implement step");
}
logger.info(
buildIssueJobLogFields(state, "implementing"),
"Implementing issue",
);
const prompt = await buildImplementPrompt(
config.skills.implement,
state.issue,
Expand Down Expand Up @@ -243,6 +279,10 @@ async function executeIssue(
state.issue.id,
`Implementation completed. Draft PR: ${state.pullRequest.url ?? "(created)"}`,
);
logger.info(
buildIssueJobLogFields(state, "implementing"),
"Implementation completed",
);
}

if (state.stage === "pr_created") {
Expand All @@ -253,6 +293,7 @@ async function executeIssue(
}

if (state.stage === "reviewing" || state.stage === "testing") {
logger.info(buildIssueJobLogFields(state, "testing"), "Testing issue");
await linear.markStage(state.issue.id, "testing");
await linear.applyStageLabel(state.issue.id, "testing");
Object.assign(state, transitionStage(state, "testing"));
Expand Down Expand Up @@ -319,6 +360,10 @@ async function executeIssue(
await saveRunState(config.workspacePath, state);
await linear.markStage(state.issue.id, "done");
await linear.comment(state.issue.id, "Review/testing passed. Marked done.");
logger.info(
buildIssueJobLogFields(state, "testing"),
"Review/testing completed",
);
}
}

Expand Down
75 changes: 75 additions & 0 deletions tests/workflow.test.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { describe, expect, it } from "bun:test";
import type { ResolvedProjectConfig } from "../src/types";
import {
buildIssueJobLogFields,
buildPlanComment,
parseReviewOutcome,
resolvePollingSettings,
Expand Down Expand Up @@ -120,3 +121,77 @@ describe("resolvePollingSettings", () => {
});
});
});

describe("buildIssueJobLogFields", () => {
it("returns consistent issue job fields", () => {
const now = new Date().toISOString();
const fields = buildIssueJobLogFields(
{
projectId: "default",
projectName: "Default",
workspacePath: "/tmp/work",
repository: {
owner: "acme",
name: "repo",
baseBranch: "main",
},
issue: {
id: "lin_123",
key: "ENG-1",
title: "Improve logging",
url: "https://linear.app/acme/issue/ENG-1/improve-logging",
},
stage: "planning",
bugs: [],
startedAt: now,
updatedAt: now,
},
"planning",
);

expect(fields).toEqual({
projectId: "default",
issueKey: "ENG-1",
issueId: "lin_123",
issueTitle: "Improve logging",
stage: "planning",
});
});

it("includes resumed flag when issue run is resumed", () => {
const now = new Date().toISOString();
const fields = buildIssueJobLogFields(
{
projectId: "default",
projectName: "Default",
workspacePath: "/tmp/work",
repository: {
owner: "acme",
name: "repo",
baseBranch: "main",
},
issue: {
id: "lin_123",
key: "ENG-1",
title: "Improve logging",
url: "https://linear.app/acme/issue/ENG-1/improve-logging",
},
stage: "implementing",
bugs: [],
startedAt: now,
updatedAt: now,
},
"implementing",
{ resumed: true },
);

expect(fields).toEqual({
projectId: "default",
issueKey: "ENG-1",
issueId: "lin_123",
issueTitle: "Improve logging",
stage: "implementing",
resumed: true,
});
});
});
Loading