review(55): Darkwing round 1 build packet (candidate manifest 93fa48c1cdfe)
The full-screen terminal app (#1544): BUILD.md, build.patch overfe5c68fa(9 files), candidate manifest, the gate logs on23d566d6and the pty smoke script. The candidate itself is not committed. Co-Authored-By: Claude Opus 5.5 <[email protected]>
This commit is contained in:
@@ -0,0 +1,203 @@
|
||||
# Row 55 (#1544): the full-screen terminal app, candidate packet, round 1
|
||||
|
||||
Author: Darkwing. Reviewers: Dewey and Filbert. Brief:
|
||||
`docs/plans/2026-10-10_design-implementation.md`, section "Full-screen
|
||||
terminal app", with `docs/design/IMPLEMENTING.md` "The full-screen
|
||||
terminal app" and "Strings to keep exactly". Base: `fe5c68fa` (row 41,
|
||||
`base.txt`). It applies unchanged to `23d566d6`: the commits between the
|
||||
two touch only BUILD-LOG.md, docs/SESSIONS.md and the queue files, none of
|
||||
them in this candidate. The feature is uncommitted; only this packet is
|
||||
committed. I pushed nothing.
|
||||
|
||||
## Files
|
||||
|
||||
`files.txt` lists the nine repository paths. `candidate-manifest.sha256`
|
||||
has their sha256. `build.patch` is `git diff --cached --binary fe5c68fa`
|
||||
over them: 9 files, +1363/−16.
|
||||
|
||||
| File | Change |
|
||||
|---|---|
|
||||
| `packages/cli/src/app.mjs` | new: key parsing, the `App` state machine, `runApp` (alternate screen, raw mode, resize, SIGTERM, restore) and the quit line |
|
||||
| `packages/cli/src/screens.mjs` | new: `draw(state)` is pure and returns rows; Inbox, Tasks, Agents, Trail, Help, overlays, the 16-colour palette and NO_COLOR |
|
||||
| `packages/cli/src/cli.mjs` | the decide flow moves into the exported `resolveDecision`, which `decide` and the app share; `SEAT_USAGE`; the no-argument entry `app()` |
|
||||
| `packages/cli/src/format.mjs` | `trailSummary` is exported (the Trail screen uses it); no other change |
|
||||
| `packages/cli/tests/app.test.mjs` | new: 14 tests, listed below |
|
||||
| `packages/cli/README.md` | the intro names row 55; new section "The app" |
|
||||
| `docs/TOOLS.md` | the `mosaic` entry: the no-argument line and a four-line paragraph |
|
||||
| `scripts/mosaic` | a decided scope addition (Sage, 2026-10-11); see below |
|
||||
| `packages/queue/tests/dispatch.test.mjs` | follows from `scripts/mosaic`; my addition, for Sage to confirm; see below |
|
||||
|
||||
`resolveDecision` writes the same lines in the same order and asks the
|
||||
same question as before. `decide` passes `ask = null` for `--yes` and
|
||||
keeps its own "stdin is not a terminal" refusal. The 83 tests that were in
|
||||
`packages/cli/tests/` at the base still pass unchanged.
|
||||
|
||||
### Scope addition: scripts/mosaic
|
||||
|
||||
`scripts/mosaic` is not in the brief's files. Sage approved it on
|
||||
2026-10-11 as a decided scope addition. Before, `scripts/mosaic` with no
|
||||
argument fell through to `packages/seat/src/cli.mjs`, which printed
|
||||
`refused: <seat usage>` and exited 4. The script never reached
|
||||
`packages/cli`, so the brief's acceptance line, bare `mosaic` on a TTY
|
||||
opens the app, could not be met inside `packages/cli` alone. The change is
|
||||
one case that sends no argument to `packages/cli/src/cli.mjs`. Nothing
|
||||
else in the script changed.
|
||||
|
||||
The entry checks in Sage's order:
|
||||
|
||||
1. Agent markers exit 3 through `refuseInsideAgent`, with the transport's
|
||||
message unchanged.
|
||||
2. stdin or stdout not a TTY exits 4 with the usage.
|
||||
3. Otherwise the app opens.
|
||||
|
||||
Test 11 below covers each branch through `main([])`. Test 12 spawns
|
||||
`scripts/mosaic` itself for exits 4 and 3.
|
||||
|
||||
### The queue dispatch test
|
||||
|
||||
My first gate run had test-queue red, 26 passed and 1 failed
|
||||
(`out/round1-first-gate/test-queue.log`). `packages/queue/tests/dispatch.test.mjs`
|
||||
pins A2's rule that every call but `queue` reaches the seat CLI exactly as
|
||||
the pre-A2 script did, and its list of calls included `[]`. Sage's approved
|
||||
change sends exactly that call somewhere else, so the pin can't hold as
|
||||
written. This file is outside the brief and outside what Sage approved. I
|
||||
changed it because the gate needs every suite green and the change follows
|
||||
directly from the approved one. Sage, please confirm or rule it out.
|
||||
|
||||
The change, 16 lines in and 5 out:
|
||||
|
||||
- `[]` leaves the list of seat calls. `[""]` stays: it is one argument, so
|
||||
it still goes to the seat CLI.
|
||||
- The fake root gains a fake `packages/cli/src/cli.mjs`.
|
||||
- A new test: no argument reaches `packages/cli/src/cli.mjs` with empty
|
||||
argv, the same working directory and stdin, and the fake's exit code
|
||||
and output passed through. The same call on the pre-A2 fixture still
|
||||
reaches the seat CLI.
|
||||
- The header comment names row 55.
|
||||
|
||||
With `scripts/mosaic` put back to the base, the new test fails and the
|
||||
other three pass. With the candidate, all four pass. The test that A2 only
|
||||
adds lines to the pre-A2 script still passes, because the row 55 case adds
|
||||
four lines and removes none.
|
||||
|
||||
## How the brief maps to the candidate
|
||||
|
||||
| Brief | Where | Test |
|
||||
|---|---|---|
|
||||
| no argument on a TTY opens the app | `cli.mjs` `app()`, `app.mjs` `runApp` | 11, 12; `out/smoke.txt` |
|
||||
| not a TTY: usage, exit 4 | `cli.mjs` `app()` | 11, 12 |
|
||||
| agent run: exit 3 | `refuseInsideAgent` in `transport.mjs` | 11, 12 |
|
||||
| Inbox, Tasks, Agents, Trail, Help; keys c i t a r j k : ? q | `app.mjs` `#key`, `screens.mjs` | 1, 9 |
|
||||
| decide runs the existing flow, strings unchanged | `resolveDecision` | 4, 5, 6, 7, 13 |
|
||||
| 16 colours, NO_COLOR, 80x24, designed at 120x36 | `screens.mjs` | 1, 3 |
|
||||
| no new dependency | `package.json` untouched | |
|
||||
| every screen fits 120 columns and 80x24 | | 1 |
|
||||
| NO_COLOR output has no escape codes | | 3 (see deviation 1) |
|
||||
| the string grep test | | 13 |
|
||||
|
||||
The tests in `packages/cli/tests/app.test.mjs`:
|
||||
|
||||
1. Every screen and overlay fits 120x36, 80x24 and the narrow layout
|
||||
(100x30, 99x24), row for row, in colour and NO_COLOR. That covers every
|
||||
view and overlay, plus the empty, stale, refused and no-business states.
|
||||
2. Broker text is shown, never interpreted. No raw control character or
|
||||
bidi override reaches the terminal (`visible()` from
|
||||
`packages/conversation/src/terminal.mjs`).
|
||||
3. NO_COLOR: no escape codes except reverse video (7/27) on the selected
|
||||
row. A frame with nothing selected has none.
|
||||
4. 1-9 shows the decide flow's own lines and question. Only y resolves,
|
||||
and it goes through the test broker.
|
||||
5. The app's decide text equals `mosaic decide` stdout for the same
|
||||
decision.
|
||||
6. n, N, Esc, Enter and q at the question cancel. Other keys wait.
|
||||
Nothing is sent.
|
||||
7. Outcome unknown shows the CLI's sentence and offers no retry. Then v
|
||||
opens the trail, or r reads again.
|
||||
8. Refused and failed reads keep the last list, marked stale. A refused
|
||||
decide says refused.
|
||||
9. Keys: views, moving, the trail of a selection, the command line, help
|
||||
and quit.
|
||||
10. No business: the app says so, and `:business <id>` picks one.
|
||||
11. `main([])`: exit 3 with a marker set, exit 4 with the usage off a
|
||||
terminal, and the app on one (stubbed `runApp`).
|
||||
12. `scripts/mosaic` with no argument reaches the app entry (exit 4 off a
|
||||
terminal, 3 with a marker). `SEAT_USAGE` still equals what the seat
|
||||
CLI prints, so the copy can't drift.
|
||||
13. Strings to keep exactly appear once, in `cli.mjs` and `transport.mjs`.
|
||||
`app.mjs` and `screens.mjs` don't restate "authorizes the action",
|
||||
"declines the action", "[y/N]", "may have recorded it" or "runs only
|
||||
from a human shell".
|
||||
14. `draw` is pure: the same state gives the same frame.
|
||||
|
||||
## Deviations, stated
|
||||
|
||||
1. **NO_COLOR keeps reverse video on the selected row.** The brief's test
|
||||
line says NO_COLOR output has no escape codes. `IMPLEMENTING.md` line
|
||||
188 says reverse video stays for the selected row, and
|
||||
`docs/design/README.md` line 148 makes reverse video the selection
|
||||
mark. I followed the design. Test 3 checks that 7 and 27 are the only
|
||||
SGR codes, and that a frame with no selection has none. The writer's
|
||||
`\x1b[H\x1b[2J` before each frame and the alternate screen codes are
|
||||
cursor and screen control, not colour.
|
||||
2. **The non-TTY message gains one line.** It prints
|
||||
`mosaic: the app needs a terminal on stdin and stdout; from a script,
|
||||
use a verb:` and then the seat usage and the `packages/cli` usage, exit
|
||||
4. Before, it printed `refused: <seat usage>`. `IMPLEMENTING.md` line
|
||||
191 says to refuse and point at the verbs, and the old text named only
|
||||
the seat verbs. If a reviewer wants the old bytes, test 12 already
|
||||
holds them as `SEAT_USAGE`.
|
||||
3. **The Terminal class is not the base.** The brief says the app is built
|
||||
on the conversation `Terminal` class. Only `visible()` is imported.
|
||||
`Terminal` is a line editor for Talk, and Talk is out of scope here. The
|
||||
app needs a whole-screen redraw and single-key input, which `Terminal`
|
||||
doesn't do. When Talk joins, the Talk screen embeds `Terminal`.
|
||||
4. **Reads block redraw.** `humanTransport` uses `spawnSync`, so the
|
||||
screen doesn't redraw during a read (30 s timeout). That is the existing
|
||||
transport, unchanged. The README says so.
|
||||
|
||||
## Evidence
|
||||
|
||||
All runs in a detached worktree of `23d566d6` with `build.patch` applied
|
||||
and `node_modules` linked (`out/gate/`).
|
||||
|
||||
- `packages/cli`: 97/97 (83 before, plus the 14 new), `out/gate/cli-tests.log`.
|
||||
- Every `scripts/test-*.sh`, run in sequence on the final candidate, all
|
||||
exit 0 (`out/gate/summary.txt`, logs beside it):
|
||||
|
||||
| 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; 4 Docker groups skipped |
|
||||
|
||||
test-task ran with `DOCKER_HOST` pointed at a missing socket, so its
|
||||
adapter, workspace, live task and live recall groups printed skip. The
|
||||
live ones call a real model. Nothing this candidate changes runs in
|
||||
those groups. Sage's gate rerun covers them with Docker.
|
||||
- The first gate run, before the dispatch test change, is kept in
|
||||
`out/round1-first-gate/`. `out/dispatch-mutant.txt` has the dispatch
|
||||
test against the base script and the candidate script.
|
||||
- Smoke, `smoke.sh`: the app in a pty at 80x24 against a fixture config,
|
||||
never the real bus. Keys ? Esc t q. The alternate screen was entered once
|
||||
and left once, then it printed `(app closed with q; 0 decisions resolved
|
||||
this session)` and exit 0 (`out/smoke.txt`).
|
||||
- `out/apply.txt`: `build.patch` applies to `23d566d6` in a fresh
|
||||
worktree and the manifest matches.
|
||||
|
||||
## Follow-ups (not in this row)
|
||||
|
||||
- The transport's "did not finish" `CliError` (`transport.mjs` line 60)
|
||||
has no `.code`, so the outcome-unknown branch in `resolveDecision` does
|
||||
not see it. `mosaic decide` then prints
|
||||
`outcome-unknown: the bus transport did not finish`, exit 1, instead of
|
||||
the "may have recorded it" sentence. The app matches the message text
|
||||
and shows its outcome-unknown overlay, so only the verb has the gap. It
|
||||
predates this row.
|
||||
- Talk and stop in the app, Decision Seen, and the web console stay out of
|
||||
scope, as the brief says.
|
||||
@@ -0,0 +1 @@
|
||||
fe5c68fa031923ff79cbd8aeebb4c8925eb5af23
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,9 @@
|
||||
fcc2791aa2f80d7e196b0ce18f02475f9fc6730c74cbb56d2db3f9aae333253e docs/TOOLS.md
|
||||
001ae6159638f783520fa300c3f9ce71c25701568058664a032760dc6d79062e packages/cli/README.md
|
||||
534fdf2e70f7459789c32f31e467e78400a44d90df53dd319a774d8cf959506e packages/cli/src/app.mjs
|
||||
c33d3fedb91be7977cf7b468c908719d8f07cd7a2dad1bb7d9c24d62bcdac432 packages/cli/src/cli.mjs
|
||||
c2f6852226c7071dec132d422773d76bdfc356450f582ac28cb7a5077860b4c0 packages/cli/src/format.mjs
|
||||
681279edb4982ce103cdbb1a5fe3490721bca27f0877163f5a8c2f5ce6ace419 packages/cli/src/screens.mjs
|
||||
17e4a3ccb8a8e896c0d9fb6d0781db9636ceff65ba3d48dd7bdff75934150e4d packages/cli/tests/app.test.mjs
|
||||
5c892aea6a455f05aabb28d312f5fc4bb256584254357c6499319ac956f58109 packages/queue/tests/dispatch.test.mjs
|
||||
ab246b3e39ea0c503d97e7af93ef8b958caedb3cf3006e387f14fa93b329856c scripts/mosaic
|
||||
@@ -0,0 +1,9 @@
|
||||
docs/TOOLS.md
|
||||
packages/cli/README.md
|
||||
packages/cli/src/app.mjs
|
||||
packages/cli/src/cli.mjs
|
||||
packages/cli/src/format.mjs
|
||||
packages/cli/src/screens.mjs
|
||||
packages/cli/tests/app.test.mjs
|
||||
packages/queue/tests/dispatch.test.mjs
|
||||
scripts/mosaic
|
||||
@@ -0,0 +1,13 @@
|
||||
base: 23d566d62db9189e5d7c252ae278c80c99213423
|
||||
check: ok
|
||||
apply: ok
|
||||
docs/TOOLS.md: OK
|
||||
packages/cli/README.md: OK
|
||||
packages/cli/src/app.mjs: OK
|
||||
packages/cli/src/cli.mjs: OK
|
||||
packages/cli/src/format.mjs: OK
|
||||
packages/cli/src/screens.mjs: OK
|
||||
packages/cli/tests/app.test.mjs: OK
|
||||
packages/queue/tests/dispatch.test.mjs: OK
|
||||
scripts/mosaic: OK
|
||||
9 files changed, 1363 insertions(+), 16 deletions(-)
|
||||
@@ -0,0 +1,16 @@
|
||||
# packages/queue/tests/dispatch.test.mjs with scripts/mosaic at fe5c68fa (the base):
|
||||
✔ every call but `queue` reaches the seat CLI exactly as before A2 (669.173751ms)
|
||||
✖ failing tests:
|
||||
ℹ fail 1
|
||||
ℹ pass 3
|
||||
✖ no argument reaches the app entry in packages/cli, and only that call (34.852154ms)
|
||||
✔ `queue` reaches the queue CLI with the rest of the arguments (210.558498ms)
|
||||
✔ the pre-A2 fixture is the script A2 changed (0.318757ms)
|
||||
|
||||
# with the candidate's scripts/mosaic:
|
||||
✔ every call but `queue` reaches the seat CLI exactly as before A2 (607.518999ms)
|
||||
✔ no argument reaches the app entry in packages/cli, and only that call (71.984814ms)
|
||||
✔ `queue` reaches the queue CLI with the rest of the arguments (224.724883ms)
|
||||
✔ the pre-A2 fixture is the script A2 changed (0.339528ms)
|
||||
ℹ pass 4
|
||||
ℹ fail 0
|
||||
@@ -0,0 +1,107 @@
|
||||
✔ every screen and overlay fits 120x36, 80x24 and the narrow layout, row for row (657.730481ms)
|
||||
✔ broker text is shown, never interpreted: no raw control character or bidi override reaches the terminal (86.710314ms)
|
||||
✔ NO_COLOR: no escape codes, except reverse video on the selected row (85.638005ms)
|
||||
✔ 1-9 shows the decide flow's own lines and question; only y resolves (159.198152ms)
|
||||
✔ the app shows the same decide text as mosaic decide (130.463236ms)
|
||||
✔ n, Esc, Enter and q at the question cancel; other keys wait; nothing is sent (658.383117ms)
|
||||
✔ outcome unknown: the CLI's sentence, no retry, then the trail or a re-read (128.743686ms)
|
||||
✔ refused and failed reads keep the last list, marked stale; a refused decide says refused (177.266492ms)
|
||||
✔ keys: views, moving, trail of a selection, the command line, help and quit (163.853199ms)
|
||||
✔ no business: the app says so and :business picks one (82.097108ms)
|
||||
✔ mosaic with no argument: exit 3 in an agent run, exit 4 with the usage off a terminal, the app on one (1.988286ms)
|
||||
✔ scripts/mosaic with no argument reaches the app entry; the seat usage it printed before has not drifted (258.258873ms)
|
||||
✔ strings to keep exactly live once, in the decide flow and the transport; the app does not restate them (1.575316ms)
|
||||
✔ draw is pure: same state, same frame (1.081875ms)
|
||||
✔ inbox lists only decisions routed to the human, with what approving authorizes and how to decide (129.136475ms)
|
||||
✔ decide resolves by id prefix with --yes, and the trail shows it in broker order with the task to follow (138.929431ms)
|
||||
✔ decide refuses without a terminal or --yes, on an unknown option and on a short reference (117.191582ms)
|
||||
✔ decide prints a declining choice as declining (131.258653ms)
|
||||
✔ an unknown outcome is reported once and never resent (124.564502ms)
|
||||
✔ a decision closed before the answer arrives exits 2 and points at its trail (100.431046ms)
|
||||
✔ a prefix that matches two open decisions exits 2 and resolves neither (115.464391ms)
|
||||
✔ without --business a command uses the live host's business, and a stale host.json is not a host (105.521856ms)
|
||||
✔ every human command refuses inside an agent run before it touches the bus (105.068364ms)
|
||||
✔ usage errors exit 4; no business and no host is a usage error (93.017952ms)
|
||||
✔ agents and tasks print through the broker (106.492206ms)
|
||||
✔ notify.json: missing, loose, malformed and extra keys refuse; a binding or null passes (1.649953ms)
|
||||
✔ bootConfig builds the broker's boot message for one business, with no trackers key when no project names one (88.090598ms)
|
||||
✔ trackers come from the tracker.* variables of the one project that names a tracker project (66.070636ms)
|
||||
✔ with two projects, the one that sets tracker.project supplies the tracker and the other is no conflict (67.66776ms)
|
||||
✔ two projects that each name a tracker project refuse, since the boot shape holds one (54.311037ms)
|
||||
✔ a business without tracker.baseUrl gets no trackers entry (42.543295ms)
|
||||
✔ an unknown business and a broken system config refuse with exit 3 (103.913445ms)
|
||||
✔ empty views say so (1.499ms)
|
||||
✔ the trail keeps the broker's order and names a decision's task without its rows (1.958082ms)
|
||||
✔ tasks print the tracker fields the snapshot carries (0.30169ms)
|
||||
✔ the host boots the broker, binds a launch in process, and the notifier DMs a blocking decision exactly once (1040.624201ms)
|
||||
✔ a notifier that dies takes the host down with exit 1, so the unit restarts the pair (348.328083ms)
|
||||
✔ a second host for the same data root refuses with exit 3 while the first runs (219.533172ms)
|
||||
✔ a broker reply that misses the wait breaks the channel: the late reply answers nothing, later requests refuse, the host exits 1 (1190.312123ms)
|
||||
✔ a broker reply with another request's id, or none, breaks the channel and the host exits 1 (317.451334ms)
|
||||
✔ a broker reply with no request waiting breaks the channel and the host exits 1 (169.522641ms)
|
||||
✔ a notifier that refuses stops the broker and the host refuses with exit 3 (462.061614ms)
|
||||
✔ a notifier that refuses after the broker died still refuses with exit 3, without a send to the dead broker (220.316923ms)
|
||||
✔ a notifier that dies before it replies, after the broker died, still refuses, without a send to the dead broker (141.186895ms)
|
||||
✔ a close send that fails with EPIPE after the notifier refuses still gives the notifier's refusal, exit 3 (207.730923ms)
|
||||
✔ a close send that fails with EPIPE after the notifier dies unanswered still gives the notifier's error (173.736902ms)
|
||||
✔ close() whose stop and close sends fail with EPIPE still finishes, with exit 1 (326.023075ms)
|
||||
✔ watchChildren reports a child that died before it was called, and one that dies later (38.675069ms)
|
||||
✔ bus stop refuses to signal a live pid that is not a bus host (205.281889ms)
|
||||
✔ bus start refuses with exit 3 and the code when the broker refuses to boot; bus status names the lock (343.372644ms)
|
||||
✔ bus start refuses with exit 3 without a notifier config (114.419596ms)
|
||||
✔ bus start runs until bus stop; status reports it while it runs (865.081835ms)
|
||||
✔ bus-service.sh renders the unit and installs it into a given directory (40.694753ms)
|
||||
✔ bus start --pm: the PM runs under its own PID namespace, registered, with a manifest (1368.731037ms)
|
||||
✔ the PM launches a coder through its launch tool; the coder answers; refusals name their code (787.355153ms)
|
||||
✔ a runner that stops at once ends its launch with the runner's reason (255.975513ms)
|
||||
✔ a host that died hard leaves its sessions to the next host, which kills them and ends their runs so the role can be launched again (803.364181ms)
|
||||
✔ a host that died hard leaves its sessions to the next host, which finds one already exited (23) and ends their runs so the role can be launched again (3947.590018ms)
|
||||
✔ a malformed sessions.json refuses the host start with exit 3 and stays as it was (140.226608ms)
|
||||
✔ an over-long launch request is refused at once, not at the 10 s idle timeout (151.079496ms)
|
||||
✔ a launch client that never closes its side doesn't hold the host's close (149.960005ms)
|
||||
✔ a runner that ignores SIGTERM is killed when the host closes (1294.430684ms)
|
||||
✔ zoned uses the IANA zone across DST (26.013355ms)
|
||||
✔ each open blocking decision is DM'd once, across polls and a restart; non-blocking ones are not (147.099869ms)
|
||||
✔ two blocking decisions get two DMs with different nonces (132.429819ms)
|
||||
✔ the digest nonce differs per business and per day and fits Discord's 25 characters (0.4487ms)
|
||||
✔ a failed DM is journaled, backs off, and is retried until it lands (106.047926ms)
|
||||
✔ five definite refusals stop a DM: one gave-up line, one log line, and a restart keeps the count (126.668979ms)
|
||||
✔ 429s, 5xx-style unknowns and refusals without a status never count toward the limit (134.274487ms)
|
||||
✔ a crash between the fifth refusal and its gave-up line: the next poll appends it and sends nothing (109.872742ms)
|
||||
✔ polled every POLL_MS against a permanent 403, a DM is sent at 0, 30, 60, 90 and 120 min and gives up only then (197.106078ms)
|
||||
✔ a restart after the second refusal does not send before that refusal's 30 min are up (156.492966ms)
|
||||
✔ the digest goes at 08:00 Chicago once a day, with blocking ones marked as DM'd (138.849231ms)
|
||||
✔ a late start with no digest for the day sends one at once; an empty inbox gets one line (89.590199ms)
|
||||
✔ an inbox read failure is logged and the next poll retries (1.258133ms)
|
||||
✔ no Discord id reaches the journal or the log (161.188556ms)
|
||||
✔ the journal: a torn tail is copied out and truncated, so an append after it reopens cleanly (35.942562ms)
|
||||
✔ the journal: a crash between the copy and the truncate leaves a tail the next open repairs (55.52388ms)
|
||||
✔ the journal: a whole file that is one torn line truncates to empty (28.58631ms)
|
||||
✔ the journal: a malformed complete line refuses and leaves the file and any torn tail alone (2.411597ms)
|
||||
✔ the journal: a loose file mode, a loose directory or a symlinked journal refuses (1.159198ms)
|
||||
✔ the journal: a line with a wrong type refuses with exit 3 and names the field (5.236096ms)
|
||||
✔ the journal: a symlinked directory refuses and says it is a link (0.461047ms)
|
||||
✔ the journal: a dangling directory link, a parent that is a file and a journal that is a directory each refuse with exit 3 (0.645397ms)
|
||||
✔ the journal: an append after the file was swapped for a symlink refuses and writes nothing through it (0.530816ms)
|
||||
✔ the journal: a directory it cannot write or create refuses with exit 3 and names the path (0.583496ms)
|
||||
✔ digest content stays within Discord's 2000 characters (0.355957ms)
|
||||
✔ runLoop never overlaps ticks and stops after the one in flight (111.677618ms)
|
||||
task.close {} answered: invalid-request; fake saw 18 requests, first GET /info 200, GET /projects/1 200, GET /projects/1/views 200
|
||||
task.close on a missing task answered: task-not-found; it made GET /tasks/999 404
|
||||
✔ bootConfig trackers reach the S3 adapter in the real broker child, which goes ready against a fake Vikunja (521.916131ms)
|
||||
✔ the transport writes {business, verb, args} to the child and reads its JSON (61.362517ms)
|
||||
✔ a bus code on stderr becomes the exit code; garbage and timeouts are outcome problems (2268.525051ms)
|
||||
✔ busExit and refuseInsideAgent (0.502004ms)
|
||||
✔ talk sends a REQUEST, prints and reads everything that arrives, and stops at the reply (236.288887ms)
|
||||
✔ talk --wait 0 only sends; no reply within --wait exits 1 and says where it will show (1126.015734ms)
|
||||
✔ talk, stop and launches off/on refuse inside an agent run; bad arguments are usage errors (181.101284ms)
|
||||
✔ launches off and on go to the broker and change the business's launch state (124.237954ms)
|
||||
✔ launches list reads sessions.json and marks a stale entry; stop reports it and refuses a non-runner (100.26265ms)
|
||||
ℹ tests 97
|
||||
ℹ suites 0
|
||||
ℹ pass 97
|
||||
ℹ fail 0
|
||||
ℹ cancelled 0
|
||||
ℹ skipped 0
|
||||
ℹ todo 0
|
||||
ℹ duration_ms 9077.632776
|
||||
@@ -0,0 +1,11 @@
|
||||
cli rc=0
|
||||
test-auth rc=0
|
||||
test-conductor rc=0
|
||||
test-config rc=0
|
||||
test-discord rc=0
|
||||
test-extension-package rc=0
|
||||
test-foundation rc=0
|
||||
test-queue rc=0
|
||||
test-release rc=0
|
||||
test-task rc=0
|
||||
DONE
|
||||
@@ -0,0 +1,17 @@
|
||||
OK status with missing harness credential exits 3 and still lists accounts
|
||||
OK status reports harness credential (read-only) + mosaic accounts
|
||||
OK api key material never reaches output
|
||||
OK oauth token material never reaches output
|
||||
OK unparseable credential file exits 2
|
||||
OK symlinked credential file exits 4
|
||||
OK env-side credential names reported
|
||||
OK env var values never reach output
|
||||
OK accounts without an accounts dir reports none and creates nothing
|
||||
OK accounts lists files and marks the active one
|
||||
OK loose account perms flagged in listing
|
||||
OK agent --auth with missing account file refuses (exit 4)
|
||||
OK agent --auth with non-0600 account file refuses
|
||||
OK agent --auth with invalid account name refuses
|
||||
OK auth.sh without valid config refuses
|
||||
|
||||
selftest: 15 passed, 0 failed
|
||||
@@ -0,0 +1,23 @@
|
||||
On branch refactor
|
||||
Your branch is up to date with 'origin/refactor'.
|
||||
|
||||
nothing to commit, working tree clean
|
||||
OK dry-run: allowed change, exit 0, nothing committed (exit 0)
|
||||
OK dry-run committed nothing
|
||||
OK apply: allowed change exits 0 (exit 0)
|
||||
OK apply: attribution in commit subject
|
||||
OK apply: target tree clean after commit
|
||||
OK disallowed path refused (exit 1)
|
||||
OK disallowed path: target untouched
|
||||
OK syntax gate refused broken .mjs (exit 1)
|
||||
OK syntax gate: target untouched
|
||||
OK suite failure refused (exit 1)
|
||||
OK suite failure: target reverted to clean
|
||||
OK disabled policy refused (exit 2)
|
||||
OK disabled policy: target untouched
|
||||
OK failed run refused (exit 1)
|
||||
OK failed run: target untouched
|
||||
OK missing run exits 4 (exit 4)
|
||||
OK invalid policy exits 2 (exit 2)
|
||||
|
||||
selftest: 17 passed, 0 failed
|
||||
@@ -0,0 +1,26 @@
|
||||
OK absent adapter defaults to pi
|
||||
OK adapter mock validates (exit 0)
|
||||
OK unsupported adapter exits 2 (exit 2)
|
||||
OK env exports adapter
|
||||
OK bootstrap creates default when absent (exit 0)
|
||||
OK bootstrap wrote config file
|
||||
OK bootstrap is idempotent on existing config (exit 0)
|
||||
OK bootstrap did not rewrite existing config
|
||||
OK validate missing config exits 3 (exit 3)
|
||||
OK malformed JSON exits 2 (exit 2)
|
||||
OK unsupported configVersion exits 2 (exit 2)
|
||||
OK unknown top-level key exits 2 (exit 2)
|
||||
OK unknown execution key exits 2 (exit 2)
|
||||
OK unsupported backend exits 2 (exit 2)
|
||||
OK unsupported environment exits 2 (exit 2)
|
||||
OK relative dataRoot exits 2 (exit 2)
|
||||
OK non-canonical dataRoot exits 2 (exit 2)
|
||||
OK filesystem root dataRoot exits 2 (exit 2)
|
||||
OK home directory dataRoot exits 2 (exit 2)
|
||||
OK dataRoot containing config dir exits 2 (exit 2)
|
||||
OK control character in provider exits 2 (exit 2)
|
||||
OK symlinked config file exits 2 (exit 2)
|
||||
OK env exports resolve correctly
|
||||
OK failed validation modified nothing
|
||||
|
||||
selftest: 24 passed, 0 failed
|
||||
@@ -0,0 +1,70 @@
|
||||
toolchain: node v26.8.1
|
||||
|
||||
OK syntax: packages/discord/src/approvals.mjs
|
||||
OK syntax: packages/discord/src/authorize.mjs
|
||||
OK syntax: packages/discord/src/binding.mjs
|
||||
OK syntax: packages/discord/src/cli.mjs
|
||||
OK syntax: packages/discord/src/connector.mjs
|
||||
OK syntax: packages/discord/src/context.mjs
|
||||
OK syntax: packages/discord/src/engine-pi.mjs
|
||||
OK syntax: packages/discord/src/errors.mjs
|
||||
OK syntax: packages/discord/src/gateway.mjs
|
||||
OK syntax: packages/discord/src/git.mjs
|
||||
OK syntax: packages/discord/src/journal.mjs
|
||||
OK syntax: packages/discord/src/notify.mjs
|
||||
OK syntax: packages/discord/src/rest.mjs
|
||||
OK syntax: packages/discord/src/setspark.mjs
|
||||
OK syntax: packages/discord/src/tools.mjs
|
||||
OK syntax: packages/discord/src/web.mjs
|
||||
OK syntax: packages/discord/bin/git-credential.mjs
|
||||
OK syntax: packages/discord/extension/tools.mjs
|
||||
OK syntax: packages/discord/tests/approvals.test.mjs
|
||||
OK syntax: packages/discord/tests/authorize.test.mjs
|
||||
OK syntax: packages/discord/tests/binding.test.mjs
|
||||
OK syntax: packages/discord/tests/connector.test.mjs
|
||||
OK syntax: packages/discord/tests/context.test.mjs
|
||||
OK syntax: packages/discord/tests/engine.test.mjs
|
||||
OK syntax: packages/discord/tests/fake-pi.mjs
|
||||
OK syntax: packages/discord/tests/gateway.test.mjs
|
||||
OK syntax: packages/discord/tests/git.test.mjs
|
||||
OK syntax: packages/discord/tests/helpers.mjs
|
||||
OK syntax: packages/discord/tests/journal.test.mjs
|
||||
OK syntax: packages/discord/tests/notify.test.mjs
|
||||
OK syntax: packages/discord/tests/recover.test.mjs
|
||||
OK syntax: packages/discord/tests/rest.test.mjs
|
||||
OK syntax: packages/discord/tests/setspark.test.mjs
|
||||
OK syntax: packages/discord/tests/tools.test.mjs
|
||||
OK syntax: packages/discord/tests/web.test.mjs
|
||||
OK syntax: packages/discord/fixtures/claim-worker.mjs
|
||||
OK syntax: packages/discord/fixtures/legacy-owner-worker.mjs
|
||||
OK syntax: scripts/discord.sh
|
||||
OK syntax: scripts/discord-service.sh
|
||||
OK packages/discord declares no dependencies
|
||||
OK no bot-token-shaped string in packages/discord
|
||||
OK fixture binding uses placeholder ids only
|
||||
OK fixture binding validates
|
||||
OK real pi with the extension exposes exactly list_dir, read_file, search and no built-in tool
|
||||
OK real pi with a writable root exposes exactly the three reads plus write_file and edit_file, and writes nothing at start
|
||||
OK real pi with a web key exposes the three reads plus web_fetch and web_search, and no write tool without a writable root
|
||||
OK real pi with a git root exposes the reads, writes and the four git verbs, commits nothing at start, and never shows the token
|
||||
OK real pi with protocol vault adds reserve_id to the git verbs
|
||||
OK real pi with a setspark key exposes the reads and the eight record verbs, no counters, and never shows the key
|
||||
OK real pi refuses a git key on a read-only root (fail closed)
|
||||
OK real pi with the pilot flags (--no-tools) exposes no tool at all
|
||||
OK real pi exits non-zero without MOSAIC_DISCORD_TOOLS: no session, no tools (fail closed)
|
||||
OK a failing nested test fails the run under a parent runner's NODE_TEST_CONTEXT
|
||||
OK node --test packages/discord/tests/ (ℹ pass 178)
|
||||
OK scripts/discord.sh --help exits 0
|
||||
OK scripts/discord.sh check without a binding exits 4
|
||||
OK scripts/discord.sh recover without a binding exits 4
|
||||
OK scripts/discord.sh reload without a binding exits 4
|
||||
OK scripts/discord-service.sh without a command exits 4
|
||||
OK service unit renders with the repository path, a supervised run as the main process, exit 3 never retried, and reload as SIGHUP
|
||||
OK service install writes the rendered unit (0644) and leaves no temp file
|
||||
OK service install a second time reports unchanged
|
||||
OK systemd-analyze verify accepts the rendered unit
|
||||
OK service uninstall removes the unit file
|
||||
OK service install with an unknown flag exits 4
|
||||
OK service install with USER unset finishes and names the account for lingering
|
||||
|
||||
discord suite: 66 passed, 0 failed
|
||||
@@ -0,0 +1,21 @@
|
||||
OK initial ordinary-file install
|
||||
OK installed tree matches canonical source
|
||||
OK installed tree has no symlinks
|
||||
OK check detects installation drift
|
||||
OK sync refuses to overwrite installation drift
|
||||
OK check detects an extra destination file
|
||||
OK check detects an extra destination directory
|
||||
OK check rejects a destination symlink
|
||||
OK sync accepts a canonical source update
|
||||
OK updated installation matches canonical source
|
||||
scripts/test-extension-package.sh: line 14: 414866 Killed "$@" > /dev/null 2>&1
|
||||
OK forced interruption kills the replacing process
|
||||
OK next invocation recovers old consistent installation
|
||||
OK interrupted replacement rolled back
|
||||
OK sync succeeds after interruption recovery
|
||||
OK unlocked stale lock file does not block
|
||||
OK active lock refuses a concurrent sync
|
||||
OK source symlink fails closed
|
||||
OK nested second entrypoint fails closed
|
||||
|
||||
extension package selftest: 18 passed, 0 failed
|
||||
@@ -0,0 +1,53 @@
|
||||
toolchain: node v26.8.1, python 3.12.8, jsonschema 4.26.0
|
||||
|
||||
OK syntax: scripts/foundation-inspect.mjs
|
||||
OK syntax: scripts/foundation/strict-json.mjs
|
||||
OK syntax: scripts/foundation/canonical.mjs
|
||||
OK syntax: scripts/foundation/resolve.mjs
|
||||
OK syntax: scripts/foundation/validate-record.mjs
|
||||
OK syntax: scripts/foundation/fixtures/build-fixtures.mjs
|
||||
OK syntax: scripts/foundation/canonical.test.mjs
|
||||
OK syntax: scripts/foundation/cli.test.mjs
|
||||
OK syntax: scripts/foundation/fixtures.test.mjs
|
||||
OK syntax: scripts/foundation/resolve.test.mjs
|
||||
OK syntax: scripts/foundation/strict-json.test.mjs
|
||||
OK syntax: scripts/foundation/verify-schema.py (ast only; no bytecode written)
|
||||
OK fixture generator runs
|
||||
OK checked-in fixtures/bundles equal a fresh generation
|
||||
OK checked-in fixtures/raw equal a fresh generation
|
||||
OK checked-in fixtures/index.json equal a fresh generation
|
||||
OK checked-in demo bundles equal a fresh generation
|
||||
OK a failing nested test fails the run under a parent runner's NODE_TEST_CONTEXT
|
||||
OK node --test scripts/foundation/ (ℹ pass 80)
|
||||
OK differential schema oracle: PASS: differential schema oracle (finite corpus; compatibility evidence, not equivalence proof)
|
||||
platform witness: strftime('%Y') for year 999 -> '999' (pinned checker refuses years 0001..0999)
|
||||
node v26.8.1; corpus 1568 records (38 pinned fixtures, 478 unique bundle records, 1052 typeCase/mutation/lexical cases)
|
||||
schema column: agree-valid 540, agree-invalid 991, DISAGREEMENTS 0; strict-only (parser-bound) cases: 27; unsupported-kind records not schema-assessed by the inspector: 10
|
||||
profile column (schema-valid records only): profile-valid 510, profile-invalid 30
|
||||
profile refusals asserted: 30 schema-agreed-valid records refused only by the strict typed-string profile (rule profile-pattern-mismatch), 12 declared by name; 73 named probes verified against declared schema/profile columns
|
||||
OK oracle: zero schema-column disagreements with the pinned checker
|
||||
OK oracle: strict-only profile refusals are counted and asserted
|
||||
OK demo: permitted read preview exits 0 (exit 0)
|
||||
OK demo: permitted file.change preview exits 0 (exit 0)
|
||||
OK demo: assignment.change proposal is unresolved (exit 3) (exit 3)
|
||||
OK demo: revoked registration is refused (exit 3) (exit 3)
|
||||
OK demo: message is not authority (exit 3) (exit 3)
|
||||
OK usage: no arguments exits 2 (exit 2)
|
||||
OK io: missing file exits 4 (exit 4)
|
||||
OK io: directory exits 4 (exit 4)
|
||||
OK io: symlink exits 4 (O_NOFOLLOW) (exit 4)
|
||||
OK bound: oversize fixture exits 2 (exit 2)
|
||||
OK profile: one final LF in a typed selection id is refused before admission (exit 2) (exit 2)
|
||||
OK profile: two final LFs fail the schema pattern itself (exit 2) (exit 2)
|
||||
OK profile: escaped newlines in free-form text stay allowed (exit 0) (exit 0)
|
||||
OK profile refusal is invalid-request/profile-pattern-mismatch with selection and operation withheld, value not echoed
|
||||
OK text output starts with the disclaimer
|
||||
OK json output is valid JSON with result allowed and exactly the charter §7 fields
|
||||
OK json golden matches byte-for-byte
|
||||
OK sandboxed bundle run (env -i, PATH=/nonexistent) produced the unresolved proposal
|
||||
OK sandbox inventory (path/type/size/mode/uid/gid/inode/mtime/sha256) unchanged by runs
|
||||
OK canary never printed (bundle run and credential-file run)
|
||||
OK a non-bundle JSON file is refused at the shape gate, not read into output
|
||||
OK no field of the non-bundle file is echoed
|
||||
|
||||
selftest: 44 passed, 0 failed
|
||||
@@ -0,0 +1,35 @@
|
||||
toolchain: node v26.8.1, git version 2.55.0
|
||||
|
||||
OK syntax: packages/queue/src/cli.mjs
|
||||
OK syntax: packages/queue/src/errors.mjs
|
||||
OK syntax: packages/queue/src/io.mjs
|
||||
OK syntax: packages/queue/src/lock.mjs
|
||||
OK syntax: packages/queue/src/queue.mjs
|
||||
OK syntax: packages/queue/src/review.mjs
|
||||
OK syntax: packages/queue/src/store.mjs
|
||||
OK syntax: packages/queue/tests/commit.test.mjs
|
||||
OK syntax: packages/queue/tests/data.test.mjs
|
||||
OK syntax: packages/queue/tests/dispatch.test.mjs
|
||||
OK syntax: packages/queue/tests/helpers.mjs
|
||||
OK syntax: packages/queue/tests/lock.test.mjs
|
||||
OK syntax: packages/queue/tests/migration.test.mjs
|
||||
OK syntax: packages/queue/tests/review.test.mjs
|
||||
OK syntax: packages/queue/tests/store.test.mjs
|
||||
OK syntax: packages/queue/tests/write.test.mjs
|
||||
OK syntax: packages/queue/tests/fixtures/fake-gitea.mjs
|
||||
OK syntax: packages/queue/tests/fixtures/kill-at.mjs
|
||||
OK syntax: packages/queue/tests/fixtures/lock-child.mjs
|
||||
OK syntax: packages/queue/tests/fixtures/mosaic-pre-a2.sh
|
||||
OK syntax: scripts/queue-commit.sh
|
||||
OK syntax: scripts/git-hooks/pre-commit
|
||||
OK syntax: scripts/mosaic
|
||||
OK queue-commit.sh, the guard and scripts/mosaic are executable
|
||||
OK packages/queue declares no dependencies
|
||||
ℹ tests 150
|
||||
ℹ pass 150
|
||||
ℹ fail 0
|
||||
OK node --test packages/queue/tests/
|
||||
OK scripts/mosaic queue help
|
||||
skip queue verify and render --check: this checkout (/home/jwoltje/darkwing-scratch/r55/gate) is not the queue's canonical root (/mnt/storage/src/mosaic-stack)
|
||||
|
||||
queue suite: 27 passed, 0 failed
|
||||
@@ -0,0 +1,16 @@
|
||||
OK valid RELEASE resolves (exit 0)
|
||||
OK invalid RELEASE exits 1 (exit 1)
|
||||
OK missing RELEASE exits 1 (exit 1)
|
||||
OK valid RELEASE leaves image tag consistent with version
|
||||
OK status safe on empty state (exit 0)
|
||||
OK status created no pointer
|
||||
OK fault-injected activation refuses (exit 1)
|
||||
OK refused activation wrote no pointer
|
||||
OK refusal logged exactly once with valid fields
|
||||
OK healthy activation succeeds (exit 0)
|
||||
OK pointer written with valid fields
|
||||
OK repeat activation succeeds (log grows) (exit 0)
|
||||
OK log is append-only across activations
|
||||
OK rollback without previous refuses (exit 1)
|
||||
|
||||
selftest: 14 passed, 0 failed
|
||||
@@ -0,0 +1,32 @@
|
||||
OK valid task validates (exit 0)
|
||||
OK unknown task key exits 2 (exit 2)
|
||||
OK unsupported taskVersion exits 2 (exit 2)
|
||||
OK invalid task id exits 2 (exit 2)
|
||||
OK empty prompt exits 2 (exit 2)
|
||||
OK NUL in expectExact exits 2 (exit 2)
|
||||
OK out-of-range timeout exits 2 (exit 2)
|
||||
OK missing mission file exits 4 (exit 4)
|
||||
OK task with valid mission validates (exit 0)
|
||||
OK invalid mission exits 2 (exit 2)
|
||||
OK validate missing task exits 4 (exit 4)
|
||||
OK validation does not modify the task file
|
||||
OK prune dry-run exits 0 (exit 0)
|
||||
OK dry-run deleted nothing
|
||||
OK prune --keep=2 --yes removes oldest (exit 0)
|
||||
OK kept exactly 2 newest runs
|
||||
OK newest run kept, oldest pruned
|
||||
OK append-only receipt written (3 entries)
|
||||
OK sessions/workspaces untouched by prune
|
||||
OK prune with invalid keep exits 4 (exit 4)
|
||||
skip adapter seam cases (docker daemon unavailable)
|
||||
skip workspace/capability cases (docker daemon unavailable)
|
||||
skip live task cases (docker unavailable)
|
||||
OK onboard without name exits 4 (non-interactive) (exit 4)
|
||||
OK onboard --name renders profile (exit 0)
|
||||
OK profile written
|
||||
OK canon structure: required filled, optional placeholdered
|
||||
OK canon sections present
|
||||
skip live recall pair: user recall run, recalled user name (docker daemon unavailable)
|
||||
OK no agent identity on headless run
|
||||
|
||||
selftest: 26 passed, 0 failed
|
||||
@@ -0,0 +1,11 @@
|
||||
cli rc=0
|
||||
test-auth rc=0
|
||||
test-conductor rc=0
|
||||
test-config rc=0
|
||||
test-discord rc=0
|
||||
test-extension-package rc=0
|
||||
test-foundation rc=0
|
||||
test-queue rc=1
|
||||
test-release rc=0
|
||||
test-task rc=0
|
||||
DONE
|
||||
@@ -0,0 +1,41 @@
|
||||
toolchain: node v26.8.1, git version 2.55.0
|
||||
|
||||
OK syntax: packages/queue/src/cli.mjs
|
||||
OK syntax: packages/queue/src/errors.mjs
|
||||
OK syntax: packages/queue/src/io.mjs
|
||||
OK syntax: packages/queue/src/lock.mjs
|
||||
OK syntax: packages/queue/src/queue.mjs
|
||||
OK syntax: packages/queue/src/review.mjs
|
||||
OK syntax: packages/queue/src/store.mjs
|
||||
OK syntax: packages/queue/tests/commit.test.mjs
|
||||
OK syntax: packages/queue/tests/data.test.mjs
|
||||
OK syntax: packages/queue/tests/dispatch.test.mjs
|
||||
OK syntax: packages/queue/tests/helpers.mjs
|
||||
OK syntax: packages/queue/tests/lock.test.mjs
|
||||
OK syntax: packages/queue/tests/migration.test.mjs
|
||||
OK syntax: packages/queue/tests/review.test.mjs
|
||||
OK syntax: packages/queue/tests/store.test.mjs
|
||||
OK syntax: packages/queue/tests/write.test.mjs
|
||||
OK syntax: packages/queue/tests/fixtures/fake-gitea.mjs
|
||||
OK syntax: packages/queue/tests/fixtures/kill-at.mjs
|
||||
OK syntax: packages/queue/tests/fixtures/lock-child.mjs
|
||||
OK syntax: packages/queue/tests/fixtures/mosaic-pre-a2.sh
|
||||
OK syntax: scripts/queue-commit.sh
|
||||
OK syntax: scripts/git-hooks/pre-commit
|
||||
OK syntax: scripts/mosaic
|
||||
OK queue-commit.sh, the guard and scripts/mosaic are executable
|
||||
OK packages/queue declares no dependencies
|
||||
ℹ tests 149
|
||||
ℹ pass 148
|
||||
ℹ fail 1
|
||||
✖ every call but `queue` reaches the seat CLI exactly as before A2 (423.059344ms)
|
||||
✖ failing tests:
|
||||
✖ every call but `queue` reaches the seat CLI exactly as before A2 (423.059344ms)
|
||||
AssertionError [ERR_ASSERTION]: []
|
||||
+ "Error: Cannot find module '/home/jwoltje/darkwing-scratch/r55/tmp/mosaic-queue-dispatch-CI2rEd/packages/cli/src/cli.mjs'\n" +
|
||||
actual: { status: 1, out: '', err: "node:internal/modules/cjs/loader:1573\n throw err;\n ^\n\nError: Cannot find module '/home/jwoltje/darkwing-scratch/r55/tmp/mosaic-queue-dispatch-CI2rEd/packages/cli/src/cli.mjs'\n at Module._resolveFilename (node:internal/modules/cjs/loader:1569:15)\n at wrapResolveFilename (node:internal/modules/cjs/loader:1123:27)\n at defaultResolveImplForCJSLoading (node:internal/modules/cjs/loader:1147:10)\n at resolveForCJSWithHooks (node:internal/modules/cjs/loader:1174:12)\n at Module._load (node:internal/modules/cjs/loader:1346:5)\n at wrapModuleLoad (node:internal/modules/cjs/loader:261:19)\n at Module.executeUserEntryPoint [as runMain] (node:internal/modules/run_main:154:5)\n at node:internal/main/run_main_module:33:47 {\n code: 'MODULE_NOT_FOUND',\n requireStack: []\n}\n\nNode.js v26.8.1\n", capture: null },
|
||||
FAIL node --test packages/queue/tests/
|
||||
OK scripts/mosaic queue help
|
||||
skip queue verify and render --check: this checkout (/home/jwoltje/darkwing-scratch/r55/gate) is not the queue's canonical root (/mnt/storage/src/mosaic-stack)
|
||||
|
||||
queue suite: 26 passed, 1 failed
|
||||
@@ -0,0 +1,4 @@
|
||||
(app closed with q; 0 decisions resolved this session)
|
||||
EXIT=0
|
||||
alternate screen entered: 1
|
||||
alternate screen left: 1
|
||||
Executable
+21
@@ -0,0 +1,21 @@
|
||||
#!/usr/bin/env bash
|
||||
# Row 55 smoke run: opens the app in a pty at 80x24 against a fixture
|
||||
# config (never the real bus), presses ? Esc t q, and prints what the
|
||||
# terminal got after the last frame. Usage: smoke.sh <repo> <scratch-dir>
|
||||
set -euo pipefail
|
||||
REPO="$(cd "$1" && pwd)"; S="$(mkdir -p "$2" && cd "$2" && pwd)"
|
||||
cat > "$S/mkfix.mjs" <<JS
|
||||
import { fixture } from "$REPO/packages/cli/tests/helpers.mjs";
|
||||
const f = fixture(process.argv[2]);
|
||||
console.log(f.config + " " + f.roles);
|
||||
JS
|
||||
mkdir -p "$S/fx"
|
||||
read -r CONFIG ROLES < <(node "$S/mkfix.mjs" "$S/fx")
|
||||
{ sleep 1.5; printf '?'; sleep 0.5; printf '\033'; sleep 0.5; printf 't'; sleep 0.5; printf 'q'; sleep 1; } \
|
||||
| env -u MOSAIC_BUS_CAP -u MOSAIC_RUN_ID -u CLAUDECODE -u CLAUDE_CODE_ENTRYPOINT -u MOSAIC_AGENT_RUN -u CODEX_THREAD_ID -u PI_AGENT_DIR \
|
||||
MOSAIC_CONFIG="$CONFIG" MOSAIC_ROLES_DIR="$ROLES" \
|
||||
script -qfec "stty cols 80 rows 24; '$REPO/scripts/mosaic'; echo EXIT=\$?" "$S/typescript.log" > "$S/drive.out"
|
||||
# The tail after the alternate screen closes: the quit line and the exit code.
|
||||
sed -n 's/.*\x1b\[?1049l//p; /^EXIT=/p' "$S/drive.out" | tr -d '\r'
|
||||
grep -c $'\x1b\[?1049h' "$S/drive.out" | sed 's/^/alternate screen entered: /'
|
||||
grep -c $'\x1b\[?1049l' "$S/drive.out" | sed 's/^/alternate screen left: /'
|
||||
Reference in New Issue
Block a user