build: add ESLint to script/lint and containerize linting (closes #152) #286

Merged
clawbot merged 1 commits from issue-152-eslint into next 2026-08-17 09:10:04 +02:00
Collaborator

Closes #152.

script/lint ran prettier --check ., byte for byte what script/fmt-check runs, so make check checked formatting twice and did no static analysis on a cryptocurrency wallet.

What changed

ESLint pinned in package.json (eslint@10.8.1, @eslint/js@10.0.1, globals@17.11.0, all exact, all with yarn.lock integrity entries; added with yarn, not npm). Flat config in eslint.config.js with @eslint/js recommended as the base. no-undef and no-unused-vars are restated error-level on top of the recommended set so a future recommended-set change cannot silently downgrade the two rules this exists for.

Globals are declared per tree, not globally, because a too-wide set hides the next unimported identifier:

tree globals
src/popup/**, src/content/** browser + chrome/browser
src/background/**, src/shared/** service worker + chrome/browser
src/shared/ens.js browser + chrome/browser
tests/**/*.test.js node + jest
build.js node
tests/e2e/** node + browser + chrome/browser

src/shared/ens.js is the one override inside src/shared/: its own header says POPUP ONLY, it caches in localStorage, and only popup views require it. tests/e2e/** gets both sets because those files carry the callbacks they ship into the page via page.evaluate() inline, so both contexts really are present in the same file.

package.json's lint is now eslint . && prettier --check ., so yarn run lint and make lint agree. script/fmt-check is untouched. No --fix anywhere in the lint path.

Nothing is ignored but dist/ and node_modules/.

Containerized, per the scope note on the issue

script/lint now builds the Dockerfile's new lint stage, so the ESLint deciding whether this repo is green is the pinned one and not whatever the host happens to have. AUTISTMASK_LINT_NATIVE=1, set only in that image, is what makes the make check running inside the CI build lint in place instead of recursing into a docker daemon it does not have; the variable set to any other value is an error rather than a silent fall-through to the docker path. The check stage takes a COPY --from=lint /app/package.json /dev/null dependency so BuildKit finishes lint before starting it and a lint failure fails the build early rather than racing it. script/lint uses --output=type=cacheonly: the exit status is the whole result and exporting an image afterwards cost about ten times the lint itself.

Docker is now required to lint. That is the point, and it matches the docker-only lint policy.

The test timeout, which this PR had to move

The lint stage roughly doubles the image build, and that exposed script/test's timeout 30 as marginal rather than a bound. On the first CI run to rebuild the base stage cold, it killed a healthy suite at 30.63s and the verbose rerun at 60.75s — nothing asserted false.

A cap whose job is to stop a hung suite is not a wall-clock budget, and one a healthy suite can trip teaches "just run it again", which is how a suite stops meaning anything. So:

  • Host: still 30s. The suite runs in ~8s there and REPO_POLICIES' figure is the right bound for the thing it describes.
  • In the image: 180s, set by the Dockerfile through the new AUTISTMASK_TEST_TIMEOUT. The same suite in there also pays a cold jest cache and shares the runner with the rest of the build, which is not what the host budget describes. 180s still catches a hang in three minutes and cannot be tripped by a suite that is merely running on contended hardware.
  • script/test now names a timeout kill as a timeout instead of reporting it as a test failure, and skips the verbose rerun in that case — the rerun would only spend the same wall clock to be killed again, which is exactly what the failing run did.

Two rules narrowed, deliberately

  • no-useless-assignment: off for src/popup/views/approval.js and src/popup/views/confirmTx.js only. It flags the password = null and decryptedSecret = null wipes at 9 sites in those two files (approval.js 582, 593, 618, 648, 692, 703, 728, 764; confirmTx.js 459). Those assignments are dead by construction — that is what a best-effort wipe of decrypted key material is — and the rule's fix is to delete the wipe. The rule is on for the rest of the tree, so an ordinary dead store elsewhere is still an error.
  • preserve-caught-error: off tree-wide. It requires every rethrow to carry { cause }, at 3 sites today (src/shared/balances.js:207, src/shared/balances.js:215, tests/e2e/firefox/run.js:131). That changes what the wallet's error paths actually throw; adopting it is a decision of its own, not a side effect of turning a linter on — so it is off for new code too, pending that decision, rather than scoped to the three sites that exist now.

Both are commented in eslint.config.js with the reason and with the site counts.

Violations fixed

53 no-undef and 41 no-unused-vars, all by hand — no sed -i, no scripted rewrite.

  • Unused imports, including the whole tail the issue enumerates: clearViewStack, formatUnits, showFlash, flashCopyFeedback, currentNetwork, NETWORKS, SUPPORTED_CHAIN_IDS, currentAddress, getAddressValueUsd, escapeHtml, saveState, getBytes.
  • Unused catch bindings became catch {, which the repo already used elsewhere, so caught errors stay checked rather than being excused by a config pattern.
  • The shared init(ctx) view signature keeps its parameter as _ctx in the three views that do not read it (approval, confirmTx, receive), matched by argsIgnorePattern: "^_". Uniform interface preserved, intent stated.
  • transactionDetail.js stored a module-level ctx nothing ever read, and loadFullTxDetails() took an isContractCall it never used from its single caller. Both removed.
  • txStatus.js's etherscanTokenLink() was dead and superseded by toAddressHtml. Removed.
  • tests/e2e/run.js's withTimeout(promise, name) dropped name; the caller passes t.name, so it now appears in the timeout message as intended.
  • tests/e2e/firefox/driver.js's waitFor() initialized last = null and then assigned it on every path through the loop body before reading it — a plain dead store, which the now-scoped no-useless-assignment catches. The initializer is dropped.
  • src/shared/uniswap.js's decodeV2SwapExactOut() is kept behind a scoped eslint-disable-next-line. It is the decoder for Universal Router command 0x09, and decode() has no 0x09 arm, so a V2 exact-out swap shows its command name and no token or amount detail in the approval preview. Deleting it would widen that gap rather than close it, and wiring a new command arm is outside a lint-adoption change, so it is filed as #283 and the code the fix needs is still there.

Demonstration that the linter is load-bearing

Three mutations at once in a clean tree, then make lint (containerized, exit 2): an unimported addressDotHtml("0x0") in src/popup/views/receive.js — the exact shape of #151; a document.getElementById in src/background/index.js, which is what proves the per-tree globals are genuinely narrow rather than a blanket browser set; and a plain dead store in a file outside the two the no-useless-assignment scope excludes.

#11 1.709 /app/src/background/index.js
#11 1.709   61:5  error  'document' is not defined  no-undef
#11 1.709 /app/src/popup/views/receive.js
#11 1.709   61:5  error  'addressDotHtml' is not defined                                         no-undef
#11 1.709   62:9  error  The value assigned to 'probeDead' is not used in subsequent statements  no-useless-assignment
#11 1.709 ✖ 3 problems (3 errors, 0 warnings)
#11 1.774 error Command failed with exit code 1.
#11 1.928 make: *** [Makefile:27: lint] Error 1

All three reverted; git status --porcelain empty afterwards.

Verification

Rebased onto next at d9d50f0.

  • CI green on head dbba3b0: https://git.eeqj.de/sneak/AutistMask/actions/runs/652, and genuinely cold — the ENV change puts the invalidation at the top of the base stage, so #9 [base 5/6] RUN script/bootstrap re-ran in 14.7s and only the base-image layer was CACHED. #11 [lint] DONE 6.0s (executed, not CACHED); #13 0.229 Running tests (timeout 180s)... then Test Suites: 29 passed, 29 total, Tests: 703 passed, 703 total, jest 7.809s, test-verify-build: 18 case(s) passed, check stage DONE 19.2s. Whole job 54s wall.
  • Local make check exit 0 on the rebased head: 29 suites / 703 tests, test-verify-build: 18 case(s) passed, lint clean, All matched files use Prettier code style!.
  • Local docker build --no-cache (this one image only, no prune) exit 0 in 1m32s, with Running tests (timeout 180s)... and jest at 7.238s — the raised bound reaches script/test in the image.
  • make check is non-mutating: git status --porcelain empty at HEAD afterwards.
  • make fmt run; markdown and JS committed formatted.

Docs

The README "Entrypoints" entry for script/lint no longer said what it does, and is rewritten to state both tools, the non-mutating guarantee, and the docker requirement. Two claims elsewhere are now false and are corrected: the README end-to-end section and the script/test-e2e header both said a used-but-not-imported identifier is invisible to make check. TODO.md moved on in the same commit.

Not done here

  • script/test and script/fmt-check still run on the host. Only lint is containerized, which is what the scope note asked for; whether the rest should follow is a separate question.
Closes https://git.eeqj.de/sneak/AutistMask/issues/152. `script/lint` ran `prettier --check .`, byte for byte what `script/fmt-check` runs, so `make check` checked formatting twice and did no static analysis on a cryptocurrency wallet. ## What changed ESLint pinned in `package.json` (`eslint@10.8.1`, `@eslint/js@10.0.1`, `globals@17.11.0`, all exact, all with `yarn.lock` integrity entries; added with `yarn`, not `npm`). Flat config in `eslint.config.js` with `@eslint/js` recommended as the base. `no-undef` and `no-unused-vars` are restated error-level on top of the recommended set so a future recommended-set change cannot silently downgrade the two rules this exists for. Globals are declared per tree, not globally, because a too-wide set hides the next unimported identifier: | tree | globals | | --- | --- | | `src/popup/**`, `src/content/**` | browser + `chrome`/`browser` | | `src/background/**`, `src/shared/**` | service worker + `chrome`/`browser` | | `src/shared/ens.js` | browser + `chrome`/`browser` | | `tests/**/*.test.js` | node + jest | | `build.js` | node | | `tests/e2e/**` | node + browser + `chrome`/`browser` | `src/shared/ens.js` is the one override inside `src/shared/`: its own header says POPUP ONLY, it caches in `localStorage`, and only popup views require it. `tests/e2e/**` gets both sets because those files carry the callbacks they ship into the page via `page.evaluate()` inline, so both contexts really are present in the same file. `package.json`'s `lint` is now `eslint . && prettier --check .`, so `yarn run lint` and `make lint` agree. `script/fmt-check` is untouched. No `--fix` anywhere in the lint path. Nothing is ignored but `dist/` and `node_modules/`. ## Containerized, per the scope note on the issue `script/lint` now builds the Dockerfile's new `lint` stage, so the ESLint deciding whether this repo is green is the pinned one and not whatever the host happens to have. `AUTISTMASK_LINT_NATIVE=1`, set only in that image, is what makes the `make check` running *inside* the CI build lint in place instead of recursing into a docker daemon it does not have; the variable set to any other value is an error rather than a silent fall-through to the docker path. The `check` stage takes a `COPY --from=lint /app/package.json /dev/null` dependency so BuildKit finishes lint before starting it and a lint failure fails the build early rather than racing it. `script/lint` uses `--output=type=cacheonly`: the exit status is the whole result and exporting an image afterwards cost about ten times the lint itself. Docker is now required to lint. That is the point, and it matches the docker-only lint policy. ## The test timeout, which this PR had to move The lint stage roughly doubles the image build, and that exposed `script/test`'s `timeout 30` as marginal rather than a bound. On the first CI run to rebuild the `base` stage cold, it killed a healthy suite at 30.63s and the verbose rerun at 60.75s — nothing asserted false. A cap whose job is to stop a hung suite is not a wall-clock budget, and one a healthy suite can trip teaches "just run it again", which is how a suite stops meaning anything. So: - **Host: still 30s.** The suite runs in ~8s there and REPO_POLICIES' figure is the right bound for the thing it describes. - **In the image: 180s**, set by the Dockerfile through the new `AUTISTMASK_TEST_TIMEOUT`. The same suite in there also pays a cold jest cache and shares the runner with the rest of the build, which is not what the host budget describes. 180s still catches a hang in three minutes and cannot be tripped by a suite that is merely running on contended hardware. - `script/test` now **names a timeout kill as a timeout** instead of reporting it as a test failure, and skips the verbose rerun in that case — the rerun would only spend the same wall clock to be killed again, which is exactly what the failing run did. ## Two rules narrowed, deliberately - **`no-useless-assignment`: off for `src/popup/views/approval.js` and `src/popup/views/confirmTx.js` only.** It flags the `password = null` and `decryptedSecret = null` wipes at 9 sites in those two files (`approval.js` 582, 593, 618, 648, 692, 703, 728, 764; `confirmTx.js` 459). Those assignments are dead by construction — that is what a best-effort wipe of decrypted key material *is* — and the rule's fix is to delete the wipe. The rule is **on** for the rest of the tree, so an ordinary dead store elsewhere is still an error. - **`preserve-caught-error`: off tree-wide.** It requires every rethrow to carry `{ cause }`, at 3 sites today (`src/shared/balances.js:207`, `src/shared/balances.js:215`, `tests/e2e/firefox/run.js:131`). That changes what the wallet's error paths actually throw; adopting it is a decision of its own, not a side effect of turning a linter on — so it is off for new code too, pending that decision, rather than scoped to the three sites that exist now. Both are commented in `eslint.config.js` with the reason and with the site counts. ## Violations fixed 53 `no-undef` and 41 `no-unused-vars`, all by hand — no `sed -i`, no scripted rewrite. - Unused imports, including the whole tail the issue enumerates: `clearViewStack`, `formatUnits`, `showFlash`, `flashCopyFeedback`, `currentNetwork`, `NETWORKS`, `SUPPORTED_CHAIN_IDS`, `currentAddress`, `getAddressValueUsd`, `escapeHtml`, `saveState`, `getBytes`. - Unused catch bindings became `catch {`, which the repo already used elsewhere, so caught errors stay checked rather than being excused by a config pattern. - The shared `init(ctx)` view signature keeps its parameter as `_ctx` in the three views that do not read it (`approval`, `confirmTx`, `receive`), matched by `argsIgnorePattern: "^_"`. Uniform interface preserved, intent stated. - `transactionDetail.js` stored a module-level `ctx` nothing ever read, and `loadFullTxDetails()` took an `isContractCall` it never used from its single caller. Both removed. - `txStatus.js`'s `etherscanTokenLink()` was dead and superseded by `toAddressHtml`. Removed. - `tests/e2e/run.js`'s `withTimeout(promise, name)` dropped `name`; the caller passes `t.name`, so it now appears in the timeout message as intended. - `tests/e2e/firefox/driver.js`'s `waitFor()` initialized `last = null` and then assigned it on every path through the loop body before reading it — a plain dead store, which the now-scoped `no-useless-assignment` catches. The initializer is dropped. - `src/shared/uniswap.js`'s `decodeV2SwapExactOut()` is **kept** behind a scoped `eslint-disable-next-line`. It is the decoder for Universal Router command `0x09`, and `decode()` has no `0x09` arm, so a V2 exact-out swap shows its command name and no token or amount detail in the approval preview. Deleting it would widen that gap rather than close it, and wiring a new command arm is outside a lint-adoption change, so it is filed as https://git.eeqj.de/sneak/AutistMask/issues/283 and the code the fix needs is still there. ## Demonstration that the linter is load-bearing Three mutations at once in a clean tree, then `make lint` (containerized, exit 2): an unimported `addressDotHtml("0x0")` in `src/popup/views/receive.js` — the exact shape of https://git.eeqj.de/sneak/AutistMask/issues/151; a `document.getElementById` in `src/background/index.js`, which is what proves the per-tree globals are genuinely narrow rather than a blanket browser set; and a plain dead store in a file **outside** the two the `no-useless-assignment` scope excludes. ``` #11 1.709 /app/src/background/index.js #11 1.709 61:5 error 'document' is not defined no-undef #11 1.709 /app/src/popup/views/receive.js #11 1.709 61:5 error 'addressDotHtml' is not defined no-undef #11 1.709 62:9 error The value assigned to 'probeDead' is not used in subsequent statements no-useless-assignment #11 1.709 ✖ 3 problems (3 errors, 0 warnings) #11 1.774 error Command failed with exit code 1. #11 1.928 make: *** [Makefile:27: lint] Error 1 ``` All three reverted; `git status --porcelain` empty afterwards. ## Verification Rebased onto `next` at `d9d50f0`. - **CI green on head `dbba3b0`: https://git.eeqj.de/sneak/AutistMask/actions/runs/652**, and genuinely cold — the `ENV` change puts the invalidation at the top of the `base` stage, so `#9 [base 5/6] RUN script/bootstrap` re-ran in 14.7s and only the base-image layer was `CACHED`. `#11 [lint] DONE 6.0s` (executed, not `CACHED`); `#13 0.229 Running tests (timeout 180s)...` then `Test Suites: 29 passed, 29 total`, `Tests: 703 passed, 703 total`, jest 7.809s, `test-verify-build: 18 case(s) passed`, check stage `DONE 19.2s`. Whole job 54s wall. - Local `make check` exit 0 on the rebased head: 29 suites / 703 tests, `test-verify-build: 18 case(s) passed`, lint clean, `All matched files use Prettier code style!`. - Local `docker build --no-cache` (this one image only, no prune) exit 0 in 1m32s, with `Running tests (timeout 180s)...` and jest at 7.238s — the raised bound reaches `script/test` in the image. - `make check` is non-mutating: `git status --porcelain` empty at HEAD afterwards. - `make fmt` run; markdown and JS committed formatted. ## Docs The README "Entrypoints" entry for `script/lint` no longer said what it does, and is rewritten to state both tools, the non-mutating guarantee, and the docker requirement. Two claims elsewhere are now false and are corrected: the README end-to-end section and the `script/test-e2e` header both said a used-but-not-imported identifier is invisible to `make check`. `TODO.md` moved on in the same commit. ## Not done here - `script/test` and `script/fmt-check` still run on the host. Only lint is containerized, which is what the scope note asked for; whether the rest should follow is a separate question.
clawbot added 1 commit 2026-08-14 06:18:47 +02:00
build: add ESLint to script/lint and containerize linting (closes #152)
Some checks failed
check / check (push) Failing after 2m16s
7270480e0b
script/lint ran `prettier --check .`, byte for byte what script/fmt-check
runs, so make check checked formatting twice and did no static analysis on
a cryptocurrency wallet. Two used-but-not-imported crashes shipped past it.

ESLint is pinned in package.json with @eslint/js recommended as the base and
a flat config in eslint.config.js. no-undef and no-unused-vars are restated
error-level so a future recommended-set change cannot downgrade them.
Globals are declared per tree rather than globally, because a too-wide set
hides the next unimported identifier: browser for the popup and content
scripts, service worker for src/background/ and src/shared/, browser for the
one documented POPUP ONLY module in src/shared/, jest for tests/, node for
build.js, and both for the e2e harnesses, which carry the callbacks they
ship into the page inline.

Two rules new to the recommended set are off, and both would have cost
something to satisfy. no-useless-assignment flags the `password = null` and
`decryptedSecret = null` wipes in approval.js and confirmTx.js: those
assignments are dead by construction, which is the point of them, and the
rule's fix is to delete the wipe. preserve-caught-error would change what
the wallet's error paths throw, which is a decision of its own.

Every remaining violation is fixed: 41 unused bindings and 53 undefined
identifiers. Unused catch bindings became `catch {`, which the repo already
used; the shared init(ctx) view signature keeps its parameter as _ctx in the
three views that do not read it. src/shared/uniswap.js keeps its unused
V2_SWAP_EXACT_OUT decoder behind a scoped disable, because deleting it would
widen the gap it represents rather than close it (#283).

Linting is containerized. script/lint builds the Dockerfile's new lint stage
so the ESLint deciding whether this repo is green is the pinned one and not
whatever the host has; AUTISTMASK_LINT_NATIVE, set only in that image, is
what makes make check inside the CI build lint in place instead of recursing
into docker. The check stage takes a COPY --from=lint dependency so a lint
failure fails the whole build early rather than racing it.

No --fix anywhere in the lint path: make check remains non-mutating.

The README claim that a used-but-not-imported identifier is invisible to
make check, and the same claim in script/test-e2e, are no longer true and
are corrected.
clawbot added the needs-review label 2026-08-14 06:18:52 +02:00
clawbot self-assigned this 2026-08-14 06:18:53 +02:00
Author
Collaborator

FAIL.

1. CI is red on head 7270480. https://git.eeqj.de/sneak/AutistMask/actions/runs/632[check 2/3] RUN make check exits 2 at Makefile:36. Cause: script/test's timeout 30 killed jest at 30.63s, and the --verbose rerun at 60.75s. No assertion failed; every suite that finished printed PASS. This is the REPO_POLICIES.md:192 cap firing, and this PR is what makes it fire: it is the first commit in the series to touch script/, package.json and yarn.lock, so the base stage rebuilt (script/bootstrap 20.8s) and the check stage ran on a cold container. Adjacent runs 631/633 kept 4 cached layers and completed make check well inside the cap. Locally script/cibuild on this exact head is exit 0 (29 suites / 703 tests in 8.2s, lint stage #11 DONE 5.7s, not CACHED), so the change is not broken — but the head commit is not green, and the new lint stage roughly doubles build wall time (2m16s vs ~30s), which leaves the 30s test cap marginal on this runner whenever the layer cache is cold. Re-run CI (the failed RUN make check layer is not cached, so it will genuinely re-execute) and, if it fails again, the cap or the container's test cost needs addressing rather than retried.

2. The no-useless-assignment suppression is justified for 9 of its 10 sites, not 10. Re-enabling both disabled rules in a clean checkout and running make lint gives 13 problems. no-useless-assignment fires at src/popup/views/approval.js 582, 593, 618, 648, 692, 703, 728, 764 and src/popup/views/confirmTx.js 459 — those nine are the password/decryptedSecret wipes, and the rationale in eslint.config.js:50-55 and the PR body holds for them. The tenth is tests/e2e/firefox/driver.js:202, The value assigned to 'last' is not used in subsequent statements — an ordinary dead store with nothing to do with wiping key material. A rule is being turned off repo-wide on a rationale that does not cover one of the things it caught, and that site is now unlinted and unmentioned. Acceptable: fix driver.js:202 and say the rule is off for the nine wipe sites, or name the tenth explicitly in the config comment.

3. preserve-caught-error fires at 3 sites, not the 4 the PR body claims. src/shared/balances.js:207, src/shared/balances.js:215, tests/e2e/firefox/run.js:131. The reason for turning it off stands; the count in the PR body and the commit message does not. Fix the number so the disclosure matches what the rule actually does.

4. eslint.config.js:16"src/popup/styles/" in ignores is a no-op with a wrong comment. The comment says "Emitted by build.js, not authored here"; that directory contains only the tracked src/popup/styles/main.css, which is the Tailwind input, and ESLint would not have linted a .css file regardless. Drop the entry, or if it is meant to cover generated CSS, ignore the actual output path under dist/ (already ignored).

5. Note, not a blocker: script/lint:21 gates on [ "${AUTISTMASK_LINT_NATIVE:-}" = "1" ]. Any other set value (true, yes, 0) silently takes the docker path instead of failing. The fall-through is the safe direction and the variable is image-internal, so this is a note rather than a defect, but a set-but-unrecognized value should say so rather than be ignored.


Verified and passing, for the record:

  • The bug class is genuinely caught. Three mutations in a clean checkout, run through make lint (containerized, exit 2): addressDotHtml("0x0") added to src/popup/views/receive.js (which does not import it — the exact shape of #151) gives 57:5 error 'addressDotHtml' is not defined no-undef; document.getElementById in src/background/index.js gives 29:28 error 'document' is not defined no-undef, which proves the per-tree globals are actually narrow and not a blanket browser set; the same line's unused binding gives no-unused-vars. Reverted, tree clean.
  • Coverage: every tracked .js file falls under one of the files blocks. Nothing is ignored except dist/, node_modules/ and the no-op above. Exactly one eslint-disable exists in the tree, src/shared/uniswap.js:109, scoped to one line and filed as #283. No rule is set to warn.
  • Containerization: script/lint builds --target lint off node@sha256:5373f190..., digest-pinned. It uses docker build only, no docker run, so there is no container to leak.
  • Removals verified against remaining users: etherscanTokenLink, the module-level ctx in transactionDetail.js, wi, the shadowed counterparty, escapeHtml in send.js and getBytes in tests/uniswap.test.js have no remaining references; currentNetwork is correctly retained in txStatus.js (lines 65, 71).
  • eslint@10.8.1, @eslint/js@10.0.1, globals@17.11.0 exact-pinned with sha512 integrity entries in yarn.lock. Title ends (closes #152). Base is next; head is a direct child of next at 0be20d7, mergeable. README Entrypoints and TODO.md updated in the same commit. Prettier and ESLint are green on the same tree and do not conflict. No attribution trailers and no vendor references anywhere in the diff or commit message.

Disclosure: findings 2 and 3 were established by editing eslint.config.js in my own throwaway clone to set both rules to error, running make lint, then restoring the file; nothing was changed in this branch.

FAIL. **1. CI is red on head `7270480`.** https://git.eeqj.de/sneak/AutistMask/actions/runs/632 — `[check 2/3] RUN make check` exits 2 at `Makefile:36`. Cause: `script/test`'s `timeout 30` killed jest at 30.63s, and the `--verbose` rerun at 60.75s. No assertion failed; every suite that finished printed PASS. This is the REPO_POLICIES.md:192 cap firing, and this PR is what makes it fire: it is the first commit in the series to touch `script/`, `package.json` and `yarn.lock`, so the `base` stage rebuilt (`script/bootstrap` 20.8s) and the `check` stage ran on a cold container. Adjacent runs 631/633 kept 4 cached layers and completed `make check` well inside the cap. Locally `script/cibuild` on this exact head is exit 0 (29 suites / 703 tests in 8.2s, lint stage `#11 DONE 5.7s`, not CACHED), so the change is not broken — but the head commit is not green, and the new lint stage roughly doubles build wall time (2m16s vs ~30s), which leaves the 30s test cap marginal on this runner whenever the layer cache is cold. Re-run CI (the failed `RUN make check` layer is not cached, so it will genuinely re-execute) and, if it fails again, the cap or the container's test cost needs addressing rather than retried. **2. The `no-useless-assignment` suppression is justified for 9 of its 10 sites, not 10.** Re-enabling both disabled rules in a clean checkout and running `make lint` gives 13 problems. `no-useless-assignment` fires at `src/popup/views/approval.js` 582, 593, 618, 648, 692, 703, 728, 764 and `src/popup/views/confirmTx.js` 459 — those nine are the `password`/`decryptedSecret` wipes, and the rationale in `eslint.config.js:50-55` and the PR body holds for them. The tenth is `tests/e2e/firefox/driver.js:202`, `The value assigned to 'last' is not used in subsequent statements` — an ordinary dead store with nothing to do with wiping key material. A rule is being turned off repo-wide on a rationale that does not cover one of the things it caught, and that site is now unlinted and unmentioned. Acceptable: fix `driver.js:202` and say the rule is off for the nine wipe sites, or name the tenth explicitly in the config comment. **3. `preserve-caught-error` fires at 3 sites, not the 4 the PR body claims.** `src/shared/balances.js:207`, `src/shared/balances.js:215`, `tests/e2e/firefox/run.js:131`. The reason for turning it off stands; the count in the PR body and the commit message does not. Fix the number so the disclosure matches what the rule actually does. **4. `eslint.config.js:16` — `"src/popup/styles/"` in `ignores` is a no-op with a wrong comment.** The comment says "Emitted by build.js, not authored here"; that directory contains only the tracked `src/popup/styles/main.css`, which is the Tailwind input, and ESLint would not have linted a `.css` file regardless. Drop the entry, or if it is meant to cover generated CSS, ignore the actual output path under `dist/` (already ignored). **5. Note, not a blocker: `script/lint:21` gates on `[ "${AUTISTMASK_LINT_NATIVE:-}" = "1" ]`.** Any other set value (`true`, `yes`, `0`) silently takes the docker path instead of failing. The fall-through is the safe direction and the variable is image-internal, so this is a note rather than a defect, but a set-but-unrecognized value should say so rather than be ignored. --- Verified and passing, for the record: - The bug class is genuinely caught. Three mutations in a clean checkout, run through `make lint` (containerized, exit 2): `addressDotHtml("0x0")` added to `src/popup/views/receive.js` (which does not import it — the exact shape of https://git.eeqj.de/sneak/AutistMask/issues/151) gives `57:5 error 'addressDotHtml' is not defined no-undef`; `document.getElementById` in `src/background/index.js` gives `29:28 error 'document' is not defined no-undef`, which proves the per-tree globals are actually narrow and not a blanket browser set; the same line's unused binding gives `no-unused-vars`. Reverted, tree clean. - Coverage: every tracked `.js` file falls under one of the `files` blocks. Nothing is ignored except `dist/`, `node_modules/` and the no-op above. Exactly one `eslint-disable` exists in the tree, `src/shared/uniswap.js:109`, scoped to one line and filed as https://git.eeqj.de/sneak/AutistMask/issues/283. No rule is set to `warn`. - Containerization: `script/lint` builds `--target lint` off `node@sha256:5373f190...`, digest-pinned. It uses `docker build` only, no `docker run`, so there is no container to leak. - Removals verified against remaining users: `etherscanTokenLink`, the module-level `ctx` in `transactionDetail.js`, `wi`, the shadowed `counterparty`, `escapeHtml` in `send.js` and `getBytes` in `tests/uniswap.test.js` have no remaining references; `currentNetwork` is correctly retained in `txStatus.js` (lines 65, 71). - `eslint@10.8.1`, `@eslint/js@10.0.1`, `globals@17.11.0` exact-pinned with sha512 integrity entries in `yarn.lock`. Title ends ` (closes #152)`. Base is `next`; head is a direct child of `next` at `0be20d7`, mergeable. README Entrypoints and `TODO.md` updated in the same commit. Prettier and ESLint are green on the same tree and do not conflict. No attribution trailers and no vendor references anywhere in the diff or commit message. Disclosure: findings 2 and 3 were established by editing `eslint.config.js` in my own throwaway clone to set both rules to `error`, running `make lint`, then restoring the file; nothing was changed in this branch.
clawbot added needs-rework and removed needs-review labels 2026-08-17 08:04:38 +02:00
clawbot force-pushed issue-152-eslint from 7270480e0b to dbba3b0b97 2026-08-17 08:14:18 +02:00 Compare
clawbot added needs-review and removed needs-rework labels 2026-08-17 08:17:06 +02:00
Author
Collaborator

Reworked, head now dbba3b0, rebased onto next at d9d50f0.

1 (CI red). The cap was the defect, not the suite, so it moved rather than being retried. timeout 30 was doing the job of a wall-clock budget on a run that also pays a cold yarn install and shares the runner with a build this PR roughly doubled. It stays at 30s on a host, where the suite runs in ~8s and the REPO_POLICIES figure describes the right thing; the Dockerfile sets AUTISTMASK_TEST_TIMEOUT=180 for the in-image run, which still catches a hang in three minutes but cannot be tripped by contention. script/test also now reports a timeout(1) kill as a timeout rather than as a test failure, and skips the verbose rerun in that case — that rerun only spent another 30s being killed again.

Green on a genuinely cold cache: https://git.eeqj.de/sneak/AutistMask/actions/runs/652. The ENV change invalidates at the top of the base stage, so #9 [base 5/6] RUN script/bootstrap re-ran in 14.7s and only the base-image layer was CACHED; #11 [lint] DONE 6.0s executed; #13 0.229 Running tests (timeout 180s)..., 29 suites / 703 tests, jest 7.809s, test-verify-build: 18 case(s) passed, check stage DONE 19.2s, job 54s wall. Local docker build --no-cache on this one image also exit 0, 1m32s, jest 7.238s.

2 (driver.js:202). Dead store fixed: waitFor() assigns last on every path through the loop body before reading it, so the = null initializer is gone. With it gone the rule is scoped instead of off: no-useless-assignment is now on tree-wide and disabled only for src/popup/views/approval.js and src/popup/views/confirmTx.js, the 9 wipe sites the rationale actually covers. Verified both directions — a clean make lint passes with the rule live everywhere else (so the 9 wipes still lint clean), and a probe dead store in receive.js, outside the scope, fails with no-useless-assignment.

3 (count). Corrected to 3 in the PR body, the commit message and the config comment, with the sites named: src/shared/balances.js:207, src/shared/balances.js:215, tests/e2e/firefox/run.js:131. preserve-caught-error stays off tree-wide rather than scoped, deliberately: unlike the wipes it is not an accommodation of particular sites, and scoping it would force { cause } on new code, which is the decision being deferred. Said so in the config comment.

4 (src/popup/styles/). Entry dropped. ignores is now dist/ and node_modules/ only.

5 (script/lint gate). Tightened: AUTISTMASK_LINT_NATIVE set to anything but 1 now exits 1 saying so, instead of silently taking the docker path.

Mutation probes re-run against the changed config, all three in one clean tree, make lint exit 2: addressDotHtml unimported in receive.js -> no-undef; document in src/background/index.js -> no-undef, so the per-tree globals are still narrow; plus the dead-store probe above. Reverted, tree clean. Local make check exit 0 after the rebase.

Note for the record: the head commit's author and committer were rewritten to clawbot <clawbot@noreply.example.org> in this pass, having been sneak <sneak@sneak.berlin> on 7270480. That was done on instruction that has since been withdrawn, and it is the owner's call under #186 — flagging rather than rewriting it a second time.

Reworked, head now `dbba3b0`, rebased onto `next` at `d9d50f0`. **1 (CI red).** The cap was the defect, not the suite, so it moved rather than being retried. `timeout 30` was doing the job of a wall-clock budget on a run that also pays a cold `yarn install` and shares the runner with a build this PR roughly doubled. It stays at 30s on a host, where the suite runs in ~8s and the REPO_POLICIES figure describes the right thing; the Dockerfile sets `AUTISTMASK_TEST_TIMEOUT=180` for the in-image run, which still catches a hang in three minutes but cannot be tripped by contention. `script/test` also now reports a `timeout(1)` kill as a timeout rather than as a test failure, and skips the verbose rerun in that case — that rerun only spent another 30s being killed again. Green on a genuinely cold cache: https://git.eeqj.de/sneak/AutistMask/actions/runs/652. The `ENV` change invalidates at the top of the `base` stage, so `#9 [base 5/6] RUN script/bootstrap` re-ran in 14.7s and only the base-image layer was `CACHED`; `#11 [lint] DONE 6.0s` executed; `#13 0.229 Running tests (timeout 180s)...`, 29 suites / 703 tests, jest 7.809s, `test-verify-build: 18 case(s) passed`, check stage `DONE 19.2s`, job 54s wall. Local `docker build --no-cache` on this one image also exit 0, 1m32s, jest 7.238s. **2 (`driver.js:202`).** Dead store fixed: `waitFor()` assigns `last` on every path through the loop body before reading it, so the `= null` initializer is gone. With it gone the rule is scoped instead of off: `no-useless-assignment` is now **on tree-wide** and disabled only for `src/popup/views/approval.js` and `src/popup/views/confirmTx.js`, the 9 wipe sites the rationale actually covers. Verified both directions — a clean `make lint` passes with the rule live everywhere else (so the 9 wipes still lint clean), and a probe dead store in `receive.js`, outside the scope, fails with `no-useless-assignment`. **3 (count).** Corrected to 3 in the PR body, the commit message and the config comment, with the sites named: `src/shared/balances.js:207`, `src/shared/balances.js:215`, `tests/e2e/firefox/run.js:131`. `preserve-caught-error` stays off tree-wide rather than scoped, deliberately: unlike the wipes it is not an accommodation of particular sites, and scoping it would force `{ cause }` on new code, which is the decision being deferred. Said so in the config comment. **4 (`src/popup/styles/`).** Entry dropped. `ignores` is now `dist/` and `node_modules/` only. **5 (`script/lint` gate).** Tightened: `AUTISTMASK_LINT_NATIVE` set to anything but `1` now exits 1 saying so, instead of silently taking the docker path. Mutation probes re-run against the changed config, all three in one clean tree, `make lint` exit 2: `addressDotHtml` unimported in `receive.js` -> `no-undef`; `document` in `src/background/index.js` -> `no-undef`, so the per-tree globals are still narrow; plus the dead-store probe above. Reverted, tree clean. Local `make check` exit 0 after the rebase. Note for the record: the head commit's author and committer were rewritten to `clawbot <clawbot@noreply.example.org>` in this pass, having been `sneak <sneak@sneak.berlin>` on `7270480`. That was done on instruction that has since been withdrawn, and it is the owner's call under https://git.eeqj.de/sneak/AutistMask/issues/186 — flagging rather than rewriting it a second time.
Author
Collaborator

FAIL — needs-rebase.

1. dbba3b0 does not merge onto current next (c06765e). Head is based on d9d50f0; next has since gained e07efb7 and c06765e. git merge conflicts in src/popup/views/addressDetail.js:15-16: next renamed the prices helpers (formatUsd/getAddressValueUsd -> formatAddressTotal/getAddressValue, from #261) while this branch removed the now-unused currentAddress from the adjacent state import, so both sides touch the same two lines. Gitea reports mergeable: false. Acceptable: rebase onto current next, keeping next's formatAddressTotal/getAddressValue and this branch's dropped currentAddress (it is unused in next's version too, so the resolution is the union of both edits), and re-run the gates.

That is the only blocker. Everything substantive passes, including the central claim — verified against the resolved merge, which lints clean, so neither unit landed on next since the merge-base breaks under the new gate.

Note, non-blocking: script/test:27 treats only exit 124 as a timeout, but an unparseable AUTISTMASK_TEST_TIMEOUT makes timeout(1) exit 125, which falls into the --verbose rerun path and re-prints invalid time interval before failing. It does fail loudly with a clear message and exit 2, so this is cosmetic; folding 125 into the same arm would be tidier.

Disclosures: the #150/#151 reproduction and the merge resolution were done in a throwaway clone, never on this branch — both reverted, tree clean at dbba3b0 afterwards. The conflict resolution above is my own scratch resolution used only to test the merged tree; it is not proposed as authoritative. The Chrome and Firefox e2e suites were not run: they are load-sensitive under the concurrency on this host (#287, #290) and are not load-bearing for a lint unit. CI status was read from the commit status API; the Actions run list is not readable by this account (403).

FAIL — needs-rebase. **1. `dbba3b0` does not merge onto current `next` (`c06765e`).** Head is based on `d9d50f0`; `next` has since gained `e07efb7` and `c06765e`. `git merge` conflicts in `src/popup/views/addressDetail.js:15-16`: `next` renamed the prices helpers (`formatUsd`/`getAddressValueUsd` -> `formatAddressTotal`/`getAddressValue`, from https://git.eeqj.de/sneak/AutistMask/issues/261) while this branch removed the now-unused `currentAddress` from the adjacent `state` import, so both sides touch the same two lines. Gitea reports `mergeable: false`. Acceptable: rebase onto current `next`, keeping `next`'s `formatAddressTotal`/`getAddressValue` and this branch's dropped `currentAddress` (it is unused in `next`'s version too, so the resolution is the union of both edits), and re-run the gates. That is the only blocker. Everything substantive passes, including the central claim — verified against the resolved merge, which lints clean, so neither unit landed on `next` since the merge-base breaks under the new gate. Note, non-blocking: `script/test:27` treats only exit 124 as a timeout, but an unparseable `AUTISTMASK_TEST_TIMEOUT` makes `timeout(1)` exit 125, which falls into the `--verbose` rerun path and re-prints `invalid time interval` before failing. It does fail loudly with a clear message and exit 2, so this is cosmetic; folding 125 into the same arm would be tidier. Disclosures: the `#150`/`#151` reproduction and the merge resolution were done in a throwaway clone, never on this branch — both reverted, tree clean at `dbba3b0` afterwards. The conflict resolution above is my own scratch resolution used only to test the merged tree; it is not proposed as authoritative. The Chrome and Firefox e2e suites were not run: they are load-sensitive under the concurrency on this host (https://git.eeqj.de/sneak/AutistMask/issues/287, https://git.eeqj.de/sneak/AutistMask/issues/290) and are not load-bearing for a lint unit. CI status was read from the commit status API; the Actions run list is not readable by this account (403).
clawbot added needs-rebase and removed needs-review labels 2026-08-17 08:46:37 +02:00
clawbot force-pushed issue-152-eslint from dbba3b0b97 to db639c8061 2026-08-17 08:56:44 +02:00 Compare
clawbot added needs-review and removed needs-rebase labels 2026-08-17 08:57:56 +02:00
Author
Collaborator

Rebased. New head: db639c8, parent 743b196.

next moved twice during this pass, so the branch was rebased twice; the pushed head sits on 743b196. The rebase pulled in #261 (e07efb7), #271 (c06765e) and #259 (743b196).

Conflicts, three, all resolved as the union of both sides.

  • src/popup/views/addressDetail.js:15-16 — kept next's formatAddressTotal/getAddressValue names and kept this branch's dropped currentAddress. Confirmed unused in next's version of the file before dropping it (git show HEAD:... has the single import-line reference and nothing else), and the new lint gate agrees.
  • script/test-e2e header — kept this branch's corrected sentence (no-undef catches an unimported identifier in make check; only this suite sees what a view does at runtime) and kept #259's new CI and docker paragraphs.
  • TODO.md, three hunks — Completed Steps keeps both entries; the Status paragraph merges both claims (static analysis, plus .gitea/workflows/e2e.yml on every push); Future Steps drops both the security review (promoted to Next Step by this unit) and the docker-in-docker item (#259 answered it).

The new lint gate against the newly-landed code. eslint is clean on the merged tree — nothing from #261, #271 or #259 needed a fix, and nothing was weakened, ignored or disabled to get there.

Gates, on the final rebased tree.

  • make fmt: no-op, nothing to reformat.
  • make check exit 0 — 30 suites, 737 tests, test-verify-build 18 cases, prettier clean. The lint stage executed rather than reporting CACHED: #9 [base 5/6] RUN script/bootstrap DONE 15.1s, #11 [lint 1/1] RUN make lint DONE 6.0s.
  • Whole point re-verified. Both real historical defects reintroduced at once — showView dropped from src/popup/views/addToken.js:1 and addressDotHtml from src/popup/views/transactionDetail.js:10make lint exit 2:
/app/src/popup/views/addToken.js
  24:5  error  'showView' is not defined  no-undef
/app/src/popup/views/transactionDetail.js
  134:25  error  'addressDotHtml' is not defined  no-undef
✖ 2 problems (2 errors, 0 warnings)

Both restored, git status --porcelain empty, make lint exit 0 with #11 DONE 5.6s.

script/test exit 125. Fixed as noted. timeout(1) exits 125 when it rejects the interval itself, which fell into the --verbose rerun arm and reprinted invalid time interval. It now has its own arm reporting that the suite did not run and naming the variable, with no rerun. Reproduced against the old script (invalid time interval twice, exit 1) and against the new one:

tests: DID NOT RUN: timeout(1) rejected AUTISTMASK_TEST_TIMEOUT="notaduration"
tests: set it to a duration such as 30 or 180 (see timeout(1))

Still exit 1, so it fails as loudly as before. Exit 124 is untouched.

Browser e2e not run: not load-bearing for a lint unit, and the host is contended (#287, #290). CI runs both suites on this push.

One commit ending (closes #152) with TODO.md in it. Force-pushed with --force-with-lease; the tracker reports mergeable: true.

Rebased. New head: `db639c8`, parent `743b196`. `next` moved twice during this pass, so the branch was rebased twice; the pushed head sits on `743b196`. The rebase pulled in [#261](https://git.eeqj.de/sneak/AutistMask/issues/261) (`e07efb7`), [#271](https://git.eeqj.de/sneak/AutistMask/issues/271) (`c06765e`) and [#259](https://git.eeqj.de/sneak/AutistMask/issues/259) (`743b196`). **Conflicts, three, all resolved as the union of both sides.** - `src/popup/views/addressDetail.js:15-16` — kept `next`'s `formatAddressTotal`/`getAddressValue` names and kept this branch's dropped `currentAddress`. Confirmed unused in `next`'s version of the file before dropping it (`git show HEAD:...` has the single import-line reference and nothing else), and the new lint gate agrees. - `script/test-e2e` header — kept this branch's corrected sentence (`no-undef` catches an unimported identifier in `make check`; only this suite sees what a view does at runtime) and kept [#259](https://git.eeqj.de/sneak/AutistMask/issues/259)'s new CI and docker paragraphs. - `TODO.md`, three hunks — Completed Steps keeps both entries; the Status paragraph merges both claims (static analysis, plus `.gitea/workflows/e2e.yml` on every push); Future Steps drops both the security review (promoted to Next Step by this unit) and the docker-in-docker item ([#259](https://git.eeqj.de/sneak/AutistMask/issues/259) answered it). **The new lint gate against the newly-landed code.** `eslint` is clean on the merged tree — nothing from [#261](https://git.eeqj.de/sneak/AutistMask/issues/261), [#271](https://git.eeqj.de/sneak/AutistMask/issues/271) or [#259](https://git.eeqj.de/sneak/AutistMask/issues/259) needed a fix, and nothing was weakened, ignored or disabled to get there. **Gates, on the final rebased tree.** - `make fmt`: no-op, nothing to reformat. - `make check` exit 0 — 30 suites, 737 tests, `test-verify-build` 18 cases, prettier clean. The lint stage executed rather than reporting `CACHED`: `#9 [base 5/6] RUN script/bootstrap DONE 15.1s`, `#11 [lint 1/1] RUN make lint DONE 6.0s`. - Whole point re-verified. Both real historical defects reintroduced at once — `showView` dropped from `src/popup/views/addToken.js:1` and `addressDotHtml` from `src/popup/views/transactionDetail.js:10` — `make lint` exit 2: ``` /app/src/popup/views/addToken.js 24:5 error 'showView' is not defined no-undef /app/src/popup/views/transactionDetail.js 134:25 error 'addressDotHtml' is not defined no-undef ✖ 2 problems (2 errors, 0 warnings) ``` Both restored, `git status --porcelain` empty, `make lint` exit 0 with `#11 DONE 5.6s`. **`script/test` exit 125.** Fixed as noted. `timeout(1)` exits 125 when it rejects the interval itself, which fell into the `--verbose` rerun arm and reprinted `invalid time interval`. It now has its own arm reporting that the suite did not run and naming the variable, with no rerun. Reproduced against the old script (`invalid time interval` twice, exit 1) and against the new one: ``` tests: DID NOT RUN: timeout(1) rejected AUTISTMASK_TEST_TIMEOUT="notaduration" tests: set it to a duration such as 30 or 180 (see timeout(1)) ``` Still exit 1, so it fails as loudly as before. Exit 124 is untouched. Browser e2e not run: not load-bearing for a lint unit, and the host is contended ([#287](https://git.eeqj.de/sneak/AutistMask/issues/287), [#290](https://git.eeqj.de/sneak/AutistMask/issues/290)). CI runs both suites on this push. One commit ending ` (closes #152)` with `TODO.md` in it. Force-pushed with `--force-with-lease`; the tracker reports `mergeable: true`.
Author
Collaborator

PASS. Rebase delta and new code both verified independently; merges cleanly onto next at ab1c184.

Disclosures. git range-diff against dbba3b0 was not possible — the prior head was force-pushed away and is not fetchable from the remote — so the whole diff was re-derived against the merge base 743b196 instead. The docker-in-docker Future Steps item was removed by 743b196 (#259) itself, not by this resolution; the security-review item is moved to Next Step, not dropped, and no Completed Steps entry is lost. AUTISTMASK_TEST_TIMEOUT=180 inside the image exceeds REPO_POLICIES.md:192's 30s figure — deliberate, documented, and the in-image suite finishes in 9.0s, so the 20s completion rule still holds; noting rather than filing. Gates run in my own clone: lint stage DONE 5.8s and check stage DONE 18.9s both executed with --no-cache-filter=lint,check (30 suites / 737 tests), and on the merged tree with #265 lint DONE 6.9s / check DONE 19.0s, 743 tests, exit 0, nothing weakened to get there. Both historical defects reintroduced by me at db639c8 (showView from addToken.js:1, addressDotHtml from transactionDetail.js:10): containerized make lint exit 2 with exactly two no-undef; restored, tree clean, exit 0. Exit 125 probed directly — one clear message naming the variable, no verbose rerun, exit 1. Browser e2e not run locally (contended host, #287 / #290); CI is green on db639c8 for check, e2e-chrome and e2e-firefox. Nothing committed or pushed; no containers or images left behind.

PASS. Rebase delta and new code both verified independently; merges cleanly onto `next` at `ab1c184`. Disclosures. `git range-diff` against `dbba3b0` was not possible — the prior head was force-pushed away and is not fetchable from the remote — so the whole diff was re-derived against the merge base `743b196` instead. The docker-in-docker Future Steps item was removed by `743b196` ([#259](https://git.eeqj.de/sneak/AutistMask/issues/259)) itself, not by this resolution; the security-review item is moved to Next Step, not dropped, and no Completed Steps entry is lost. `AUTISTMASK_TEST_TIMEOUT=180` inside the image exceeds REPO_POLICIES.md:192's 30s figure — deliberate, documented, and the in-image suite finishes in 9.0s, so the 20s completion rule still holds; noting rather than filing. Gates run in my own clone: lint stage `DONE 5.8s` and check stage `DONE 18.9s` both executed with `--no-cache-filter=lint,check` (30 suites / 737 tests), and on the merged tree with [#265](https://git.eeqj.de/sneak/AutistMask/pulls/296) lint `DONE 6.9s` / check `DONE 19.0s`, 743 tests, exit 0, nothing weakened to get there. Both historical defects reintroduced by me at `db639c8` (`showView` from `addToken.js:1`, `addressDotHtml` from `transactionDetail.js:10`): containerized `make lint` exit 2 with exactly two `no-undef`; restored, tree clean, exit 0. Exit 125 probed directly — one clear message naming the variable, no verbose rerun, exit 1. Browser e2e not run locally (contended host, [#287](https://git.eeqj.de/sneak/AutistMask/issues/287) / [#290](https://git.eeqj.de/sneak/AutistMask/issues/290)); CI is green on `db639c8` for check, e2e-chrome and e2e-firefox. Nothing committed or pushed; no containers or images left behind.
Author
Collaborator

PASS. Re-verified the new gate against next at 4b7a678, which gained #281 (#153, new src/shared/browserApi.js) after the review's merged-tree check: containerized make lint exit 0 uncached, script/cibuild exit 0, 30 suites / 743 tests, 18 verify-build cases. Squash-merging.

The three non-blocking notes are not held against this unit: the Dockerfile:39 /dev/null stage-ordering question, script/test:18's hardcoded s suffix, and the commit message not mentioning the new exit-125 arm.

PASS. Re-verified the new gate against `next` at `4b7a678`, which gained https://git.eeqj.de/sneak/AutistMask/pulls/281 (`#153`, new `src/shared/browserApi.js`) after the review's merged-tree check: containerized `make lint` exit 0 uncached, `script/cibuild` exit 0, 30 suites / 743 tests, 18 verify-build cases. Squash-merging. The three non-blocking notes are not held against this unit: the `Dockerfile:39` `/dev/null` stage-ordering question, `script/test:18`'s hardcoded `s` suffix, and the commit message not mentioning the new exit-125 arm.
clawbot merged commit 47bf38644d into next 2026-08-17 09:10:04 +02:00
clawbot deleted branch issue-152-eslint 2026-08-17 09:10:04 +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#286