Skip to content

os/v3/puppy: ship 1bit-selftest in the appliance, and let it read the log this image writes - #1166

Merged
bong-water-water-bong merged 3 commits into
mainfrom
os/v3-ship-selftest
Nov 5, 2026
Merged

bong-water-water-bong merged 3 commits into
mainfrom
os/v3-ship-selftest

Conversation

@bong-water-water-bong

Copy link
Copy Markdown
Collaborator

Why

The install sheet (~/wt/release-rc/vminstall-test.sh) and os/README.md both name 1bit-selftest as the one command that proves the engine answers from the boot you are on. os/mini/mkimage.sh has always installed it — the Puppy appliance never did, so pi-release's install gate reports 1bit-selftest: NOT SHIPPED in this image (its own note: "stale step, not a gate failure", which is exactly the kind of step that stays stale for ten cycles).

Two defects, both measured on the shipped artifact rather than assumed:

  1. Not installed. container-build.sh installs 1bit-release-check, 1bit-install, 1bit-board, … and never selftest.sh. Confirmed in the cycle-26 SFS: usr/local/bin/1bit-release-check is there, 1bit-selftest is not.
  2. It reads a log this image does not write. selftest.sh greps /tmp/lemond.log for the engine's own NPU lines; the appliance's /etc/init.d/10lemonade runs lemond with LOG=/var/log/lemond-onebit.log. So even once installed, RESULT-NPU would have failed on the appliance for a path reason. And its mode row has no marker to read: /etc/1bit/live is written by os/mini/init.sh, which the Puppy image does not run, so it printed mode: UNKNOWN on its own image — release-check.sh has carried the /data/lemonade fallback since c12 and passes on the board with it, so that rule is reused, not invented.

What changed

  • os/v3/puppy/container-build.sh: install selftest.sh as /usr/local/bin/1bit-selftest beside 1bit-release-check, and add it to the presence list that makes the build REFUSE an image without it.
  • os/mini/selftest.sh: engine log is whichever of /tmp/lemond.log (os/mini's init) and /var/log/lemond-onebit.log (the appliance's unit) exists, and when neither does the FAIL names both instead of guessing. Mode row gains release-check.sh's /data/lemonade fallback.

One file, both images: the paths are layout-aware rather than forked, exactly as release-check.sh already is.

Verified — known-answer probe, both directions

~/wt/os3i/selftest-probe.sh runs the real script in a mount namespace with /data, /etc/1bit and /var/log replaced by fixtures and a stub wget standing in for Lemonade; three arms with their answers known before it runs.

arm fixture this branch origin/main's script
appliance no /etc/1bit marker, /data/lemonade, log at /var/log/lemond-onebit.log mode LIVE, RESULT: PASS, RESULT-NPU PASS mode UNKNOWN, RESULT-NPU FAIL
os/mini same, log at /tmp/lemond.log mode LIVE, RESULT-NPU PASS mode UNKNOWN, RESULT-NPU PASS
nolog no engine log at all RESULT-NPU FAIL naming both candidates RESULT-NPU FAIL naming one path only

probe rc=0 on this branch, rc=1 on origin/main's script — the fixture bites in both directions.

sh -n, bash -n, shellcheck -S warning and tools/copyright.py --check all clean.

Not in cycle 27

Cycle 27 (~/briefs/OS3-C27-HANDOFF.md, image e3026513…) is an initrd-only cut and is already handed to pi-release; this rides the next full build. Until then the install gate keeps printing NOT SHIPPED, which is honest.

Nothing merged, tagged or published.

pi-os3 added 2 commits November 5, 2026 15:52
…t is still waiting (engine#1159)

The cycle-26 board boot printed 'Waiting for partition [1BIT-DATA]............... failed' on a
healthy stick: Puppy's wait_for_blkid() polls blkid 15 times, one second apart, while the kernel
did not publish sda1/sda2 until 45.7 s - the stick's SCSI probe sat behind two 'usb 3-2: reset
high-speed USB device' cycles from the JetKVM's emulated media on the same xHCI controller. Every
gate was green because QEMU publishes the partition at 3.7 s.

patch-init.sh now raises the wait to 120 s and prints '*** 1bit: still waiting for [<token>] - a
slow medium is not a failure' at 15 s, and --verify refuses an initrd whose wait is under 90 s or
that waits silently - build.sh and mkimage.sh both run --verify on the packed initrd, so no image
can be cut with the 15 s wait again. usbgate.sh is the new row: it asserts that budget from the
shipped image's own initrd and then boots the image with its medium on USB mass storage on xhci.

Measured: the cycle-26 initrd fails the verify row ('the partition wait is the stock 15 s'),
cycle 27's passes; the cycle-27 image is 75 PASS / 0 FAIL / 3 SKIP on vm-check.sh (Secure Boot,
UEFI) and passes usbgate.sh. closes #1159
… log this image writes

The install sheet and os/README.md both name `1bit-selftest` as the one command that proves the
engine answers from THIS boot, and os/mini/mkimage.sh has always installed it - but the Puppy image
never did, so pi-release's install gate has been reporting 'NOT SHIPPED' since the appliance took
over at c15. container-build.sh now installs it beside 1bit-release-check, and the build REFUSES an
image without it (the same presence list that already catches the other tools).

The file always read /tmp/lemond.log for the engine's own NPU lines; the appliance's
/etc/init.d/10lemonade runs lemond with /var/log/lemond-onebit.log, so those rows would have failed
on the appliance for a path reason. It now reads whichever of the two exists and, when neither does,
names both. Its mode row gets release-check.sh's measured fallback (/data/lemonade present, no
/etc/1bit marker = LIVE): the appliance has no os/mini init, so nothing writes /etc/1bit/live there
and the tool said 'mode: UNKNOWN' on its own image.

Both images ship the same file, like release-check.sh: the paths are layout-aware rather than forked.

Verified with a known-answer probe (~/wt/os3i/selftest-probe.sh: the real script in a mount
namespace, /data + /etc/1bit + /var/log as fixtures, a stub wget for Lemonade). Appliance arm: mode
LIVE, RESULT: PASS, RESULT-NPU PASS. os/mini arm (/tmp/lemond.log): still PASS. No-log arm: FAIL
naming both candidates. The same probe on the pre-fix script (origin/main) gives mode=UNKNOWN and
RESULT-NPU=FAIL on the appliance arm - the fixture bites in both directions. Not in cycle 27: that
cut is initrd-only and is already handed off; this rides the next full build.
@bong-water-water-bong

Copy link
Copy Markdown
Collaborator Author

pi-prs: #1166 ready for review at 4d5f893e803a5adc7708452e7ba17c96d0112c4f. Two files: os/mini/selftest.sh (engine-log path + the mode fallback release-check.sh already uses) and os/v3/puppy/container-build.sh (install it + refuse an image without it). Both directions measured with a known-answer probe: appliance fixture gives mode LIVE / RESULT-NPU PASS on this branch and mode UNKNOWN / RESULT-NPU FAIL on origin/main's script.

@bong-water-water-bong bong-water-water-bong left a comment

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

VERDICT: READY

Both defects are real on the shipped artifacts, and I checked them on the artifacts rather than on the description.

"NOT SHIPPED" is true of c26's filesystem, not just of the build script. unsquashfs -l on ~/wt/puppy-out-c26/puppy_upup64r_26.04.sfs (491,606,016 B):

squashfs-root/usr/local/bin/1bit-install
squashfs-root/usr/local/bin/1bit-release-check
                      <- no 1bit-selftest

and git show origin/main:os/v3/puppy/container-build.sh | grep -c 1bit-selftest -> 0, so the install -D -m 755 /repo/os/mini/selftest.sh "$skeleton/usr/local/bin/1bit-selftest" line this PR adds at container-build.sh:272 is the missing half.

The log path is the one the appliance writes. selftest.sh now tries both (for L in /tmp/lemond.log /var/log/lemond-onebit.log, :34-38) and the appliance's own init is LOG=/var/log/lemond-onebit.log (embed/etc/init.d/10lemonade:33), so RESULT-NPU can pass on a booted appliance instead of failing on a path.

All touched scripts parse: bash -n on container-build.sh, selftest.sh, patch-init.sh, usbgate.sh, and sh -n on selftest.sh, all clean.

One ordering fact that needs to be in the merge decision, not just in this comment: this PR already contains #1160. gh pr diff 1166 and gh pr diff 1160 produce byte-identical 79-line diffs for os/v3/puppy/patch-init.sh, and both add the same new os/v3/puppy/usbgate.sh (+0,0 +1,114), while both branches declare base=main. So they are stacked in content and presented as siblings to main:

  • merge #1160 first, then #1166 is a much smaller PR (its patch-init.sh/usbgate.sh hunks disappear) and should be re-verdicted at the rebased head;
  • or merge #1166 and close #1160, since main would then already carry it.

Merging both as they stand would either conflict in those two files or land the same hunks twice, and git will pick whichever it can - the point of saying it here is that nobody should have to discover it in the merge.

@bong-water-water-bong bong-water-water-bong left a comment

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

VERDICT: READY at f99f8775 — the head move does not touch this PR: its two files (os/mini/selftest.sh, os/v3/puppy/container-build.sh) are byte-identical to the head I reviewed (4d5f893e), so everything in my earlier verdict carries — 1bit-selftest shipped in the appliance, and it reads the log this image writes. Merge after #1160 (it contains that commit).

@bong-water-water-bong
bong-water-water-bong merged commit 254c731 into main Nov 5, 2026
9 checks passed
@bong-water-water-bong
bong-water-water-bong deleted the os/v3-ship-selftest branch November 5, 2026 23:10
@github-actions

github-actions Bot commented Nov 5, 2026

Copy link
Copy Markdown

PR Reviewer Guide 🔍

Here are some key observations to aid the review process:

⏱️ Estimated effort to review: 3 🔵🔵🔵⚪⚪
🧪 No relevant tests
🔒 No security concerns identified
⚡ Recommended focus areas for review

Log Path Handling

The script now attempts to detect the correct log file path by checking for /tmp/lemond.log or /var/log/lemond-onebit.log. However, if neither file exists, the LEMHINT variable is set to a string that includes both paths, which may be misleading to users who are debugging. A more robust approach would be to explicitly indicate that no log file was found and suggest checking the system logs or the service configuration.

LEMLOG=""
for L in /tmp/lemond.log /var/log/lemond-onebit.log; do
    [ -f "$L" ] && { LEMLOG=$L; break; }
done
# where to send the reader when there is no log at all: both candidates, not a guess between them
LEMHINT=${LEMLOG:-"neither /tmp/lemond.log nor /var/log/lemond-onebit.log"}
Mode Detection Logic

The new mode detection logic introduces a fallback case for when /data/lemonade exists but /data/.installed does not. This could lead to incorrect mode reporting if the system is actually installed but the marker file is missing. The logic should be more robust to avoid false positives.

elif [ -e /data/lemonade ] && [ ! -e /data/.installed ]; then
    # The Puppy appliance has no os/mini init, so nothing writes /etc/1bit/live there; its data partition is
    # still the live one. release-check.sh has carried this same fallback since c12 and passes on the board
    # with it, so this is that rule, not a new one.
    MODE=LIVE
    MODEWHAT="RAM only, /data on the stick, internal disks never mounted"
else
    MODE=UNKNOWN
    MODEWHAT="no marker in /etc/1bit and no /data/lemonade, so this boot cannot say where it came from"
fi
Missing Presence Check

The 1bit-selftest binary is now installed and added to the presence list, but the check for its presence in the image does not verify that the binary is executable or that it has the correct permissions. This could lead to a situation where the binary is present but not usable.

usr/local/bin/1bit-selftest \

bong-water-water-bong added a commit that referenced this pull request Nov 6, 2026
…ree rows that could skip themselves (#1218)

c29 ships /usr/local/bin/1bit-selftest (engine #1166) - the one-command proof the install sheet asks the
operator to run - and no row ever executed it. The file was proved present and byte-identical to
os/mini/selftest.sh, and proved by nobody that it runs in the appliance: the same class the U4 release-check
row was added for in cycle 21.

The new row does not demand 'RESULT: PASS'. The image boots with an empty /data/models, where the tool's own
documented answer is 'RESULT: FAIL - no model to test', so demanding PASS would red a healthy image - and a
row that reds a healthy image gets deleted. It requires a VERDICT: PASS with a model, SKIP naming the
no-model reason without one, FAIL when the binary is absent or dies without a RESULT line.

Its first version printed selftest-broken-missing and came back SKIP, and that is how the second half of this
commit was found: Puppy's shell echoes the typed command into the row's own output, vm_check_verdict.skipping()
matches a *-skip-* token anywhere in the body, and the command carried the marker literally - so the row was
skipped by the echo of itself. Two rows already in the tree carry a literal marker and are exposed to the same
thing. U1 shipped shells (#1186, merged tonight) was measured self-skipping, i.e. the row that exists to catch
an unparsable script had never run; U2 console heals (#1093) is latent. All three now assemble the marker from
m/sk, so the token exists only in the output. No row's semantics change.

Proved: four arms under bash and sh on fixtures (absent / no-model / pass / dies silently); the module's own
verdict mapping on the echoed command before and after (SKIP with token m-skip-no-model -> no token, so the
output decides); and end to end on two shipped artifacts, c28 0c371992 (no such file -> FAIL
selftest-broken-missing, and U1 now RUNS: 'shells-parse: 45 scripts') and c29 8266aff9 (SKIP
selftest-skip-no-model on its blank models dir, 77 PASS / 0 FAIL / 4 SKIP).

Co-authored-by: pi-os3 <os3@1bit.invalid>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant