Code review · P3 Task 32, the screens: job files, failure summary, resources and Launch checks¶
review-code on autopilot/P3-t32, base b690bea, 2026-11-01. The step is P1 Task 32, the screens of milestone M8 (P2 §8). It is limited to the API that P1 Tasks 28 and 29 built, plus Thomas's two Launch items of todo.md (2026-10-31, "avec 32"). Resume, Copy to a new launch, and results and reports wait for P1 Tasks 30 and 31 (inbox #96). The step adds:
web/src/runs/TaskFiles.tsx: each attempt's files in the task sheet (tabs, every LiteTaskFilesStates state, the size line, Reload, Download), an Attempt select, and the work or launch directory with Copy;web/src/runs/FailureSummary.tsx: one card per job that failed on its last attempt, inside "Why it failed";web/src/runs/resources.ts,RunDetailPage.tsx,DetailSheet.tsx,RunState.tsx(NearLimit): the resources columns, the phone's resources line, a Files button per row, and the task sheet's facts;web/src/launch/schemaFields.ts(checkValue) andLaunchPage.tsx: each value is checked against its schema before Launch, and each row says "(required)" or "(optional)";- server:
Failure.work_dir(failure.py,api/models.py, OpenAPI snapshot), for the card's Copy button; - tests:
TaskFiles.test.tsx(14),FailureSummary.test.tsx(8),resources.test.ts(4), new cases inLaunchPage.test.tsx,schemaFields.test.ts,DetailSheet.test.tsx,test_failure.py, and 10 Playwright tests intests/e2e/skeleton.spec.ts. Four of those aretest.fixme, for Tasks 30 and 31.
The reviewers were code-reviewer and security-reviewer, independent and in parallel. Format: README.md. The owning rules: ui-spec §4.4 (the 0.0.1 run tools note of 2026-10-28, and the as-built note of 2026-11-01 written with this record), ui-spec §4.6 (Launch checks, note of 2026-11-01) and api §5 (Failure.work_dir, note of 2026-11-01).
| Round | Head | code-reviewer | security-reviewer |
|---|---|---|---|
| 1 | 0070ab00 (the step) |
approve (6 minors, 2 simplify) | approve (2 minors) |
| 2 | c8427fcd (CR-P3T32-01 to -04 fixed, tests first) |
approve (1 minor) | approve (none) |
| simplify | 00f38eaa |
code-simplifier: changed, −16 +13 (one commit) |
The coordinator relayed the reports in summary. Finding IDs restart in each round, so the same ID can name two different findings. This record writes the round after each ID.
Test-first. The worker reports that each fix was written as a test first and seen to fail. doc-writer re-checked this on 00f38eaa. With LaunchPage.tsx, TaskFiles.tsx and RunDetailPage.tsx taken from 0070ab00, the four tests fail; with HEAD restored, they pass (P3-M8 Evidence).
Gate at 00f38eaa, from the worktree, re-run by doc-writer:
cd web && npx vitest run # Tests 223 passed (223), 21 files
cd web && npx tsc --noEmit # clean
uv run --locked pytest -q # 650 passed, 17 deselected
uv run --locked ruff check . # All checks passed!
uv run --locked ruff format --check . # 105 files already formatted
uv run --locked mypy packages # Success: no issues found in 104 source files
uv run --locked lint-imports # Contracts: 4 kept, 0 broken.
uv lock --check # Resolved 55 packages
cd tests/e2e && SPECTIO_E2E_BASE_URL=http://127.0.0.1:8766 SPECTIO_E2E_SEEDED=1 \
npx --no-install playwright test skeleton.spec.ts --project desktop --project phone
# 24 passed, 22 skipped
The worker did not re-run the Playwright smoke after c8427fcd; doc-writer did, on 00f38eaa. Its mock sets the run's failure message to '', so the smoke does not exercise CR-P3T32-04; the Vitest case does.
CODE step=P3-t32 review=approve b/M/m=0/0/7 security=approve b/M/m=0/0/2 rounds=2 simplify=changed -16/+13 tests=pass
The counts add up both rounds for each reviewer. The two simplify findings (CR-P3T32-07 and -08 r1) are not counted.
Findings¶
| Id | Severity | Finding | Status |
|---|---|---|---|
| CR-P3T32-01 (r1) | minor | Required fields still showed Mantine's asterisk beside the new "(required)": TextInput, NativeSelect and Textarea read "input (required) *". | fixed in c8427fcd: withAsterisk: false in common; the inputs keep required. The "required or optional" test also checks that no .mantine-InputWrapper-required is on the page |
| CR-P3T32-02 (r1) | minor | Retry on a 404 read did nothing when the reloaded listing still said the file was present: the panel kept its key and its effect's inputs, so the file was never read again. | fixed in c8427fcd: Retry reloads the listing and reads the file again. Test: "a read that 404s while the listing still says present" |
| CR-P3T32-03 (r1) | minor | Picking another attempt briefly showed "This attempt has no file to show." instead of "Loading the files…", because the earlier attempt's listing was still set. | fixed in c8427fcd: loading while listing?.attempt !== n; the empty state only when they match. Test: "picking another attempt says 'Loading the files…'" |
| CR-P3T32-04 (r1) | minor | The run's failure message was shown twice: once by "Why it failed" and again on the culprit's card, which carries the same message. The e2e mock hid it (message: ''). |
fixed in c8427fcd: the run-level message is left out when a card's message equals it after trimming. Test: "the run's message, also on the culprit's card, is shown once" |
| CR-P3T32-05 (r1) | minor | Two differences from ui-spec §4.4 Job files, not noted: an array task's job log is labelled ".command.log · missing", where the spec says "· none for array tasks"; a file of 1 MiB or less gets "Download", not "Download last 1 MiB". | fixed by the docs commit: the ui-spec §4.4 note of 2026-11-01 records both as built; inbox #96 rule 3 |
| CR-P3T32-06 (r1) | minor | Unrelated edit: the task sheet's "Name" fact was removed (DetailSheet.tsx:188 at the base). Nothing asked for it; the sheet's title still shows the task's label. |
open: recorded as built in the ui-spec §4.4 note; restore or keep is inbox #96 rule 4. FU-P3-33 |
| CR-P3T32-07 (r1) | simplify | The "near limit" badge was written four times: Pair in DetailSheet, Near in FailureSummary, and two spans in RunDetailPage's table. |
fixed in 00f38eaa: one NearLimit in RunState.tsx; the DOM is unchanged |
| CR-P3T32-08 (r1) | simplify | resources.ts seconds() repeats the s / min / h wording of stateWord.duration. |
open: left by code-simplifier. The outputs differ ("4 h" and "4 h 00"), duration takes two ISO times, and stateWord.ts is outside the step's diff. FU-P3-33 |
| SEC-P3T32-01 (r1) | minor | A schema pattern is compiled and run on every render with no bound, so a pipeline schema with a catastrophic regex can hang the Launch tab (ReDoS). |
open: probe below. FU-P3-33 |
| SEC-P3T32-02 (r1) | minor | The Attempt select builds one option per attempt from attempt_n, with no cap. A spool event with a huge attempt crashes or freezes the task sheet and the card's Open job files. |
open: Array.from({ length: last }, …) in TaskFiles.tsx at 00f38eaa. FU-P3-33 |
| CR-P3T32-05 (r2) | minor | The work directory row and its Copy button read the listing even when it belongs to another attempt, the staleness CR-P3T32-03 fixed for the files. After a pick, the old directory stays on screen and can be copied while the new listing loads, or beside the error when it fails. In Nextflow each attempt has its own work directory. | open: const directory = listing?.work_dir ?? null in TaskFiles.tsx at 00f38eaa. FU-P3-33, before the 0.0.1 release |
Probe of SEC-P3T32-01, re-run by doc-writer with Node v22.22.0. It times the call checkValue makes, new RegExp(pattern).test(value):
pattern ^(a+)+$, 20 a then !: 43 ms
pattern ^(a+)+$, 24 a then !: 106 ms
pattern ^(a+)+$, 28 a then !: 1720 ms
ParamRows calls checkValue for every touched field on every render, so each key press in any field runs the pattern again.
Fixes by round¶
| Commit | Findings |
|---|---|
c8427fcd fix(web): P3-t32 review findings CR-P3T32-01 to 04 |
CR-P3T32-01, -02, -03, -04 (r1) |
00f38eaa refactor(P3-t32): simplify the near-limit badge into one NearLimit component |
CR-P3T32-07 (r1) |
| the docs commit | CR-P3T32-05 (r1) |
code-simplifier reran the step's check command before and after its change: pytest 650 passed and 17 deselected; ruff, ruff format, mypy, lint-imports and uv lock --check gave the same results. It also ran Vitest itself. The first run after its change had one failure (222 of 223), while the Python suite ran alongside; it did not record which test. The next two runs passed 223 of 223. doc-writer's run on 00f38eaa, with nothing else running, passed 223 of 223.
For the Progress page¶
The Task 32 section is written: P3-M8, with its findings and open questions.