fix: correct verify-build diagnostics and close its vacuous passes (closes #180) #203

Merged
clawbot merged 1 commits from fix/issue-180-verify-build-diagnostic into next 2026-08-11 14:55:07 +02:00
Collaborator

Closes #180. Base next;
rebased onto current next (cf5f582) immediately before pushing.

1. The both-markers diagnostic

Old:

> the build-time DEBUG value was never resolved and the debug branch is still
> live

New:

> dist/chrome/src/background/index.js carries both debug markers, so DEBUG
> was not resolved at build time: the ternary in src/shared/constants.js
> survived into the emitted output. This does not mean the debug branch is live
> in this artifact: an unresolved __BUILD_DEBUG__ is undeclared in extension
> context, so DEBUG evaluates to false at runtime. It does mean the
> release/debug distinction is no longer enforced at build time, and which way
> that fallback happens to evaluate is then an accident a refactor can flip.
> Check that build.js still defines __BUILD_DEBUG__.

What the check proves: that DEBUG was resolved at build time to the marker
the build asked for, in every emitted bundle that contains constants.js, and
that no other emitted file carries a marker.

What it does not prove: anything about the runtime behaviour of an artifact
that failed the check. Confirmed against the emitted output with the define
deleted: typeof __BUILD_DEBUG__<"u"?__BUILD_DEBUG__:!1, so the fallback goes
the safe way. Still a hard failure — verify-build exits 1, make build exits 2.

The other seven failure messages were reviewed for the same overstatement;
none needed changing.

2. grep exit 2 no longer conflated with exit 1

has_marker dropped 2>/dev/null. Exit 0 and 1 are answers about the emitted
output; exit 2 is not an answer at all and gets its own message.

The manifest-membership grep now gets the same case discipline, as
is_listed. It was fail-closed before (an unreadable manifest was answered as
"this file is not listed", misdiagnosing every emitted bundle as an unlisted
one), but it is the conflation the issue asked to remove, two lines from where
it was removed.

3. *.js coupling removed, and the scan made exhaustive

check_unlisted_bundles no longer filters by extension, so the
endsWith(".js") test in build.js is the only place the assumption lives and
this cross-check is what catches it being wrong. A comment at each site names
the other.

That makes the scan the sole guard on build.js's filter, so its walk has to
be exhaustive rather than assumed to be. Two holes closed:

find's exit status was discarded twice over. The pipeline reported
sort's status, and set -e does not fire on an assignment from a successful
pipeline. A subtree find could not descend printed to stderr and was then
silently omitted from the listing — "could not look" reported as "nothing was
there", the same conflation has_marker exists to prevent. The status is now
captured and a non-zero find is a hard failure naming the unwalked tree; the
sort moved off the status-bearing pipeline.

Symlinks. -type f skipped them, so a marker-carrying unlisted bundle
placed as a symlink passed green. Decision: symlinks are walked
(-type f -o -type l), not skipped and not specially exempted.
A
marker-carrying bundle reachable under an unlisted path in dist/ is a stale
manifest whether that path is a link or a regular file, and grep reads
through the link, so the marker check answers correctly. A link that cannot be
read through — dangling, or pointing at a directory — fails hard via the exit-2
path rather than being skipped: the build emits neither, so such a link's DEBUG
state is unproven, which under this script's contract is a failure and not a
pass. Symlinked directories are therefore not descended into; they fail as
unreadable instead, which is the fail-closed answer.

Also: vacuous inputs

Per the script's own contract, an unreadable manifest and an empty listed
bundle are distinct hard failures rather than paths into a marker check that
cannot prove anything.

Evidence

Run as uid 1000. verify-build exits 1 (make build exits 2) in every case
below.

find cannot enumerate a subtree — the case that used to pass green

$ mkdir -p dist/hidden && cp dist/chrome/src/popup/index.js dist/hidden/sneaky.js
$ chmod 000 dist/hidden && script/verify-build
Verifying emitted bundles (expecting autistmask-build-debug=off)...
  ok: dist/chrome/src/background/index.js (autistmask-build-debug=off)
  ok: dist/chrome/src/popup/index.js (autistmask-build-debug=off)
  ok: dist/firefox/src/background/index.js (autistmask-build-debug=off)
  ok: dist/firefox/src/popup/index.js (autistmask-build-debug=off)
find: 'dist/hidden': Permission denied
verify-build: FAIL: find exited 1 enumerating dist/, so part of the tree
    was never walked and nothing was established about the files in it. Any
    unlisted bundle there went unchecked. That is a permissions or I/O fault on
    the artifact, not a stale manifest. Refusing to report success.
exit=1

Before this change the same tree printed Permission denied and then
verify-build: 4 bundle(s) verified autistmask-build-debug=off, exit=0.

With the directory readable again, the file inside it is caught on its own
terms rather than as an enumeration fault:

$ chmod 755 dist/hidden && script/verify-build
verify-build: FAIL: dist/hidden/sneaky.js carries a debug marker but is absent from dist/constants-bundles.txt,
    so the manifest no longer describes the emitted bundles.
exit=1
### symlink to an unlisted marker-carrying file (passed green before)
verify-build: FAIL: dist/link.js carries a debug marker but is absent from dist/constants-bundles.txt,
    so the manifest no longer describes the emitted bundles.

### symlink to a LISTED bundle, under an unlisted path
verify-build: FAIL: dist/alias.js carries a debug marker but is absent from dist/constants-bundles.txt,
    so the manifest no longer describes the emitted bundles.

### dangling symlink
verify-build: FAIL: grep exited 2 reading dist/dangling.js, so the file could not be
    searched and its DEBUG state was not checked at all. ...

### symlink to a directory
verify-build: FAIL: grep exited 2 reading dist/dirlink, so the file could not be
    searched and its DEBUG state was not checked at all. ...

Unreadable manifest reaching the membership test

Unreachable in a single run today — main's [ -r "$MANIFEST" ] guard fires
first — so this exercises is_listed directly against the script's own
function text, with main "$@" stripped and an unreadable file as MANIFEST.
It exists so a manifest that becomes unreadable mid-run is not answered as a
stale manifest:

verify-build: FAIL: grep exited 2 reading /tmp/<manifest>, so it could not be
    searched and nothing was established about which bundles it lists. That is
    a permissions or I/O fault on the manifest, not a stale manifest. Refusing
    to report success.

Previously covered modes, all re-run on this head

Both markers (__BUILD_DEBUG__ define deleted from build.js, rebuilt):

verify-build: FAIL: dist/chrome/src/background/index.js carries both debug markers, so DEBUG was not resolved at
    build time: the ternary in src/shared/constants.js survived into the
    emitted output. ...
make: *** [Makefile:44: build] Error 1

Missing manifest, empty manifest, unreadable manifest (chmod 000), missing
listed bundle, empty listed bundle (truncated to 0 bytes), unreadable listed
bundle (grep exit 2, diagnosed as an I/O fault), unlisted .mjs, unlisted
filename containing spaces — each a distinct hard failure with its own message.
Wrong marker: AUTISTMASK_DEBUG=1 script/verify-build against a release
dist/ → "is autistmask-build-debug=off but this build expects
autistmask-build-debug=on". dist/ verified byte-identical before and after
the battery.

make build and make build-debug green, 4 bundles each, off and on
respectively.

Checks

make check green on the rebased branch (host), re-run after the rebase onto
cf5f582:

Test Suites: 8 passed, 8 total
Tests:       149 passed, 149 total
All matched files use Prettier code style!

script/cibuild green — the make check and make build layers executed
rather than hitting cache (timings, not CACHED; one CACHED layer in the
whole build, an early base layer):

#11 [7/8] RUN make check
#11 3.160 Test Suites: 8 passed, 8 total
#11 3.160 Tests:       149 passed, 149 total
#11 10.60 All matched files use Prettier code style!
#11 15.04 All matched files use Prettier code style!
#11 DONE 15.4s
#12 [8/8] RUN make build
#12 5.563 verify-build: 4 bundle(s) verified autistmask-build-debug=off
#12 DONE 6.8s

sh -n and dash -n clean; POSIX sh, no bashisms. make fmt leaves no diff.

Not covered by an automated test: verify-build asserts against the real
dist/, which make check does not build, and the container runs as root,
where chmod 000 does not stop find — a permission-based test would be a
false green there. The cases above were run by hand as uid 1000.

Closes [#180](https://git.eeqj.de/sneak/AutistMask/issues/180). Base `next`; rebased onto current `next` (`cf5f582`) immediately before pushing. ## 1. The both-markers diagnostic Old: > the build-time DEBUG value was never resolved and the debug branch is still > live New: > `dist/chrome/src/background/index.js` carries both debug markers, so DEBUG > was not resolved at build time: the ternary in `src/shared/constants.js` > survived into the emitted output. This does not mean the debug branch is live > in this artifact: an unresolved `__BUILD_DEBUG__` is undeclared in extension > context, so DEBUG evaluates to false at runtime. It does mean the > release/debug distinction is no longer enforced at build time, and which way > that fallback happens to evaluate is then an accident a refactor can flip. > Check that `build.js` still defines `__BUILD_DEBUG__`. **What the check proves**: that DEBUG was resolved at build time to the marker the build asked for, in every emitted bundle that contains `constants.js`, and that no other emitted file carries a marker. **What it does not prove**: anything about the runtime behaviour of an artifact that failed the check. Confirmed against the emitted output with the define deleted: `typeof __BUILD_DEBUG__<"u"?__BUILD_DEBUG__:!1`, so the fallback goes the safe way. Still a hard failure — `verify-build` exits 1, `make build` exits 2. The other seven failure messages were reviewed for the same overstatement; **none needed changing**. ## 2. `grep` exit 2 no longer conflated with exit 1 `has_marker` dropped `2>/dev/null`. Exit 0 and 1 are answers about the emitted output; exit 2 is not an answer at all and gets its own message. The manifest-membership `grep` now gets the same `case` discipline, as `is_listed`. It was fail-closed before (an unreadable manifest was answered as "this file is not listed", misdiagnosing every emitted bundle as an unlisted one), but it is the conflation the issue asked to remove, two lines from where it was removed. ## 3. `*.js` coupling removed, and the scan made exhaustive `check_unlisted_bundles` no longer filters by extension, so the `endsWith(".js")` test in `build.js` is the only place the assumption lives and this cross-check is what catches it being wrong. A comment at each site names the other. That makes the scan the sole guard on `build.js`'s filter, so its walk has to be exhaustive rather than assumed to be. Two holes closed: **`find`'s exit status was discarded twice over.** The pipeline reported `sort`'s status, and `set -e` does not fire on an assignment from a successful pipeline. A subtree `find` could not descend printed to stderr and was then silently omitted from the listing — "could not look" reported as "nothing was there", the same conflation `has_marker` exists to prevent. The status is now captured and a non-zero `find` is a hard failure naming the unwalked tree; the `sort` moved off the status-bearing pipeline. **Symlinks.** `-type f` skipped them, so a marker-carrying unlisted bundle placed as a symlink passed green. Decision: **symlinks are walked (`-type f -o -type l`), not skipped and not specially exempted.** A marker-carrying bundle reachable under an unlisted path in `dist/` is a stale manifest whether that path is a link or a regular file, and `grep` reads through the link, so the marker check answers correctly. A link that cannot be read through — dangling, or pointing at a directory — fails hard via the exit-2 path rather than being skipped: the build emits neither, so such a link's DEBUG state is unproven, which under this script's contract is a failure and not a pass. Symlinked directories are therefore not descended into; they fail as unreadable instead, which is the fail-closed answer. ## Also: vacuous inputs Per the script's own contract, an unreadable manifest and an empty listed bundle are distinct hard failures rather than paths into a marker check that cannot prove anything. ## Evidence Run as uid 1000. `verify-build` exits 1 (`make build` exits 2) in every case below. ### `find` cannot enumerate a subtree — the case that used to pass green ``` $ mkdir -p dist/hidden && cp dist/chrome/src/popup/index.js dist/hidden/sneaky.js $ chmod 000 dist/hidden && script/verify-build Verifying emitted bundles (expecting autistmask-build-debug=off)... ok: dist/chrome/src/background/index.js (autistmask-build-debug=off) ok: dist/chrome/src/popup/index.js (autistmask-build-debug=off) ok: dist/firefox/src/background/index.js (autistmask-build-debug=off) ok: dist/firefox/src/popup/index.js (autistmask-build-debug=off) find: 'dist/hidden': Permission denied verify-build: FAIL: find exited 1 enumerating dist/, so part of the tree was never walked and nothing was established about the files in it. Any unlisted bundle there went unchecked. That is a permissions or I/O fault on the artifact, not a stale manifest. Refusing to report success. exit=1 ``` Before this change the same tree printed `Permission denied` and then `verify-build: 4 bundle(s) verified autistmask-build-debug=off`, `exit=0`. With the directory readable again, the file inside it is caught on its own terms rather than as an enumeration fault: ``` $ chmod 755 dist/hidden && script/verify-build verify-build: FAIL: dist/hidden/sneaky.js carries a debug marker but is absent from dist/constants-bundles.txt, so the manifest no longer describes the emitted bundles. exit=1 ``` ### Symlinks ``` ### symlink to an unlisted marker-carrying file (passed green before) verify-build: FAIL: dist/link.js carries a debug marker but is absent from dist/constants-bundles.txt, so the manifest no longer describes the emitted bundles. ### symlink to a LISTED bundle, under an unlisted path verify-build: FAIL: dist/alias.js carries a debug marker but is absent from dist/constants-bundles.txt, so the manifest no longer describes the emitted bundles. ### dangling symlink verify-build: FAIL: grep exited 2 reading dist/dangling.js, so the file could not be searched and its DEBUG state was not checked at all. ... ### symlink to a directory verify-build: FAIL: grep exited 2 reading dist/dirlink, so the file could not be searched and its DEBUG state was not checked at all. ... ``` ### Unreadable manifest reaching the membership test Unreachable in a single run today — `main`'s `[ -r "$MANIFEST" ]` guard fires first — so this exercises `is_listed` directly against the script's own function text, with `main "$@"` stripped and an unreadable file as `MANIFEST`. It exists so a manifest that becomes unreadable mid-run is not answered as a stale manifest: ``` verify-build: FAIL: grep exited 2 reading /tmp/<manifest>, so it could not be searched and nothing was established about which bundles it lists. That is a permissions or I/O fault on the manifest, not a stale manifest. Refusing to report success. ``` ### Previously covered modes, all re-run on this head Both markers (`__BUILD_DEBUG__` define deleted from `build.js`, rebuilt): ``` verify-build: FAIL: dist/chrome/src/background/index.js carries both debug markers, so DEBUG was not resolved at build time: the ternary in src/shared/constants.js survived into the emitted output. ... make: *** [Makefile:44: build] Error 1 ``` Missing manifest, empty manifest, unreadable manifest (`chmod 000`), missing listed bundle, empty listed bundle (truncated to 0 bytes), unreadable listed bundle (`grep` exit 2, diagnosed as an I/O fault), unlisted `.mjs`, unlisted filename containing spaces — each a distinct hard failure with its own message. Wrong marker: `AUTISTMASK_DEBUG=1 script/verify-build` against a release `dist/` → "is autistmask-build-debug=off but this build expects autistmask-build-debug=on". `dist/` verified byte-identical before and after the battery. `make build` and `make build-debug` green, 4 bundles each, `off` and `on` respectively. ## Checks `make check` green on the rebased branch (host), re-run after the rebase onto `cf5f582`: ``` Test Suites: 8 passed, 8 total Tests: 149 passed, 149 total All matched files use Prettier code style! ``` `script/cibuild` green — the `make check` and `make build` layers executed rather than hitting cache (timings, not `CACHED`; one `CACHED` layer in the whole build, an early base layer): ``` #11 [7/8] RUN make check #11 3.160 Test Suites: 8 passed, 8 total #11 3.160 Tests: 149 passed, 149 total #11 10.60 All matched files use Prettier code style! #11 15.04 All matched files use Prettier code style! #11 DONE 15.4s #12 [8/8] RUN make build #12 5.563 verify-build: 4 bundle(s) verified autistmask-build-debug=off #12 DONE 6.8s ``` `sh -n` and `dash -n` clean; POSIX `sh`, no bashisms. `make fmt` leaves no diff. Not covered by an automated test: `verify-build` asserts against the real `dist/`, which `make check` does not build, and the container runs as root, where `chmod 000` does not stop `find` — a permission-based test would be a false green there. The cases above were run by hand as uid 1000.
clawbot added 1 commit 2026-08-11 14:23:43 +02:00
fix: correct verify-build diagnostics and close two robustness gaps (closes #180)
Some checks failed
check / check (push) Has been cancelled
ef39b4f57f
The both-markers diagnostic claimed the debug branch was still live. It is
not: with the __BUILD_DEBUG__ define removed, the emitted bundle carries
`typeof __BUILD_DEBUG__<"u"?__BUILD_DEBUG__:!1`, and in extension context the
identifier is undeclared, so DEBUG evaluates to false at runtime. The message
now states what the check does prove -- DEBUG was not resolved at build time,
so the release/debug distinction is no longer enforced and which way the
unresolved fallback evaluates is an accident a refactor can flip -- and it
remains a hard failure. The other seven failure messages were reviewed and
none needed rewording.

has_marker no longer swallows grep's exit 2 with 2>/dev/null. Match and
no-match are answers about the emitted output; an unreadable file is not, and
is now reported as a permissions or I/O fault instead of as "the emitted
output changed shape". Both paths still fail hard.

The unlisted-bundle scan no longer filters by extension, so the endsWith(".js")
test in build.js is the only place that assumption lives. A bundle emitted
under another extension previously escaped the manifest and the cross-check at
once; it now fails as unlisted. Both sites carry a comment naming the other.

Also: the manifest must be readable and a listed bundle must be non-empty,
so a vacuous input fails loudly rather than reaching a marker check that
cannot prove anything.
clawbot force-pushed fix/issue-180-verify-build-diagnostic from ef39b4f57f to b9ae64d396 2026-08-11 14:24:37 +02:00 Compare
clawbot self-assigned this 2026-08-11 14:24:45 +02:00
clawbot added the needs-review label 2026-08-11 14:24:46 +02:00
clawbot force-pushed fix/issue-180-verify-build-diagnostic from b9ae64d396 to 3350105a48 2026-08-11 14:27:06 +02:00 Compare
Author
Collaborator

FAIL (needs-rework), reviewed at 3350105.

1. script/verify-build:103 — vacuous pass: an un-enumerable subtree of dist/ silently drops out of the cross-check and the script still exits 0.

_listing="$(find dist -type f | sort)" discards find's exit status twice over: the pipeline reports sort's status, and set -e does not fire on an assignment whose command substitution ended in a successful pipeline. A directory find cannot descend is reported on stderr and then omitted from the listing, so check_unlisted_bundles reports success on files it never saw. Reproduced on this head, uid 1000:

$ mkdir -p dist/hidden && cp dist/chrome/src/popup/index.js dist/hidden/sneaky.js
$ chmod 000 dist/hidden && script/verify-build
Verifying emitted bundles (expecting autistmask-build-debug=off)...
  ok: dist/chrome/src/background/index.js (autistmask-build-debug=off)
  ...
find: 'dist/hidden': Permission denied
verify-build: 4 bundle(s) verified autistmask-build-debug=off
exit=0
$ chmod 755 dist/hidden && script/verify-build   # same tree, dir now readable
verify-build: FAIL: dist/hidden/sneaky.js carries a debug marker but is absent from dist/constants-bundles.txt
exit=1

Same failure class this PR just fixed for grep exit 2 in has_marker — "could not read it" reported as "there was nothing there" — left in place for find in the function the PR rewrote. It matters more here than before the change: this PR deliberately removed the *.js filter and makes this scan the only thing that catches build.js's endsWith(".js") assumption being wrong (comments at build.js:32-35 and script/verify-build:96-101 both assert "every file under dist/ is searched"). It is not, and the failure is silent-green. Note the guard prints Permission denied and then prints verified — exactly the outcome the script's header comment forbids.

Acceptable: capture find's status and hard-fail on non-zero, e.g. _listing="$(find dist -type f)" || fail "find could not enumerate dist/ ..." with the sort moved off the status-bearing pipeline (or find ... > "$tmp" and check $?). Message should say the tree could not be enumerated and nothing was proven about the unlisted files, not that the manifest is stale.

2. script/verify-build:106 — the sibling grep still conflates exit 2 with exit 1. grep -q -x -F -e "$_file" -- "$MANIFEST" treats "could not read the manifest" as "this file is not listed". Fail-closed today (misdiagnosed as an unlisted bundle, still exit 1) and mostly covered by the new [ -r "$MANIFEST" ] guard, but it is the exact conflation item 2 of the issue asked to remove, two lines from where it was removed. Give it the same case discipline as has_marker.

3. -type f skips symlinks — pre-existing, not introduced here, but now load-bearing. A marker-carrying, unlisted bundle placed as a symlink under dist/ passes green (verified, exit 0). Before this PR the *.js filter shared the blind spot; now the cross-check is the sole guard, so its exhaustiveness is the whole argument for removing the filter.

Verified and passing, for the record: the corrected both-markers diagnostic is accurate — rebuilt with the __BUILD_DEBUG__ define deleted and confirmed the emitted typeof __BUILD_DEBUG__&lt;"u"?__BUILD_DEBUG__:!1, so the "evaluates to false at runtime" claim holds for the cause it diagnoses, and it still exits 1; the guard still catches a debug artifact checked against a release expectation, and hard-fails on missing/empty/unreadable manifest, missing/empty/unreadable listed bundle, and an unlisted marker-carrying .mjs (and one with spaces in its name); make check green on this head (8 suites, 149 tests, prettier clean) and green containerized via script/cibuild with make check/make build layers demonstrably executed, not CACHED; make build and make build-debug green, 4 bundles each; sh -n and dash -n clean; make fmt leaves no diff; single commit titled (closes #180), base next, rebased onto 19cb1ca, mergeable, one TODO.md line, no forbidden references or trailers.

Two notes, neither a blocker: the PR body's check evidence (7 passed/143 total, #11 3.833) predates the rebase — this head is 8/149, also green; and CI on 3350105 was still pending/"Waiting to run" throughout the review, so the verdict rests on my own containerized run rather than on a green tracker check.

FAIL (`needs-rework`), reviewed at `3350105`. **1. `script/verify-build:103` — vacuous pass: an un-enumerable subtree of `dist/` silently drops out of the cross-check and the script still exits 0.** `_listing="$(find dist -type f | sort)"` discards `find`'s exit status twice over: the pipeline reports `sort`'s status, and `set -e` does not fire on an assignment whose command substitution ended in a successful pipeline. A directory `find` cannot descend is reported on stderr and then omitted from the listing, so `check_unlisted_bundles` reports success on files it never saw. Reproduced on this head, uid 1000: ``` $ mkdir -p dist/hidden && cp dist/chrome/src/popup/index.js dist/hidden/sneaky.js $ chmod 000 dist/hidden && script/verify-build Verifying emitted bundles (expecting autistmask-build-debug=off)... ok: dist/chrome/src/background/index.js (autistmask-build-debug=off) ... find: 'dist/hidden': Permission denied verify-build: 4 bundle(s) verified autistmask-build-debug=off exit=0 $ chmod 755 dist/hidden && script/verify-build # same tree, dir now readable verify-build: FAIL: dist/hidden/sneaky.js carries a debug marker but is absent from dist/constants-bundles.txt exit=1 ``` Same failure class this PR just fixed for `grep` exit 2 in `has_marker` — "could not read it" reported as "there was nothing there" — left in place for `find` in the function the PR rewrote. It matters more here than before the change: this PR deliberately removed the `*.js` filter and makes this scan the *only* thing that catches `build.js`'s `endsWith(".js")` assumption being wrong (comments at `build.js:32-35` and `script/verify-build:96-101` both assert "every file under `dist/` is searched"). It is not, and the failure is silent-green. Note the guard prints `Permission denied` and then prints `verified` — exactly the outcome the script's header comment forbids. Acceptable: capture `find`'s status and hard-fail on non-zero, e.g. `_listing="$(find dist -type f)" || fail "find could not enumerate dist/ ..."` with the `sort` moved off the status-bearing pipeline (or `find ... > "$tmp"` and check `$?`). Message should say the tree could not be enumerated and nothing was proven about the unlisted files, not that the manifest is stale. **2. `script/verify-build:106` — the sibling `grep` still conflates exit 2 with exit 1.** `grep -q -x -F -e "$_file" -- "$MANIFEST"` treats "could not read the manifest" as "this file is not listed". Fail-closed today (misdiagnosed as an unlisted bundle, still exit 1) and mostly covered by the new `[ -r "$MANIFEST" ]` guard, but it is the exact conflation item 2 of the issue asked to remove, two lines from where it was removed. Give it the same `case` discipline as `has_marker`. **3. `-type f` skips symlinks — pre-existing, not introduced here, but now load-bearing.** A marker-carrying, unlisted bundle placed as a symlink under `dist/` passes green (verified, exit 0). Before this PR the `*.js` filter shared the blind spot; now the cross-check is the sole guard, so its exhaustiveness is the whole argument for removing the filter. Verified and passing, for the record: the corrected both-markers diagnostic is accurate — rebuilt with the `__BUILD_DEBUG__` define deleted and confirmed the emitted `typeof __BUILD_DEBUG__&lt;"u"?__BUILD_DEBUG__:!1`, so the "evaluates to false at runtime" claim holds for the cause it diagnoses, and it still exits 1; the guard still catches a debug artifact checked against a release expectation, and hard-fails on missing/empty/unreadable manifest, missing/empty/unreadable listed bundle, and an unlisted marker-carrying `.mjs` (and one with spaces in its name); `make check` green on this head (8 suites, 149 tests, prettier clean) and green containerized via `script/cibuild` with `make check`/`make build` layers demonstrably executed, not `CACHED`; `make build` and `make build-debug` green, 4 bundles each; `sh -n` and `dash -n` clean; `make fmt` leaves no diff; single commit titled ` (closes #180)`, base `next`, rebased onto `19cb1ca`, mergeable, one `TODO.md` line, no forbidden references or trailers. Two notes, neither a blocker: the PR body's check evidence (`7 passed`/`143 total`, `#11 3.833`) predates the rebase — this head is 8/149, also green; and CI on `3350105` was still `pending`/"Waiting to run" throughout the review, so the verdict rests on my own containerized run rather than on a green tracker check.
clawbot added needs-rework and removed needs-review labels 2026-08-11 14:32:54 +02:00
clawbot force-pushed fix/issue-180-verify-build-diagnostic from 3350105a48 to 0e3dd14001 2026-08-11 14:41:15 +02:00 Compare
clawbot changed title from fix: correct verify-build diagnostics and close two robustness gaps (closes #180) to fix: correct verify-build diagnostics and close its vacuous passes (closes #180) 2026-08-11 14:41:58 +02:00
clawbot force-pushed fix/issue-180-verify-build-diagnostic from 0e3dd14001 to 26dd7e0bfb 2026-08-11 14:42:48 +02:00 Compare
clawbot added needs-review and removed needs-rework labels 2026-08-11 14:46:16 +02:00
Author
Collaborator

PASS (merge-ready), independently re-reviewed at 26dd7e0: the find-status exploit is closed and not over-eager (chmod 755 on the same tree still fails as unlisted, not as an enumeration fault); every symlink case fails closed — unlisted target, listed-bundle alias, dangling, directory-pointing, loop, and dist/ itself being a symlink (which reduces the walk to one entry); is_listed's exit-2 path exercised directly and its message is accurate; missing/empty/unreadable manifest, missing/empty/unreadable listed bundle, unlisted .mjs, unlisted name with internal spaces, 16-deep nesting, wrong marker, debug artifact checked as release, and the reworded both-markers diagnostic (emitted typeof __BUILD_DEBUG__&lt;"u"?__BUILD_DEBUG__:!1 confirmed, make build exits 2) all re-verified; make check green (8 suites, 149 tests, prettier clean), script/cibuild green with the make check and make build layers executed (22.7s / 8.3s, not CACHED); sh -n/dash -n clean, no bashisms, make fmt no diff; single commit ending (closes #180), base next, author clawbot, mergeable, TODO.md additive (16 to 17 entries), no forbidden references or trailers.

Disclosures, none blocking:

  1. I did construct two vacuous passes, and am not failing on them. An unlisted marker-carrying file named dist/chrome/src/popup/index.js (trailing space) or dist/chrome/src/popup/index.js\n (trailing newline) both yield exit 0, 4 bundle(s) verifiedread -r strips the trailing blank / splits the line, the remnant matches a listed path, and the real file is never checked. Both behave identically on origin/next's script, so this is pre-existing and not a regression, and it is exactly the item #180 recorded as "considered and rejected, so they are not re-raised" (read -r not representing paths with leading or trailing whitespace). Not reachable from the failure mode the guard defends — esbuild emits no such name. Noting only that the new comment's absolute "Every file under dist/ is searched" is now load-bearing and is untrue for whitespace-bearing paths; a NUL-delimited walk (find -print0 / -exec) would make it true.

  2. The fail-closed answer for a symlinked or unreadable directory rests on GNU grep exiting 2 on a directory, not on anything the script asserts. It holds on node:22-slim. Under a grep that skips directories with exit 1, dist/ being a symlink would enumerate one entry and pass green. Undocumented dependency, correct here.

  3. The no-automated-test reasoning is sound but broader than what it covers. Root in CI genuinely makes a chmod 000 test a false green, and make check builds no dist/. That argument does not extend to the symlink and manifest-shape cases, which need no permissions and could be covered by a synthetic dist/ fixture. Not required by the issue's definition of done, so not a blocker.

  4. CI on this head is pending / "Waiting to run" (known-broken runner); the verdict rests on my own containerized run. build.js was temporarily edited to force the both-markers path and restored via git checkout; the tree is clean.

PASS (`merge-ready`), independently re-reviewed at `26dd7e0`: the `find`-status exploit is closed and not over-eager (`chmod 755` on the same tree still fails as unlisted, not as an enumeration fault); every symlink case fails closed — unlisted target, listed-bundle alias, dangling, directory-pointing, loop, and `dist/` itself being a symlink (which reduces the walk to one entry); `is_listed`'s exit-2 path exercised directly and its message is accurate; missing/empty/unreadable manifest, missing/empty/unreadable listed bundle, unlisted `.mjs`, unlisted name with internal spaces, 16-deep nesting, wrong marker, debug artifact checked as release, and the reworded both-markers diagnostic (emitted `typeof __BUILD_DEBUG__&lt;"u"?__BUILD_DEBUG__:!1` confirmed, `make build` exits 2) all re-verified; `make check` green (8 suites, 149 tests, prettier clean), `script/cibuild` green with the `make check` and `make build` layers executed (22.7s / 8.3s, not `CACHED`); `sh -n`/`dash -n` clean, no bashisms, `make fmt` no diff; single commit ending ` (closes #180)`, base `next`, author `clawbot`, mergeable, `TODO.md` additive (16 to 17 entries), no forbidden references or trailers. Disclosures, none blocking: 1. **I did construct two vacuous passes, and am not failing on them.** An unlisted marker-carrying file named `dist/chrome/src/popup/index.js ` (trailing space) or `dist/chrome/src/popup/index.js\n` (trailing newline) both yield exit 0, `4 bundle(s) verified` — `read -r` strips the trailing blank / splits the line, the remnant matches a listed path, and the real file is never checked. Both behave identically on `origin/next`'s script, so this is pre-existing and not a regression, and it is exactly the item [#180](https://git.eeqj.de/sneak/AutistMask/issues/180) recorded as "considered and rejected, so they are not re-raised" (`read -r` not representing paths with leading or trailing whitespace). Not reachable from the failure mode the guard defends — esbuild emits no such name. Noting only that the new comment's absolute "Every file under `dist/` is searched" is now load-bearing and is untrue for whitespace-bearing paths; a NUL-delimited walk (`find -print0` / `-exec`) would make it true. 2. **The fail-closed answer for a symlinked or unreadable directory rests on GNU grep exiting 2 on a directory**, not on anything the script asserts. It holds on `node:22-slim`. Under a `grep` that skips directories with exit 1, `dist/` being a symlink would enumerate one entry and pass green. Undocumented dependency, correct here. 3. **The no-automated-test reasoning is sound but broader than what it covers.** Root in CI genuinely makes a `chmod 000` test a false green, and `make check` builds no `dist/`. That argument does not extend to the symlink and manifest-shape cases, which need no permissions and could be covered by a synthetic `dist/` fixture. Not required by the issue's definition of done, so not a blocker. 4. CI on this head is `pending` / "Waiting to run" (known-broken runner); the verdict rests on my own containerized run. `build.js` was temporarily edited to force the both-markers path and restored via `git checkout`; the tree is clean.
clawbot merged commit 93e3f6e4e2 into next 2026-08-11 14:55:07 +02:00
clawbot deleted branch fix/issue-180-verify-build-diagnostic 2026-08-11 14:55:07 +02:00
Sign in to join this conversation.
No Reviewers
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: sneak/AutistMask#203