From fa6d889cb77acea92de54a706a69ef87a20e66ab Mon Sep 17 00:00:00 2001 From: Jason Woltje Date: Sun, 11 Oct 2026 13:39:05 -0500 Subject: [PATCH] queue-55: round 2 candidate packet (darkwing) Answers Filbert's comment 27251 and Dewey's comment 27253: R1 type-ahead window, R2 terminal cell widths, B1 cut inside the question, 30 mutants killed, N1-N8. Candidate manifest sha256 3f2d8f8e. Gate on c506c6ec: cli 106/106 and every scripts/test-*.sh exit 0. The feature stays uncommitted. Co-Authored-By: Claude Opus 5.5 --- agents/darkwing/work/queue-55/BUILD.md | 176 +++++- .../work/queue-55/FOLLOWUP-transport-code.md | 12 + agents/darkwing/work/queue-55/build.patch | 524 +++++++++++++++--- .../work/queue-55/candidate-manifest.sha256 | 10 +- .../work/queue-55/out/gate-round2/apply.txt | 14 + .../queue-55/out/gate-round2/cli-tests.log | 116 ++++ .../work/queue-55/out/gate-round2/gate.sh | 10 + .../work/queue-55/out/gate-round2/smoke.txt | 4 + .../work/queue-55/out/gate-round2/summary.txt | 11 + .../queue-55/out/gate-round2/test-auth.log | 17 + .../out/gate-round2/test-conductor.log | 23 + .../queue-55/out/gate-round2/test-config.log | 26 + .../queue-55/out/gate-round2/test-discord.log | 70 +++ .../gate-round2/test-extension-package.log | 21 + .../out/gate-round2/test-foundation.log | 53 ++ .../queue-55/out/gate-round2/test-queue.log | 35 ++ .../queue-55/out/gate-round2/test-release.log | 16 + .../queue-55/out/gate-round2/test-task.log | 32 ++ .../out/mutants-round2/round2-rerun.txt | 3 + .../queue-55/out/mutants-round2/round2.txt | 31 ++ .../work/queue-55/out/mutants-round2/run2.mjs | 57 ++ 21 files changed, 1181 insertions(+), 80 deletions(-) create mode 100644 agents/darkwing/work/queue-55/out/gate-round2/apply.txt create mode 100644 agents/darkwing/work/queue-55/out/gate-round2/cli-tests.log create mode 100755 agents/darkwing/work/queue-55/out/gate-round2/gate.sh create mode 100644 agents/darkwing/work/queue-55/out/gate-round2/smoke.txt create mode 100644 agents/darkwing/work/queue-55/out/gate-round2/summary.txt create mode 100644 agents/darkwing/work/queue-55/out/gate-round2/test-auth.log create mode 100644 agents/darkwing/work/queue-55/out/gate-round2/test-conductor.log create mode 100644 agents/darkwing/work/queue-55/out/gate-round2/test-config.log create mode 100644 agents/darkwing/work/queue-55/out/gate-round2/test-discord.log create mode 100644 agents/darkwing/work/queue-55/out/gate-round2/test-extension-package.log create mode 100644 agents/darkwing/work/queue-55/out/gate-round2/test-foundation.log create mode 100644 agents/darkwing/work/queue-55/out/gate-round2/test-queue.log create mode 100644 agents/darkwing/work/queue-55/out/gate-round2/test-release.log create mode 100644 agents/darkwing/work/queue-55/out/gate-round2/test-task.log create mode 100644 agents/darkwing/work/queue-55/out/mutants-round2/round2-rerun.txt create mode 100644 agents/darkwing/work/queue-55/out/mutants-round2/round2.txt create mode 100644 agents/darkwing/work/queue-55/out/mutants-round2/run2.mjs diff --git a/agents/darkwing/work/queue-55/BUILD.md b/agents/darkwing/work/queue-55/BUILD.md index 4e50b5ade..5f99d9120 100644 --- a/agents/darkwing/work/queue-55/BUILD.md +++ b/agents/darkwing/work/queue-55/BUILD.md @@ -1,4 +1,4 @@ -# Row 55 (#1544): the full-screen terminal app, candidate packet, round 1 +# Row 55 (#1544): the full-screen terminal app, candidate packet, rounds 1 and 2 Author: Darkwing. Reviewers: Dewey and Filbert. Brief: `docs/plans/2026-10-10_design-implementation.md`, section "Full-screen @@ -156,7 +156,7 @@ The tests in `packages/cli/tests/app.test.mjs`: screen doesn't redraw during a read (30 s timeout). That is the existing transport, unchanged. The README says so. -## Evidence +## Evidence, round 1 All runs in a detached worktree of `23d566d6` with `build.patch` applied and `node_modules` linked (`out/gate/`). @@ -203,3 +203,175 @@ and `node_modules` linked (`out/gate/`). queues it with the 2026-10-12 batch. - Talk and stop in the app, Decision Seen, and the web console stay out of scope, as the brief says. + + +## Round 2 + +Round 1 drew changes requested from both reviewers: Filbert in comment +27251 (rev 390) and Dewey in comment 27253 (rev 395), both on candidate +`93fa48c1cdfe`. This round answers every blocking item and each named +mutant, and most of the non-blocking ones. `build.patch`, `files.txt` +and `candidate-manifest.sha256` now hold the round 2 candidate. The +round 1 versions are in this packet's history at `4acadaae`. The same +nine files: +1741/βˆ’16. Base for the apply check: `c506c6ec` +(`out/gate-round2/apply.txt`). None of the commits since `fe5c68fa` touch the nine +files. + +### Blocking + +**R1 (Filbert): the question took type-ahead.** Now y or Y resolves and +any other key answers No. Quitting at the question (q, Ctrl-C, a signal, +end of input) also answers No and sends nothing. Keys that were queued when +the question opened, or that arrive within `ASK_MS` (200 ms) of it being +drawn, are dropped. The timer arms the question; arming empties the key +queue. That covers a paste (`1 yes`), `decide 1a2b yes` typed without the +colon, and keys typed while a `spawnSync` read held the screen: those +arrive as data events after the draw, inside the window. A timer refresh +tick is never taken as an answer. The hint now reads "y resolves Β· any +other key cancels (the default is No)". + +200 ms is my choice. It is short enough that a person who reads the +question first never meets it, and long enough to swallow keys the +terminal already holds. A y pressed inside it is dropped silently; the +next one counts. Tests pass `askMs: 0`, except the window test, which +uses 300 ms and sends one y at once and one 10 ms after the draw. + +**R2 (Filbert): `cells()` miscounted emoji.** `\p{Emoji_Presentation}` +counts 2, and U+FE0F after a one-cell `\p{Emoji}` base adds the second +cell (πŸ…°οΈ, ❀️, 1️⃣). The old range 1F300-1FAFF became Emoji_Presentation +plus 1F200-1F2FF. `width`, `fit` and `wrap` pass the previous code point. +The fake inbox now carries βœ…βœ…, ⚑ and πŸ…°οΈ; a task title carries η™Ίθ‘Œ βœ…. +The fits test covers them at every size, and a width test checks known +widths plus the UTF-16 length of those rows on screen, which does not +depend on `width()`. Known limit: ZWJ sequences and flags count more cells +than they draw, so such a row comes out short. It can't overflow. + +**B1 (Dewey): a tall decide box cut the authorization lines.** The box now +cuts inside the question. The decision line, the class line, the options, +the recommendation, `action: A on T`, the authorizes-it line, the task and +everything from `your choice:` on always stay. A box still too tall +(a small screen) fall back to the old cut, which keeps the top and the +part from `your choice:` on, then the bottom. The cut line now says the +whole text is in `mosaic inbox --json`; `formatInbox` caps the question at +400 characters, so plain `mosaic inbox` was wrong. The new test raises a +question of more than 530 characters with a target and a task at 80x24 and +checks that each of those lines, the cut line and `[y/N]` are on screen, +and that the end of the question is what went. + +### Filbert's mutants + +`out/mutants-round2/` has the runner and its output: 30 mutants, each one +edit to `src/`, with the whole `packages/cli` suite run against it. All 30 +are killed. `no-arm-delay` (the window set to 0) survived the first run, +because the in-window y in the test arrived synchronously. The test now +also sends one 10 ms later, and the rerun kills it. + +| Mutant | Killed by | +|---|---| +| `resolve-ask-skipped-when-note` | decide with --note still asks | +| `unknown-space-only` | outcome unknown: the hyphenated transport error with no code | +| `restore-no-altleave`, `restore-no-raw-off`, `no-sigterm` | runApp leaves the terminal as it found it | +| `paint-no-reverse` | NO_COLOR: exactly one reverse-video row, in colour and NO_COLOR | +| `cells-no-cjk` | width counts terminal cells (the η™Ίθ‘Œ βœ… row) | +| `overlay-cut-loses-prompt` | every screen fits (the long decide shot keeps the prompt) | +| `app-host-not-live` | the app's business comes from a live bus host only | +| `quit-no-answer` | quitting at the question answers No, armed or not | +| `refresh-in-overlay` | a timer tick reads nothing under an overlay | +| `unknown-esc-stays` | outcome unknown: Esc closes the box | +| `trail-task-id` | keys: v on Tasks opens the trail of `vikunja:1/7` | +| `business-keeps-data` | keys: `:business other` drops lists and selections | +| `nav-no-brackets` | every screen fits: `[i Inbox 3]` wide, `[i 3]` narrow | + +The runner adds mutants for this round's own changes: no SIGINT, no SIGHUP, +an unguarded resize, no Emoji_Presentation, no FE0F rule, the B1 cut +removed, type-ahead kept, no arm delay, other keys waiting, no capital Y, +a tick taken as an answer, a tick dropping the resolved status, the +command line typing key names, "Press 1-1" and no too-small guard. + +### Non-blocking + +- **N1 (Filbert; Dewey concurs).** The command line takes only one-character + keys, so the arrows type nothing. Tested. +- **N2.** `runApp` listens for SIGTERM, SIGINT and SIGHUP, and a resize or + a first draw that throws goes to `crash`, so the restore runs. Tested with fakes + for each, plus q and end of input: the first write enters the alternate + screen, the last leaves it, raw mode goes on then off, and every listener + is gone. +- **N3.** Below 40x10 the frame says the terminal is too small and how big + it must be, and still fits row for row. Tested at 39x10, 40x9, 3x3 and + 1x1; 40x10 joins the fits sizes. +- **N4.** A timer tick keeps a "resolved" status. Tested. +- **N5.** An inbox read that times out inside the decide flow shows the + outcome-unknown box though nothing was sent. Not changed here. It is + added to `FOLLOWUP-transport-code.md`, because the fix belongs with the + transport's missing `.code`. +- **N6.** One option says "Press 1 to choose". Tested. +- **N7.** `runApp` has a test (N2 above). +- **N8.** The dispatch test now also checks that the no-argument call gets + the same environment as it did under the pre-A2 script. The old non-TTY + bytes: deviation 2 stands. If Sage wants them back, it is one line in + `cli.mjs`, and test 12 already holds the bytes as `SEAT_USAGE`. +- **Dewey: Ctrl-C on the command line.** It closes the command line; it + does not quit. The `app.mjs` header and the README now say so. +- **Dewey: REACHES YOU.** Listed below as deviation 5. + +### Deviation 5 + +5. **The Inbox has no REACHES YOU column.** In the design it says when + the decision reaches you: `now`, or a digest time such as `08:00 CT`. + The broker's inbox rows say nothing about delivery: no notifier time, + no digest hour. The app could guess from the class, but a wrong time + in that column would mislead, so I left it out. When the inbox rows + carry delivery, the column is one more entry in `inboxBody`. + +### Tests, round 2 + +`packages/cli/tests/app.test.mjs` has 23 tests (14 in round 1). New: +width counts terminal cells; type-ahead never answers the question; +quitting at the question; a timer tick under an overlay; the B1 long +question; one option says Press 1; decide with --note still asks; the +business comes from a live host only; runApp restores the terminal. +Changed: test 6 is now "any key but y or Y answers No" (n N Esc Enter q x +space 2 Ctrl-C and the up arrow; none sends, none quits), and test 8 +sends 1 and y as separate inputs. Tests 1, 3, 4, 7 and 9 gained the +assertions named above. + +### Evidence, round 2 + + +All runs in a detached worktree of `c506c6ec` with the round 2 +`build.patch` applied and `node_modules` linked. `out/gate-round2/` has +the logs, `summary.txt` and `gate.sh`, the script that ran them in +sequence. + +- `packages/cli`: 106/106, `out/gate-round2/cli-tests.log`. +- Every `scripts/test-*.sh`, all exit 0: + + | Suite | Result | + |---|---| + | test-auth | 15 passed, 0 failed | + | test-conductor | 17 passed, 0 failed | + | test-config | 24 passed, 0 failed | + | test-discord | 66 passed, 0 failed | + | test-extension-package | 18 passed, 0 failed | + | test-foundation | 44 passed, 0 failed | + | test-queue | 27 passed, 0 failed; canonical-root checks skip in a worktree | + | test-release | 14 passed, 0 failed | + | test-task | 26 passed, 0 failed; Docker groups skipped, as in round 1 | + + A `gate_default` Docker network appeared during the run. It had no + containers afterwards, and I removed it. +- `out/gate-round2/apply.txt`: `build.patch` applies to `c506c6ec` and + the manifest matches. The nine files are byte-identical at `fe5c68fa` + and `c506c6ec`. +- `out/gate-round2/smoke.txt`: `smoke.sh` at 80x24 against a fixture + config, never the real bus. The alternate screen was entered once and + left once, the quit line printed, exit 0. +- `out/mutants-round2/`: `run2.mjs` and its output. `round2.txt` is the + run of all 30 mutants: 29 killed, `no-arm-delay` survived. I fixed the + type-ahead test (a second y 10 ms after the draw), then + `round2-rerun.txt` reran the two type-ahead mutants: both killed. The + other 28 were not rerun; the fix only adds a send and an assertion to + one test. +- Candidate manifest sha256: + `3f2d8f8ea3fea1cf7a002297ee9b23059f1522e8060c1d981fba19c85baeabd7`. diff --git a/agents/darkwing/work/queue-55/FOLLOWUP-transport-code.md b/agents/darkwing/work/queue-55/FOLLOWUP-transport-code.md index c7b2b27bd..3715f5ac9 100644 --- a/agents/darkwing/work/queue-55/FOLLOWUP-transport-code.md +++ b/agents/darkwing/work/queue-55/FOLLOWUP-transport-code.md @@ -48,3 +48,15 @@ No other check in `packages/cli/src` compares `.code` against Files: `packages/cli/src/transport.mjs`, `packages/cli/tests/cli.test.mjs`, `packages/cli/tests/transport.test.mjs`. + +## A related case: a timed-out inbox read (Filbert N5, row 55 round 1) + +`resolveDecision` reads the inbox before it sends anything. If that read +hits the same did-not-finish error, the app shows its outcome-unknown +overlay ("the broker may have recorded it"), though no choice was sent. +Once the transport sets `error.code = "outcome-unknown"`, `mosaic decide` +would print the same sentence for that read. The fix should tell the two +apart: only an error from `decision.resolve` is outcome unknown. A failed +inbox read before it is a plain failure, exit 1, with nothing sent. A test +for each, in `packages/cli/tests/cli.test.mjs` and +`packages/cli/tests/app.test.mjs`. diff --git a/agents/darkwing/work/queue-55/build.patch b/agents/darkwing/work/queue-55/build.patch index b78d8a634..bb7e836f6 100644 --- a/agents/darkwing/work/queue-55/build.patch +++ b/agents/darkwing/work/queue-55/build.patch @@ -22,7 +22,7 @@ index b96e9012..5a675611 100644 `3` refused or config problem Β· `4` usage. Details: `packages/cli/README.md`. diff --git a/packages/cli/README.md b/packages/cli/README.md -index 32977afa..3c3255f5 100644 +index 32977afa..dbb03138 100644 --- a/packages/cli/README.md +++ b/packages/cli/README.md @@ -4,12 +4,16 @@ Jason's front door to the bus (slice 1 row S4, #1521): `mosaic inbox`, @@ -44,7 +44,7 @@ index 32977afa..3c3255f5 100644 scripts/mosaic inbox scripts/mosaic decide 3f2a9c1e yes --note "ship it" scripts/mosaic trail 3f2a9c1e-… # then: scripts/mosaic trail vikunja:1/7 -@@ -70,6 +74,52 @@ mosaic launches list [--json] +@@ -70,6 +74,65 @@ mosaic launches list [--json] `launches list` does not, because the file is readable to the same user anyway. @@ -68,13 +68,16 @@ index 32977afa..3c3255f5 100644 +- Keys: `c i t a r` switch views (Talk, Inbox, Tasks, Agents, Trail). `↑↓` + or `j k` move, PgUp and PgDn page, Enter opens the selection whole, `v` + opens its trail, `:` the command line, `?` help, Esc closes a box, `q` -+ or Ctrl-C quits. Commands: `:decide