Skip to content

Review · APPLY7, job files and run tools in 0.0.1

doc-reviewer, ux-reviewer and security-reviewer on commit bbeff35 of branch autopilot/apply7-logs-features, 2026-10-28, relayed by the coordinator in one message. The doc review approved (11/14) with four required and six optional findings; the coordinator accepted all of them. The UX and security findings came without severities, so the Severity column says which review they come from; every one was accepted. All were applied on the same branch, in one commit on top of bbeff35, with the decisions below. Specs changed: ui-spec §4.4 note 2026-10-28, api-and-data-model §5 and §5.1 notes 2026-10-28, architecture §13.7, checking-scenarios §11.1, dev-env §15.3 req. 13 to 16, mvp-0.0.1, P1 Tasks 28 to 32, and the decision log. The progress page is APPLY7.

DOC-REVIEW  step=APPLY7  commit=bbeff35  verdict=approve  score=11/14  required=4  optional=6  applied=10/10
UX-REVIEW   step=APPLY7  commit=bbeff35  verdict=changes  findings=11  applied=11/11
SEC-REVIEW  step=APPLY7  commit=bbeff35  verdict=changes  findings=10  applied=10/10

Doc review (D1 to D10)

Id Severity Finding Status
D1 required Copy was not shown in every run state applied: one run header for every tab, with the Copy menu in every state ("Command", "Parameters (params.json)" or "Config (config.yaml)", "Run directory", "Run ID"; the last two only for a run started from a terminal), in the ui-spec note; added to LiteRunTasks, LiteRunLogs, LiteRunDetailSnakemake, LiteRunObserved, LiteTaskFiles, MLiteRunTasks, MLiteRunLogs, MLiteRunObserved, MLiteTaskFilesSheet; the state table on LiteRunActions
D2 required test_smk_message_parsed covered one Snakemake message form applied: architecture §13.7 lists the four forms with their lines in snakemake-executor-plugin-slurm 91f51af (__init__.py one reason 1594-1599, several reasons 1569-1606, NODE_FAIL 1546-1555, not from sacct 1557-1567); P1 Task 29's test now has the single "Reason:", the "Reasons:" list and the NODE_FAIL cases
D3 required the board lists disagreed between documents applied: the same 16 boards in the ui-spec note's Boards line, P1 Task 32, the mvp-0.0.1 rows and the decision log's Changed line
D4 required the figure did not show what was there before applied: apply7_run_tools gains a "before (5e2493e)" strip above row A: head output, head errors and engine log, and the work directory as a path only; both alt texts updated
D5 optional Task 30 did not depend on Task 28, and nothing tested that :relaunch needs the write checks applied: Task 30 depends on 9, 10, 17, 18 and 28; test_req16_relaunch_origin_csrf (no Origin, a foreign Origin or no CSRF token → 403, no child)
D6 optional test names did not say which requirement they prove, and req. 13 lacked three tests applied: the security tests of Tasks 28 to 31 are named test_req13_* to test_req16_*; req. 13 gains test_req13_symlink_to_home_refused, test_req13_smk_name_exact and test_req13_file_pool_separate
D7 optional the list of files never served was in two places applied: it lives in dev-env §15.3 req. 13 only; architecture §13.7 links to it
D8 optional "Copy work directory" did not fit Snakemake applied: "Copy work directory" (Nextflow) or "Copy launch directory" (Snakemake) in the ui-spec note and on LiteRunFailed and MLiteRunFailed
D9 optional figure row B2 implied one query pattern for both engines applied: "Nextflow: sacct per failed attempt; Snakemake: batch", with the limiter of req. 15 under it
D10 optional the prefill banner differed between the ui-spec and LiteLaunch applied: one format, "Prefilled from (): values differ from the pipeline's defaults: .", in both

UX review (UX-325 to UX-335)

Id Severity Finding Status
UX-325 UX the failure summary had no cap and no "accounting unavailable" case applied: api failures[] holds the first 10, with failures_total; a null source means "SLURM accounting unavailable"; the Failure fact reads "tool_error · 2 jobs failed"; LiteRunFailed says "Showing 2 of 2 failed jobs" and draws the haplotype_caller card with "used: SLURM accounting unavailable"; P1 test_failures_capped
UX-326 UX the job files states were not drawn applied: new board LiteTaskFilesStates with nine panels: not yet written; running and growing (Reload, "updated 12 s ago"); deleted by the Snakemake SLURM plugin after success (keep_successful_logs, __init__.py:1484); work directory removed (410); array task SALMON_QUANT 8812345_3 (no .command.log); not text (415); .command.run with values «hidden»; runner offline (503) with Retry; a Snakemake job. Tab labels carry the state as text (".command.log · not yet"); the size line says "whole file · 2.4 KiB" or "last 64 KiB of 3.1 MiB"; LiteTaskFiles follows
UX-327 UX Resume had no confirm, no disabled state and no links between runs applied: new boards LiteRunActions (the dialog text, the four disabled reasons as visible text tied by aria-describedby, "Resumed as" and "Resume of" links) and MLiteResumeSheet; api actions.resume {allowed, reason} and resumed_as [{id, name, state}]; P1 test_resume_actions_field
UX-328 UX no phone board for a failed run applied: new board MLiteRunFailed: a full-width 44 px Resume, Copy to a new launch and Copy, one-column cards with label and value rows, the plugin's message, two log lines, 44 px Open job files and Copy launch directory, the accounting-unavailable case
UX-329 UX results showed recursive counts and sizes, and reports opened in a tab of their own applied: folders show their modified date only; FastQC's entry is "Browse fastqc/" to the new board LiteResults (path bar, Name / Type / Size / Modified, a link outside not followed; states: more than 1,000 entries, not created yet, failed run with no MultiQC); reports open in the new board LiteReportViewer (banner, Download, sandbox="allow-scripts allow-downloads", "Report blank?" fallback)
UX-330 UX the phone file tabs scrolled off without a cue, and the work directory was cut applied: MLiteTaskFilesSheet uses a native <select> labelled "File" and shows the work directory in full
UX-331 UX phone task rows: small Files buttons, "near limit" wrapping, an unlabelled resources line applied: MLiteRunTasks and the page behind MLiteTaskFilesSheet: 44 px Files buttons, "near limit" never wraps, "asked 8 GB · peak 8.0 GB · limit 8 h"
UX-332 UX Copy was not in every state or on every tab, and its menu was not drawn applied with D1; LiteRunActions draws the open menu (role="menu", menuitem) and the "Copied the command" status line
UX-333 UX MLiteRunLogs differed from LiteRunLogs applied: the engine log's full path .spectio/nextflow.log with Copy, and "Each job's own files: open a job in Tasks"
UX-334 UX the job file download button did not say how much applied: "Download last 1 MiB" on LiteTaskFiles, MLiteTaskFilesSheet and LiteTaskFilesStates; the run logs keep "Download last 64 KiB"
UX-335 UX LiteLaunch banner disagreed with non_default, the outdir warning was only in the banner, and the banner was a live region applied: the banner names exactly input, outdir, min_mapped_reads and pseudo_aligner (multiqc_title cleared, so the preview, "4 changed" and the groups' counts agree); "Warning: same as rnaseq-2026-10-25. Change it to keep the two runs' results apart." under outdir, tied by aria-describedby; role="status" dropped

Security review (SEC-150 to SEC-159)

The controls are in dev-env §15.3 req. 13 to 16; the tests are in P1 Tasks 28 to 31.

Id Severity Finding Status
SEC-150 security resolving a path then opening it left a swap window applied: the walk in checking-scenarios §11.1 (revised 2026-10-28): openat from the root with O_DIRECTORY and O_NOFOLLOW per component, fstat on the leaf, readlinkat followed only into the same run's work directory, fstatat for listings; req. 13; test_req13_dir_swapped_mid_request, test_req13_symlink_to_home_refused, test_req13_published_symlink_served, test_req13_symlink_fifo_refused
SEC-151 security a report in a tab of its own could be framed or framed away applied: req. 14: the report viewer page with an <iframe sandbox="allow-scripts allow-downloads">, raw HTML served with the CSP sandbox allow-scripts allow-downloads and frame-ancestors 'self'; test_req14_headers; boards LiteReportViewer, LiteResults
SEC-152 security Snakemake Resume could unlock a directory whose head still runs applied: req. 16 and api §5.1: a head Spectio cannot check counts as alive; --unlock only when the head is proven gone; one child per launch directory with .spectio-launch.lock and BEGIN IMMEDIATE; test_req16_parent_head_unverified_409, test_req16_no_unlock_when_live, test_req16_one_child_parallel
SEC-153 security sacct load on the shared accounting database applied: req. 15: one limiter (one call in flight, 60 s apart, 100 IDs a call), no bulk queries on rebuild or replay, a cache in .spectio/sacct.json; test_req15_limiter, test_req15_rebuild_no_calls, test_req15_cache_file
SEC-154 security the launch directory could change between the check and the child's start applied: req. 16: dev and inode checked again before the child starts, umask 022; test_req16_dir_swapped, test_req16_planted_config
SEC-155 security hiding values in .command.run with a regex applied: req. 13: a shell lexer that fails closed, container environment flags included, and unset NXF_DEBUG in the head script; test_req13_redact_lexer, test_req13_redact_container_env, test_req13_redact_fails_closed, test_req13_command_run_redacted
SEC-156 security Snakemake log names matched by prefix, and .spectio/ files not listed applied: req. 13: exact Snakemake names from the job's own events, and the .spectio/ files allowed by name; test_req13_smk_name_exact, test_req13_spectio_dir
SEC-157 security listings, report search and streams had no caps applied: req. 14: 1,000 entries per folder, no recursion, report search stops at 10,000 entries or 5 s, the first 50 reports, at most 4 streams; a file pool separate from the API's; test_req14_scandir_cap, test_req14_report_search_caps, test_req14_stream_cap, test_req13_file_pool_separate
SEC-158 security response headers were not stated applied: req. 13 and 14: nosniff, no-store, no-referrer, Content-Disposition: attachment with filename* for every non-HTML file; test_req13_headers, test_req14_headers
SEC-159 security Copy to a new launch carried parameters that load code applied: req. 16 and api launch-template.code_loading: custom_config_base, -c files, --configfile and a local pipeline location are marked "loads code or configuration", and "writable by others" when they are; test_req16_code_loading_flagged

Every new or changed board was rendered (desktop 1440 × 1180, phone 390 × 844) with node tools/canvas/render.js and looked at, below the fold too; the six new boards are registered in canvas.json and design/README.md.