Step 4: results and Pages deployment (#286)
Results contract
The CSV checklist covers all OutputHandlerCsv column IDs. The worker requests
those columns plus internal target/query IDs, coordinates, energy and hybridDP
needed for pair grouping, result validation and visualization. Downloads project the untouched native CSV
cells onto the user’s selected columns, in selection order, retaining every row
regardless of table filtering/visibility. No computed mfe difference is inserted
into native CSV. The selected-column catalog is checked against the C++ mapping.
Native output is unquoted and semicolon-delimited. Semicolons in IDs become
underscores; quotes remain literal. The parser checks column IDs, finite core
energy/coordinate values, integer inclusive coordinates and ascending bounds.
Signed coordinates are allowed. Optional NAN and colon-separated seed lists
remain strings; numeric sorting puts unavailable/list values after numbers.
Sequence 1 is target, sequence 2 is query. Energy uses kcal/mol. The
computed mfe Δ is E - min(reported E for the same query–target pair), rounded
to hundredths. Evaluation uses the best supplied structure as its reference.
This is relative suboptimality, not a free-energy change or probability.
Every heading is a keyboard-operable sort button with aria-sort; its hover
tooltip repeats the matching CSV-selection description. Search covers all
returned values. A separate checklist shows/hides table columns after the run;
it does not change selected CSV output. All identifiers, output previews and
diagnostics render with textContent. The table has a 500 px maximum height with
vertical scrolling for longer results and a sticky heading; CSV downloads still
contain every selected row. Selected extra files appear below the table with
their original filename, covered sequence/sequence-pair names, a header-level
download, and bounded text previews, including SVG source. Empty output is
successful and still permits CSV/accessibility downloads.
Additional output visualizations
Plot-capable additional CSV files open on a Visualization tab, with the original
File Content in an adjacent tab. Query/target minimum-energy and spot-probability
profiles use line plots; pair minimum-energy and all-spot probability matrices
use heatmaps; explicitly requested spot probabilities use a bar plot. Plot axes
use the literal coordinate strings from IntaRNA rather than numerical scales, so
signed indices cross from -1 to 1 without inventing coordinate zero. Plot titles
and additional-file headers identify the target/query records covered by native
-t#q# or -s# filename suffixes.
Plotly renders locally from the pinned Cartesian bundle. A separate Export PNG button uses the active plot’s current dimensions. The plotting area can be resized from its lower-right corner, and matrix plots provide Swap X/Y to transpose query and target axes without changing the data. The normal file download and reproducible ZIP retain untouched native data. Plots render lazily when their file is unfolded, resize with restored sessions, and are purged when a session is deleted. Non-plot formats keep the direct content view.
Selected interaction in vaRRI
Each row has a keyboard-accessible View button; clicking a row also selects it.
When computation returns interactions, the first row in the initial energy-sorted
table is selected and visualized automatically. Selection survives table sorting,
filtering and visibility changes within each session. Input edits, new runs, errors
and cancellation preserve prior sessions and their archive snapshots. Folding a
session unloads its viewer; reopening restores its selected interaction.
visualization.js uses native seq1 = target, seq2 = query and maps the native
CSV identifiers to the completed run’s normalized FASTA records (including
semicolon-to-underscore ID conversion). It pads the interaction dot-bracket
structure with unpaired positions to the full input lengths, using each start
index and the native negative-to-positive transition that skips zero.
When all four seed-boundary columns are present, each reported seed is passed to
vaRRI as a translucent purple paired-region highlight. Prediction defaults select
those columns; custom CSV selections without complete boundaries omit highlights.
The URL follows vaRRI’s embedding API:
sequence, structure, seqName1/2, startIndex1/2, and
showRenderingOnly=true. Names use the main-v2 API; older deployed vaRRI versions
may retain generic strand names. A separate Open in vaRRI link opens the full
viewer. The local status always identifies the selected pair and coordinates.
Zero-based CLI start indices remain accepted for computation/export, but vaRRI
requires nonzero start indices; choosing View for such a run explains this limit.
The first successful interaction result loads the external viewer automatically; choosing another row shares that pair and structure through vaRRI’s URL. Computation and ZIP generation remain local. CSP permits frames only at the vaRRI deployment; the iframe sends no referrer. Viewer availability depends on that external site; the full-viewer link permits retry, and scientific outputs remain downloadable.
Reproducible ZIP download
Download ZIP sits in each session header, including while its output is folded.
archive.js creates a standard UTF-8, uncompressed ZIP with CRC-32 checksums locally, without a library or CDN. It uses
the completed request and result, independent of table sorting/filtering and
column visibility. Editing inputs preserves every completed snapshot.
The archive contains only parameter.json, intarna-interactions.csv, and every
generated additional output with its original filename and contents. JSON embeds
normalized FASTA and any SHAPE/accessibility text, non-default settings, selected
CSV columns, compiled provenance and native replay commands in _comment.
Additional report commands override CSV-only settings and use another native
invocation, matching the worker. All JSON is valid with standard parsers.
Load/Store use this same JSON format. Store snapshots the current editable form; ZIP snapshots the completed run and ignores table filtering/sorting/visibility. Store requires the compiled version file. Completed sessions still retain their results if that file is unavailable, recording the missing version information explicitly in JSON. See the JSON workflow.
Packaging and deployment
bash scripts/wasm/build.sh
node scripts/wasm/smoke.mjs
node scripts/wasm/smoke-protocol.mjs
bash scripts/wasm/stage.sh
npm test --prefix gui
python3 -m pip install -r scripts/wasm/requirements-browser.txt
python3 -m playwright install chromium firefox
python3 -m http.server 8765 --directory build-wasm/site
# In another terminal:
python3 scripts/wasm/test-browser.py http://127.0.0.1:8765
python3 scripts/wasm/test-browser.py http://127.0.0.1:8765 --browser firefox
The staging script includes matched WASM assets, only the application source
files needed at runtime, compiled version metadata and dependency notices. It
also verifies and stages the pinned Plotly.js 4.1.2 Cartesian bundle and MIT
license plus the Pyodide 314.0.7 core runtime and MPL-2.0 license; no CDN is used. Build-time build-info.json
and build-info.txt record actual CLI IntaRNA/ViennaRNA/Boost versions, source
commit and tracked modifications, SDK version and asset SHA-256 digests. Staging
checks these digests and preserves the original metadata; it never replaces the
compiled revision with a later staging checkout. The footer displays versions.
It needs the same activated SDK and dependency build directory as the build.
No generated WASM or Pyodide artifacts are committed; both runtimes are staged
from checksum-verified build inputs. HTTP(S), module workers and WebAssembly
are required; file:// and legacy browsers are unsupported. No backend,
SharedArrayBuffer, credentials, CDN or cross-origin headers are needed. CSP
keeps scripts/styles same-origin and allows ephemeral blob images solely for
PNG export.
The existing repository already publishes its user guide and API with Pages.
Publishing a second independent artifact would overwrite them. Therefore
documentation.yml is now a reusable build (also manually dispatchable), and
build-wasm.yml is the single publisher. Both builds run on PRs (including dependent feature-branch PRs) and on
the actual default branch, master. After JS tests, the real WASM smoke test and
Chromium/Firefox integration tests pass, the package job combines:
- Existing Jekyll guide at
/IntaRNA/and API at/IntaRNA/api/. - Tested application at
/IntaRNA/gui/.
The artifact intarna-pages-preview permits PR review of the complete site.
Pull requests and manual runs on feature branches do not publish. Only a push
or manual run on BackofenLab/IntaRNA’s master may upload the Pages artifact
and use the github-pages deployment environment. Pages permissions are scoped
to that deploy job. Concurrent deployments are serialized without cancellation.
The repository’s current Pages source is already GitHub Actions. A fork would need that source enabled, its repository guard adjusted deliberately, and an environment allowing its default branch. Existing environment protection rules still apply. After merging this PR, check the workflow’s deploy job and its returned environment URL. This implementation PR does not merge itself or claim that deployment happened before merge.
Validation scope
Node tests cover input boundaries, lifecycle races, parser failures, numeric sorting and suboptimal differences. The WASM smoke test compares a real Turner/default-accessibility prediction with the native reference. Browser integration runs real WASM (no mocked scientific results), checking coordinates, energy, sorting, filtering, CSV download, mobile overflow, abort during download and active C++, clean restart, empty predictions and asset-load recovery.
Local verification, 2026-10-20
- Emscripten 4.0.23 with the exact SHA-256-pinned official archives: successful dependency probe, cross-build and real WASM smoke test. The structured-strand reference reports E = -28.04, ED1 = 4.60, ED2 = 4.86 kcal/mol; the short example reports E = -21.40 with target/query coordinates 1–10 and ten suboptimals. Invalid CLI input returns a nonzero status through C++ error handling.
npm test --prefix gui: all five input/lifecycle/CSV test groups pass.- Playwright 1.64.0, Chromium and Firefox: full integration suite passes with the
official-dependency engine, including a nested
/site/URL as used by Pages, active-C++ cancellation, literal HTML-like identifiers and retry after failure. Desktop screenshot visually reviewed; 390 px viewport has no page overflow. - Native release build with OpenMP, conda GCC 16.2.0 and ViennaRNA 2.7.2:
make tests -j2passes all eight API/CLI groups, including 462,689 assertions across 119 API cases. For the separate build directory, trackedtests/datafixtures were linked into the build’s data directory because the existing shell harness discovers fixtures relative to its working directory. - Shell syntax, workflow YAML parsing and
git diff --check: pass.
GitHub’s platform checks independently cover GCC 14 release/debug and Apple Clang. Deployment itself occurs only after merge; local browser tests do not claim a live Pages deployment.
PR #287 visualization review verification, 2026-10-26
npm test --prefix gui: all 25 protocol, results, output-visualization, archive, upload and worker tests pass. Profile and matrix cases cover signed categorical coordinates, sequence names, missing values and malformed data.- Playwright 1.64.0 with Chromium 156 and Firefox 157 passes the full real-WASM
suite, including the compact desktop/mobile parameter layout, separate CSV
tab, output tooltips, native-file headers, Plotly rendering and a PNG whose
signature is checked after download. The staged GUI uses the matching
revision
7378801engine; scientific code and compiled assets are unchanged. - Desktop screenshots for every parameter tab and the completed session were visually reviewed; the 390 px viewport has no document overflow.
- Plotly 4.1.2 is pinned by npm integrity and bundle SHA-256, staged locally with
its MIT license. Shell/Python syntax and
git diff --checkpass.
PR #287 review verification, 2026-10-21
- Emscripten 4.0.23 rebuild using the pinned official dependency archives passes
with
-Werror=inconsistent-missing-override. An all-public-header audit using-Wsuggest-override -Wsuggest-destructor-overridereports no project override diagnostics (third-party Easylogging++ is intentionally unchanged). node scripts/wasm/smoke.mjsretains the original native-reference energy, coordinates, structured accessibility and C++ error-handling checks.node scripts/wasm/smoke-protocol.mjspasses real CLI translations for default prediction, shifted negative coordinates, all-vs-all/pairwise multi-FASTA, evaluation with all CSV columns, extra reports/probability SVGs, imported accessibility and non-default energy/seed/helix/SHAPE/overlap settings. Native imported accessibility reserves one length column for dangling-end treatment; its energy is compared with the corresponding restricted native interaction length rather than the unrestricted prediction.npm test --prefix gui: ten protocol/results/lifecycle groups pass.- Real Chromium/Firefox integration covers all parameter groups, CLI defaults, compiled versions/logos, task changes, multiple records, signed coordinates, selected/visible CSV columns, additional previews/downloads and the original cancellation, empty/error, literal-ID and mobile checks. Desktop screenshot reviewed; the 390 px viewport has no document overflow.
- Native OpenMP release build with GCC 16.2.0/ViennaRNA 2.7.2:
make tests -j3passes all eight groups, including 462,689 assertions across 119 API cases. Tracked CLI fixtures are linked into the separate build data directory as in the previous verification; expected results are unchanged. - Staging preserves compiled metadata byte-for-byte; a deliberately changed
copied JS engine is rejected by its asset digest check. Shell syntax,
workflow YAML, Python syntax and
git diff --checkpass.
PR #287 follow-up verification, 2026-10-22
- All 12 Node test groups pass, including coordinate/strand mapping, non-default export settings, auxiliary inputs and report configurations.
node scripts/wasm/smoke.mjsandnode scripts/wasm/smoke-protocol.mjspass using the previously compiled Emscripten 4.0.23 engine (revisionfc62a23). The latter executes exported parameter files in fresh engines with only archived inputs and verifies byte-identical CSV, per-record outputs, probability SVGs and ASCII/detailed/ensemble reports. Cases include signed multi-FASTA, changed energy/seed settings, SHAPE and accessibility imports.- Chromium 156 and Firefox 157 with Playwright 1.64.0 pass the real-WASM browser
suite at a nested URL. It covers tab keyboard behavior, selection/encoded
viewer URLs, Python
zipfilevalidation of downloaded ZIP contents, hidden CSV columns, evaluation reports, empty results and the existing lifecycle checks. Only the third-party iframe response is stubbed in CI; computation runs the real engine. Desktop/parameter/mobile screenshots were reviewed. - A separate live Chromium check renders RNA base circles in the actual hosted
vaRRI iframe. An independent Python
zipfilecheck verifies CRCs, Unicode filenames/data and empty files in the ZIP writer’s output. - Native
make tests -j2passes all eight API/CLI groups (462,689 assertions in 119 API cases). No C++ or compiled engine changes were needed for this follow-up. - Staging, shell/Python syntax and
git diff --checkpass.