NTU VIRAL attached-storage recovery — 2026-08-17
Decision
The canonical NTU VIRAL tnp_01 acquisition is now actionable on a host whose
root filesystem cannot hold the complete working set. Exact implementation
8a856f521de825976c80c6a3c410224c4fb4e433 adds the read-only storage
resolver and bundle closure; 657746f… registers the new regression surface,
and d6e8bad… adds exact byte reporting.
This is acquisition readiness only. The blocking ntu_viral_tnp_01 release
profile remains NO_DATA; no archive, bag, conversion, benchmark result, tag,
image, or comparative claim was created.
Reproduced user problem
A fresh default preparation retains the official archive, extracted ROS 1 bag, converted rosbag2, and RKO-LIO-restamped rosbag2 until completion. The exact planner therefore requires 49,209,878,965 bytes, including its conservative 10% reserve. The final implementation-carrier dry-run observed only 6,326,681,600 bytes free on the root filesystem, an exact shortfall of 42,883,197,365 bytes.
The host already exposes one suitable attached filesystem:
- device:
/dev/sda1; - model: SanDisk Extreme Portable SSD;
- filesystem: ext4;
- label:
aiueo; - partition size: 2,000,397,795,328 bytes; and
- state: unmounted, with free capacity deliberately unverified.
The old recovery only said to choose a large path and replace --dest DIR.
That left device discovery, mounting, mount-path lookup, and command
reconstruction to the user.
Enforced recovery contract
The shared helper performs one bounded Linux lsblk read with a three-second
timeout. It accepts only writable hotplug/USB filesystems large enough to be a
candidate, excludes mounted and internal NVMe partitions, strips control
characters from optional display text, and never probes filesystem contents.
Partition size is never counted as available bytes.
The NTU command now:
- reports the candidate and
UNVERIFIED_UNTIL_MOUNTEDcapacity boundary; - chooses
udisksctl mount -b /dev/sda1as the one next action when available; - accepts mutually exclusive
--dest-device /dev/sda1; - resolves exactly one printable absolute mountpoint after the user mounts it;
- appends
ntu_viralwithout mount-path substitution; - preserves
--keep-zip,--no-convert, and--no-restampin generated retry commands; and - reruns the real free-space plan before any download or destination creation.
An absent, malformed, unmounted, ambiguously mounted, or control-character mountpoint fails closed. The helper does not mount, request a password, bypass Polkit, inspect data, write a directory, or start network work.
The exact current handoff is:
udisksctl mount -b /dev/sda1
bash scripts/download_ntu_viral_tnp01.sh \
--dest-device /dev/sda1 \
--dry-run
Only after the second command reports READY may the operator remove
--dry-run.
Release-bundle closure
docs/benchmarking.md already starts the recommended benchmark with
scripts/download_ntu_viral_tnp01.sh, but the curated candidate bundle
previously omitted that script. The release inventory now carries both the NTU
helper and scripts/attached_storage.py. The bundle regression extracts and
executes both help surfaces, so the documented first command is no longer a
source-checkout-only accident.
Validation before evidence synchronization
- shared storage, NTU, and RTK acquisition regressions: 34 passed;
- deterministic release-bundle regressions: 9 passed;
- focused total: 43 passed;
- every pytest file registered with CTest: PASS;
- changed-Python Jazzy
ament_flake8: PASS; - NTU shell parse and repository
git diff --check: PASS; - real
--dry-run: BLOCKED_INSUFFICIENT_SPACE, exact candidate and commands reported, exit 0, destination absent; and - real
--dest-device /dev/sda1 --dry-runwhile unmounted: exit 2 with the single mount action, before planning or side effects.
Clean validation carrier b01b251cb8e4b7430d730476977e4c012e091afe
passes the complete source-explicit product gate:
graph_based_slam: 1,477 passed / 13 skipped / 11 known ImageIO warnings;lidarslam: 1,040 passed; and- combined maintained Python gate: 2,517 passed / 13 skipped.
Strict MkDocs, the 317-path publication plan, shell parsing, repository diff,
and changed-Python Jazzy style also pass. The same clean carrier passes the
canonical two-build and two-reverification candidate-bundle rehearsal. Both
builds are byte-identical and contain 267 manifest files; the retained
11,959,011-byte archive has SHA-256
f963391bf76f67e27828bf0c8eadada484ac8b8b9481e5f904fcec72e1c64bad.
Its archived NTU, attached-storage, and RTK helpers execute their bounded help
or identity-list surfaces. This is local candidate evidence, not a published
release asset, and a later commit must rerun rather than reuse its checksum.