Files
AutistMask/TODO.md
clawbot d74dd40743
All checks were successful
check / check (push) Successful in 29s
fix: WaitTx timeout no longer overwrites a rendered success screen (closes #155)
A poll tick that found a receipt called showSuccess() and then fell through
to the elapsed check, so on the tick crossing the 60-second deadline the
"Transaction Confirmed" screen was immediately replaced by "not confirmed
within 60 seconds" — the user is told a confirmed transaction failed.

The wait now has an explicit lifecycle. A wait id is bumped by endWait(),
which is called on receipt, on timeout, when a new wait starts and when the
user navigates away; every timer callback and every post-await continuation
checks it, so exactly one outcome can be rendered per wait and no stale
timer or in-flight receipt lookup can touch a view it no longer owns.

A receipt lookup that throws is treated as "no answer this tick" rather than
"no receipt": the poll returns before the deadline check and keeps running,
so one transient RPC failure cannot declare a timeout. This matters most on
a resumed wait, whose first poll is immediate and may already be past the
deadline, where a single error would otherwise be terminal. Retrying is
bounded: six consecutive failed lookups — 60 seconds at the poll cadence,
the same patience the confirmation deadline gets — end the wait and report
that the network could not be reached, pointing at the RPC URL in Settings.
That is a different fact from the timeout, because the chain was never
asked, and it says so rather than claiming the transaction did not confirm.
Any lookup that answers, with a receipt or with null, resets the count. An
unbounded retry would be worse than the bug it avoids: the wait is persisted,
so a mistyped RPC URL would leave a wait that every popup open resumes and
nothing ever ends, on a view with no exit control of its own.

The wait is also persisted (state.viewData.pendingWait) and "wait-tx" is now
restorable: reopening the popup resumes the poll with the elapsed counter
and the deadline still measured from the original broadcast, instead of
silently abandoning the wait. restoreWait() validates every field startWait()
goes on to use, not just the presence of the containers — hash, a non-array
object txInfo carrying a string to and a string amount, and a finite numeric
broadcastTime — and returns false otherwise. txInfo.to reaches addressTitle(),
which calls address.toLowerCase(), so a payload merely missing that one field
would throw a TypeError out of restoreView(), which init() does not guard:
the rest of popup init is skipped and wait-tx stays on screen with no back
control. A non-numeric broadcastTime leaves an unexitable wait counting
"NaNs". Polling stays in the popup rather than moving to the background,
which would depend on setInterval surviving in an MV3 service worker.

"wait-tx" is added to src/popup/restorableViews.js, and a test pins its
membership. restoreView() refuses any view outside that set, so dropping the
entry would kill the resume feature silently — the other tests call
restoreWait() directly and never read the set.

The 60-second threshold and the timeout copy are unchanged.
2026-08-12 08:17:55 +00:00

184 lines
10 KiB
Markdown

# Workflow
- `git pull` `next` and cut a branch from it — one branch per issue, named
`issue-<N>-<slug>`. Never branch from `main`.
- Do the work as one commit whose title ends with ` (closes #N)`, with the
`TODO.md` update in that same commit.
- Move Next Step to the top of Completed Steps; move the top item of Future
Steps into Next Step.
- Run `make fmt`, then `make check`. A feature branch may be red; `next` and
`main` may not.
- Rebase onto current `next` immediately before pushing — other branches land on
`next` continuously — and re-run `make check` after resolving, because a clean
textual merge can still break the build.
- Push the branch and open one PR per issue with base `next`. Never base `main`.
- An independent reviewer who did not write the change gates the merge. On a
passed review the PR is squash-merged into `next`.
- `next` is the branch for the next milestone. It is kept green and mergeable to
`main` at any moment, without notice.
- `main` receives exactly one PR per milestone, from `next`. Releases are tagged
from `main`.
# Status
pre-1.0, working towards the 1.0.0 milestone. Tagged v0.1.0 on 2026-02-27. The
milestone is in flight on `next`; its `next` -> `main` PR is
[#190](https://git.eeqj.de/sneak/AutistMask/pulls/190). `make check` verified
green on `next` at `e9fa8be` on 2026-08-10, and `make build` produces
`dist/chrome/` and `dist/firefox/` with every bundle verified to have `DEBUG`
compiled off.
The backlog lives on the
[Gitea tracker](https://git.eeqj.de/sneak/AutistMask/issues), which is
authoritative; this file does not duplicate it. Full policy file set present. A
real-browser end-to-end suite (`make test-e2e`) now sits alongside `make check`,
which cannot see a runtime `ReferenceError` in a popup view.
# Next Step
Land [#152](https://git.eeqj.de/sneak/AutistMask/issues/152): add ESLint to
`script/lint`. `make check` is `prettier --check` only today and cannot catch
undefined identifiers, which is how
[#150](https://git.eeqj.de/sneak/AutistMask/issues/150) and
[#151](https://git.eeqj.de/sneak/AutistMask/issues/151) shipped.
# Completed Steps
- 2026-08-11: WaitTx lifecycle: a receipt and the 60-second timeout can no
longer both render on one tick, no timer or in-flight lookup outlives its
wait, a failed receipt lookup no longer counts as a timeout (but six in a row
end the wait, reported as an unreachable network rather than as a timeout),
and the wait now resumes after a popup close
([#155](https://git.eeqj.de/sneak/AutistMask/issues/155)).
- 2026-08-11: Known-symbol spoof verification became a Settings toggle
(`hideSpoofedSymbols`), on by default, governing the transaction-history
filter and the fraud-contract learning it feeds
([#176](https://git.eeqj.de/sneak/AutistMask/issues/176)).
- 2026-08-11: `script/verify-build` now walks `dist/` NUL-delimited and asserts
`dist/` is a real directory, so a path with a trailing space or a newline can
no longer carry a debug marker past the unlisted-bundle check
([#223](https://git.eeqj.de/sneak/AutistMask/issues/223)).
- 2026-08-11: UTC Timestamps checkbox moved from the Token Spam Protection well
into Display, next to the theme selector
([#212](https://git.eeqj.de/sneak/AutistMask/issues/212)).
- 2026-08-11: Network fee counted in the confirmation-screen balance check for
both ETH and ERC-20 sends, reserving what the node actually charges a type-2
transaction, with the arithmetic in a pure, unit-tested
`src/shared/txValidation.js`
([#154](https://git.eeqj.de/sneak/AutistMask/issues/154)).
- 2026-08-11: A dust threshold of `0` now means "hide nothing" instead of
falling back to the 100,000 gwei default, and every address comparison in
`src/shared/transactions.js` goes through one case-normalising helper so a
checksummed genuine contract is no longer read as a spoof
([#179](https://git.eeqj.de/sneak/AutistMask/issues/179)).
- 2026-08-11: Password-gated recovery phrase display for HD wallets, reached
from the wallet row in Settings, wiped on leaving the screen and excluded from
the views the popup can reopen onto
([#161](https://git.eeqj.de/sneak/AutistMask/issues/161)).
- 2026-08-11: Extended-key import hardened — the base58 checksum is now enforced
on every xprv and xpub, and a non-master key is refused with an explanation
instead of being derived beneath
([#210](https://git.eeqj.de/sneak/AutistMask/issues/210)).
- 2026-08-11: the balance refresh and the 24-hour phishing list refresh moved
from `setInterval` to the extension alarms API, with the phishing delta and
its fetch timestamps persisted to extension storage, so neither job dies with
the MV3 service worker. Each job's freshness guard was decoupled from its
alarm period at the same time — timed to the period, a guard vetoes its own
scheduled tick and halves the real refresh rate
([#158](https://git.eeqj.de/sneak/AutistMask/issues/158)).
- 2026-08-11: Policy compliance sweep — conditional verbose test rerun, local
Tailwind binary instead of `npx`, `--frozen-lockfile` on `make install`, and
the Makefile-only targets documented in the README
([#166](https://git.eeqj.de/sneak/AutistMask/issues/166)).
- 2026-08-11: `script/verify-build` diagnostics corrected: the both-markers
message now states what is and is not proven, an unreadable bundle is
diagnosed as an I/O fault rather than as changed output, the `*.js` assumption
lives only in `build.js`, and the unlisted-bundle scan hard-fails when it
cannot enumerate `dist/`
([#180](https://git.eeqj.de/sneak/AutistMask/issues/180)).
- 2026-08-11: Known-answer test coverage for the crypto core — BIP-39/BIP-32
derivation in `wallet.js` and the Argon2id vault in `vault.js`
([#159](https://git.eeqj.de/sneak/AutistMask/issues/159)).
- 2026-08-11: Three `README.md` claims corrected against the code — blocklist
attribution, token-display rule, navigation model
([#213](https://git.eeqj.de/sneak/AutistMask/issues/213)).
- 2026-08-11: README Screen Map rebuilt from the code — every screen, element
and transition re-verified against `src/popup/`
([#164](https://git.eeqj.de/sneak/AutistMask/issues/164)).
- 2026-08-11: `docs/README.md` rewritten against the code: no competitor names,
all five network destinations documented, password/Settings/Add Wallet
sections corrected ([#163](https://git.eeqj.de/sneak/AutistMask/issues/163)).
- 2026-08-11: `loadState()` now derives `hasWallet` from the wallet list instead
of trusting the persisted flag, so a profile already saved inconsistent no
longer stays broken on every load
([#195](https://git.eeqj.de/sneak/AutistMask/issues/195)).
- 2026-08-11: Wallet deletion repairs its own state — `hasWallet` follows the
remaining wallets, the selection only moves when it was deleted, and the
active-address change is broadcast to connected sites
([#156](https://git.eeqj.de/sneak/AutistMask/issues/156)).
- 2026-08-11: One row per on-chain value movement in transaction history: the
merge moved into the pure `mergeTransactions` and the zero-ETH native side of
a plain ERC-20 transfer absorbed into its token row
([#177](https://git.eeqj.de/sneak/AutistMask/issues/177)).
- 2026-08-11: `TODO.md` Workflow rewritten to the branch-and-PR-per-issue model
on `next`, with Status and Next Step refreshed
([#191](https://git.eeqj.de/sneak/AutistMask/issues/191)).
- 2026-08-09: `DEBUG` became a build-time constant defaulting to off, injected
as the `__BUILD_DEBUG__` esbuild define and turned on with
`AUTISTMASK_DEBUG=1`, so a plain `make build` no longer hands every newly
created wallet the publicly committed test recovery phrase
([#149](https://git.eeqj.de/sneak/AutistMask/issues/149)).
- 2026-08-09: dApp approval signing moved into the popup — the password no
longer crosses the extension messaging boundary; the background broadcasts and
resolves approvals only, and verifies the signed artifact against the approval
it holds (#157).
- 2026-08-09: Post-build assertion that every emitted bundle containing
`constants.js` has `DEBUG` compiled off, via `script/verify-build` on the
`make build` path (#170).
- 2026-08-09: Containerized Chrome end-to-end harness (`make test-e2e` /
`script/test-e2e`) driving the real popup with all network intercepted, plus
the two used-but-not-imported crashes it caught: AddToken unreachable (#150)
and TransactionDetail broken for every ERC-20 transfer (#151). Harness
demonstrated failing before the fixes and passing after (#181). Interception
covers the MV3 background service worker, not just the popup page, and a
launch-time canary aborts the suite if worker traffic starts escaping.
- 2026-08-09: Reviewed the repo end to end and filed the 1.0.0 backlog
(#149-#168).
- 2026-08-09: Test coverage for the address-poisoning defense in
`src/shared/transactions.js` (#160)
- 2026-07-26: About well in settings with build info, repo link and the version
click easter egg (#145); proper view navigation stack (#146).
- 2026-07-07 Adopted scripts-to-rule-them-all: `script/` entrypoints, Makefile
shims, README Entrypoints section (#148)
- 2026-03-01: USD display suppressed on testnets (#142); estimated USD for ETH
in approve-tx view (#141).
- Sepolia testnet support (#137); etherscan links go to token-specific URLs
(#136).
- Transaction detail improvements: Type field and on-chain details (#130),
txid-first reordering (#133), swap display corrections (#128), expanded
confirm-tx warnings (#118).
- Dark mode theme setting (Light/Dark/System) with contrast fixes (#126);
timestamps include timezone offset (#120); layout shift audit, reserved space
for error messages (#124).
- Copy-flash visual feedback with timing tune (#113, #121); cross-wallet-type
duplicate detection (#115).
- 2026-02-27: v0.1.0 tagged.
- 2026-02-24: Initial scaffolding: popup UI, BIP-39 wallet creation via
ethers.js, wallet persistence, real ETH balances over RPC, ENS forward and
reverse resolution.
# Future Steps
Only work that has no issue of its own belongs here; everything else is on the
tracker.
- Pre-1.0 security review of the extension (key handling, DEBUG mode policy, RPC
input validation) before any 1.0rc tag. Individual filed issues are parts of
it, but the review is broader than any of them.
- Decide whether docker-in-docker makes `make test-e2e` runnable in the Gitea
workflow. Extending the suite itself is tracked as
[#183](https://git.eeqj.de/sneak/AutistMask/issues/183) and
[#184](https://git.eeqj.de/sneak/AutistMask/issues/184).
- Cut 1.0.0 once the milestone is empty, then continue tagging as milestones
land.