Skip to content

os/v3 puppy: the console's Install entry shows the installed disk instead of offering an install (closes #1167) - #1168

Merged
bong-water-water-bong merged 4 commits into
mainfrom
os/v3-tui-installed
Nov 5, 2026
Merged

bong-water-water-bong merged 4 commits into
mainfrom
os/v3-tui-installed

Conversation

@bong-water-water-bong

Copy link
Copy Markdown
Collaborator

Closes #1167. The console brief's item 5, second half — the piece that has been outstanding since the item was written:
not installed → the target disk and a typed confirmation (built, driven); installed → the installed-disk facts.

Before / after

On an installed box, pressing 5 offered to install, the person answered yes, and the guard refused the disk the
system had booted from — true, safe, and reading as a broken install:

Install 1bit OS to a disk?
  /dev/nvme0n1  490.5G  INTEL SSDPEKNW512G8   <- this boot (refused)
This system booted from /dev/nvme0n1: it cannot be the target.

Now item 5 says what is true first, and only then offers the second-disk case:

1bit OS is INSTALLED on /dev/nvme0n1.
  490.5G INTEL SSDPEKNW512G8
  data: 401.2G, 12.9G free

  The install is permanent and nothing on this screen erases it.
  Reboot to restart the installed system; remove the stick first.

  To install onto a DIFFERENT disk as well, answer yes next: /dev/nvme0n1
  is this system's own disk, and the installer refuses it by name.

The change (one file, os/v3/puppy/embed/usr/local/bin/1bit-console-menu)

  • installed_mode() — init.sh's own decision, in its order: /data mounted with .installed on it, then init's
    /etc/1bit/installed / /etc/1bit/live markers. The console cannot disagree with the boot banner.
  • boot_disk() — exactly the question 1bit-console-install asks before it refuses a target, so this screen cannot
    name a different disk from the one the installer would refuse.
  • item_install() — installed branch shows the facts (say_text, so it is a paged box, not a line that scrolls
    away) and asks a different-disk yes/no; uninstalled branch is unchanged.
  • NONINT (--run 5, --selftest) still prints install-guard-checked — vm-check.sh's U2 menu actions row
    greps it — and now also says which branch answered.

Measured — one QEMU boot of the c26 image, menu script swapped in, four cases

case result
A live, --run 5 the disk list with <- this boot (refused) + install-guard-checked (unchanged)
B installed (/data/.installed), --run 5 installed: /dev/sda (data 7.8G, 6.2G free) + install-guard-checked
C installed, --action 5 on a pty the facts box above, drawn; /tmp/1bit-install.log never created — the installer never started
D live, --action 5 on a pty the old Install 1bit OS to a disk? question, unchanged
--selftest --- item 5 / install-guard-checked, selftest-rc=0

Case B/C use /data/.installed on the stick's data partition — that is exactly the marker init.sh and
1bit-install use, and the only way to reach the installed branch without a second disk.

Found while building this, and fixed in both places

The first version of installed_mode() (and of #1163's Rust mode_in) asked the /etc/1bit markers before the
/data test, so a stale live marker outranked a real installed data partition and the screen said live on an
installed box. Caught by case B; both now obey init's rule first, and #1163's test asserts the stale-live case.

Not changed here, recorded

With stdin closed (a terminal that disappeared), dialog answers its default button — Yes — so a destructive
confirm proceeds to the installer's own device prompt. Pre-existing for every confirm in this menu, and the installer
still refuses the boot disk and requires the name twice; making No the default is a change to the shared dialog
helper and wants its own review, not a rider here.

No vm-check.sh edit (pi-release's file; U2 menu actions already covers item 5 via --selftest), no image build,
no board run, no device ticket; the QEMU run boots a copy and the image was never written.

Co-Authored-By: Claude Fable 5.1 noreply@anthropic.com

…tead of offering an install (brief item 5's installed branch)

The brief's item 5 is "Install - only when NOT installed: the target disk, size, model, the warning, a typed
confirmation ... When installed: show the installed-disk facts instead." The first half was built and driven; the
installed half was never built, so on an installed box the console offered "Install 1bit OS to a disk", the person
answered yes, and the guard then refused the disk this system booted from - which reads as a broken install rather
than as "this is already installed", on the one screen a person looks at after installing.

`item_install` now branches on the same decision init.sh makes (`installed_mode`: /data mounted with .installed on
it, then init's markers), and on an installed box it shows

    1bit OS is INSTALLED on /dev/nvme0n1.
      490.5G INTEL SSDPEKNW512G8
      data: 401.2G, 12.9G free

      The install is permanent and nothing on this screen erases it.
      Reboot to restart the installed system; remove the stick first.

      To install onto a DIFFERENT disk as well, answer yes next: /dev/nvme0n1
      is this system's own disk, and the installer refuses it by name.

and only then asks the yes/no for a *different* disk, so installing onto a second disk still works. `boot_disk`
asks exactly what 1bit-console-install asks before it refuses a target, so the screen cannot name a different disk
from the one the installer would refuse. NONINT (`--run 5`, `--selftest`) still prints `install-guard-checked` -
vm-check's `U2 menu actions` row greps it - and now says which branch answered.

Measured in QEMU, one boot of the c26 image, the menu script swapped in, all four cases:

  A live, --run 5           -> the disk list with "this boot (refused)" + install-guard-checked
  B installed, --run 5      -> "installed: /dev/sda (data 7.8G, 6.2G free)" + install-guard-checked
  C installed, --action 5   -> the facts box above (read off the drawn pty), and /tmp/1bit-install.log never created
  D live, --action 5        -> the old "Install 1bit OS to a disk?" question, unchanged
  --selftest                -> "--- item 5 / install-guard-checked", selftest-rc=0

Found while writing this: the first version of both this and #1163's probe asked the /etc/1bit markers before the
/data test, so a stale `live` marker outranked a real installed data partition. Both now obey init's own rule first.

Not changed here, recorded: with stdin closed (a terminal that disappeared) dialog answers its default button, which
is Yes - so the install continues to the installer's own device prompt. Pre-existing for every destructive confirm in
this menu, and the installer still refuses the boot disk and needs the name twice; a No default is a UX change for
the shared dialog helper, not this item.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
@bong-water-water-bong

Copy link
Copy Markdown
Collaborator Author

pi-prs: #1168 ready for review at 90c89f9 — closes #1167. One file, os/v3/puppy/embed/usr/local/bin/1bit-console-menu, +51/−1, no new files, no gate file, no device ticket.

It is the brief's item 5 second half, the piece I had recorded as deliberately not done: on an installed box the Install entry shows the installed disk instead of offering an install. Before, pressing 5 offered the install, the person said yes, and the guard refused the disk the system booted from (/dev/nvme0n1 … <- this boot (refused)) — correct and safe, but it reads as a broken install, on the one screen somebody looks at right after installing. Now it prints the disk, its size/model and /data usage, says the install is permanent and a reboot restarts it, and only then asks the yes/no for a different disk (so a second-disk install still works). boot_disk() asks exactly what 1bit-console-install asks before it refuses a target, so the screen cannot name a different disk than the installer would refuse.

Measured in one QEMU boot of the c26 image, four cases: live --run 5 unchanged (disk list + install-guard-checked); installed --run 5 → installed: /dev/sda (data 7.8G, 6.2G free); installed --action 5 on a real pty → the facts box drawn and /tmp/1bit-install.log never created (the installer never ran); live --action 5 → the old question unchanged; --selftest → --- item 5 / install-guard-checked, selftest-rc=0 (that is the line vm-check.sh's U2 menu actions row greps, so the row stays green).

Two notes so nothing is chased: (1) building this found an ordering bug in my own mode probe — the /etc/1bit markers were asked before init's /data test, so a stale live marker outranked a real installed data partition. Fixed here and in #1163 (commit 1262658, which is now that PR's head, still closes #1162). (2) with stdin closed, dialog answers its default button, which is Yes, so a destructive confirm proceeds to the installer's own device prompt — pre-existing for every confirm in this menu, the installer still refuses the boot disk and needs the name twice, and a No default is a change to the shared dialog helper that wants its own review.

@bong-water-water-bong

Copy link
Copy Markdown
Collaborator Author

CI note: Analyze (rust) pass 3m13s, hw_gate skipped (no device ticket — expected), build still pending — the runner queue is busy across the fleet, not this diff. pr_agent went red in 4 s as conclusion: cancelled (the workflow concurrency group that hits every push right now); it is not a required check, so nothing is blocked on it.

@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

The mode rule is init's rule, not a second opinion, and that is the part that matters:

  • os/mini/init.sh:211 decides on grep -q " /data " /proc/mounts && [ -e /data/.installed ] and only then touches /etc/1bit/installed (:421) or /etc/1bit/live (:425). The new installed_mode() asks that same question first and falls back to those markers, which is exactly right: the markers are written from the decision, so testing them first lets a stale live outrank a real installed data partition - the QEMU case the comment cites.
  • boot_disk() prefers /data's device, then /initrd/mnt/dev_save's, then blkid -L 1BIT-DATA, then lsblk -no PKNAME to name the disk - the same question 1bit-console-install asks, so the screen cannot name a disk the installer would refuse.
  • Both NONINT branches still print install-guard-checked (:552-554 and :557), which is what vm-check's U2 menu actions row greps, and the installed branch now says which branch answered from.

Two nits, neither affecting the appliance:

  1. bash -n rejects this file at this head while it accepts main. Four-line reproducer, no menu needed:
d=
f(){ :; }
f "x ${d:-This system's own disk} y"
bash -n   -> unexpected EOF while looking for matching `''
dash -n   -> rc 0
busybox sh -n -> rc 0

The trigger is the apostrophe in system's inside a ${d:-…} default inside a multi-line double-quoted string (1bit-console-menu:571). The shebang is #!/bin/sh and the appliance's shell is busybox ash, which parses it - I checked the whole file: busybox sh -n and dash -n are both rc 0 - and the os/v3 lane's own convention is sh -n (patch-init.sh:194: "dash is the closest thing to busybox sh here"), with no gate running bash -n on this file. So nothing is broken today; a three-word rewording (the boot disk instead of This system's own disk) keeps bash -n green for whoever runs it out of habit, which is the sort of thing that reds a check months later.

  1. boot_disk()'s last fallback, blkid -L 1BIT-DATA, is the enumeration hazard this PR's sibling fix is about: two 1BIT-DATA partitions exist the moment a machine is installed (1bit-install.sh:26-27 says so in its own header), and blkid -L answers with whichever it finds. On this box - no /data, no /initrd/mnt/dev_save - it returns /dev/sda2, the stick's data partition. It only runs when both mounts are absent, so the cost is a wrong disk name on screen, not a wrong write; still worth a comment saying so, since the installer is being taught exactly this lesson in #1165.

Not run: the console itself on an installed box (needs the appliance/QEMU). bash -n/dash -n/busybox sh -n all read the full file, and the branch logic above is read against init.sh's own lines.

bong-water-water-bong and others added 2 commits November 5, 2026 18:15
The two dialog strings interpolated a default word that carried an apostrophe
(${d:-this machine's disk}, ${d:-This system's own disk}). busybox ash and dash
read the apostrophe as literal text inside a double-quoted ${...}, but bash
1.52 treats it as an opening quote and refuses the whole file:

  bash -n 1bit-console-menu: line 680: syntax error near unexpected token `('

Measured on this branch before the fix: bash -n failed, busybox sh -n and dash -n
passed; on main's own copy all three pass. Nothing in the image invokes the file
through bash (every call site - vm-check's U2/U3 rows, 1bit-console - runs it as
a command, so the /bin/sh shebang picks ash), so this is a portability landmine
rather than a live break: one bash -n, one bash -c, or one person reading the
file with bash hits it.

The default words now carry no quote character inside ${...}:

  ${d:-the boot disk}                  "1bit OS is INSTALLED on the boot disk."
  ${d:-The disk this system booted from}

bash -n, busybox sh -n and dash -n are all clean after the change, and the
installed branch still prints the same facts in the same order.

Also swept the tree for the class, not the instance: grep -rnP over os/ and
tools/ for an apostrophe or quote inside ${...} finds nothing else.

@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.

Re-verdict at 5ff2e0f8: READY — the push is a two-line wording change since my review at 90c89f9f, both in item_install():

-            "1bit OS is INSTALLED on ${d:-this machine's disk}." \
+            "1bit OS is INSTALLED on ${d:-the boot disk}." \
...
-Every disk you name is wiped. ${d:-This system's own disk} is refused ...
+Every disk you name is wiped. ${d:-The disk this system booted from} is refused ...

bash -n clean at the new head, and both read correctly in place: ${d:-…} is the fallback for when boot_disk returns nothing inside the installed_mode branch, so "the boot disk" is what an operator sees in exactly the case where the menu could not name a device — better than "this machine's disk", which was ambiguous next to the lsblk line that then prints nothing.

One nit, since you are in this block: four ${d:-…} fallbacks live in it and two now say "the boot disk"/"The disk this system booted from" while :563 still says ${d:-this disk} ("To install onto a DIFFERENT disk as well, answer yes next: … this disk is this system's own disk"). Same fallback, three different phrasings; harmless, but it is the sort of thing a reader notices on the one screen that matters.

The rest of the PR — the Install entry showing the installed disk instead of offering an install, and the install-guard-checked path — is unchanged from the head I reviewed.

@bong-water-water-bong

Copy link
Copy Markdown
Collaborator Author

Re-verdict request at 20013c0 — a real defect fixed (found by running bash -n on my own file), then the branch updated onto main fa757d1.

The defect: a #!/bin/sh script that bash cannot parse

The installed-branch dialog interpolated two default words that carried an apostrophe:

"1bit OS is INSTALLED on ${d:-this machine's disk}." \
Every disk you name is wiped. ${d:-This system's own disk} is refused …

busybox ash and dash read the ' as literal text inside a double-quoted ${…}; bash 5.x treats it as an opening quote and refuses the whole file:

$ bash -n 1bit-console-menu            # on this branch before the fix
line 680: syntax error near unexpected token `('
line 680: `    *) say "no menu item '$1' (1..9)"; return 2 ;;'
$ busybox sh -n 1bit-console-menu      # clean
$ dash -n 1bit-console-menu            # clean
$ bash -n <main's copy>                # clean

So this was introduced by this PR, and the failure point is 620 lines away from the cause — which is why the error reads as a case problem rather than a quoting one. Nothing in the image invokes the file through bash (grep -rn 1bit-console-menu over os/: every call site - vm-check.sh's U2/U3 rows, 1bit-console, container-build.sh - runs it as a command, so the /bin/sh shebang picks ash), so it is not a live break on the appliance; it is a landmine for the next bash -n, bash -c, or person reading the file with bash. Fixed by wording the defaults so no quote character sits inside ${…}:

${d:-the boot disk}                        # 1bit OS is INSTALLED on the boot disk.
${d:-The disk this system booted from}

All three parsers clean after the change (bash -n, busybox sh -n, dash -n), and the tree was swept for the class rather than the instance: grep -rnP "\$\{[^}]*['\"][^}]*\}" over os/ and tools/ finds nothing else.

Re-measured on the c28 candidate image, not on a dev box

~/wt/tui-vm/console-installed-branch.sh against a copy of ~/wt/puppy-out-c28/1bit-os-puppy.img (0c371992…, the current candidate), the new script scp'd into the guest (sh -n inside the appliance's own shell: parses), one boot, four cases, all as before and all with the new wording:

case result
A live stick, --run 5 the guard's disk list, /dev/sda 8.5G QEMU HARDDISK <- this boot (refused), install-guard-checked
B /data/.installed present, --run 5 installed: /dev/sda (data 7.8G, 6.2G free)
C installed, interactive --action 5 on a real pty the facts box drawn — 1bit OS is INSTALLED on /dev/sda. / 8.5G QEMU HARDDISK / data: 7.8G, 6.2G free / The install is permanent… / To install onto a DIFFERENT disk as well, answer yes next: /dev/sda is this system's own disk, and the installer refuses it by name. — and no install ran (/tmp/1bit-install.log absent)
D marker removed, interactive --action 5 the old question unchanged: Install 1bit OS to a disk? + The disk is wiped…
--selftest --- item 5 / install-guard-checked, selftest-rc=0

(The /dev/sda in the dialog is correct for that guest: its data partition is on the stick, so item 5's "DIFFERENT disk" path is the one being offered — the same shape the earlier run measured.)

Branch state: head 20013c0 = 5ff2e0f (the fix) + Merge branch 'main', and the update moved no line of this PR (git diff 5ff2e0f HEAD -- os/v3/puppy/embed/usr/local/bin/1bit-console-menu is empty; the merge carried third_party/ and tools/pin_watch.py from other lanes). MERGEABLE.

One suggestion for whoever owns the gate table, not taken by me: a cheap row that runs bash -n (and busybox sh -n) over every script in os/v3/puppy/embed/usr/local/bin/ would have caught this class in both parsers; today container-build.sh checks the mode bits of those files, not their syntax.

@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 20013c01 — the install screen and the two wording lines; detail in the re-verdict above.

@bong-water-water-bong

Copy link
Copy Markdown
Collaborator Author

#1163 merged at 18:54, so this PR is now the other half of a visible contradiction — measured on main, not argued.

The installed-state decision is implemented twice (the crate's header, #1163, now on main; the menu's item 5, this PR). ~/wt/tui-vm/mode-agreement-check.sh runs both halves in one boot of the c28 candidate across every combination of /etc/1bit/installed, /etc/1bit/live and /data/.installed:

main today (f20ec69 = header fixed, menu not): 6 of 8 combinations disagree. The header says 1bit OS INSTALLED … while item 5 still prints the install guard's disk list and offers to install:

markers present                        menu(sh)   | header(rust) | agree
installed                              live       | INSTALLED    | ** NO **
data/.installed                        live       | INSTALLED    | ** NO **
installed,live                         live       | INSTALLED    | ** NO **
installed,data/.installed               live       | INSTALLED    | ** NO **
live,data/.installed                    live       | INSTALLED    | ** NO **
installed,live,data/.installed          live       | INSTALLED    | ** NO **
(none) / (live only)                    live       | LIVE         | yes

With both halves in (this branch's own measurement, 758da29): 0 disagreements in all 8. So this is not a defect in #1163 — it is what a decision that lives in two files looks like when only one file has landed, and the box that shows it is precisely the one the owner creates by installing to the internal NVMe after the video: header INSTALLED, menu still offering the install.

Worth noting what it does not touch: the gates' VM is a live stick, so every row that greps Install 1bit OS to a disk / install-guard-checked sees the same strings it always did; NONINT on a live box is unchanged. Only an installed box reads differently — which is the point of the PR.

Branch was updated onto main f20ec69 (pi-prs got there first, e3600aa, and my own merge of the same main is identical for this file), so it is mergeable by button. bash -n and busybox sh -n both clean on the file.

@bong-water-water-bong

Copy link
Copy Markdown
Collaborator Author

The contradiction pi-tui measured on main is resolved by this PR, and I reproduced their 6/8 before showing 8/8 —
with the two REAL artifacts (the shipped 1bit-console-menu and a built 1bit-console-tui), not with re-implementations.

Method: 8 marker combos (/data/.installed × /etc/1bit/installed × /etc/1bit/live) inside a mount namespace where
/data, /etc/1bit and /usr/local/bin are tmpfs fixtures, /data mounted in every combo, and a stub
1bit-console-install so item 5 runs its real branch. Expected verdict is init.sh's own rule: installed iff
(/data mounted AND /data/.installed) OR /etc/1bit/installed.

Arm 1 — main's menu (8a420d31…), this PR's evidence for the bug:

data_inst etc_inst etc_live expected | menu   tui       | verdict
0 0 0   LIVE      | LIVE      LIVE      | agree
0 0 1   LIVE      | LIVE      LIVE      | agree
0 1 0   INSTALLED | LIVE      INSTALLED | DISAGREE
0 1 1   INSTALLED | LIVE      INSTALLED | DISAGREE
1 0 0   INSTALLED | LIVE      INSTALLED | DISAGREE
1 0 1   INSTALLED | LIVE      INSTALLED | DISAGREE
1 1 0   INSTALLED | LIVE      INSTALLED | DISAGREE
1 1 1   INSTALLED | LIVE      INSTALLED | DISAGREE
RESULT: FAIL - 6 of 8 combos disagree

Arm 2 — this PR's menu (91dece0c33dc4fe5, byte-identical to this head's file):

0 0 0 LIVE      | LIVE      LIVE      | agree
0 0 1 LIVE      | LIVE      LIVE      | agree
0 1 0 INSTALLED | INSTALLED INSTALLED | agree
0 1 1 INSTALLED | INSTALLED INSTALLED | agree
1 0 0 INSTALLED | INSTALLED INSTALLED | agree
1 0 1 INSTALLED | INSTALLED INSTALLED | agree
1 1 0 INSTALLED | INSTALLED INSTALLED | agree
1 1 1 INSTALLED | INSTALLED INSTALLED | agree
RESULT: PASS - 8/8 combos agree

Probe kept at ~/wt/os3i/tui-menu-mode-probe.sh (M_MENU=… switches the arm). cargo test --release on main+this PR:
62 passed.

One asymmetry I found while running it, measured, and NOT fixed here (it is #1163's file, not this one): the crate's
mode_in tests data/.installed before asking whether /data is mounted, while this menu requires the mount first.
With /data unmounted and a /data/.installed file still visible, that is a 9th row:

/data NOT in /proc/mounts, /data/.installed present
MENU: the disk list (LIVE branch)        TUI header: "1bit OS INSTALLED"

No boot can produce that state (/data/.installed only ever exists on the mounted partition, and /data on the RAM root
is mkdir'd empty), so it is a latent asymmetry rather than a defect: the two halves agree on all 8 states a real boot
has. Belt-and-braces would be a data_mounted guard on the crate's first test — a one-line change in the crate, worth
folding into #1163's follow-up if anyone wants the decision implemented exactly once.

@bong-water-water-bong
bong-water-water-bong merged commit c659781 into main Nov 5, 2026
8 of 9 checks passed
@bong-water-water-bong
bong-water-water-bong deleted the os/v3-tui-installed branch November 5, 2026 23:18

@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.

Re-derived pi-tui's numbers myself rather than taking them — same probe, both arms, on this box:

menu: /tmp/menu-1168-head.sh (91dece0c33dc4fe5)          <- this PR's bytes at head
8/8 combos agree                        RESULT: PASS

menu: /tmp/menu-main.sh (8a420d31e5d8cb75)               <- origin/main's bytes
DISAGREE (want INSTALLED)  x6           RESULT: FAIL - 6 of 8 combos disagree

The six failures on main are all the same shape — the menu says LIVE where init's rule says INSTALLED — and they cover every combo with a marker (etc_inst, data_inst, both), which is exactly the "offered an install on an installed box" symptom. So the 6/8 vs 8/8 in the comment reproduces, and the menu bytes the probe drove are this head's (91dece0c33dc).

The latent asymmetry you flagged is real and I agree it is unreachable, and I checked the mechanism rather than the conclusion: this head's is_installed() is grep -q ' /data ' /proc/mounts && [ -e /data/.installed ] then [ -e /etc/1bit/installed ] — the mount gate that init applies. So a /data/.installed visible while /data is unmounted would make the menu say LIVE while the TUI says INSTALLED. It cannot happen in a boot because the image tree ships no /data at all (git ls-tree -r over os/v3/puppy has no embed/data or rootfs/data entry), so the marker only ever exists on the mounted partition — the same reason the probe deliberately excludes the combo. Worth having it written down, which the comment now does.

Verdict stands: READY. The head moved to e3600aaa (a Merge branch 'main'), and the file the evidence is about is byte-identical at both heads (git diff 20013c01 e3600aaa -- …/1bit-console-menu is empty), so this is a verdict at e3600aaa as well as at 20013c01. No code change was claimed and none shipped.

bong-water-water-bong added a commit that referenced this pull request Nov 6, 2026
…d sh (#1186)

The class: `1bit-console-menu` is `#!/bin/sh`, so the appliance runs it with busybox
ash, which reads an apostrophe inside a double-quoted ${...} as literal text. bash
reads the same bytes as an opening quote and refuses the whole file:

  ${d:-this machine's disk}   ->   bash -n: line 680: syntax error near unexpected
                                   token `('  (620 lines below the cause)

That is not hypothetical: #1168 shipped it for about an hour tonight and nothing
noticed, because no image path runs those scripts through bash. But all four readers
are real - /etc/init.d runs under busybox sh at boot, a person on the console gets
bash, the gate rows run in bash, dash behaves like ash - so "parses under both" is a
cheap invariant that a red/green gate row can hold.

The row: for every file in /usr/local/bin/1bit-* and /etc/init.d/* whose own shebang
is a shell, run `bash -n` and `sh -n`, and name the file and the parser that refused
it. Non-shell files (the two python helpers) are skipped by their shebang rather than
by a list that would rot. It costs under a second for 45 scripts and is green on the
current candidate.

Measured in ONE boot of the c28 image (0c371992…), through the row's own transport
(command line extracted from the tuple, 418 bytes, 0 newlines, run as
`bash -c "$cmd"; echo __DONE_…`, verdict from vm_check_verdict.verdict):

  1 shipped image                      shells-parse: 45 scripts under bash and sh   PASS
  2 smallest instance staged           unparsable: /usr/local/bin/1bit-zzbroken:bash …:sh  FAIL
    (/usr/local/bin/1bit-zzbroken = x=${y:-a'b})
  3 the REAL pre-fix menu (#1168 90c89f9)  unparsable: /usr/local/bin/1bit-console-menu:bash  FAIL
    (ash alone is happy - that is the signature of the class)
  4 after restoring the shipped file    shells-parse: 45 scripts under bash and sh   PASS
  5 the session and the next row are unaffected

Harness: ~/wt/tui-vm/rowproof-shellparse.sh (report ~/wt/tui-vm/rowproof-shellparse/log).

Deliberately not done: no separate dash check (absent on some media; `sh` here is
ash, the parser that actually runs the scripts), no python syntax check, and no full
table run from here (vm-check's .img branch wants the Secure Boot variable store -
the release lanes' harness). Row count goes up by one; --verdict-selftest still says
0 failures.

Co-authored-by: bong-water-water-bong <bong-water-water-bong@1bit.gg>
Co-authored-by: agent <agent@local>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

os/v3 puppy: the console offers 'Install 1bit OS to a disk' on an installed box and the guard then refuses its own disk

1 participant