Repository navigation
test: check that encodeMouse reads every caller field once - #48
Conversation
The #41 test counted reads only on x and geometry.screenWidth. This one puts a counting getter on all 19 caller fields and asserts one read each. Signed-off-by: Thomas Kosiewski <tk@coder.com> --- _Generated with [`xum`](https://github.com/coder/xum) • Model: `anthropic:claude-opus-5-5` • Thinking: `medium`_ Change-Id: Ie3e1dfaad4e4207c7c670bd1ce9cf928549d854e
|
Verdict: PASS Parent: 9f8ec06 (main). Ghostty 83fb956 prefixes. Node 26.12.1 inside npm ( Field list. At the head, Expected bytes. I derived them from Ghostty's Test fails on the bug it guards.
Package user view. An ES module that imports Gates at the head (all exit 0):
Diff. Generated with |
## Why Ship the `snapshot()` crash fix (#46) to npm. Every earlier version, `0.1.0-beta.2` included, crashes Node when a JS accessor on `Object.prototype` disposes the terminal while `snapshot()` builds its result. ## What changed - `package.json` and `package-lock.json` go from `0.1.0-beta.2` to `0.1.0-beta.3`. The branch name matches the version, so `publish.yml` creates `v0.1.0-beta.3` after merge. - The `release-changelog` workflow adds the CHANGELOG entry to this branch. - beta.3 also ships #48 (tests only) and #49 (the raw native addon's `feed` now rejects typed arrays other than `Uint8Array`). The package exports only `createTerminal`'s module, which already rejected those values in JS, so neither gets a CHANGELOG line. - The beta.2 `## Fixed` heading, which the changelog bot had joined to the previous bullet, is restored. ## Blast Radius The merge publishes `0.1.0-beta.3` to npm under the `beta` dist-tag. `latest` stays on `0.1.0-beta.0`. Snapshot output is byte-identical to beta.2, as #46's verifier checked. ## Verification - CI (linux-x64, linux-arm64, linux-x64-ubsan, macos-arm64) passes on head `3365375`. Codex code and security reviews are clean on that head. - `prepare-release-changelog.mjs` reports `changelog-entry-exists`. `resolve-npm-publish-tag.mjs` prints `beta`. The remote has no `v0.1.0-beta.3` tag. - An independent verifier built prebuilds, ran `verify:prebuilds` (loads `prebuilds/linux-x64`), packed, and installed into a scratch consumer: `0.1.0-beta.3` loads from `prebuilds/`, and 8 accessor cases that crash beta.2 exit 0. It passed the CHANGELOG at `da96287`. --- _Generated with [`xum`](https://github.com/coder/xum) • Model: `anthropic:claude-opus-5-5` • Thinking: `high`_
## Why The `verify-native-lifecycle` skill keeps a sync commit. At that commit, its TLA+ model describes the covered native code. The sync commit was still `7366b75` (#12). Since then, #40 to #43, #46, and #49 changed covered code. #46 promised this follow-up. ## What changed - The sync commit moves to `c54051e`, the current main. It includes #48 and #49. - The coverage rows now name `ghostty_terminal_set` (scrollback byte limit) in the constructor and its failure path, `Resize`'s second `ghostty_terminal_resize` call, and the `GHOSTTY_TERMINAL_DATA_MODE` read in `EncodeMouse`. Ghostty v1.4.0 (#43) brought these. - The "no JS inside native methods" assumption now says how each method reads its arguments: 1. `Snapshot` reads its options before `RequireTerminal`, and it builds its result with `DefineValue` and `DefineIndex`. 2. `Feed` and `Resize` call `RequireTerminal` first. After that, they only test types and read strings, bytes, and numbers, which runs no JS. 3. `GetVisibleText`, `FormatPlain`, and `FormatHtml` read no arguments. 4. The assumption was false for `snapshot` until #46. - The bullet that lists where caller JS can run now names public `feed`: `assertFeedData` runs `data instanceof Uint8Array`, which runs a Proxy's `getPrototypeOf` trap after `#assertUsable`. The native null check covers it, as it does for `snapshot`. - `AddonEnvLifecycle.tla` changes in comments only. The `CreateFails` comment now names the `ghostty_terminal_set` failure too. The model itself does not change. #48 changes only a test. #49 changes only `Feed`'s argument checks, so the `Use(t)` step still describes `feed`. ## Verification - I checked each row against `native/terminal.cc` and `src/index.ts` at `c54051e`. - `git diff 7366b75 c54051e` on the covered paths adds no new static state, `Napi::Reference`, or new place that frees `terminal_` or `mouse_encoder_`. - `run.sh`: all 35 rows match EXPECT (`OK: every result matches EXPECT.`). The current config holds all 5 rules. Each of the 6 mutants breaks the rule it guards. The verifier's two notes on `0ff0941` (the `feed` window and the `CreateFails` comment) are fixed in `584c561`. `run.sh` still passes all 35 rows. Workspace 36beaed175 drafted the first version of this commit. I corrected the assumption bullet: it said that every method reads its arguments before `RequireTerminal`, but `Feed` and `Resize` do not. --- _Generated with [`xum`](https://github.com/coder/xum) • Model: `anthropic:claude-opus-5-5` • Thinking: `xhigh`_
Why
The goal is to handle and land the items still open after the #12 follow-ups, one PR each. This PR adds the test coverage for the read-once contract that #41 added.
src/README.mdpromises thatencodeMousereads each caller field once. The #41 test checks onlyxandgeometry.screenWidth. A later change that readsyorcellWidthtwice still passes it.What changed
test/terminal.test.tshas one new native test, "reads every event, modifier, option, and geometry field once", right after the #41 test.encodeMouseonce and checks the bytes:ESC[<12;4;6M. The SGR rule gives this value: left button 0, plus 4 for shift, plus 8 for alt. Column and row are 1-based (x 3 → 4, y 5 → 6).Scope
Test only. No change to
src/ornative/.Blast Radius
One test in the native suite. It skips with the rest of the suite when the addon is not built.
Verification
I proved the test can fail. I put the parent of #41's fix (
git show 30242de^:src/index.ts) intosrc/index.ts, rebuilt, and ran the test. It fails and names the 10 fields read more than once:The other 9 fields showed 1 read each. Then I restored
src/index.ts, rebuilt, and ran the test again:Gates on the final commit. Each one exited 0:
npm ci && LIBGHOSTTY_VT_PREFIX=<ReleaseFast> npm run build:native && npm run build && npm run verify. 78 tests passed.rm -rf dist && LIBGHOSTTY_VT_PREFIX=<ReleaseFast> CXXFLAGS="-fsanitize=undefined,float-cast-overflow -fno-sanitize-recover=all" LDFLAGS="-fsanitize=undefined,float-cast-overflow" npm run build:native && npm test && npm run smoke. 78 tests passed, smoke passed.Generated with
xum• Model:anthropic:claude-opus-5-5• Thinking:medium