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