Skip to content
Open
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
10 changes: 10 additions & 0 deletions apps/api/src/tools/task-board/claude-code-task-run.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -281,6 +281,16 @@ describe("pickSoleTaskRepo", () => {
),
).toBeNull();
});
/** The card's `repo` is free text, so it goes stale: a renamed repository, a
* value from the github-connection era, a typo. It must narrow the choice,
* never veto it — an org with exactly one repo bound that repo before this
* parameter existed, and still has to. */
test("a preference that matches nothing falls back to the sole repo", () => {
const sole = [choice("one", "acme", "web")];
expect(pickSoleTaskRepo(sole, "acme/renamed-away")?.id).toBe("one");
expect(pickSoleTaskRepo(sole)?.id).toBe("one");
});

test("no clonable repo is not eligible", () => {
expect(pickSoleTaskRepo([])).toBeNull();
});
Expand Down
15 changes: 11 additions & 4 deletions apps/api/src/tools/task-board/claude-code-task-run.ts
Original file line number Diff line number Diff line change
Expand Up @@ -75,16 +75,23 @@ export interface TaskRepo {
export function pickSoleTaskRepo(
choices: RepoChoice[],
/** The card's own `repo`, when it has one: narrow to it first, so a
* multi-repo org still binds a checkout before dispatch. An unknown or
* ambiguous name narrows to nothing and falls back to the mid-run pick. */
* multi-repo org still binds a checkout before dispatch.
*
* A HINT, never a veto. `repo` is free text that goes stale — a renamed
* repository, a value from the github-connection era, a typo — and letting
* a stale one empty the set would stop binding the sole repo of a
* single-repo org, which this bound before the parameter existed. So a
* preference that matches nothing is discarded, not honored. */
preferredRepo?: string,
): TaskRepo | null {
if (preferredRepo)
choices = choices.filter(
if (preferredRepo) {
const narrowed = choices.filter(
(choice) =>
`${choice.owner}/${choice.name}`.toLowerCase() ===
preferredRepo.toLowerCase(),
);
if (narrowed.length > 0) choices = narrowed;
}
if (choices.length !== 1) return null;
const chosen = choices[0]!;
return {
Expand Down
Loading