feat(lanes): let Add Lane choose the pipeline template

Creation is the only point the UI could ever set a lane's template, and it
never offered the choice — so every lane added from "+ Add lane" was born
on `default` and rendered an 8-node map for a 16-node workflow, with no
screen able to change it afterwards. That is the defect that made the
ship-feature template unreachable from the browser.

The modal now shows a *Pipeline template* select fed by
`GET /api/lanes/pipelines`, labelled with each template's node count so the
consequence of the choice is visible. A failed fetch degrades to a `default`
option rather than blocking lane creation.

`pipeline` was already accepted by `POST /api/lanes` but silently dropped by
`/ensure` and `/worktree`, which build their own createLane payloads; both
now pass it through, and both map `EBADPIPELINE` to 400 like `EBADCWD`.
This commit is contained in:
2026-08-07 09:44:48 +07:00
parent 67edda77eb
commit 8fcef5a10b
8 changed files with 143 additions and 7 deletions
+11 -1
View File
@@ -1900,11 +1900,20 @@ export const api = {
* @param body The cwd and optional title.
* @returns `{ lane, created }` — the lane (newly created or existing) and whether it was created.
*/
ensure: (body: { cwd: string; title?: string }) =>
ensure: (body: { cwd: string; title?: string; pipeline?: string }) =>
request<{ lane: Lane; created: boolean }>("/lanes/ensure", {
method: "POST",
body: JSON.stringify(body),
}),
/**
* GET /api/lanes/pipelines — every pipeline template the server can render
* a lane against, built-in plus any `DASHBOARD_PIPELINES_DIR` override.
* @returns `{ pipelines }` — each with its `id`, display `name` and `nodes`.
*/
pipelines: () =>
request<{ pipelines: { id: string; name: string; nodes: { id: string }[] }[] }>(
"/lanes/pipelines"
),
/**
* GET /api/lanes/branches — a candidate source repo's local branches.
* @param repo Absolute path to an existing git repository.
@@ -1928,6 +1937,7 @@ export const api = {
title?: string;
base?: string;
branch?: string;
pipeline?: string;
}) =>
request<{ lane: Lane }>("/lanes/worktree", {
method: "POST",