P2 · Execution guide for the P1 plan¶
For agentic workers: this page does not repeat P1. It says in which order step P3 runs P1's Tasks 1–32, which run in parallel, what proves each milestone done, which review each task gets, and the few tasks P1 still lacks. Each task's files, interfaces, tests and commands are in P1.
Written 2026-10-29 (step P2). One file instead of one plan per milestone, and no task copied from P1: Thomas's narrowing of P2 (decision log), after the GO of 2026-10-29 (whole plan M1–M8, five changes).

Step page: Progress · P2.
Goal: run P1 to its "Done when" list with the fewest blocked hours: lanes that run in parallel, the cluster-bound tasks batched, and the gaps the GO opened closed before the tasks that need them.
Spec: P1 and the specs it names per task; dev-env §15.1 and §15.3; autopilot §9; the P3 prompt in next-phase.md.
0. How P3 runs this¶
- Skill.
subagent-driven-development, as the P3 prompt says: one fresh implementer per task,test-driven-developmentthroughout (P1's Step 1 → Step 2 FAIL → Step 3 → Step 4 PASS),verification-before-completionbefore a task is marked done, then the task's review track (§9). Sections 2 and 3 ofkarpathy-guidelinesgo to every task reviewer as global constraints, with P1's Global Constraints. - Branches. One worktree and branch
autopilot/P3-<task>per task (for exampleautopilot/P3-t03), or per small group named in §1–§8 (autopilot/P3-t12-t14); a gap task uses its id (autopilot/P3-g6). Each branch is based on the currentautopilot/integrationand merged into it by pull request once its checks and its review pass. Never force-push, never delete a branch, never edit a status inpipeline.yaml(CLAUDE.md). - Commits. P1's commit messages; every
featcarriesRelease-As: 0.0.1(P1 Global Constraints). - Docs. Each PR leaves its docs through
write-docs: when code contradicts a spec, the owning spec changes in the same PR with a dated log entry (P1 Process). One Progress page per milestone, not per task (P1 Process):docs/progress/W5/P3-M<n>.md, opened with the milestone's first PR and closed with its done proof (§1–§8, item "Done"). - Order. Milestones are not strict phases: a task starts when the tasks it depends on are merged. The critical path is 1 → 2 → 4 → 9 → 10 → 11 → 12 → 16 → 17 → 18 → 20 → 21 → 22 → 23.
- Stop and inbox. A spec ambiguity, a contradiction between P1 and a spec, or a cluster fact that P1 marks [to be validated] and that turns out otherwise becomes an entry in
docs/reviews/inbox.mdwith a safe default assumed and marked (assumed — pending Thomas, inbox #N); the task goes on if the default is reversible. The task stops (blocked) only when every default would loosen a §15.3 requirement, lose data, publish something, or be hard to undo. A review fix loop that fails twice blocks the task with an inbox entry (autopilot §9). Blocked tasks do not block their independent neighbours.
0.1 Where each part can run¶
| Part | Cloud session | Needs |
|---|---|---|
| M1–M4, M6–M8 unit, API and Playwright tests; Tasks 6 and 7 with real local engine runs | yes (dev-env §2: engines, the UI and Chromium work) | Java for Task 6, as in spike T2; tmux for Task 10's -m tmux tests |
Tasks 22 and 23; the slurm-mini checks in Step 4 of Tasks 28–30 |
no: cloud sessions boot with cgroup v1, so sind cannot run (dev-env §2, inbox #51) | pipelines-slurm.yml job e2e on a GitHub runner, iterated by push (the route of spikes t3-ci and t4-ci), or Thomas's machine with Docker on cgroup v2 (option (a), which his 2026-10-29 answer accepts, pending his confirmation) |
| Task 24 Step 4; inbox #64 (which directory is node-local, BeeGFS locks) | no | a person on an EMBL login node; nothing in CI reaches EMBL |
The SQLite location stays P1 Assumption 21 (node-local) for Tasks 2 and 11 until Thomas confirms his dashboard answer on inbox #64 (the home folder for the MVP).
The code of Tasks 22 and 23 is written in a cloud session; only their runs go to CI. Until the e2e job is green, M5 stays open while M6–M8 go on (none of their tasks depends on Task 22).
1. M1 · Events in¶
- Tasks: 1, 2, 3, 4, 5, 6, 7.
- Order: Task 1 alone first (it creates the workspace and the CI job everything else uses). Then four parallel worktrees: 2 → 4 → 5; 3; 6 (Groovy); 7. Task 4 waits for 3. Task 8 (M2) may start as soon as 2 is merged.
- Done:
uv run pytest packages/spectio-sdk packages/spectio-server/tests/test_store.py packages/spectio/tests/test_spool_reader.py packages/spectio-server/tests/test_projector_nextflow.py packages/spectio-server/tests/test_projector_snakemake.py -v, then the bridge suites with-m engines(Task 6 on 26.10.4 and 25.10.9; Task 7's own suite). No scenario proves M1: the files intests/scenarios/describe whole runs. The stand-in is Task 5's replay of the T2 fixtures and Tasks 6 and 7's real local runs. - Review: all seven are product code; Tasks 1 and 3 (§15.3 req. 6) are security boundaries.
2. M2 · Heads¶
- Tasks: 8, 9, 10; gap G6.
- Order: 8 after 2 (parallel with 3–7). 9 after 4, 6, 7 and 8. 10 after 9. G6's
initanddoctorpart goes with Task 8's branch or right after it. - Done:
uv run pytest packages/spectio/tests -vanduv run pytest -m tmux packages/spectio/tests/test_tmux_real.py -v.happy-local.yaml(Nextflowhappy, local executor,expect: run succeeded) is not checked by any code yet; Task 9's local-launcher tests stand in until G7. - Review: product code. Task 9 (head script, Review Focus 1 and 4; §15.3 Launch) and Task 10 (tmux, adoption) are security boundaries.
3. M3 · Served safely¶
- Tasks: 11, 12, 13, 14, 15, 16; G6's start-text part.
- Order: 11 after 3, 8, 9 and 10. Then 12 ∥ 14. Then 13 ∥ 15 (after 12) and 16 (after 12 and 14). Task 24 (M5) may start once 11 and 12 are merged.
- Done:
uv run pytest packages/spectio-server/tests/security packages/spectio-server/tests/test_serve.py -vall green (P1 Task 16 Step 4). No scenario. - Review: every task is a security boundary (§15.3 req. 1–5 and 7).
4. M4 · API and screens¶
- Tasks: 17, 18, 19, 20, 21; gaps G1, G2, G3a, G4, G5.
- Order: G1 → G2 and G3a are board work and can start at once, in parallel with M1–M3. 17 after 9 and 16, with G5's API tests. Then 18 ∥ 19 (19 also after G1 and G2, with G5's UI tests). G4 after 19. 20 after 18, G3a and G4 (T6 is done). 21 after 6, 7 and 20.
- Done:
uv run pytest packages/spectio-server/tests/api -v;npm --prefix web test;bash tests/e2e/serve-local.sh & npx --prefix dev/npm playwright test tests/e2e/skeleton.spec.tson the desktop and phone projects;bash tests/packaging/test_wheel.shprintsRESULT wheel pass. The Playwright test "link → empty list → launch happy (Nextflow, local) → detail shows Succeeded" is the stand-in forhappy-local.yamluntil G7 checks it. - Review: 17, 18, 19, 20, G4 and G5 are product code; 17 (Git fetch: argument list, protocol allow-list) is a security boundary. Task 21 is build tooling: tests and one review, but the hooks decide what the wheel ships, so
review-codeapplies if in doubt. G1–G3a are board changes: rendered withnode tools/canvas/render.js, looked at, and reviewed byux-reviewer.
5. M5 · On the cluster¶
- Tasks: 22, 23, 24; gap G7.
- Order: 22 after 21; 23 after 22. 24 runs in parallel from M3 on (it needs only 11 and 12). G7 after 21, in parallel with 22. Runs of 22 and 23 go to CI (§0.1).
- Done:
CLUSTER=mini bash tests/slurm/e2e.sh launch nextflow,… launch snakemake,… cancel nextflow,… cancel snakemakeand… restarteach printRESULT … pass, and thee2ejob ofpipelines-slurm.ymlis green. That job makes theexpectlines oftests/scenarios/happy-slurm-mini.yamlchecked (P1 Task 22 Files), so this scenario proves M5;python3 -m unittest discover -s tests/scenarios -p 'test_*.py'must stay green after that edit.uv run pytest packages/spectio/tests/test_open.py -v, and Task 24's manual check on an EMBL login node, recorded on the Progress page.happy-k8s-mini.yamlstays a description: Kubernetes is out of P1. - Review: 22, 23 and G7 are test-only (tests and one review); every fix they reveal lands in the owning task's code and gets
review-codethere (P1 Task 22 Step 3). Task 24 (ssh argument building, §15.3 req. 8) is a security boundary.
6. M6 · Runs from a terminal¶
- Tasks: 25, 26.
- Order: 25 after 6, 7, 9, 11, 18 and 20; 26 after 6, 7 and 21. They run in parallel. Neither waits for M5.
- Done:
uv run pytest packages/spectio/tests/test_observed.py -v, the bridge suites with-m engines, the Playwright smoke with its terminal run (no Cancel), andbash tests/packaging/test_bridges_release.shprintsRESULT bridges-release dry-run pass. No scenario. - Review: 25 is a security boundary (§15.3 req. 12, pointer scan). 26 is release tooling but touches the supply chain (§15.3 req. 12, Release):
review-code.
7. M7 · Stages¶
- Tasks: 27.
- Order: after 4, 5, 18, 20 and 25; in parallel with M8's Task 28. It edits files that M8 also touches (
RunDetailPage.tsx, the projector): merge 27 before 32. - Done:
uv run pytest packages/spectio-server/tests/test_stages.py -vagainst the golden output of the four example pipelines, and the Playwright checks of P1 Task 27 Step 4. No scenario. - Review: product code (
review-code); thespectio.ymloverlay read is untrusted input.
8. M8 · Job files and run tools¶
- Tasks: 28, 29, 30, 31, 32; gap G3b.
- Order: 28 after 4–7, 9 and 18. Then 29 ∥ 30 ∥ 31. 32 after 20, 28–31, 27 and G3b. Each task's unit part merges without the cluster; the
slurm-minilines of Step 4 in Tasks 28–30 run once Task 22'se2ejob exists, batched in one CI iteration (§0.1, and Notes for P1 item 1). - Done: the Step 2 commands of Tasks 28–31 PASS;
npm --prefix web testand the Playwright smoke PASS with Task 32; the cluster checks of 28–30 in thee2ejob. No scenario. - Review: 28 (confinement, req. 13), 29 (req. 15), 30 (req. 7 and 16) and 31 (report sandbox, req. 13 and 14) are security boundaries; 32 is product code. G3b is a board change (
ux-reviewer).
9. Review tracks¶
Per inbox #52 option (b), decided in the GO entry:
| Track | Tasks |
|---|---|
review-code (code-reviewer and security-reviewer in parallel, then code-simplifier) |
1–20, 24–32, G4, G5, G6 |
| tests and one review | 21, 22, 23, G7 |
render, look, ux-reviewer |
G1, G2, G3a, G3b |
CLAUDE.md (Coding behaviour) still asks review-code for every code change, and Thomas has not edited it yet (autopilot §9, note 2026-10-29). Until he does, the stricter rule wins whenever a task's track is in doubt. The whole branch also gets one review-code pass before P3 ends (P3 prompt).
10. Gaps: tasks P1 does not carry¶
The GO's five changes, checked against P1: change 1 (EMBL login nodes) needs no task; change 2 (process) is §9. Changes 3, 4 and 5 are in P1's Assumptions 2, 23 and 24 and in some interfaces, but no task builds or tests the parts below. Each stub is at most half a day.
10.1 G1 · Paper chrome on the shared lite-board components¶
Spec: ui-spec §7 (Paper shell, note 2026-10-29), P1 Assumption 24 · Before: Task 19. Restyle LiteSidebar, LiteTopbar, MLiteTopbar and MLiteTabbar in design/ui-canvas/ to the Paper shell: floating top bar with breadcrumbs and the theme toggle, foldable sidebar with the "Workspace" eyebrow and server status in its foot, bottom tab bar on phones. Search (⌘K), notifications and the avatar are left out: 0.0.1 has no users and no search (assumed — pending Thomas, inbox #72). Thirty of the 34 lite boards import these four, so the chrome changes everywhere at once. Test: every Lite* and MLite* board renders with render.js and is looked at; the keyboard rules of CLAUDE.md hold; ux-reviewer approves.
10.2 G2 · The launch boards: engine buttons and path-or-Git¶
Spec: ui-spec §4.6, P1 Assumption 2, mvp-0.0.1 §2 (note 2026-10-29) · After: G1 · Before: Task 19. LiteLaunch, LiteLaunchSnakemake and MLiteLaunch show the two engine buttons (<button aria-pressed>, one pre-selected, with "Guessed from main.nf" beside it), and a Pipeline field that takes a path, or a repository and a revision. Their states: no guess (both or neither file), a refused URL (file://). Test: rendered and looked at; ux-reviewer approves; Task 19 then names these boards in place of Launch and LaunchSnakemake.
10.3 G3a · Paper page bodies of the run boards¶
Spec: ui-spec §7 (page head: tinted 48 px icon tile, Outfit h1, one-line description, primary action on the right) · After: G1 · Before: Task 20. LiteHome, LiteRuns, LiteRunDetail, LiteRunDetailSnakemake, LiteRunObserved, LiteStates, LiteStatesRun, LiteCancelDialog and their phone twins. Test: rendered, looked at, ux-reviewer.
10.4 G3b · Paper page bodies of the run-tool boards¶
Same rule as G3a · Before: Task 32. LiteRunTasks, LiteTaskFiles, LiteTaskFilesStates, LiteRunLogs, LiteRunFailed, LiteRunActions, LiteResults, LiteReportViewer, LiteLegend and their phone twins (MLiteResumeSheet, MLiteTaskFilesSheet…). Test: as G3a.
10.5 G4 · The Paper shell in web/¶
Spec: ui-spec §7, architecture §6 (Mantine 7), P1 Assumption 24 · After: Task 19, G1 · Before: Task 20. Rewrite the Paper shell as Spectio code (no import from depictio): web/src/shell/{AppShell.tsx,TopBar.tsx,Sidebar.tsx,TabBar.tsx,theme.ts}, Spectio's palette in theme.ts tokens, light and dark. Test: Vitest with Testing Library, web/src/shell/shell.test.tsx: test("theme toggle is a button and switches the scheme"), test("sidebar folds with a button[aria-expanded]"), test("below 768 px the tab bar replaces the sidebar"), test("breadcrumbs follow the route"); then npm --prefix web run build, and a Playwright screenshot compared by eye with the G1 boards.
10.6 G5 · Tests for the engine buttons and the Git source¶
Spec: P1 Assumption 2, api §5 (imports are untrusted input) · Folded into Tasks 17 and 19, not a branch of its own. Task 17 adds test_git_url_refused_schemes (file://, ext::, git:// and a bare local path given as a URL → 422, field workflow.location; the clone argv carries the three -c protocol… flags), test_git_revision_not_an_option (a revision starting with -, such as --upload-pack=x → 422, field workflow.revision), and test_git_short_name (nf-core/rnaseq → https://github.com/nf-core/rnaseq; the test fakes git and fetches nothing). Task 19 adds guessEngine(files: string[]) -> "nextflow" | "snakemake" | null in web/src/launch/guessEngine.ts with test("main.nf → nextflow; Snakefile or workflow/Snakefile → snakemake; both or neither → null"), and a component test: both buttons are native <button>s, the guessed one is pressed, a click changes it.
10.7 G6 · Local mode: init, doctor and start text¶
Spec: dev-env §15.3 (Local mode), P1 Assumption 23 · With: Task 8 (first three tests), Task 11 (last two). Test: test_init_local_environment (--local → environment kind local, no [slurm] options written, GET /version later says executor local); test_doctor_local_skips_sbatch (no sbatch on PATH → no fail in local mode, still fail on a SLURM environment); test_init_local_refused_off_linux (sys.platform darwin → exit 2, "Local mode needs Linux in 0.0.1"; assumed — pending Thomas, inbox #73: refuse rather than warn); test_serve_local_start_text (local mode → the link and "Open this link in a browser on this machine", no ssh -N -L line); test_serve_local_same_requirements (local mode still binds 127.0.0.1 only and needs the ticket).
10.8 G7 · happy-local checked on both engines¶
Spec: P1 Done when (Local mode), dev-env §5 · After: Task 21 · Test-only. tests/local/e2e.sh launch nextflow|snakemake installs the wheel, runs spectio init --local and spectio serve --detach in a temporary home, launches the synthetic happy through the HTTP API, and compares the counts per process with tests/pipelines/synthetic/golden/. It uses Task 22's client tests/slurm/e2e_client.py; if G7 runs first, it creates that client at that path and Task 22 consumes it (one owner). It prints RESULT e2e-local-<engine> pass, runs in the e2e-local CI job of Task 20, and makes the expect line of tests/scenarios/happy-local.yaml checked; a Snakemake twin happy-local-snakemake.yaml is added, and python3 -m unittest discover -s tests/scenarios -p 'test_*.py' stays green.
11. Notes for P1¶
P1 is not edited here; these go to the next apply pass.
- Cluster checks without a dependency. Step 4 of Tasks 28, 29 and 30 runs on
slurm-mini, but none of them lists Task 22 in "Depends on". P2 batches them after Task 22 (§8). Also, Task 29 callssacctand injects an out-of-memory task onslurm-mini, butslurm-minihas no accounting;slurmdbdandConstrainRAMSpace=yesare inslurm-site(dev-env §4.1). Task 29's cluster check probably belongs onslurm-site[to be validated]. - "Done when", first line, asks a
review-codepass for every task; P1's own Process line and inbox #52 (b) ask it for product code only. The stricter line holds until Thomas editsCLAUDE.md(§9). - Assumption 24 says the lite boards are restyled before Task 19, but no task does it, and Task 19 does not name the Paper shell: G1–G4.
- Task 8's
doctorcheckssbatch(afailline when it is missing), and Task 11's start text always prints thessh -N -Lline; both are wrong in local mode (Assumption 23): G6. - Task 17's only Git test allows the
filetransport; nothing tests the refusal of other schemes or a revision that starts with-: G5. - Task 19's boards are
LaunchandLaunchSnakemake(reference only); after G2,LiteLaunchis the reference. happy-local.yamlis never made checked by a P1 task, though "Done when" asks for local mode on both engines: G7.- The figure at the top of P1 and the counts on Progress · P1 still show M1–M5 and 24 tasks; P1 already notes it.