work: row 110 round 2 packet, the role-cap envelope case (#1604)
Folds Filbert's N1 per Sage's comment 28355: the envelope test sends a role cap with a tester proof. The narrowing mutant at server.mjs:50 survives the full bus suite on round 1 and is killed on round 2. Full gate passed with the real bwrap tester case running. Co-Authored-By: Claude Opus 5.5 <[email protected]>
This commit is contained in:
@@ -0,0 +1,68 @@
|
||||
# Row 110 (#1604) round 2: one more envelope case
|
||||
|
||||
Sage asked for changes in comment 28355 (rev 1093) and folded Filbert's note N1 into this round. Round 1 is agents/darkwing/work/queue-110/r1/. Everything in it still holds except the one test file below.
|
||||
|
||||
BASE: badfa52b9d5b956392bd9cb141ff1f4ce9f402f0, unchanged. Since BASE, the branch has changed only packet files, docs/SESSIONS.md, docs/plans/QUEUE.md and docs/plans/queue.json. No manifest path moved.
|
||||
Candidate: build.patch against BASE. candidate-manifest.sha256 lists the same 19 files as round 1. I applied the patch to a fresh `git archive badfa52b9` export and every digest matched.
|
||||
|
||||
## What changed
|
||||
|
||||
One file, packages/bus/tests/tester.test.mjs, in the test "an envelope carries exactly one of cap, human and tester; no tester authenticator refuses". The envelope loop gains a role cap beside the reader cap. r1-to-r2.diff is the whole change between the two candidates' applied trees:
|
||||
|
||||
```
|
||||
const cap = b.bindReader({ business: 'demo' });
|
||||
- for (const r of [{ tester: p, cap }, { tester: p, human: p }, { tester: p, human: p, cap }])
|
||||
+ const role = b.bindLaunch({ business: 'demo', role: 'pm', run: 'pm-run', harness: 'pi' });
|
||||
+ for (const r of [{ tester: p, cap }, { tester: p, cap: role }, { tester: p, human: p }, { tester: p, human: p, cap }])
|
||||
```
|
||||
|
||||
The manifest line for that file goes from 39c225c5... to 167aa918.... The other 18 lines match round 1 byte for byte.
|
||||
|
||||
## The narrowing mutant
|
||||
|
||||
mutant-n1.py in this packet. It narrows the conflict check at packages/bus/src/server.mjs:50 so a cap counts only when it is the envelope's sole credential or is a reader cap (`broker.identity` gives role null and no human). With the mutant, a role cap sent with a tester proof passes the envelope, and the request runs as the tester. The script swaps that one line, runs the whole bus suite (`node --test tests/*.test.mjs` in packages/bus), and puts server.mjs back from its `.orig` copy. Both runs report `restored=True`.
|
||||
|
||||
| Candidate | Bus suite under the mutant | Result |
|
||||
|---|---|---|
|
||||
| round 1 (01:21:34Z) | 147 pass, 0 fail | survived |
|
||||
| round 2 (01:21:50Z) | 146 pass, 1 fail | killed |
|
||||
|
||||
On round 1 it survives every bus test, which is the gap Sage named. On round 2 the only failure is the envelope test. The new tuple gets `{ ok: true, result: [] }` where the test expects `{ ok: false, error: 'invalid-envelope' }` (mutant-n1-r2.log, the assertion at tester.test.mjs:337). The reader-cap tuple still refuses under the mutant, so the failure comes from the role-cap case.
|
||||
|
||||
The assertion message is `Object.keys(r).join('+')`, so both cap tuples print `tester+cap`. Sage's scope said nothing else changes, so I left the label alone. The `actual` value in the log shows which tuple failed.
|
||||
|
||||
## Gate
|
||||
|
||||
gate.sh in this packet, run on the round-2 candidate with the slot from Sage, 01:22:27-01:27:55Z, on BASE badfa52b9 with `DOCKER_HOST` unset and docker 29.7.2. It runs every package's node suite and every scripts/test-*.sh. GATE_PASSED, exit 0. gate-run.log has the summary, and gate/ has one log per step. The worktree still matched the manifest after the run.
|
||||
|
||||
| Step | Passed | Failed |
|
||||
|---|---|---|
|
||||
| bus | 147 | 0 |
|
||||
| business | 67 | 0 |
|
||||
| cli | 184 | 0 |
|
||||
| control-board | 124 | 0 |
|
||||
| conversation | 182 | 0 |
|
||||
| discord | 178 | 0 |
|
||||
| harness | 80 | 0 |
|
||||
| ledger | 78 | 0 |
|
||||
| mosaic | 69 | 0 |
|
||||
| queue | 156 | 0 |
|
||||
| runs | 43 | 0 |
|
||||
| seat | 120 (0 skipped) | 0 |
|
||||
| tasks | 92 | 0 |
|
||||
| webui | 108 | 0 |
|
||||
| scripts | auth 15, conductor 17, config 24, discord 66, extension-package 18, foundation 44, queue 28 (node 156), release 14, task 98 | 0 |
|
||||
|
||||
The counts match round 1. Bus stays at 147 because the new case sits inside an existing test.
|
||||
|
||||
The real bwrap tester case ran and passed. It did not skip. From gate/pkg-seat.log:77:
|
||||
|
||||
```
|
||||
✔ a confined session that runs tester-cli.mjs is refused as tester, and the same shim outside the sandbox binds (262.780721ms)
|
||||
```
|
||||
|
||||
Neither the review.test.mjs:123 flake (#261) nor the release `active.json` flake (#258) came back. The release step made `wt_default` at 01:27:13Z with no containers, and I removed it after the run.
|
||||
|
||||
## Mutants from round 1
|
||||
|
||||
I didn't rerun the 96 round-1 mutants. The only change adds one more assertion to an existing test. A test with an extra assertion fails on everything it failed on before, so this round can only kill more mutants, never fewer. Two of them, s00 and s01, touch server.mjs:50, and round 1 killed both. Round 1's mutants-run.log (94 killed, b44 and b45 equivalent) ran on the round-1 candidate.
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,19 @@
|
||||
03840e1ea7cedac04b000a238f8960d5a45d3f31207075fd0150c99ef65f9b0e packages/business/README.md
|
||||
4e0c4f56557843ca4953d55ee09f26bdcdf96de8e71c9362567be0d9c3a16dd8 packages/business/src/vars.mjs
|
||||
88073d8bbe0dda96e981549197954bd9f3a5d8d557430f48fda2349398196fb1 packages/business/tests/vars.test.mjs
|
||||
a2103a3cab534469ffdbc40f0b4259ae19839e5173e3d97fdc6463f53b90c01a packages/bus/README.md
|
||||
071406a566efc819e031dec8563df2e7005391926a9dedd0bdc6cb7fbcb7844d packages/bus/src/broker.mjs
|
||||
a3d6052f93fea672a0a78c5a655450af90170c3442fae513aea61885c05ec6f5 packages/bus/src/client.mjs
|
||||
dc3633f9c1c2049b4cbf3527e7e51fa379361093abd30bbefed52c9796c700c5 packages/bus/src/runtime.mjs
|
||||
c4f37a6194abf0433f86d439c0e9896a8402ec90038e421b166d4d14b5647ea8 packages/bus/src/server.mjs
|
||||
c04c434ec76252fbc93fb047e7a0c996225590e805f2c82a8d521c161cc9b80b packages/bus/src/tester-cli.mjs
|
||||
5e9e08ca8069e1de35232f26d48a77792388128dda659893909f2d34b5af2346 packages/bus/src/tester.mjs
|
||||
167aa91889dcc0acfe2d0e558562a6405e44833d9b2d80b08b16995168d0059e packages/bus/tests/tester.test.mjs
|
||||
f408c0fb5753b19159d6cfbf57f051ca379ef9a697697b0923f286605b1c5818 packages/cli/README.md
|
||||
e6e8b6164f2dd97ddb2a0f5c8982ddba1ace3de0a5d88f29f1522696cc3aff43 packages/cli/src/cli.mjs
|
||||
5129c44b4d9f642e825344df65c9bfb4bac4fc8d6b25343b42d39853dbd40774 packages/cli/src/transport.mjs
|
||||
76e267cdd221d395a545fb1c3631cecc5bbb67d367e6f42e0eb552a8238184ec packages/cli/tests/stack-copy.test.mjs
|
||||
a7ecb7c95c76730308b32a43cf3ed01b2809ce14983ec89290729b8d0031f472 packages/cli/tests/tester.test.mjs
|
||||
819a6bd2c77199d88c4b7e0374c08b3732fb3b22cc874c35baf957f35bcb94da packages/harness/src/runner.mjs
|
||||
45024be17bfeb039979a7bafbaba9357a52f4b83ffdd072c2c1cb9f21fe8abb8 packages/harness/tests/runner.test.mjs
|
||||
34802b1d5773e79e0c282921b968466b306661146bf15d7093df3be226b7e184 packages/seat/tests/sandbox.test.mjs
|
||||
@@ -0,0 +1,26 @@
|
||||
base badfa52b9 DOCKER_HOST=unset docker=29.7.2 start 01:22:27
|
||||
pkg-bus rc=0 01:22:27-01:22:31 ℹ pass 147 ℹ fail 0 ℹ skipped 0 | ℹ duration_ms 3802.497134
|
||||
pkg-business rc=0 01:22:31-01:22:33 ℹ pass 67 ℹ fail 0 ℹ skipped 0 | ℹ duration_ms 1799.397393
|
||||
pkg-cli rc=0 01:22:33-01:22:52 ℹ pass 184 ℹ fail 0 ℹ skipped 0 | ℹ duration_ms 18879.944578
|
||||
pkg-control-board rc=0 01:22:52-01:22:52 ℹ pass 124 ℹ fail 0 ℹ skipped 0 | ℹ duration_ms 602.596078
|
||||
pkg-conversation rc=0 01:22:52-01:23:40 ℹ pass 182 ℹ fail 0 ℹ skipped 0 | ℹ duration_ms 47286.327145
|
||||
pkg-discord rc=0 01:23:40-01:23:42 ℹ pass 178 ℹ fail 0 ℹ skipped 0 | ℹ duration_ms 2585.336653
|
||||
pkg-harness rc=0 01:23:42-01:23:53 ℹ pass 80 ℹ fail 0 ℹ skipped 0 | ℹ duration_ms 11124.299046
|
||||
pkg-ledger rc=0 01:23:53-01:24:12 ℹ pass 78 ℹ fail 0 ℹ skipped 0 | ℹ duration_ms 18792.388342
|
||||
pkg-mosaic rc=0 01:24:12-01:24:14 ℹ pass 69 ℹ fail 0 ℹ skipped 0 | ℹ duration_ms 2070.961118
|
||||
pkg-queue rc=0 01:24:14-01:24:43 ℹ pass 156 ℹ fail 0 ℹ skipped 0 | ℹ duration_ms 28460.561057
|
||||
pkg-runs rc=0 01:24:43-01:24:44 ℹ pass 43 ℹ fail 0 ℹ skipped 0 | ℹ duration_ms 741.001299
|
||||
pkg-seat rc=0 01:24:44-01:24:49 ℹ pass 120 ℹ fail 0 ℹ skipped 0 | ℹ duration_ms 4860.236596
|
||||
pkg-tasks rc=0 01:24:49-01:24:54 ℹ pass 92 ℹ fail 0 ℹ skipped 0 | ℹ duration_ms 5434.915646
|
||||
pkg-webui rc=0 01:24:54-01:25:49 ℹ pass 108 ℹ fail 0 ℹ skipped 0 | ℹ duration_ms 54783.596269
|
||||
script-auth rc=0 01:25:49-01:25:49 | selftest: 15 passed, 0 failed
|
||||
script-conductor rc=0 01:25:49-01:25:55 | selftest: 17 passed, 0 failed
|
||||
script-config rc=0 01:25:55-01:25:56 | selftest: 24 passed, 0 failed
|
||||
script-discord rc=0 01:25:56-01:26:03 | discord suite: 66 passed, 0 failed
|
||||
script-extension-package rc=0 01:26:03-01:26:04 | extension package selftest: 18 passed, 0 failed
|
||||
script-foundation rc=0 01:26:04-01:26:42 | selftest: 44 passed, 0 failed
|
||||
script-queue rc=0 01:26:42-01:27:13 ℹ pass 156 ℹ fail 0 | queue suite: 28 passed, 0 failed
|
||||
script-release rc=0 01:27:13-01:27:24 | selftest: 14 passed, 0 failed
|
||||
script-task rc=0 01:27:24-01:27:55 | selftest: 98 passed, 0 failed
|
||||
GATE_PASSED 01:27:55
|
||||
exit=0
|
||||
@@ -0,0 +1,20 @@
|
||||
#!/bin/bash
|
||||
set -o pipefail
|
||||
# Darkwing gate, row 110 (#1604) round 2, candidate in the r110 worktree on base badfa52b9 (rows 109 and 111 landed).
|
||||
# Every package's node suite, then every scripts/test-*.sh, sequential, each
|
||||
# teed to its own log; stops at the first failure.
|
||||
G=$HOME/darkwing-scratch/r110/wt
|
||||
OUT=$HOME/darkwing-scratch/r110/gate-r2
|
||||
export TMPDIR=$HOME/darkwing-scratch/r110/tmp
|
||||
mkdir -p "$OUT" "$TMPDIR"
|
||||
cd "$G" || exit 9
|
||||
[ -e "$G/node_modules" ] || { echo "STOP no node_modules in $G"; exit 8; }
|
||||
echo "base $(git rev-parse --short HEAD) DOCKER_HOST=${DOCKER_HOST:-unset} docker=$(docker info --format '{{.ServerVersion}}' 2>/dev/null || echo none) start $(date -u +%H:%M:%S)"
|
||||
run() { local name="$1" dir="$2" cmd="$3" t0; t0=$(date -u +%H:%M:%S)
|
||||
(cd "$G/$dir" && bash -c "$cmd") 2>&1 | tee "$OUT/$name.log" > /dev/null; local rc=$?
|
||||
echo "$name rc=$rc $t0-$(date -u +%H:%M:%S) $(grep -E '^ℹ (pass|fail|skipped) [0-9]+' "$OUT/$name.log" | tr '\n' ' ') | $(tail -n1 "$OUT/$name.log")"
|
||||
[ $rc -eq 0 ] || { echo "STOP $name"; exit $rc; }; }
|
||||
for d in packages/*/; do p=$(basename "$d"); [ -d "$d/tests" ] || continue
|
||||
run "pkg-$p" "packages/$p" 'node --test tests/*.test.mjs'; done
|
||||
for s in scripts/test-*.sh; do n=$(basename "$s" .sh); run "script-${n#test-}" . "bash $s"; done
|
||||
echo "GATE_PASSED $(date -u +%H:%M:%S)"
|
||||
@@ -0,0 +1,155 @@
|
||||
✔ agents: a held role with nothing on the bus is idle; a role session gets the claim rows unchanged (141.508943ms)
|
||||
✔ agents: working counts what the holder received and has not read; routed alone is not working (205.734691ms)
|
||||
✔ agents: input needed is the role's open decisions routed to the human, gated first, then oldest (338.818973ms)
|
||||
✔ agents: working wins over waiting, and Input needed still shows (176.473643ms)
|
||||
✔ agents: a new holder doesn't inherit what the last one left unread; decisions stay with the role (207.918719ms)
|
||||
✔ agents: state reads only its own business, even for a run name both use (209.094691ms)
|
||||
✔ agents: a message the holder released unread is stranded; the next holder gets nothing and cannot read it (215.956919ms)
|
||||
✔ agents: a message the holder read is not stranded; one the current holder has not read is not listed (213.224082ms)
|
||||
✔ agents: stranded is oldest first and capped at 50 for a role (782.782848ms)
|
||||
✔ agents: stranded is per role and per business (292.17445ms)
|
||||
✔ agents: a message routed to a held role and not yet received is not stranded (108.703304ms)
|
||||
✔ launch identity is stamped, payload identity is refused and stale holder cannot send (159.476678ms)
|
||||
✔ decision classes route from policy; gated resolution is human-only, choice and target must match (267.688344ms)
|
||||
✔ claim exclusion, holder release, gated revoke and rerouting to a new holder are atomic (279.425081ms)
|
||||
✔ launch events require a human CLI capability; generic emit cannot forge authority events (187.135921ms)
|
||||
✔ within-role decisions close atomically and invalid options or blocking omissions refuse (187.539681ms)
|
||||
✔ observer capabilities read human inbox but cannot mutate or forge launch identity (196.213168ms)
|
||||
✔ task action subjects and linked decision trail are complete and ordered (198.75758ms)
|
||||
✔ launch binding is durable and reconnecting requires the identical trusted record (112.063085ms)
|
||||
✔ business isolation includes inherited object names and cross-business message references (215.706925ms)
|
||||
✔ authority never transfers between action, run, target, unresolved or replaced role holder (250.789896ms)
|
||||
✔ task projection uses schema current view, skipping earlier and equal-start polls (131.747706ms)
|
||||
✔ revocation permanently bars the old run from reclaiming first, including after broker restart (193.487993ms)
|
||||
✔ empty message references refuse before storage; refusal-evidence failure stays a typed error (131.13833ms)
|
||||
✔ both arbiters require human resolution when their cross-role route is themselves (209.313351ms)
|
||||
✔ the shipped coder role raises task.reassign as gated to the human; task.scope.change stays cross-role (149.926828ms)
|
||||
✔ S1 adapter takes resolved limits and refs, rejects mismatched instance, never mutates input (2.416018ms)
|
||||
✔ credentials gives human and reader sessions service, account, role, date and state, and nothing else (92.748744ms)
|
||||
✔ credentials refuses a role session and any argument (171.937969ms)
|
||||
✔ credentials reads the status on each request, and a broker without one lists none (219.354783ms)
|
||||
✔ the broker process answers credentials without a token, path or variable in its output, log or store (196.462691ms)
|
||||
✔ only validated broker references load; returned data and exceptions cannot expose a known token (5.108764ms)
|
||||
✔ bad file modes, symlinks, repository/data paths, malformed tokens and missing dates refuse (3.129039ms)
|
||||
✔ expiry refuses use and env references never become client data (0.829989ms)
|
||||
✔ S1 parsed service refs work, service mismatch refuses, Gitea rotation due is a warning state (1.706077ms)
|
||||
✔ opaque tokens shorter than 16 characters refuse before use (0.439239ms)
|
||||
✔ roles lists open decisions routed to a role, and never the human inbox (223.215045ms)
|
||||
✔ recent lists closed decisions of every route, newest close first, with how each closed (292.984298ms)
|
||||
✔ recent gives the closing event as closed when the decision was seen first (307.542491ms)
|
||||
✔ recent stops at RECENT rows (822.534811ms)
|
||||
✔ every row is what trail already gives the same session for that id (263.144175ms)
|
||||
✔ a role session can't list decisions, so it can't read another role's open decisions (354.397973ms)
|
||||
✔ decisions is a read: one business, strict arguments, nothing written (336.357996ms)
|
||||
✔ endLaunch writes session.ended, releases the run claim and kills its capabilities (128.85552ms)
|
||||
✔ endLaunch refuses an unknown run, a second end and a rebind of the ended run (131.847206ms)
|
||||
✔ a restarted broker refuses to rebind an ended run; a refused rebind leaves the run unbound (155.113914ms)
|
||||
✔ endLaunch leaves a claim another run took alone (188.242019ms)
|
||||
✔ refuse records action.refused against the caller with the code only (143.537293ms)
|
||||
✔ launches off refuses role.launch with launch-revoked until launches on (189.210449ms)
|
||||
✔ broker process: launch ops authorize role.launch, record refusals and end runs (275.058565ms)
|
||||
✔ human proof binds CLI entry, process start and nonce; agents and incomplete ancestry refuse (2.539177ms)
|
||||
✔ process reader gets own kernel identity without exposing environment values (0.840522ms)
|
||||
✔ EACCES ancestor environments skip only markers; commands and registered launches still refuse (1.04976ms)
|
||||
✔ real pid 1 remains inspectable when its environment is protected (0.403969ms)
|
||||
✔ within-role sends cite an open gated launch decision without spending it or naming it in grants (185.870084ms)
|
||||
✔ missing and foreign-business citations refuse and roll back message and grant (190.488162ms)
|
||||
✔ cross-role sends still need a matching resolved decision and consume it once (247.987919ms)
|
||||
✔ messages pages all human messages newest first without writes; read state is history (2500.143178ms)
|
||||
✔ messages refuses role callers, invalid paging and cursors outside this human feed (229.801984ms)
|
||||
✔ views messages passes only its paging arguments through (0.245814ms)
|
||||
✔ messages isolates businesses even with a valid foreign cursor (151.2364ms)
|
||||
✔ broker process binds trusted launches, offers reader capabilities, refuses human mutation, closes cleanly (190.858588ms)
|
||||
✔ startup token refusal returns safe code without value or partial listening broker (38.640467ms)
|
||||
✔ loaded fixture token is absent from socket replies and SQLite, including refusal evidence (178.458986ms)
|
||||
✔ killed broker leaves an explicit stale lock; another process cannot silently reclaim it (183.39908ms)
|
||||
✔ trusted host registers later launches; socket clients never have a registration verb (182.613114ms)
|
||||
✔ runtime excludes declared project roots even when host supplies no repoRoots (38.076899ms)
|
||||
✔ a refused launch binding leaves the broker and existing capabilities alive; bad protocol stops it (174.221812ms)
|
||||
✔ a reply is listed for human and reader sessions, and its delivery rows are unchanged (221.643564ms)
|
||||
✔ the reply still reaches the human message.receive (mosaic talk) after the Console showed it (208.436042ms)
|
||||
✔ a message with no reply gives an empty list (171.005944ms)
|
||||
✔ replies come oldest first and stop at REPLIES (790.298172ms)
|
||||
✔ an id that names no message in the business refuses message-not-found, and the refusal is recorded (309.506205ms)
|
||||
✔ strict arguments; a role session gets human-required (350.978081ms)
|
||||
✔ views passes replies through as one call with the id (0.253194ms)
|
||||
✔ requirements returns the committed list to a holding role session, and no one else (197.490825ms)
|
||||
✔ the PM's prd.approve with no task, when no id fits, reaches the human's Inbox (158.400262ms)
|
||||
✔ the PM's non-blocking role.revoke with no task reaches the human's Inbox (184.969656ms)
|
||||
✔ a push is recorded as the action.allowed for git.push.working on the task, and only the host records one (238.013653ms)
|
||||
✔ review.request names the latest pushed commit and sends the reviewer a REVIEW-REQUEST (255.997651ms)
|
||||
✔ review.request says which run holds the reviewer role, or null, and a null request waits for a claim (300.897868ms)
|
||||
✔ review.request refuses a commit that isn't the latest push, a non-reviewer and a role without the action (392.876413ms)
|
||||
✔ reviewFetch answers only the addressed reviewer, for the request it cites (257.235254ms)
|
||||
✔ review.verdict answers one request once, to the role that asked (321.532673ms)
|
||||
✔ task.close on a pushed task needs an approve verdict for the latest push (327.034732ms)
|
||||
✔ a refusal the host decided is filed on the task it names, and only a task ref counts (222.374419ms)
|
||||
✔ v3b prototype refusals, views and append-only mutations (1068.741806ms)
|
||||
✔ gated approval authorizes once, survives store reopen, and fresh approval works (267.892969ms)
|
||||
✔ another run cannot consume an approval; a failed check leaves it usable (229.449498ms)
|
||||
✔ two scheduled callers have exactly one grant and one consumed refusal (171.004849ms)
|
||||
✔ failed commit rolls consumption back; cross-role consumes and within-role stays reusable (329.116627ms)
|
||||
✔ class drift gated to cross-role refuses before consumption (248.15602ms)
|
||||
✔ class drift cross-role to gated refuses before consumption (262.507549ms)
|
||||
✔ class drift gated to within-role refuses before consumption (237.817592ms)
|
||||
✔ class drift cross-role to within-role refuses before consumption (226.987416ms)
|
||||
✔ class drift within-role to gated refuses before consumption (179.443596ms)
|
||||
✔ class drift within-role to cross-role refuses before consumption (169.687333ms)
|
||||
✔ message.send consumes approval and prevents a later send or authorize (200.474188ms)
|
||||
✔ role.revoke consumes approval and prevents a later revoke or authorize (226.511089ms)
|
||||
✔ a business that never switched launches reads allowed with no time and no event (140.860705ms)
|
||||
✔ launches revoked and then restored read allowed, with the restore time and event id (167.762188ms)
|
||||
✔ a task closed on the tracker leaves the open list, and total counts the open ones (157.255213ms)
|
||||
✔ a tombstone is not open (143.743669ms)
|
||||
✔ the open list stops at the cap and says so; total still counts every open task (151.981088ms)
|
||||
✔ a role and a reader session read state; arguments, and an unbound cap, refuse (141.553662ms)
|
||||
✔ a human session reads the same state, with the same cap (149.56023ms)
|
||||
✔ reading state writes nothing: every table keeps its row count (167.019249ms)
|
||||
✔ creates private WAL store and excludes a second writer until explicit close (114.309446ms)
|
||||
✔ rollback is atomic and schema metadata is checked against trusted DDL, not just itself (187.8324ms)
|
||||
✔ existing empty database and symlink runtime directory refuse, never initialize over damage (185.470377ms)
|
||||
✔ crash during a transaction recovers no partial event after explicit fixture-only lock removal (177.83601ms)
|
||||
✔ writer refuses mixed at/read_at forms atomically, even through trusted SQL helpers (109.257187ms)
|
||||
✔ async transactions refuse before invoking their function (91.39401ms)
|
||||
✔ the session default fits under the reply cap and pages to the end with next (301.609634ms)
|
||||
✔ the short form: no description, assignees by role, every listed key (250.108375ms)
|
||||
✔ each filter, alone and together, and total counts every match (321.784793ms)
|
||||
✔ task_ref reads one task in full, as the whole board has it (288.130339ms)
|
||||
✔ no arguments is the read the Console and mosaic make, unchanged (232.532681ms)
|
||||
✔ an unknown argument or a bad value refuses invalid-request (869.018207ms)
|
||||
✔ tasks rows carry priority and requirement for every session that reads tasks (317.205631ms)
|
||||
✔ the read model rebuilds from the ledger: a reopened bus and each trail give the same values (358.105476ms)
|
||||
✔ an empty business and one with snapshots but no task.created read cleanly (122.109779ms)
|
||||
✔ recordTask keeps sync reads and a role write apart (182.807882ms)
|
||||
✔ read_at must be one canonical UTC format, so the projection compares strings safely (108.663637ms)
|
||||
✔ a bad entry refuses the whole record (119.811385ms)
|
||||
✔ taskView reads the projection for one business (144.711528ms)
|
||||
✔ requestTask hands only a holder and a task verb to the handler, and records refusals (258.147844ms)
|
||||
✔ the server sends task verbs to the adapter with its own timeout; other verbs stay synchronous (392.047995ms)
|
||||
✔ without an adapter the server refuses every task verb (199.61229ms)
|
||||
✔ the runtime refuses an invalid adapter and closes a valid one (193.013972ms)
|
||||
✔ the process loads the S3 adapter from plain-data trackers (238.815371ms)
|
||||
✔ the tester binds only when the business sets bus.tester to exactly true (810.079923ms)
|
||||
✔ a business may not name a role tester, so the tester address is never a role (221.033807ms)
|
||||
✔ every verb outside the tester list refuses tester-refused and is recorded against the tester (484.811362ms)
|
||||
✔ a tester message is from tester, starts [mos test], and writes no human.input or authority (195.088848ms)
|
||||
✔ a role answers a tester only in reply to a tester message, and the answer never reaches the human (221.448931ms)
|
||||
✔ the tester reads the human views a reader reads (73.220618ms)
|
||||
✔ tester proof binds its own CLI, start time and nonce, refuses a managed run and allows an agent (1.115371ms)
|
||||
✔ neither CLI's nonce proves the other: each proof is bound to its own CLI and nonce variable (0.389404ms)
|
||||
✔ testerNonce reads only the tester's variable and gives null on any read error (0.195776ms)
|
||||
✔ an envelope carries exactly one of cap, human and tester; no tester authenticator refuses (50.694886ms)
|
||||
✔ tester-cli.mjs binds through the runtime when the switch is on, and refuses off or under a run marker (696.439765ms)
|
||||
✔ socket capability stamps launch identity; shared views use wire, no SQL client (175.481657ms)
|
||||
✔ two wire claims serialize; a lost reply never automatically retries (179.89021ms)
|
||||
✔ malformed, oversized and identity-forging envelopes refuse without echoing input (115.406737ms)
|
||||
✔ client preserves UTF-8 when a response divides a multibyte character (11.992367ms)
|
||||
✔ committed mutation followed by dropped reply reports unknown and is never retried (143.082664ms)
|
||||
ℹ tests 147
|
||||
ℹ suites 0
|
||||
ℹ pass 147
|
||||
ℹ fail 0
|
||||
ℹ cancelled 0
|
||||
ℹ skipped 0
|
||||
ℹ todo 0
|
||||
ℹ duration_ms 3802.497134
|
||||
@@ -0,0 +1,75 @@
|
||||
✔ config directory and file path follow MOSAIC_CONFIG (1.109815ms)
|
||||
✔ the fixture business validates and comes back frozen (3.261029ms)
|
||||
✔ two instances may share a definition (1.258841ms)
|
||||
✔ top-level refusals (3.103686ms)
|
||||
✔ arbiters and projects (4.84218ms)
|
||||
✔ role instances (3.396019ms)
|
||||
✔ Vikunja bots (5.509823ms)
|
||||
✔ a role without Vikunja takes no tracker block (1.882958ms)
|
||||
✔ credential references match the definition's services (2.342714ms)
|
||||
✔ launch (10.086901ms)
|
||||
✔ loadBusiness: file checks (1.655292ms)
|
||||
✔ loadBusiness: not a regular file (39.630265ms)
|
||||
✔ loading writes nothing (1.005445ms)
|
||||
✔ names that are Object.prototype properties don't count as declared (2.449512ms)
|
||||
✔ the shipped example refuses as written and validates once filled in (0.500634ms)
|
||||
✔ the role file loads and resolves through loadBusiness (6.252118ms)
|
||||
✔ the shipped file is exactly decision 104's list (0.850397ms)
|
||||
✔ the resolved record matches decision 104, so a widened file fails (1.966502ms)
|
||||
✔ message.send is within role and every other action is gated (1.601645ms)
|
||||
✔ a business grant of a gated action through limits does not widen the role (1.633186ms)
|
||||
✔ a launch block whose by is the chief-of-staff instance is refused (1.82978ms)
|
||||
✔ the instance takes no credential references and no tracker (2.76933ms)
|
||||
✔ usage errors exit 4 (287.611086ms)
|
||||
✔ validate: a good business exits 0 and prints instance digests (63.497887ms)
|
||||
✔ validate: project files (304.734629ms)
|
||||
✔ validate: missing files and a broken system config (230.709653ms)
|
||||
✔ validate: credential reference problems exit 2 and name each one (61.452751ms)
|
||||
✔ validate: a token file inside the repository is refused (64.021321ms)
|
||||
✔ validate: role definitions come from MOSAIC_ROLES_DIR (184.299912ms)
|
||||
✔ resolve: prints one instance's record (177.725921ms)
|
||||
✔ resolve: refusals (363.91945ms)
|
||||
✔ parse: exactly one of file or env, plus the service's date (2.218508ms)
|
||||
✔ check: a good file has no problems (0.653489ms)
|
||||
✔ check never opens the file: a write-only token passes (0.312823ms)
|
||||
✔ check: file problems (0.719856ms)
|
||||
✔ check: token files can't live in the repository or dataRoot, even through a linked directory (0.671479ms)
|
||||
✔ check: dates and environment references (0.324265ms)
|
||||
✔ path and load (1.657775ms)
|
||||
✔ refusals (0.910635ms)
|
||||
✔ systemVars flattens the validated config (2.312078ms)
|
||||
✔ precedence: system, business, project, project role, agent (5.741744ms)
|
||||
✔ limits narrow the definition and never widen it (2.787483ms)
|
||||
✔ role.launch stays within-role only for the instance the launch block names (5.755605ms)
|
||||
✔ limits.authority without role.launch leaves the launcher with no launch block (2.287863ms)
|
||||
✔ limits.authority narrows cross-role actions too (0.880058ms)
|
||||
✔ classify (0.99725ms)
|
||||
✔ the record carries what the broker and launcher need (0.847769ms)
|
||||
✔ digest: key order doesn't matter, any value change does (4.839386ms)
|
||||
✔ refusals (1.863991ms)
|
||||
✔ the four shipped version 2 roles load (2.418585ms)
|
||||
✔ shipped role scopes match addendum B section 2 and the SR runbook (0.735235ms)
|
||||
✔ shipped authority follows the note's table (0.420412ms)
|
||||
✔ version 1 files keep loading with no authority (0.727549ms)
|
||||
✔ the conductor policy isn't a role (0.245007ms)
|
||||
✔ a missing role file is exit 4, a symbolic link too (0.330645ms)
|
||||
✔ version 2 refusals (1.084103ms)
|
||||
✔ authority: closed vocabulary, no gated-only action, no overlap (2.361837ms)
|
||||
✔ credentials: Gitea scopes (1.009208ms)
|
||||
✔ credentials: Vikunja scopes are a group-to-verbs map from the grantable list (1.331503ms)
|
||||
✔ credentials: services (0.544628ms)
|
||||
✔ contract: a non-empty regular Markdown file beside the role file (0.5185ms)
|
||||
✔ every key names known layers and a merge rule (0.681264ms)
|
||||
✔ unknown keys and wrong layers refuse (0.57581ms)
|
||||
✔ types (1.526514ms)
|
||||
✔ merge: defaults, then the most specific layer wins (0.212046ms)
|
||||
✔ merge: limits only narrow, and provenance lists each source (0.263578ms)
|
||||
✔ merge doesn't change its inputs (0.143187ms)
|
||||
ℹ tests 67
|
||||
ℹ suites 0
|
||||
ℹ pass 67
|
||||
ℹ fail 0
|
||||
ℹ cancelled 0
|
||||
ℹ skipped 0
|
||||
ℹ todo 0
|
||||
ℹ duration_ms 1799.397393
|
||||
@@ -0,0 +1,194 @@
|
||||
✔ every screen and overlay fits 120x36, 80x24 and the narrow layout, row for row (1231.482493ms)
|
||||
✔ width counts terminal cells: wide CJK and emoji 2, U+FE0F after a one-cell base 1, marks 0 (64.243185ms)
|
||||
✔ broker text is shown, never interpreted: no raw control character or bidi override reaches the terminal (160.596665ms)
|
||||
✔ NO_COLOR: no escape codes, except reverse video on the selected row (234.07442ms)
|
||||
✔ 1-9 shows the decide flow's own lines and question; only y resolves (162.228232ms)
|
||||
✔ the app shows the same decide text as mosaic decide (122.733407ms)
|
||||
✔ any key but y or Y at the question answers No; nothing is sent (3105.773075ms)
|
||||
✔ below 40x10 the question is hidden, so every key answers it No, y included (Filbert R3) (438.633164ms)
|
||||
✔ under 40 columns a tall terminal shows the too-small screen even where the decide box would fit; y sends nothing (Filbert N10) (1739.092101ms)
|
||||
✔ a hidden decide box names the way out, and the status line under it does not promise y (Filbert N12) (89.890458ms)
|
||||
✔ a throw while answering the question goes to crash, sends nothing and leaves no job pending (104.322708ms)
|
||||
✔ type-ahead never answers the question: a paste, keys typed before it is drawn, keys inside askMs (798.559587ms)
|
||||
✔ quitting at the question answers No and sends nothing, armed or not (192.497464ms)
|
||||
✔ a timer tick reads nothing under an overlay, never answers the question, and keeps a resolved status (115.69507ms)
|
||||
✔ a long question is cut inside the question; the action, target, choice and prompt stay (Dewey B1) (115.046952ms)
|
||||
✔ a decide box too big for the terminal says so, and every key answers it No, y included (Dewey B1, round 3) (1068.634792ms)
|
||||
✔ at every size the decide box shows each line y depends on, or says it is hidden (8106.672333ms)
|
||||
✔ outcome unknown: the CLI's sentence, no retry, then the trail or a re-read (144.549807ms)
|
||||
✔ refused and failed reads keep the last list, marked stale; a refused decide says refused (100.748669ms)
|
||||
✔ help or a detail box cut to fit keeps its last line, Esc included (Dewey) (56.384534ms)
|
||||
✔ keys: views, moving, trail of a selection, the command line, help and quit (217.183474ms)
|
||||
✔ one option: the status line says Press 1, not 1-1 (90.688333ms)
|
||||
✔ no business: the app says so and :business picks one (47.085473ms)
|
||||
✔ mosaic with no argument: exit 3 in an agent run, exit 4 with the usage off a terminal, the app on one (1.782808ms)
|
||||
✔ scripts/mosaic with no argument reaches the app entry; the seat usage it printed before has not drifted (160.371739ms)
|
||||
✔ strings to keep exactly live once, in the decide flow and the transport; the app does not restate them (0.442282ms)
|
||||
✔ decide with --note still asks; a No sends nothing (56.231536ms)
|
||||
✔ the app's business comes from a live bus host only (1.015013ms)
|
||||
✔ runApp leaves the terminal as it found it on q, end of input, SIGTERM, SIGINT, SIGHUP and an error, the first frame's included (7.128704ms)
|
||||
✔ draw is pure: same state, same frame (0.769797ms)
|
||||
✔ inbox lists only decisions routed to the human, with what approving authorizes and how to decide (138.059274ms)
|
||||
✔ decide resolves by id prefix with --yes, and the trail shows it in broker order with the task to follow (122.847093ms)
|
||||
✔ decide refuses without a terminal or --yes, on an unknown option and on a short reference (103.599025ms)
|
||||
✔ decide prints a declining choice as declining (149.692148ms)
|
||||
✔ an unknown outcome is reported once and never resent (110.73959ms)
|
||||
✔ a decision closed before the answer arrives exits 2 and points at its trail (93.118869ms)
|
||||
✔ a prefix that matches two open decisions exits 2 and resolves neither (95.05583ms)
|
||||
✔ without --business a command uses the live host's business, and a stale host.json is not a host (141.219256ms)
|
||||
✔ every human command refuses inside an agent run before it touches the bus (102.85709ms)
|
||||
✔ usage errors exit 4; no business and no host is a usage error (86.627848ms)
|
||||
✔ agents and tasks print through the broker (118.844086ms)
|
||||
✔ notify.json: missing, loose, malformed and extra keys refuse; a binding or null passes (1.502228ms)
|
||||
✔ bootConfig builds the broker's boot message for one business, with no trackers key when no project names one (63.244278ms)
|
||||
✔ trackers come from the tracker.* variables of the one project that names a tracker project (75.021839ms)
|
||||
✔ with two projects, the one that sets tracker.project supplies the tracker and the other is no conflict (30.363921ms)
|
||||
✔ two projects that each name a tracker project refuse, since the boot shape holds one (34.205723ms)
|
||||
✔ a business without tracker.baseUrl gets no trackers entry (30.133129ms)
|
||||
✔ an unknown business and a broken system config refuse with exit 3 (59.84621ms)
|
||||
✔ follow prints an unsolicited message after plain talk returned (186.197775ms)
|
||||
✔ talk --follow continues after reply and prints a later unsolicited message (180.271111ms)
|
||||
✔ inbox lists all unread human messages under decisions, does not deliver or read them (3748.561211ms)
|
||||
✔ follow refuses agent context and bad arguments (576.254299ms)
|
||||
✔ follow Ctrl-C wakes the poll sleep and removes its handler; transport errors also clean up (113.673948ms)
|
||||
✔ plain talk leaves the process SIGINT handlers unchanged (164.211255ms)
|
||||
✔ empty views say so (1.201883ms)
|
||||
✔ the trail keeps the broker's order and names a decision's task without its rows (1.381506ms)
|
||||
✔ tasks print the tracker fields the snapshot carries (0.221282ms)
|
||||
✔ the review loop: the coder pushes and asks, the reviewer fetches and approves, and the task's trail holds each step (681.058946ms)
|
||||
✔ refusals: no action, non-fast-forward, an unknown commit or request, and a push for another task moves only that task's ref (599.728422ms)
|
||||
✔ the size cap refuses a push before any byte (307.740161ms)
|
||||
✔ a fetch over the cap refuses before any byte reaches the reviewer (559.060466ms)
|
||||
✔ the socket refuses a request with no capability, an unknown one, and a bad request line (1116.240787ms)
|
||||
✔ a rigged clone runs nothing: hooks, fsmonitor, a filter, a gitlink and a symlinked .git/config leave no marker (1602.036344ms)
|
||||
✔ an identity with no role, or of another business, refuses not-a-role and moves no ref (392.203363ms)
|
||||
✔ a review of a project the business no longer has refuses unknown-project (367.24176ms)
|
||||
✔ past maxActive connections the socket refuses git-busy, and a hung-up connection frees its slot (652.1578ms)
|
||||
✔ close() answers host-closing to a connection still sending its request line (266.336563ms)
|
||||
✔ a bundle that trickles in refuses body-timeout at its deadline and frees its slot (792.600342ms)
|
||||
✔ a reviewer gone before its bundle was sent is filed fetch-aborted, not as the host's fault (618.072905ms)
|
||||
✔ a reviewer that hangs up the moment it has the whole bundle was served, not aborted (595.38103ms)
|
||||
✔ send: a close after every byte left is served; one that fails a queued write is fetch-aborted (40.680002ms)
|
||||
✔ a bundle with no bytes for bodyIdle refuses body-timeout long before its deadline (587.785535ms)
|
||||
✔ send: a close once the loop is past its last chunk is served, and a failed write there is fetch-aborted (23.027872ms)
|
||||
✔ letGo waits for an answer to leave for at most its cap, then destroys the socket (301.367952ms)
|
||||
✔ a record that fails and can't be undone refuses rollback-failed, which the host logs (452.415059ms)
|
||||
✔ the host boots the broker, binds a launch in process, and the notifier DMs a blocking decision exactly once (923.2611ms)
|
||||
✔ a notifier that dies takes the host down with exit 1, so the unit restarts the pair (230.076068ms)
|
||||
✔ a second host for the same data root refuses with exit 3 while the first runs (201.706442ms)
|
||||
✔ two bus starts racing for one data root leave one host running; the other refuses that a host already runs (566.310693ms)
|
||||
✔ a host takes over a host.lock its dead host left, and puts back a live claim that replaced it meanwhile (3.463068ms)
|
||||
✔ a host.lock that goes between the check and the read is tried again; one that can't be parsed still refuses as unreadable (0.768882ms)
|
||||
✔ a live host's lock stays, however old the file is (0.512946ms)
|
||||
✔ a host.lock left by a host killed with SIGKILL doesn't stop the next bus start (876.929057ms)
|
||||
✔ while a host boots, a reader sees no host: the human commands still ask for --business, stop and status find none (389.52544ms)
|
||||
✔ a host started before host.lock existed, holding only host.json, still refuses a second start, which leaves no lock (34.397395ms)
|
||||
✔ a broker fork that throws leaves no host.lock and no host.json (29.316905ms)
|
||||
✔ a broker send that throws leaves no host.lock and no host.json (76.847964ms)
|
||||
✔ a broker that misses its boot wait leaves no host.lock and no host.json (30.489295ms)
|
||||
✔ a notifier fork that throws stops the broker and leaves no host.lock and no host.json (646.391124ms)
|
||||
✔ a notifier send that throws stops the broker and leaves no host.lock and no host.json (664.954718ms)
|
||||
✔ a boot reply with no reader for the business refuses with exit 3 naming it, before the notifier starts, and leaves no host.lock and no live broker (188.169846ms)
|
||||
✔ a boot reply with no reader for the business refuses with exit 3 naming it, with no notifier, and leaves no host.lock and no live broker (167.391442ms)
|
||||
✔ a host.json that can't be written stops both children and leaves no host.lock (265.102533ms)
|
||||
✔ an error event on the broker before it replies fails the start at once, and leaves no host.lock and no live child (27.610871ms)
|
||||
✔ an error event on the notifier before it replies fails the start at once, and leaves no host.lock and no live child (173.679434ms)
|
||||
✔ an error event on the broker while the notifier starts fails the start once the notifier replies (303.440939ms)
|
||||
✔ an error event on the broker once the host is up stops the host with exit 1, and leaves no host.lock and no host.json (450.799067ms)
|
||||
✔ an error event on the notifier once the host is up stops the host with exit 1, and leaves no host.lock and no host.json (259.565375ms)
|
||||
✔ a broker reply that misses the wait breaks the channel: the late reply answers nothing, later requests refuse, the host exits 1 (1251.141486ms)
|
||||
✔ a broker reply with another request's id, or none, breaks the channel and the host exits 1 (334.138797ms)
|
||||
✔ a broker reply with no request waiting breaks the channel and the host exits 1 (144.739981ms)
|
||||
✔ a notifier that refuses stops the broker and the host refuses with exit 3 (210.375812ms)
|
||||
✔ a notifier that refuses after the broker died still refuses with exit 3, without a send to the dead broker (183.486708ms)
|
||||
✔ a notifier that dies before it replies, after the broker died, still refuses, without a send to the dead broker (152.056606ms)
|
||||
✔ a close send that fails with EPIPE after the notifier refuses still gives the notifier's refusal, exit 3 (259.017438ms)
|
||||
✔ a close send that fails with EPIPE after the notifier dies unanswered still gives the notifier's error (206.666818ms)
|
||||
✔ close() whose stop and close sends fail with EPIPE still finishes, with exit 1 (189.511393ms)
|
||||
✔ watchChildren reports a child that died before it was called, and one that dies later (21.766775ms)
|
||||
✔ bus stop refuses to signal a live pid that is not a bus host (202.229778ms)
|
||||
✔ bus start refuses with exit 3 and the code when the broker refuses to boot; bus status names the lock (229.698187ms)
|
||||
✔ bus start refuses with exit 3 without a notifier config (98.355949ms)
|
||||
✔ bus start runs until bus stop; status reports it while it runs (696.052792ms)
|
||||
✔ bus-service.sh renders the unit and installs it into a given directory (54.321025ms)
|
||||
✔ bus-service.sh pins the claude binary, refuses one the unit can't carry, and renders without one (63.359975ms)
|
||||
✔ bus-service.sh refuses a directory pin and keeps a symlink default unresolved (12.831036ms)
|
||||
✔ bus-service.sh refuses a default claude without the execute bit, or in a PATH dir with a space (8.527185ms)
|
||||
✔ bus start --pm: the PM runs under its own PID namespace, registered, with a manifest (1345.627828ms)
|
||||
✔ the PM launches a coder through its launch tool; the coder answers; refusals name their code (770.051832ms)
|
||||
✔ each launch makes sure the workspace has a clone of the project: cloned, fast-forwarded, or kept and logged (2344.630458ms)
|
||||
✔ a project clone that can't be made refuses checkout-failed, from --pm and from a session, with no run and no clone left (354.684107ms)
|
||||
✔ a runner that stops at once ends its launch with the runner's reason (288.885595ms)
|
||||
✔ a Claude Code launch with no session credential refuses before it starts (176.037471ms)
|
||||
✔ a pi launch needs no session credential: pi logs in through its own ENV_ALLOW variables (254.757418ms)
|
||||
✔ a runner killed from the host ends its launch as killed, not failed (539.805712ms)
|
||||
✔ a missing or failing bwrap refuses sandbox-unavailable before anything starts; there is no unconfined fallback (206.509601ms)
|
||||
✔ a real Claude Code coder's shell can't read a stand-in secret or anything else its sandbox doesn't hold (1375.395745ms)
|
||||
✔ 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 (808.140435ms)
|
||||
✔ 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 (3687.465836ms)
|
||||
✔ a malformed sessions.json refuses the host start with exit 3 and stays as it was (125.448135ms)
|
||||
✔ an over-long launch request is refused at once, not at the 10 s idle timeout (131.909875ms)
|
||||
✔ a launch client that never closes its side doesn't hold the host's close (148.150525ms)
|
||||
✔ a runner that ignores SIGTERM is killed when the host closes (1333.577795ms)
|
||||
✔ claudeVersion asks MOSAIC_CLAUDE_BIN when set, else claude on PATH (4.475106ms)
|
||||
✔ zoned uses the IANA zone across DST (24.530162ms)
|
||||
✔ each open blocking decision is DM'd once, across polls and a restart; non-blocking ones are not (128.595103ms)
|
||||
✔ two blocking decisions get two DMs with different nonces (119.593947ms)
|
||||
✔ the digest nonce differs per business and per day and fits Discord's 25 characters (0.275645ms)
|
||||
✔ a failed DM is journaled, backs off, and is retried until it lands (109.768762ms)
|
||||
✔ five definite refusals stop a DM: one gave-up line, one log line, and a restart keeps the count (107.440724ms)
|
||||
✔ 429s, 5xx-style unknowns and refusals without a status never count toward the limit (124.113696ms)
|
||||
✔ a crash between the fifth refusal and its gave-up line: the next poll appends it and sends nothing (100.407921ms)
|
||||
✔ 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 (157.248372ms)
|
||||
✔ a restart after the second refusal does not send before that refusal's 30 min are up (189.500841ms)
|
||||
✔ the digest goes at 08:00 Chicago once a day, with blocking ones marked as DM'd (107.487359ms)
|
||||
✔ a late start with no digest for the day sends one at once; an empty inbox gets one line (102.984888ms)
|
||||
✔ an inbox read failure is logged and the next poll retries (0.714545ms)
|
||||
✔ no Discord id reaches the journal or the log (144.687409ms)
|
||||
✔ the journal: a torn tail is copied out and truncated, so an append after it reopens cleanly (22.953932ms)
|
||||
✔ the journal: a crash between the copy and the truncate leaves a tail the next open repairs (38.288973ms)
|
||||
✔ the journal: a whole file that is one torn line truncates to empty (24.233195ms)
|
||||
✔ the journal: a malformed complete line refuses and leaves the file and any torn tail alone (2.176637ms)
|
||||
✔ the journal: a loose file mode, a loose directory or a symlinked journal refuses (0.672998ms)
|
||||
✔ the journal: a line with a wrong type refuses with exit 3 and names the field (4.63535ms)
|
||||
✔ the journal: a symlinked directory refuses and says it is a link (0.345614ms)
|
||||
✔ 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.486333ms)
|
||||
✔ the journal: an append after the file was swapped for a symlink refuses and writes nothing through it (0.382938ms)
|
||||
✔ the journal: a directory it cannot write or create refuses with exit 3 and names the path (0.469359ms)
|
||||
✔ digest content stays within Discord's 2000 characters (2.316541ms)
|
||||
✔ runLoop never overlaps ticks and stops after the one in flight (110.800184ms)
|
||||
✔ shared copy: npm ci, pushed reachability, readonly/existing copy, webui delegation (479.922365ms)
|
||||
✔ wrapper preserves Q81 lease and marker, rejects foreign/symlink; check uses effective roots (385.37172ms)
|
||||
✔ bus unit copy/pm pins, install refuses all busy states and failed reads, drop-in refusal (411.967563ms)
|
||||
✔ missing package manifest refuses before npm; npm failure removes the unpublished temp copy (280.434458ms)
|
||||
✔ real pinned install loads bus and Console; same-copy mosaic talk passes proof, other trees refuse (4533.320153ms)
|
||||
✔ bus status reads effective copy/pm/drop-ins without writes; check refuses an overridden ExecStart (519.01742ms)
|
||||
✔ drop-ins in either override field and malformed metadata refuse; uninstall also checks --no-reload (188.526308ms)
|
||||
✔ install never shows a template name on systemd 261; template drop-ins still refuse (193.269833ms)
|
||||
✔ unlisted instance drop-in is found in another unit lookup directory; path discovery errors refuse (172.239224ms)
|
||||
✔ talk --tester sends a [mos test] message as tester from inside an agent and prints the role's reply (160.667337ms)
|
||||
✔ talk --tester refuses text without the [mos test] prefix before any call (96.497708ms)
|
||||
✔ the read verbs take --tester inside an agent, and the trail shows the tester as the sender (103.460593ms)
|
||||
✔ decide, stop and launches take no --tester (123.312262ms)
|
||||
✔ the tester transport runs tester-cli.mjs, and the tester's refusals exit 3 (0.326688ms)
|
||||
✔ with no stand-in transport, --tester runs tester-cli.mjs on the data root's socket (0.513551ms)
|
||||
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 (391.756875ms)
|
||||
✔ the transport writes {business, verb, args} to the child and reads its JSON (48.64863ms)
|
||||
✔ a bus code on stderr becomes the exit code; garbage and timeouts are outcome problems (2169.559517ms)
|
||||
✔ busExit and refuseInsideAgent (0.525827ms)
|
||||
✔ talk sends a REQUEST, prints and reads everything that arrives, and stops at the reply (217.947094ms)
|
||||
✔ talk --wait 0 only sends; no reply within --wait exits 1 and says where it will show (1114.401227ms)
|
||||
✔ a late reply the Console has listed still reaches the next talk (2163.247717ms)
|
||||
✔ talk, stop and launches off/on refuse inside an agent run; bad arguments are usage errors (519.04819ms)
|
||||
✔ launches off and on go to the broker and change the business's launch state (648.344357ms)
|
||||
✔ launches list reads sessions.json and marks a stale entry; stop reports it and refuses a non-runner (123.918929ms)
|
||||
ℹ tests 184
|
||||
ℹ suites 0
|
||||
ℹ pass 184
|
||||
ℹ fail 0
|
||||
ℹ cancelled 0
|
||||
ℹ skipped 0
|
||||
ℹ todo 0
|
||||
ℹ duration_ms 18879.944578
|
||||
@@ -0,0 +1,132 @@
|
||||
✔ explicit request, Seen, ordinary completion and a new request have distinct attention states (6.195499ms)
|
||||
✔ attention convention ignores reasoning/quoted examples and permits leading blank lines (0.326834ms)
|
||||
✔ completed smoke replies and ordinary questions are idle, not human blockers (0.708916ms)
|
||||
✔ only an explicit first-line input request makes a finished reply waiting (0.146241ms)
|
||||
✔ tool activity, user text, errors and unfinished turns override attention text (0.134226ms)
|
||||
✔ STOP access failure is unknown, not absence, under a non-root identity (34.52113ms)
|
||||
✔ connector Task never inherits Discord routing envelopes; ordinary Task still uses user text (2.53345ms)
|
||||
✔ connector discovery keeps only safe identity; rejects modes, mismatches, links and traversal (1.205507ms)
|
||||
✔ canonical owner identity and STOP are independent; no tmux fallback or forged registration (5.33016ms)
|
||||
✔ connector reply refusal precedes forged live tmux registration; ordinary agent still sends (0.522069ms)
|
||||
✔ server rescans connector discovery and refuses HTTP reply without transport (30.278829ms)
|
||||
✔ connector session links and linked directories are not read (0.96739ms)
|
||||
✔ newer live matching launch marks old activity, preserves history/attention/attribution, then clears on new activity (2.167405ms)
|
||||
✔ CLI print uses the relaunch notice instead of old current preview (51.936447ms)
|
||||
✔ connector owner and fixed task never inherit a native relaunch notice (1.736222ms)
|
||||
✔ equality, stale/unknown/offline, mismatched registration and unknown activity do not assert relaunch (0.949107ms)
|
||||
✔ loadConfig: missing file throws ConfigError (2.117023ms)
|
||||
✔ loadConfig: invalid JSON throws ConfigError (0.386193ms)
|
||||
✔ loadConfig: missing dataRoot throws ConfigError (0.293783ms)
|
||||
✔ loadConfig: relative dataRoot throws ConfigError (0.298464ms)
|
||||
✔ loadConfig: valid config returns dataRoot (0.648741ms)
|
||||
✔ findNewestSession: picks the newest by mtime among two files (0.420766ms)
|
||||
✔ findNewestSession: finds files in nested subdirectories (0.265464ms)
|
||||
✔ findNewestSession: returns null for a missing dir (0.110259ms)
|
||||
✔ readSession: extracts fields, collapses/truncates text, counts a truncated final line (0.613028ms)
|
||||
✔ readSession: model and provider follow the latest model_change entry or assistant turn; null when the log names neither; scanAgent carries them (1.121854ms)
|
||||
✔ readSession: lastError carries the assistant errorMessage only when the last assistant turn errored (0.417027ms)
|
||||
✔ findNewestSession/scan: never read sibling auth or secrets next to a sessions dir (1.021001ms)
|
||||
✔ deriveState: full state table (0.165427ms)
|
||||
✔ rule: newest entry is an assistant message with a tool call, after a question-looking text, is working (0.285422ms)
|
||||
✔ rule: newest entry is a tool result with no assistant text after it is working (0.249247ms)
|
||||
✔ rule: a finished ordinary turn is idle, even if it says your move (0.245664ms)
|
||||
✔ task: the first user message of the session, from text blocks (0.227673ms)
|
||||
✔ task: a plain-string user content is accepted, whitespace collapsed and long text capped (0.231867ms)
|
||||
✔ task: no user message in the log means null (shown as unknown), never a guess (0.247462ms)
|
||||
✔ workspace: the live tmux pane path wins; the session cwd is the fallback; neither means null (0.373221ms)
|
||||
✔ activeProject: basename of the nearest .git directory or .git file above the workspace; none means null (0.499675ms)
|
||||
✔ scan: the written record carries task, workspace and activeProject (0.436046ms)
|
||||
✔ registration: overrides task, project and workspace; every source says registration; registered carries the launch fields; the grouping column is untouched (0.521597ms)
|
||||
✔ registration: empty task and null project/workspace leave the derived values in place; registered is still non-null (0.36482ms)
|
||||
✔ registration: a record whose pid is gone is stale; derived values win, sources say derived, registered stays with alive false; a pid the probe cannot decide is not stale; pidAlive itself (0.907995ms)
|
||||
✔ registration: no registration leaves the Gate A fields exactly as before, and registered is null (0.377818ms)
|
||||
✔ loadRegistrations: a missing seatsDir gives empty lists (0.137555ms)
|
||||
✔ loadRegistrations: one good record, one malformed JSON, one with an unknown field; a stray file under seatsDir is ignored (0.655013ms)
|
||||
✔ matchRegistration: matches by sessionsDir, and by realpath through a symlink; sessionsDir null never matches; same seat name with a different sessionsDir does not match (fleet vs repo darkwing) (0.296228ms)
|
||||
✔ scan: writes the registration override to disk; index.json carries registered and registrationErrors (1.106637ms)
|
||||
✔ scan: a relative seatsDir throws ConfigError; an omitted seatsDir behaves as before (0.206015ms)
|
||||
✔ scanAgent: waitingOnYou is true for waiting/error and false otherwise (0.424917ms)
|
||||
✔ scanAgent: ageSeconds is computed from the injected now (0.231648ms)
|
||||
✔ scanAgent: sessionFile null and state idle when sessions dir is empty but alive (0.140228ms)
|
||||
✔ discoverRepoAgents: finds agents with a sessions dir, skips those without, sorted by name (0.29432ms)
|
||||
✔ discoverFleetAgents: finds agents with a sessions dir, sorted by name, fleet tmux fields (0.308406ms)
|
||||
✔ scan: writes per-agent files and index.json, rerun overwrites, no leftover tmp files (0.763247ms)
|
||||
✔ scan: relative boardDir throws ConfigError (0.073854ms)
|
||||
✔ CLI: scan with assume-alive liveness exits 0, prints board summary, writes board files (51.62249ms)
|
||||
✔ CLI: missing config exits 2 with a refused: message (47.254782ms)
|
||||
✔ CLI: unknown command exits 2 (47.683276ms)
|
||||
✔ CLI: unknown --liveness value exits 2 (53.139383ms)
|
||||
✔ panesRunPi: true when any trimmed line equals 'pi' (0.198234ms)
|
||||
✔ panesRunPi: false for bash-only, claude, empty, or node-pi-style lines (0.070223ms)
|
||||
✔ tmuxIsAlive: a pane running pi is alive (0.165441ms)
|
||||
✔ tmuxIsAlive: session exists but pi has exited is not alive (0.071529ms)
|
||||
✔ tmuxIsAlive: no such tmux session is not alive (0.050947ms)
|
||||
✔ tmuxIsAlive: tmux could not be run at all is unknown (null), never assumed alive (0.065116ms)
|
||||
✔ tmuxIsAlive: passes -L <socket> only when a socket is given (0.092728ms)
|
||||
✔ parsePanes: one pane per line, command and optional tab-separated path (0.074127ms)
|
||||
✔ tmuxInspect: reports the path of the pane running pi, not of a shell pane (0.086011ms)
|
||||
✔ tmuxInspect: no pi pane, no session, or no tmux gives no workspace and the matching liveness (0.094134ms)
|
||||
✔ loadSeen: missing file returns {} (0.140351ms)
|
||||
✔ loadSeen: invalid JSON throws ConfigError (0.183867ms)
|
||||
✔ loadSeen: a JSON array throws ConfigError (0.15274ms)
|
||||
✔ loadSeen: a non-string value throws ConfigError (0.166753ms)
|
||||
✔ markSeen: seen true adds the key and writes seen.json mode 0600, no leftover tmp files (0.324423ms)
|
||||
✔ markSeen: seen false deletes the key (0.2706ms)
|
||||
✔ markSeen: missing, empty, or non-string fields throw ConfigError (0.195471ms)
|
||||
✔ markSeen: project containing '/' throws ConfigError (0.122632ms)
|
||||
✔ markSeen: non-boolean seen throws ConfigError (0.113827ms)
|
||||
✔ scanAgent: a seen mark matching the waiting session's lastTimestamp clears waitingOnYou (0.284888ms)
|
||||
✔ scanAgent: a stale mark (agent wrote something newer) is not seen and waitingOnYou is true (0.217497ms)
|
||||
✔ scanAgent: a working session with a matching mark is not seen (marks only apply to waiting/error) (0.210494ms)
|
||||
✔ scanAgent: an error-state session with a matching mark is seen (0.212977ms)
|
||||
✔ scan: index.seen and waitingOnYou reflect seen.json, which scan never rewrites or deletes (0.494617ms)
|
||||
✔ scan: a corrupt seen.json makes scan throw ConfigError (fail closed) (0.179821ms)
|
||||
✔ taskSetBy: a registered task carries the record's setter; a record without the field (pre-#1511) reads unknown; the value is not copied into registered (0.575687ms)
|
||||
✔ taskSetBy: null whenever the task shown is not the registered one: no registration, an empty registered task, a stale registration; the field is always present (0.57754ms)
|
||||
✔ taskSetBy: scan() reads the field from disk through the seat package (bounded there), writes it to the per-agent record and index, and an invalid on-disk value is a registrationError, never a row value (1.299797ms)
|
||||
✔ isLoopbackHost: recognizes loopback hosts (1.113988ms)
|
||||
✔ isLoopbackHost: rejects non-loopback hosts (4.009391ms)
|
||||
✔ startServer: refuses a non-loopback host with ConfigError, never opens a socket (2.713ms)
|
||||
✔ startServer: serves page, healthz, and a rescanning /api/board (30.733085ms)
|
||||
✔ startServer: a seatsDir registration overrides the row and index.registered reflects it (5.064002ms)
|
||||
✔ startServer: /api/board returns 500 JSON with an error field when scan throws (1.935516ms)
|
||||
✔ CLI: serve refuses a non-loopback host with exit 2 and a refused: message (48.697453ms)
|
||||
✔ CLI: serve rejects a non-numeric --port with exit 2 (49.486632ms)
|
||||
✔ CLI: scan still works after the async cli refactor (51.9029ms)
|
||||
✔ CLI: live serve prints its URL and answers /healthz (63.626317ms)
|
||||
✔ page.html: esc() escapes every HTML-significant character (0.596831ms)
|
||||
✔ POST /api/seen marks a row; GET /api/board still shows it seen; seen:false clears it (7.843861ms)
|
||||
✔ POST /api/seen without a JSON content-type returns 400 and does not write a mark (2.863865ms)
|
||||
✔ POST /api/seen with invalid JSON returns 400 (1.456649ms)
|
||||
✔ POST /api/seen with a body over 4096 bytes returns 400 (or resets the connection) and writes no mark (1.856707ms)
|
||||
✔ POST /api/seen with a missing agent returns 400 (1.256404ms)
|
||||
✔ POST /api/board returns 405; PUT /api/seen returns 405 (1.614321ms)
|
||||
✔ CLI: scan --print marks a seen row with 's' and the summary line ends with 'N seen)' (50.687024ms)
|
||||
✔ page.html: seenControl() escapes rec.project/agent/lastActivity, and the POST uses a JSON content-type (0.320452ms)
|
||||
✔ page.html: has a collapsed Seen section that lists seen rows with the shared row builder (0.268318ms)
|
||||
✔ page.html: each project has a Hide seen checkbox (default on) beside Hide offline, with a hidden-count note (0.162938ms)
|
||||
✔ page.html: a project header reads "N of N" only while a checkbox hides rows (0.150981ms)
|
||||
✔ page.html: every row shows Task and Active project, derived or the word unknown, with the workspace in the detail (0.287732ms)
|
||||
✔ page.html: task and active project cells show their source via sourceTag(); the detail has a Registered row via registeredText(); SOURCE_LABEL maps registration to registered; every dynamic value in sourceTag/fromSource/registeredText is escaped (0.574402ms)
|
||||
✔ POST /api/reply: runs agent-send.sh with -s from the registration, -S <host>:control-board, -m text plus the fixed trailer, no -L on the default socket, MOSAIC_TMUX_SOCKET stripped; answers delivered with the exit code and both streams (25.543469ms)
|
||||
✔ POST /api/reply: a registration with a tmux socket adds -L <socket> (25.78583ms)
|
||||
✔ POST /api/reply: a non-zero tool exit is a 200 with delivered false, the exit code and the stderr verbatim (25.855103ms)
|
||||
✔ POST /api/reply: refusals before the tool runs: empty or blank or long text 400, unknown row 404, no registration 409, stale registration 409, no tmux session 409, bad JSON 400; the tool is never called (13.704991ms)
|
||||
✔ POST /api/reply: a missing agent-send.sh is a 500 with the path in the error, not a crash (4.280088ms)
|
||||
✔ replyToRow: DEFAULT_AGENT_SEND is the repository's tools/tmux/agent-send.sh and it is executable (0.15429ms)
|
||||
✔ page.html: the reply box appears only where canReply() holds (live registration with a tmux session), the detail has a Reply row, the submit posts JSON to /api/reply, receipts and drafts survive a refresh, and every receipt value is escaped (0.55889ms)
|
||||
✔ startServer: /api/board carries taskSetBy from a live registration and null for the derived rows (2.266986ms)
|
||||
✔ page.html: the task cell and detail show who set a registered task via setByTag()/setByText(), both escaped, only from rec.taskSetBy; the reply gate does not read it (0.280111ms)
|
||||
✔ Host/Origin guard: GET /api/board and POST /api/reply refuse a foreign Host, a wrong port and a cross-origin Origin with 403 JSON, before any scan or send, and never send CORS headers (7.698637ms)
|
||||
✔ Host/Origin guard: loopback names on this port are accepted, with or without a same-origin Origin (36.18123ms)
|
||||
✔ conversation routes (F16): a foreign Host, a wrong port and a cross-origin Origin get 403 before the reader runs, with no CORS headers (4.31861ms)
|
||||
✔ every refusal code the reader can raise has an HTTP status (0.998162ms)
|
||||
✔ conversation routes: catalogue, first page, next page and follow over HTTP; refusals map to 4xx with their code; nothing is written (46.011786ms)
|
||||
ℹ tests 124
|
||||
ℹ suites 0
|
||||
ℹ pass 124
|
||||
ℹ fail 0
|
||||
ℹ cancelled 0
|
||||
ℹ skipped 0
|
||||
ℹ todo 0
|
||||
ℹ duration_ms 602.596078
|
||||
@@ -0,0 +1,190 @@
|
||||
✔ W1: two processes acquire the same pair at once; exactly one claim (205.819215ms)
|
||||
✔ W1: two writers publish the same revision at once: one wins, the other gets null, the winner's record stays (32.372001ms)
|
||||
✔ W1: a revision name appears only after its bytes are synced; before that, only a temp file exists (18.386741ms)
|
||||
✔ W2: acquire while a claim is reserved or active refuses already-active (339.288548ms)
|
||||
✔ W3: acquire while stopping, uncertain, or stopped without proof refuses unsafe-replacement (404.919632ms)
|
||||
✔ W4: same session with another seat tuple, and the reverse, both refuse; a loser on the seat key closes it no-unit (240.48363ms)
|
||||
✔ W4: a hard link of one session under another seat is the same session: the second controller refuses already-active and launches nothing (186.9191ms)
|
||||
✔ W4: a copy of one session under another seat is the same session: the second controller refuses already-active and launches nothing (196.864865ms)
|
||||
✔ W4: a session header ID that changes after construction refuses target; nothing is claimed or launched (2.48307ms)
|
||||
✔ W5: SIGKILL between every publication barrier of acquire and transition; restart never finds two holders or a lost claim (9622.252061ms)
|
||||
✔ W5: SIGKILL between every publication barrier of release; restart finishes or holds the release (31640.911697ms)
|
||||
✔ W6: controller killed mid-turn while the engine lives; restart is uncertain, no launch, prompts refuse (257.96899ms)
|
||||
✔ W12: a live owner paused with SIGSTOP; a second controller refuses already-active and changes nothing (142.640953ms)
|
||||
✔ W13: crash after the engine spawns, before active; restart finds the live unit: uncertain, no second spawn, force stop only (421.233718ms)
|
||||
✔ W14: crash after reservation, before the spawn marker: stopped with a no-unit observation; the pair is free (265.423666ms)
|
||||
✔ W20: crash after the spawn marker, scope collected; uncertain in both runs, the marker is copied, no launch until a boot proof (337.334525ms)
|
||||
✔ W15: crash between the two keys during release; restart finishes it under the same claim ID (106.740423ms)
|
||||
✔ W7: recorded boot ID differs on the same machine: stopped with a boot proof; open tool calls become uncertain (172.585024ms)
|
||||
✔ W8: resume after a proven stop with the same pins: new claim ID, generation +1, same conversation, branch and leaf (262.558813ms)
|
||||
✔ W9: resume with a changed binary, argv digest, branch or leaf is refused and the claim is unchanged (739.992018ms)
|
||||
✔ W11: the controller writes no session file; only the fake engine's own appends appear (190.401121ms)
|
||||
✔ W16: a highest revision that won't parse holds the pair uncertain; the older stopped revision is not reused (90.321279ms)
|
||||
✔ W17: a claim root copied from another host refuses foreign-host and promotes nothing (73.529552ms)
|
||||
✔ G1: a session path or claim root under .pi/state, ~/.claude, the data root or a registration refuses at construction (4.316599ms)
|
||||
✔ G2: a symlink inside the fixture root to a live session file is refused by the real-path check (1.036505ms)
|
||||
✔ G3: a fixture path swapped for a live path after construction is refused at bind (1.859066ms)
|
||||
✔ no shim from this file's tests is left running for the after hook (4.645284ms)
|
||||
✔ K1: force stop kills a tool child that called setsid; stopped with a verified proof (554.027402ms)
|
||||
✔ K2: K1 on the process-group fallback ends uncertain, never stopped (441.843368ms)
|
||||
✔ K3: SIGTERM acknowledged while a member lives: stopping until the kill phase, never stopped from TERM (548.574617ms)
|
||||
✔ K4: two engines; force stop one; the other survives by independent observation (2782.571386ms)
|
||||
✔ K5: a stop during a tool call leaves the effect uncertain, and it is shown (510.18053ms)
|
||||
✔ K12: a member forking in a loop: the freeze stops it, enumeration is complete, populated 0 after cgroup.kill (579.348707ms)
|
||||
✔ K13: a member writing its pid into another cgroup is refused by the namespace; the kill is complete (489.255421ms)
|
||||
✔ K15: the shim gone, engine/cgroup.events unreadable, or the engine cgroup missing: evidence unavailable, not empty; uncertain (5206.820625ms)
|
||||
✔ K10: controller killed between the TERM and kill phases: restart checks the invocation ID and re-runs from TERM for the same stop (724.816798ms)
|
||||
✔ K11: controller killed after the confirmation is recorded, before TERM: restart checks the invocation ID and re-runs from TERM for the same stop (565.114502ms)
|
||||
✔ K14: a unit with the recorded name but another invocation ID: evidence unavailable, no signals, uncertain (464.270609ms)
|
||||
✔ K6: recover without proof, without confirmation, or with changed pins is refused (797.253846ms)
|
||||
✔ K7: recover after proof, then launch: new claim and execution, generation +1, same leaf; the cancelled prompt is not replayed (394.925545ms)
|
||||
✔ K8: an engine that loads another leaf on resume is refused before admission; it stays claimed until a proven stop (561.664063ms)
|
||||
✔ K9: an interrupt that never settles stays uncertain; force stop stays available; takeover is refused while fenced (3303.978305ms)
|
||||
✔ K16: a claim from another machine ID refuses foreign-host; no boot proof is issued (21.528702ms)
|
||||
✔ K17: two launcher calls with one eligibility record: one launch, the other refuses, no second engine (372.615899ms)
|
||||
✔ K18: the leaf changes after eligibility: launch refused; the reservation stays until released with proof (332.207829ms)
|
||||
✔ K19: a scope launched with only the engine environment still reaches the user manager; the engine sees no other names (83.242482ms)
|
||||
✔ R1: after a proven force stop the controller releases the scope; the shim and the unit are gone (438.645839ms)
|
||||
✔ R2: close releases the scope of a binding proven stopped, once, when the stop's own release hasn't run (504.659705ms)
|
||||
✔ R3: no release after an unavailable stop or an unverified proof: the scope and its shim stay as evidence (1165.40477ms)
|
||||
✔ E1: a natural engine exit with no member left: an engine-exit stop, stopped on a proof naming the engine, and the scope released (439.70909ms)
|
||||
✔ E2: an engine exit with a tool child alive: the stop ends uncertain and nothing is released; a force stop then ends it (2437.999445ms)
|
||||
✔ E3: an engine exit and a force stop take one escalation slot, in either order (1036.870537ms)
|
||||
✔ E4: a shim that doesn't answer: the engine-exit stop misses its deadline, ends uncertain and frees the slot (904.075167ms)
|
||||
✔ E5: an engine exit after a force stop ended uncertain starts no engine-exit stop; the force stop stays current (2563.006204ms)
|
||||
✔ E6: the engine-exit observation refuses a reaped process that isn't the recorded engine: another PID or start ticks (341.853884ms)
|
||||
✔ R5: the harness finds a running shim and reaps it, and never signals a shim outside a mosaic-chat- scope (Filbert N1 and N2 on #1533) (344.511455ms)
|
||||
✔ R6: releaseCohort releases nothing while the engine runs, for another invocation ID, or for a cohort without a scope (126.354933ms)
|
||||
✔ R7: controller killed after the claim is stopped, before the release: the restart releases the scope (2593.881826ms)
|
||||
✔ R8: controller killed between the two keys' stopped revisions: the restart releases the scope (493.413486ms)
|
||||
✔ R9: a release that ended unavailable is tried again on a confirmed recover, not a refused one (472.060895ms)
|
||||
✔ R10: close releases and signals nothing for a stopped binding whose proof no longer verifies (Filbert N3 on #1536) (372.362784ms)
|
||||
✔ R11: close({ killEngine: true }) after a proven stop sends no signal to the recorded engine PID; a restart releases nothing more (2570.174004ms)
|
||||
✔ R12: a restart onto a claim stopped on a boot proof releases nothing, though its scope is listed (231.275643ms)
|
||||
✔ no shim from this file's tests is left running for the after hook (3.744497ms)
|
||||
✔ S1: `/goal x`, with leading spaces or a tab, refuses text-policy at admission; zero engine bytes (203.124358ms)
|
||||
✔ S2: every prefix pinned Pi interprets is refused, from the list the code uses; the rest reach the engine exactly (194.703514ms)
|
||||
✔ S3: `/goal` on the second line is pinned from the source: Pi checks only index 0, so it is admitted and sent exactly (192.425143ms)
|
||||
✔ S4: a `/` left in the composer is cleared when control transfers and returns; the next submit sends only the new text (253.245706ms)
|
||||
✔ S5: an observer terminal gets a paste then Enter, as send-message.sh does: not admitted: controller, nothing sent (214.911859ms)
|
||||
✔ S6: a mediated-shaped registration (no tmux) passed to the board's replyToRow: 409 no tmux session; exec never runs (0.464569ms)
|
||||
✔ S7: ESC, bracketed-paste markers and U+2028/U+2029 travel as one JSON string; the engine receives the exact text in one record (188.79319ms)
|
||||
✔ P3: a Pi confirm, select, input or editor dialog is shown disabled with a reason and never answered (289.879115ms)
|
||||
✔ E1: send, ack, user, toolCall, toolResult, final answer: shown once, no refresh, draft and reading position kept (205.989445ms)
|
||||
✔ E2: U+2028, U+2029 inside JSON strings and CRLF line ends each parse as one record, on the splitter and through the controller (202.618256ms)
|
||||
✔ E3: a multipart final, two blocks, null request correlation and duplicate delivery (209.185733ms)
|
||||
✔ E4: a page read after message_end but before its entry is persisted: marker at the seam, re-read after run-settled, each message once (212.426313ms)
|
||||
✔ E4: a gap or a new epoch also reconciles; nothing is concatenated across a gap (10.55041ms)
|
||||
✔ E5: an unknown native event gives no client event; evidence records its type and bytes; the terminal count goes up (200.61804ms)
|
||||
✔ E6: a tool result delayed across a pause and a reconnect is reconciled without a manual refresh (210.952866ms)
|
||||
✔ E7: the terminal renders the same stream as the library client, as observer and then as controller, and submits only as controller (241.995129ms)
|
||||
✔ terminal: engine control characters are made visible; a lost connection refuses submit (194.174706ms)
|
||||
✔ terminal: outcome unknown is shown as such, with no resend offer, and nothing is resent (0.438155ms)
|
||||
✔ terminal: text after Enter in the same input chunk starts the next message; it never joins the one submitted (0.26033ms)
|
||||
✔ terminal: a paste-start marker split right after its ESC still opens the paste; the Enter inside it never submits (0.300806ms)
|
||||
✔ terminal: Ctrl-T then Enter in one chunk is judged after the takeover, as if typed one key at a time (Filbert F2, #1507) (0.487738ms)
|
||||
✔ terminal: input held behind Ctrl-T waits for that takeover while an earlier action is still pending (Darkwing T1 on #1522) (31.077679ms)
|
||||
✔ terminal: a hold inside held input holds again, and once it is drained later input and Ctrl-C still reach the terminal (Darkwing note 1 on #1522) (0.458094ms)
|
||||
✔ terminal: an action that throws still releases the input held behind it, in order, then rethrows (5.697218ms)
|
||||
✔ terminal: after an action throws, later input still runs; input() puts the error in the status line (Filbert N1 on #1522) (0.378563ms)
|
||||
✔ terminal: input() reports an error when it happens, so it never overwrites a later status; held input's promise doesn't carry the holder's error (Filbert N4 on #1522) (0.329794ms)
|
||||
✔ terminal: invisible and bidi characters are made visible; head, status and notice lines stay one line (0.099536ms)
|
||||
✔ every record these fixtures produced is a valid CHAT-01 record (E5: no record fails the schema) (217.761835ms)
|
||||
✔ H1: two takeovers with the same expected generation: one wins, +1; the other refuses generation (245.133464ms)
|
||||
✔ H2: the old controller's prompt after a takeover commits is refused with zero engine bytes (280.557401ms)
|
||||
✔ H3: a takeover while a prompt holds the dispatch lock: written under the old actor, or refused; never both (795.339489ms)
|
||||
✔ H4: self-takeover is refused (189.422156ms)
|
||||
✔ H9: Interrupt racing a prompt's dispatch: before the write, dispatch-refused and no-turn; after, §3 rules (442.92914ms)
|
||||
✔ H10: Interrupt and force stop together: one stop chain, force stop supersedes (1002.27632ms)
|
||||
✔ H10: an overlap during the pause before the abort: no abort, the stop ends uncertain (212.393585ms)
|
||||
✔ H10: a no-turn Interrupt lifts only its own fence; admission stays closed under force stop, overlap or revocation (572.975309ms)
|
||||
✔ H11: the controller disconnects mid-turn: work continues, the claim is unchanged, control stays put (244.816565ms)
|
||||
✔ H12: an exact retry after reconnecting to the same incarnation returns the same receipt; one dispatch (132.209695ms)
|
||||
✔ H13: a retry with the same request ID and different text is refused (191.719089ms)
|
||||
✔ H14: late stdout from the old engine after a replacement is dropped by incarnation, counted, never rendered (615.726197ms)
|
||||
✔ H15: a revoked connection's command is refused; the revocation fence holds (289.919838ms)
|
||||
✔ H16: a second controller for the same session refuses already-active; the first is untouched (197.196965ms)
|
||||
✔ H10: a second force stop while the first escalation runs refuses fenced; one escalation, and the claim records only the first stop's phases (840.26317ms)
|
||||
✔ a force stop whose fence throws leaves no escalation flag behind, so the next force stop runs (Darkwing F2, #1507) (367.30792ms)
|
||||
✔ H17: a confirmation reused, answered from another connection, or used after the stop changed is refused (738.071151ms)
|
||||
✔ H18: two prompts before any native output: the second refuses busy; one engine write (199.777391ms)
|
||||
✔ H19: the pipe fails mid-line under a large prompt: delivery-unknown transport-unknown, poisoned, no later write (350.169069ms)
|
||||
✔ H19: the link itself never writes again after an unknown outcome, whoever calls it (0.651063ms)
|
||||
✔ H19: the controller dies mid-write of a large line: after restart the outcome is unknown and nothing is resent (647.956369ms)
|
||||
✔ H20: the line is written but the ack is lost when the controller dies: orphan, outcome unknown, nothing resent (540.494175ms)
|
||||
✔ H21: a retry of the exact request with the old token after a crash is stale-incarnation; no second write (751.080198ms)
|
||||
✔ H22: after H21 and a valid recovery, a new request with the new token is admitted (2977.128438ms)
|
||||
✔ H23: requests pending at a restart are not resent; each shows outcome unknown (667.620505ms)
|
||||
✔ no shim from this file's tests is left running for the after hook (4.592152ms)
|
||||
✔ a plain conversation: catalogue row, one page, CHAT-01 records (6.205027ms)
|
||||
✔ native entries map to blocks: tools, thinking, bash, notices, ids that do not fit (1.532961ms)
|
||||
✔ F1: a malformed line is an unavailable part at its position, and reading continues (1.517277ms)
|
||||
✔ F1: a missing parent stops the history with a notice that names the unreadable lines (2.293518ms)
|
||||
✔ F1: an unreadable fork is never merged into another branch's history (1.306943ms)
|
||||
✔ F1: a follow stays on its branch when the next entry's parent is unreadable (1.679802ms)
|
||||
✔ F1: a file whose entries are all unreadable shows a notice per line (0.676197ms)
|
||||
✔ F2: a truncated trailing line marks the view incomplete, not an error (1.191029ms)
|
||||
✔ pagination: 100 parts, then the rest; parts concatenate to the whole branch (3.218504ms)
|
||||
✔ F3: a replaced file (new inode) refuses old cursors with reconcile (4.690781ms)
|
||||
✔ F4: a same-inode rewrite of the prefix refuses old cursors with reconcile (5.385609ms)
|
||||
✔ F5: growth between pages keeps the epoch and the page stops at the pinned length (5.876548ms)
|
||||
✔ F6: unknown, foreign and expired cursors refuse and leave the cursor usable (6.504932ms)
|
||||
✔ F7: a symlinked file and a symlinked directory component are refused, never opened (7.818119ms)
|
||||
✔ F8: a file swapped for a symlink after the catalogue is refused (1.718141ms)
|
||||
✔ F9: registrations never add or redirect a root (1.816106ms)
|
||||
✔ F10: a header cwd naming another project is refused (3.591447ms)
|
||||
✔ F11: parentSession renders with a marker and the parent is never opened (0.645592ms)
|
||||
✔ F12: two leaves: the default leaf is shown and the other branch reads alone (3.462146ms)
|
||||
✔ F12: a follow refuses when an appended duplicate id changes the branch's earlier parts (1.890839ms)
|
||||
✔ F12: a second root (Pi's resetLeaf) starts its own branch (1.211387ms)
|
||||
✔ F13: compaction is a marker in place, then the retained content (0.664611ms)
|
||||
✔ F14: long strings split into fragments and parts, reassemble exactly, and pages respect the byte cap (499.083514ms)
|
||||
✔ fragments never cut a surrogate pair and keep an empty string (4.588133ms)
|
||||
✔ F15: a Claude seat is an unsupported-harness placeholder whose directory is never read (1.547093ms)
|
||||
✔ unknown conversations, empty files and non-Pi files refuse (2.351178ms)
|
||||
✔ an unreadable file or root inside the roots is refused per row, not a failed catalogue (1.096868ms)
|
||||
✔ a seat directory without search permission refuses that root, not the catalogue (2.007112ms)
|
||||
✔ every page and cursor is a valid CHAT-01 record (784.031805ms)
|
||||
✔ the engine pin holds for the installed package (1.637466ms)
|
||||
✔ pinned Pi, sealed and without credentials, answers the controller's commands with the shapes the fake models (247.514352ms)
|
||||
✔ sealed, pinned Pi ignores a trusted project's .pi resources; --approve past the seal would load them, and checkSeal refuses it (Filbert F2 on #1522) (494.936391ms)
|
||||
✔ pinned Pi appends thinking_level_change at start when the branch lacks one, so the leaf moves (K8 then fails closed) (266.249709ms)
|
||||
✔ N25: ordinary Interrupt reconciles; a non-empty queue_update in the window is O5 (474.166916ms)
|
||||
✔ N1: an extension's follow-up queued after the fence is cleared before any abort; O5, Unknown (408.296957ms)
|
||||
✔ N1: a follow-up queued before the fence is O5 at once; the Interrupt refuses fenced (256.753579ms)
|
||||
✔ N2: with abort first, the fake runs the external item (the ordering guard has teeth) (22.126532ms)
|
||||
✔ N3: the fence lands in preflight, preflight errors, no run: failed, No run, uncertain (338.553875ms)
|
||||
✔ N4: the ack arrives after the first abort and a run starts: clear and abort again; Interrupted (196.029177ms)
|
||||
✔ N5: an input handler takes the prompt: ack, no run, delivery-unknown handled-without-run (315.42388ms)
|
||||
✔ N6: an extension queues between clear_queue and abort: O5 and O6, Unknown (469.656561ms)
|
||||
✔ N7: clear_queue times out: no abort, nativeQueue unknown, force stop still ends it (1877.798082ms)
|
||||
✔ N7: clear_queue answers an error: no abort, nativeQueue unknown, the link not poisoned (202.749842ms)
|
||||
✔ N8: an extension prompt starts a run during Mosaic preflight; the losing settle is O3 (299.346412ms)
|
||||
✔ N9: a run that started before the fence and ends aborted: failed interrupted, Interrupted (206.640588ms)
|
||||
✔ N9: decision 34: a run that ends aborted with no stop in progress: aborted-without-stop, uncertain, outcome unknown (223.921136ms)
|
||||
✔ N9: an aborted that lands after the fence but before any abort is written: aborted-without-stop, Unknown (371.48127ms)
|
||||
✔ N10: fake conformance (32.073602ms)
|
||||
✔ N11: the run fails before any user message_start: delivery-unknown ack-without-start, never failed (436.094012ms)
|
||||
✔ N12: input that starts a run after the final empty clear is O1 and not part of the stop's proof (286.615571ms)
|
||||
✔ N13: agent_start with no slot held is O1; a later prompt refuses with zero engine bytes (289.000318ms)
|
||||
✔ N14: the run completes while clear_queue is in flight: finished, Completed first, uncertain (345.18352ms)
|
||||
✔ N14: the run completes after the abort is written, before Pi applies it: finished, never relabelled (370.019226ms)
|
||||
✔ N15: the fence lands in preflight, then an input handler takes it: handled-without-run, No run (370.655151ms)
|
||||
✔ N16: Interrupt with no slot and no run refuses no-turn: no stop, no bytes, admission open (173.675062ms)
|
||||
✔ N17: the run fails on its own during the exchange: failed, Failed on its own (323.82895ms)
|
||||
✔ N18: no final assistant message_end, or a lost line: working stays working; before working, transport-unknown (685.800303ms)
|
||||
✔ N19: a losing extension prompt settles inside the Mosaic run before its user message: O3, run-overlap (576.257136ms)
|
||||
✔ N20: an extension triggerTurn during Mosaic preflight starts first; while streaming it queues with no signal (567.099725ms)
|
||||
✔ N21: a losing settle after the receipt settled finished is O2; the receipt stays finished (229.995872ms)
|
||||
✔ N22: an agent-level custom message is dropped by the clear with no signal; evidence names the seal (203.345453ms)
|
||||
✔ N23: a nextTurn message survives clear and abort and attaches to the next prompt, with no signal (185.082941ms)
|
||||
✔ N24: the seal is an allow-list: --extension, a missing --no-* flag, a second --mode or --session, a session or output flag, or a stray word refuses unsealed-engine; no engine starts (198.10033ms)
|
||||
✔ N24b: the seal covers the engine command and environment: config can't name either, the env is built from names, and a mutated command is refused at bind (170.744018ms)
|
||||
ℹ tests 182
|
||||
ℹ suites 0
|
||||
ℹ pass 182
|
||||
ℹ fail 0
|
||||
ℹ cancelled 0
|
||||
ℹ skipped 0
|
||||
ℹ todo 0
|
||||
ℹ duration_ms 47286.327145
|
||||
@@ -0,0 +1,186 @@
|
||||
✔ approvals: a request is validated before anything is posted; the rendering shows names and never ids (3.013155ms)
|
||||
✔ approvals: the ledger is appended and folded into open requests with bind and approval states (1.433547ms)
|
||||
✔ approvals: a reply approves only when it points at a request, says exactly approve, and comes from a listed approver once (0.708093ms)
|
||||
✔ approvals: a button approves only on its own request message with the matching custom id (0.603943ms)
|
||||
✔ approvals flow: a turn that opened a request posts the message with the button, records it, binds it, and both approvers approve (15.5637ms)
|
||||
✔ approvals flow: a non-approver, a repeat, a wrong custom id and a service refusal each get their fixed line and a drop entry (6.958834ms)
|
||||
✔ approvals flow: an invalid request from the model, a refused post, and no api client are recorded and post nothing (6.485611ms)
|
||||
✔ approvals flow: start retries a bind and an approval left as unknown, under their original keys (2.345759ms)
|
||||
✔ authorize: open channel, listed user (1.745605ms)
|
||||
✔ authorize: wrong guild (0.187124ms)
|
||||
✔ authorize: no guild (DM) (0.187291ms)
|
||||
✔ authorize: unlisted channel (0.191872ms)
|
||||
✔ authorize: unknown channel, no info (0.173394ms)
|
||||
✔ authorize: thread of listed parent (0.171756ms)
|
||||
✔ authorize: thread of unlisted parent (0.144353ms)
|
||||
✔ authorize: text channel that is not a thread and not listed (0.129792ms)
|
||||
✔ authorize: unlisted user (0.849819ms)
|
||||
✔ authorize: no author (0.248301ms)
|
||||
✔ authorize: bot author (listed id, bot flag) (0.143516ms)
|
||||
✔ authorize: system author (0.110899ms)
|
||||
✔ authorize: the bot itself (0.086101ms)
|
||||
✔ authorize: webhook (0.084523ms)
|
||||
✔ authorize: mention channel without mention (0.617362ms)
|
||||
✔ authorize: mention channel with bot mention (0.131209ms)
|
||||
✔ authorize: mention channel with @everyone only (0.087451ms)
|
||||
✔ authorize: mention channel mentioning someone else (0.079939ms)
|
||||
✔ authorize: mention channel, content says @bot but mentions empty (0.094929ms)
|
||||
✔ authorize: private thread under mention channel, mentioned (0.077887ms)
|
||||
✔ authorize: private thread under mention channel, not mentioned (0.062972ms)
|
||||
✔ authorize: thread in another guild per channel info (0.068051ms)
|
||||
✔ authorize: not an object (0.060053ms)
|
||||
✔ authorize: no id (0.060444ms)
|
||||
✔ authorize: oversize content is accepted and flagged (0.066489ms)
|
||||
✔ authorize: exactly the limit is not oversize (0.065594ms)
|
||||
✔ authorize: a user's channel allowlist drops them outside it, threads count as the parent, others are unaffected (1.935253ms)
|
||||
✔ authorize: order puts wrong guild before user, and user before channel (no channel lookup for strangers) (0.144748ms)
|
||||
✔ binding: a complete binding validates and is frozen (2.964064ms)
|
||||
✔ binding: unknown key, missing field, wrong type refuse with exit 2 (1.24653ms)
|
||||
✔ binding: empty allowlists refuse (0.400084ms)
|
||||
✔ binding: a user's channel allowlist must be non-empty, listed and unique; absent means every listed channel (1.291908ms)
|
||||
✔ reloadDiff: reloadable keys are summarised by id; every fixed key refuses with exit 2 (1.926171ms)
|
||||
✔ binding: dmRecipient is optional, a snowflake, one of the listed users, and a fixed key (0.832175ms)
|
||||
✔ binding: file must be 0600, regular, not a symlink (1.50918ms)
|
||||
✔ binding: token file mode, symlink, emptiness and shape are checked; token never appears in errors (7.606324ms)
|
||||
✔ cli: check refuses a non-0600 token file with exit 2 before any network use (82.667519ms)
|
||||
✔ context files: absolute paths, traversal, symlinks and out-of-repo targets refuse; in-repo files resolve (2.116672ms)
|
||||
✔ cli: check refuses a missing context file and a missing binding with exit 2; usage is exit 4 (280.781485ms)
|
||||
✔ cli: reload validates the file first (exit 2), then needs a live owner (exit 1); usage is exit 4 (194.26056ms)
|
||||
✔ cli: run refuses when STOP is present, before any network use (118.87544ms)
|
||||
✔ binding: tools is optional, validated strictly, a fixed key for reload, and its roots are resolved against the data root (1.686358ms)
|
||||
✔ binding: a git key is validated at load and reaches the extension whole, and only on a writable root (1.075203ms)
|
||||
✔ delivery: an accepted message is in the inbox before the turn, the reply is chunked with one nonce per chunk, and the turn record is write-once (14.748581ms)
|
||||
✔ delivery: refused and unknown outcomes are journaled; a later chunk is not sent after a failure (14.775206ms)
|
||||
✔ delivery: restart with an unknown entry re-sends the same nonce once and reconciles before accepting traffic (1.343111ms)
|
||||
✔ delivery: an unknown entry older than the dedupe window is marked refused, not re-sent; a still-unknown one refuses start (1.02229ms)
|
||||
✔ delivery: repeated unknown reconciliations never refresh the dedupe window; the original intent time decides (1.164814ms)
|
||||
✔ turn: a failed engine turn posts the fixed line, never model output, and writes a failed record (3.038663ms)
|
||||
✔ turn: a second message during a turn is held by the engine, both get their own reply and record (32.620638ms)
|
||||
✔ turn: a thread under a listed channel is answered in the thread; an unknown thread is looked up once (1.39232ms)
|
||||
✔ drop: an unlisted user gets silence and one drop line; no inbox entry, no REST call, no engine call (0.77145ms)
|
||||
✔ drop: an oversize message is accepted into the inbox, answered with the fixed line and journaled as a drop (0.795812ms)
|
||||
✔ restart: an inbox with three ids and a replay of the same three produces zero turns (40.170197ms)
|
||||
✔ stop: STOP present refuses start; STOP written while running refuses new turns and the current one finishes (31.61857ms)
|
||||
✔ ceiling: the ceiling plus one is refused and journaled; one fixed line per UTC day; a new day accepts again (5.166258ms)
|
||||
✔ ceiling: a burst arriving while turns are still running cannot queue past the ceiling (3.785564ms)
|
||||
✔ ceiling: a turn interrupted by a crash still counts after restart; admissions are durable (2.218992ms)
|
||||
✔ ceiling: the daily notice survives a same-day restart; one delivery attempt in total, even when the first attempt crashed mid-flight (3.912562ms)
|
||||
✔ duplicate: the same event delivered twice while the thread lookup is held yields one prompt, one admission and one reply (2.03929ms)
|
||||
✔ journal: no token-shaped string and no model output on the drop path reaches disk (0.397779ms)
|
||||
✔ receipt: an admitted message gets one eyes reaction on the inbound message; drops and refusals get none; a failed reaction is recorded and does not fail the turn (1.824773ms)
|
||||
✔ receipt: Discord refusing the reaction leaves the turn intact and records ok false (1.740353ms)
|
||||
✔ reload: a new user is silent before and answered after; a removed channel goes silent; a lower ceiling applies at once (3.510435ms)
|
||||
✔ reload: a fixed key refuses with exit 2 and the old binding stays in force (1.945578ms)
|
||||
✔ tools: with a tools binding the turn record lists every read and its outcome; without one the field is null (2.599832ms)
|
||||
✔ context: the Discord block names the server, channels and modes, and states the rules from Q15 and Q16 (2.356213ms)
|
||||
✔ context: with tools the block names the roots, keeps file content as data, and says to state refusals plainly (0.724806ms)
|
||||
✔ context: a writable root adds the write rules and says a write is real only once Jason commits (1.865269ms)
|
||||
✔ context: the envelope is one bracketed line then the text; names cannot break the line (1.20925ms)
|
||||
✔ context: a git root swaps the terminal-commit line for the git verbs, and a vault root adds the id protocol (1.337982ms)
|
||||
✔ context: assembleContext concatenates files in launcher format and appends the block; sha256 is stable (1.552623ms)
|
||||
✔ context: splitReply keeps paragraphs together under the limit and splits long ones at lines, spaces, then hard (0.643929ms)
|
||||
✔ engine: buildPiArgs carries the fixed flags, engine settings, session dir and prompt file (2.792939ms)
|
||||
✔ engine: with tools, buildPiArgs turns pi's own tools off, loads the extension explicitly and allowlists exactly our three (0.455587ms)
|
||||
✔ engine: a run with tool turns settles once, on the answer, with every tool call in the result (55.375088ms)
|
||||
✔ engine: a run that ends on a tool-only turn fails the prompt as empty; a retried run settles on the real end (27.187935ms)
|
||||
✔ engine: one prompt, one turn, text and usage come back (24.539744ms)
|
||||
✔ engine: a prompt while streaming is held until pi settles, then sent as its own run, and answered in order (329.933322ms)
|
||||
✔ engine: a held prompt that times out before pi settles fails on its own and is never sent (238.483542ms)
|
||||
✔ engine: timeout sends abort and fails only that turn; the process stays (103.638233ms)
|
||||
✔ engine: tool events from a run that outlived its timeout never land in the next prompt's record (225.497573ms)
|
||||
✔ engine: a prompt after a turn that timed out before its agent_start waits for pi to settle instead of being refused (125.264489ms)
|
||||
✔ engine: when pi has not started a timed-out turn by the end of the abort grace, the engine stops pi and fails held prompts (212.93661ms)
|
||||
✔ engine: a timed-out turn pi starts only after the grace never answers a later prompt (615.53993ms)
|
||||
✔ engine: late events of a run past its grace, before pi exits, answer nothing and nothing more is sent (early prompt response) (1.359577ms)
|
||||
✔ engine: late events of a run past its grace, before pi exits, answer nothing and nothing more is sent (late prompt response) (0.5735ms)
|
||||
✔ engine: a timed-out run pi did start outlives the grace; the next prompt goes out when it ends (425.411973ms)
|
||||
✔ engine: a malformed JSONL line fails the turn, not the process (23.201624ms)
|
||||
✔ engine: a turn that ends in error rejects with the error code; process exit fails pending turns (46.786213ms)
|
||||
✔ gateway: hello -> identify with intents, ready, heartbeat with jitter, ack (4.695407ms)
|
||||
✔ gateway: missed ack closes the socket and resumes with the last sequence (1.77047ms)
|
||||
✔ gateway: op 7 reconnect resumes; op 9 non-resumable re-identifies (0.488868ms)
|
||||
✔ gateway: op 9 resumable resumes (0.379159ms)
|
||||
✔ gateway: close 4014 is fatal, reports the missing intent, never reconnects (0.831161ms)
|
||||
✔ gateway: 4004 and 4013 are fatal too; 1006 reconnects with identify when no session (0.542973ms)
|
||||
✔ gateway: close() is final and unparseable frames are ignored (0.416172ms)
|
||||
✔ git: config validation is strict, needs write: true, a work tree and a private token file (48.702176ms)
|
||||
✔ git: the child environment drops every host git config, names one helper, and carries the token path only for origin (32.472022ms)
|
||||
✔ git: status reports the branch, ahead/behind and changed paths, and refuses off the named branch or mid-merge (56.726095ms)
|
||||
✔ git: parseStatus reads porcelain v2 including renames and conflicts (0.252599ms)
|
||||
✔ git: a commit stages exactly the named files, carries the seat author and the requester trailer, and pushes at once (72.194137ms)
|
||||
✔ git: commit refusals: message, paths, requester, nothing to commit, and an index that already holds other work (74.041603ms)
|
||||
✔ git: a commit whose push fails is still a commit, says so, and the next commit's push carries both (D6) (72.706339ms)
|
||||
✔ git: pull is fast-forward only; a diverged origin or dirty local files refuse with nothing merged (188.471426ms)
|
||||
✔ git: push pushes the named branch only and reports up to date (63.924762ms)
|
||||
✔ git: no token value or token path ever reaches a git argument list; outputs are masked and capped (79.182527ms)
|
||||
✔ git: the credential helper answers get over https from a private file and nothing else (182.74038ms)
|
||||
✔ git: the vault protocol validates before a commit, honours another owner's lock, reserves ids, and locks around writes (697.837676ms)
|
||||
✔ lock: the claim is exclusive; a second start against a live owner refuses (4.932758ms)
|
||||
✔ lock: a stale lock (dead owner, reused pid, or record without start) refuses run and is never signaled; only unlock clears it (67.175369ms)
|
||||
✔ lock: an incomplete claim (directory without owner record) is busy and refuses run; unlock clears it (0.772323ms)
|
||||
✔ lock: an owner record that exists but cannot be read is invalid: never signaled, never removed, never claimed over (2.442938ms)
|
||||
✔ lock: legacy upgrade; a live connector holding a {pid, start} record is unknown, unlock refuses and nothing changes; after it exits, unlock clears it (37.406358ms)
|
||||
✔ lock: a live pid whose record carries a malformed or noncanonical start or boot string is unknown, not a mismatch; nothing signals, removes, or claims over it (269.634422ms)
|
||||
✔ lock: identity syntax; only canonical unsigned decimal start ticks and lowercase boot uuids are identities (0.413154ms)
|
||||
✔ lock: a process whose start marker or boot id cannot be read refuses to claim (21.335101ms)
|
||||
✔ lock: a live pid whose identity cannot be read right now is unknown: never signaled, never removed, never claimed over (0.91415ms)
|
||||
✔ lock: four processes racing for the same binding; exactly one claims it and the others refuse (35.201232ms)
|
||||
✔ lock: stale handoff; concurrent starts over a stale lock all refuse, nothing reclaims, one unlock then exactly one live owner (144.598419ms)
|
||||
✔ lock: four-party schedule; claims landing inside an unlock's gap never survive, one unlock leaves no owner and no residue (73.873852ms)
|
||||
✔ notices: a kind is recorded per UTC day and found again (0.475706ms)
|
||||
✔ notify: the DM channel opens once, every send carries the nonce, and only the message id comes back (4.857804ms)
|
||||
✔ notify: refusals and unknowns surface as RestOutcome without ids; a refused send reopens the channel next time (2.14282ms)
|
||||
✔ notify: no dmRecipient, a non-0600 binding or token, or a missing binding refuse before any network use (1.559742ms)
|
||||
✔ recover: nothing to do is clean; a lock whose owner is gone or that has no record is cleared and STOP ends up absent (39.602885ms)
|
||||
✔ recover: an operator STOP refuses with exit 3 and is never removed, whatever the lock says (28.206791ms)
|
||||
✔ recover: a brake written during the unlock wins; STOP stays with both lines and the start is refused (24.733994ms)
|
||||
✔ recover: a held binding refuses with exit 3 and writes no STOP: live owner, alive pid without verifiable identity, unreadable record (59.077796ms)
|
||||
✔ cli: recover exits 0 when ready, 3 behind a brake or a held binding, and run's own STOP refusal is 3 (604.983351ms)
|
||||
✔ rest: createMessage sends nonce, enforce_nonce, empty allowed_mentions and a soft reply reference (2.344761ms)
|
||||
✔ rest: 429 waits retry_after and retries; 4xx is refused; 5xx and socket errors are unknown (2.310381ms)
|
||||
✔ rest: content and nonce limits are enforced locally; typing never throws (0.572479ms)
|
||||
✔ rest: react PUTs the encoded emoji on the inbound message for @me; 2xx is true, anything else is false and never throws (0.697562ms)
|
||||
✔ rest: createDm posts the recipient and resolves the channel id; 4xx refused, 5xx unknown, bad id never sent (0.773912ms)
|
||||
✔ setspark config: a bare https or loopback origin, a private key file, a principal (4.229812ms)
|
||||
✔ setspark config: reaches the tools config and the binding as a fixed key (1.942229ms)
|
||||
✔ setspark config: the binding's key survives resolveToolRoots and the engine's JSON hand-off to the extension (1.014011ms)
|
||||
✔ setspark config: approvers come from the binding's users, never from the binding's setspark key (1.369651ms)
|
||||
✔ setspark verbs: required_approvers go out as discord ids from names and come back as names (17.265099ms)
|
||||
✔ setspark verbs: no Discord user id reaches tool text, whatever shape the service returns it in (6.148941ms)
|
||||
✔ setspark contract: a decision made with names opens a request the connector accepts; names stored by an old record still refuse (3.843283ms)
|
||||
✔ setspark keys: read per call, one printable token per file, rotation without a restart (2.127948ms)
|
||||
✔ setspark idempotency keys: principal, turn id, call index; connector keys name a step (0.358399ms)
|
||||
✔ setspark http core: json in and out, bearer header, idempotency header, fixed user agent, no key anywhere else (1.603997ms)
|
||||
✔ setspark http core: error bodies become fixed refusals with code and the 409 fields; server text is data, cut (1007.921713ms)
|
||||
✔ setspark verbs: a setspark key enables the eight verbs and no counters (0.458234ms)
|
||||
✔ setspark verbs: writes carry the turn's key and the asserted requester, reads carry no key, and the api key never appears in text or details (5.900052ms)
|
||||
✔ setspark verbs: no turn refuses every write before any request; bad arguments refuse before any request; reads still work (1.258292ms)
|
||||
✔ setspark verbs: renderRecord caps long output and hides the accepted snapshot (0.18541ms)
|
||||
✔ setspark api: bind, add_approval (button and reply) and get use integer request ids and the connector's keys (2.203684ms)
|
||||
✔ tools: config refuses a missing, symlinked, dotted, non-directory or duplicate root and bad limits (3.637165ms)
|
||||
✔ tools: every escape is refused with a fixed reason and nothing outside the root is read (4.470581ms)
|
||||
✔ tools: happy paths list, read a window, and search case-insensitively; dotfiles and symlinks never appear (5.397081ms)
|
||||
✔ tools: the tool set renders text for the model, records details for the journal, and enforces the per-run budget (3.713878ms)
|
||||
✔ tools: listing and search caps hold (9.506352ms)
|
||||
✔ tools: credential shapes are caught; ordinary prose and ids are not (0.803287ms)
|
||||
✔ tools: the read uses the checked file itself; a symlink, a swapped file, a FIFO, a grown file or a hard link at read time is refused (5.160427ms)
|
||||
✔ tools: an unreadable file under the root is skipped by search and refused by read (1.127857ms)
|
||||
✔ tools: config accepts write: true only as a boolean, and enables the write tools only then (1.112137ms)
|
||||
✔ tools: every write outside the fence is refused before any byte lands, and no temp file remains (4.629534ms)
|
||||
✔ tools: write_file leaves the exact bytes, edit_file replaces one exact match, and the set renders the change as uncommitted (1.832831ms)
|
||||
✔ tools: a target that changed between the check and the rename is refused and the temp file is removed (1.493292ms)
|
||||
✔ web: config takes an https or loopback-http SearXNG base url and a bounded fetch cap (3.599885ms)
|
||||
✔ web: address rules refuse every private, loopback, link-local, mapped and multicast form (1.954827ms)
|
||||
✔ web: web_fetch refuses bad urls, private hosts, rebinding names, non-https redirects, too many hops, error status, non-text bodies, and times out (1021.404401ms)
|
||||
✔ web: web_fetch returns html as text with the title, follows an https redirect, keeps plain text and json, and cuts at the cap (4.213221ms)
|
||||
✔ web: html to text drops scripts, styles and comments, decodes entities and keeps block breaks (0.25189ms)
|
||||
✔ web: web_search asks the instance for json, returns at most ten clean results, and refuses a bad query, a down instance or an unusable answer (2.541241ms)
|
||||
✔ web: the tool set enables the web tools only with a web key, counts them in the budget, and records url, status and hits (2.180497ms)
|
||||
ℹ tests 178
|
||||
ℹ suites 0
|
||||
ℹ pass 178
|
||||
ℹ fail 0
|
||||
ℹ cancelled 0
|
||||
ℹ skipped 0
|
||||
ℹ todo 0
|
||||
ℹ duration_ms 2585.336653
|
||||
@@ -0,0 +1,88 @@
|
||||
✔ sessionModel: agent vars win, then the system's execution settings (12.198286ms)
|
||||
✔ a pi bundle: prompt, policy, tools and manifest, 0600 in a 0700 directory (5.9262ms)
|
||||
✔ a claude-code bundle adds the wrapped gate hook and the MCP config (3.233033ms)
|
||||
✔ the prompt's built-in tools line is the harness's own names, as the manifest lists them (12.299329ms)
|
||||
✔ the prompt names the project clones in the workspace, and none when there are none (5.575526ms)
|
||||
✔ the prompt explains the review loop to a role with push_working, and reviews to one with fetch_review (6.130564ms)
|
||||
✔ a bundle is written once: an existing file refuses (1.900136ms)
|
||||
✔ a path with a single quote can't go into the hook command (1.385972ms)
|
||||
✔ allow exits 0, a deny exits 2 with the reason on stderr (119.245875ms)
|
||||
✔ a missing or wrong policy, or a bad event, exits 2 (87.771877ms)
|
||||
✔ the bundle's wrapped command: a missing gate or node still blocks (1084.247453ms)
|
||||
✔ claude: typed tools through MCP, the hook blocks, builtins outside --tools don't exist (726.496982ms)
|
||||
✔ claude: the hook alone blocks a path outside the workspace (447.913555ms)
|
||||
✔ claude: a second turn resumes the first turn's session (753.0938ms)
|
||||
✔ claude adapter: --restricted is always passed (5.510195ms)
|
||||
✔ claude adapter: MOSAIC_CLAUDE_BIN wins over PATH and must be an absolute executable (16.126711ms)
|
||||
✔ claude adapter: a directory pin exits 2 before it runs; a pin with a space runs (5.825807ms)
|
||||
✔ claude: CLAUDE.md files and auto-memory don't reach the model; without --restricted they do (699.801578ms)
|
||||
✔ claude: a missing hook or MCP file refuses before claude starts (7.334068ms)
|
||||
✔ pi: policy tools and typed tools pass, anything else is blocked (3.190218ms)
|
||||
✔ claude: builtins map from pi names, typed tools need the mcp prefix (0.639109ms)
|
||||
✔ file tool paths must resolve inside the workspace (1.269192ms)
|
||||
✔ pi's own path normalisation can't be used to step out (0.950328ms)
|
||||
✔ a symlink inside the workspace that points out is outside (0.90052ms)
|
||||
✔ a dangling symlink is refused at any depth, in both harnesses (3.05701ms)
|
||||
✔ read is checked under every spelling pi's read would open, in both harnesses (19.847482ms)
|
||||
✔ other spellings cover directories, dangling links and pi's cwd (1.297455ms)
|
||||
✔ a relative path climbs from the workspace's real path, in both harnesses (3.96552ms)
|
||||
✔ claude path fields per tool (0.62227ms)
|
||||
✔ glob patterns stay inside the workspace (0.580081ms)
|
||||
✔ a path that can't be checked is blocked (0.378827ms)
|
||||
✔ fetch_review's have lists the working refs before origin's, and no more than HAVE_MAX (210.844167ms)
|
||||
✔ fetch_review's have orders the working refs newest first within each clone, one clone after another (136.850352ms)
|
||||
✔ a runner git past its time: SIGTERM reaches its process group, and SIGKILL follows after the grace period (1809.403034ms)
|
||||
✔ once a signalled runner git has closed, its group gets SIGKILL, so a process that ignored SIGTERM dies too (304.01072ms)
|
||||
✔ initialize, ping and tools/list (50.777612ms)
|
||||
✔ tools/call goes through the tool socket; a refusal is an isError result (35.56347ms)
|
||||
✔ replies are compact JSON; over the cap a tool error names the size and the cap (35.753948ms)
|
||||
✔ unknown tools and methods are JSON-RPC errors and never reach the socket (28.550769ms)
|
||||
✔ a missing argument is a usage error (24.967442ms)
|
||||
✔ pi: typed tools reach the socket, the gate blocks, agent_end writes the marker (372.567727ms)
|
||||
✔ pi: a reply over the cap is a tool error naming the size and the cap; list_tasks asks for open tasks (308.186932ms)
|
||||
✔ pi: a write through a dangling symlink is blocked, and nothing appears outside (320.494239ms)
|
||||
✔ pi: a read is refused when pi would open another spelling outside (322.127336ms)
|
||||
✔ pi: a relative path climbs from the real path of a workspace behind a symlink (310.997252ms)
|
||||
✔ pi: a relative path climbs from the real path of a dataRoot behind a symlink (319.773223ms)
|
||||
✔ pi: a missing extension refuses before any model call (5.534674ms)
|
||||
✔ pi: an extension without its configuration fails pi's start (246.338405ms)
|
||||
✔ founderCheck: founder variables, then a needed service without a usable token (1.396986ms)
|
||||
✔ turnRequest names the sender, class, reply and decision (0.285332ms)
|
||||
✔ turnRequest says a tester's message is a test and never Jason's instruction (0.292195ms)
|
||||
✔ turnRequest shows the message's own sent time, a day old as well as new (0.188677ms)
|
||||
✔ stateBlock names the launch state with its time and event, and the open tasks with the cap (0.284001ms)
|
||||
✔ a message becomes a turn, the answer goes back as a RESULT, SIGTERM releases and exits 0 (262.059437ms)
|
||||
✔ launches revoked and then restored: the turn's block reads allowed and gives the restore time (262.028564ms)
|
||||
✔ a task closed on the tracker is missing from the next turn's open list (219.310707ms)
|
||||
✔ a message read a day after it was sent shows the time it was sent (174.757393ms)
|
||||
✔ a failed state read fails the turn: no adapter runs, the sender hears why, the message is read (123.297687ms)
|
||||
✔ a SIGTERM before the claim stops the runner with exit 0 and no claim (93.947461ms)
|
||||
✔ typed tools carry the runner's capability; launch goes to the host's launch socket (415.185392ms)
|
||||
✔ a RESULT gets no automatic reply; failed turns reply with the reason (1291.001854ms)
|
||||
✔ SIGTERM during a turn kills the turn's process group and still exits 0 (153.656166ms)
|
||||
✔ founder credentials stop before the claim (20) (171.491672ms)
|
||||
✔ a refused claim exits 21; an ended run's capability exits 22 (191.085969ms)
|
||||
✔ the launch ending under a running session exits 22 (144.433177ms)
|
||||
✔ a broker that stays unreachable exits 23 after brokerRetries polls (246.032494ms)
|
||||
✔ a broker that is down at the claim exits 23, not 21 (94.490638ms)
|
||||
✔ no capability, or a malformed one, on stdin exits 2 (183.289581ms)
|
||||
✔ a missing or malformed policy exits 2 before the claim (149.366585ms)
|
||||
✔ the PM gets launch, its task verbs and the reads (7.62434ms)
|
||||
✔ list_requirements reads the requirement ids, for a task.create holder with a tracker (3.754964ms)
|
||||
✔ a coder gets no launch, no resolve_decision, and no task tools without a tracker (2.214337ms)
|
||||
✔ a coder with a tracker gets its own task tools and no task_reassign (4.409604ms)
|
||||
✔ launch only when the business's launch block names the instance as launcher (1.873535ms)
|
||||
✔ an action outside the instance's authority has no tool (1.992731ms)
|
||||
✔ the review loop: push_working and request_review for the coder, fetch_review and review_verdict for the reviewer, none for the PM (4.663246ms)
|
||||
✔ callTool: one JSON line out, the result back, a refusal rejects (7.938596ms)
|
||||
✔ list_tasks takes the broker's filters and pages; get_task takes a task_ref (1.989143ms)
|
||||
✔ callTool sends list_tasks done false unless the session names done; other tools as given (2.532793ms)
|
||||
✔ replyText: compact JSON up to the cap, an error naming the size and the cap over it (0.782873ms)
|
||||
ℹ tests 80
|
||||
ℹ suites 0
|
||||
ℹ pass 80
|
||||
ℹ fail 0
|
||||
ℹ cancelled 0
|
||||
ℹ skipped 0
|
||||
ℹ todo 0
|
||||
ℹ duration_ms 11124.299046
|
||||
@@ -0,0 +1,86 @@
|
||||
✔ a raw token file, with or without one trailing newline, reaches curl only through the config stream (111.645166ms)
|
||||
✔ the raw path accepts nothing else, and refuses before curl runs (353.788561ms)
|
||||
✔ the file checks still apply on the raw path: mode, symlink, missing, directory (256.097473ms)
|
||||
✔ the raw path base URL has no override (54.324844ms)
|
||||
✔ the JSON path is unchanged, and JSON never falls through to the raw path (233.047074ms)
|
||||
✔ a file that changes between the two reads refuses before curl runs, with or without a body (688.743118ms)
|
||||
✔ the token reaches no child environment, even with an inherited CFG or SHELLOPTS=allexport (646.46038ms)
|
||||
✔ real helper GET HTTP 200 preserves exit 0 without credentials (13.245219ms)
|
||||
✔ real helper POST HTTP 201 preserves exit 0 without credentials (9.838656ms)
|
||||
✔ real helper GET HTTP 403 preserves exit 1 without credentials (8.184859ms)
|
||||
✔ fixture git subjects only, follow-ups and three session kinds (154.313118ms)
|
||||
✔ text and JSON carry same numbers, open and truncated title (210.947279ms)
|
||||
✔ missing credentials exit 2, no-issues never calls API and shows unknown (179.545986ms)
|
||||
✔ empty range gives no rows and zero totals (147.660046ms)
|
||||
✔ inclusive UTC dates, first-line preamble only, role and seat boundaries (140.393997ms)
|
||||
✔ close-only issue included, even median, missing metadata stays unknown (131.254178ms)
|
||||
✔ unique commits but per-issue links count multiple tags once each (136.138677ms)
|
||||
✔ page cap refuses rather than silently undercounting (140.015266ms)
|
||||
✔ bad API payload not JSON refuses (131.079688ms)
|
||||
✔ bad API payload {} refuses (131.435104ms)
|
||||
✔ bad API payload [{"number":1}] refuses (132.683312ms)
|
||||
✔ partial or malformed session log refuses with location, not content (139.943482ms)
|
||||
✔ a U+2028 or U+2029 inside a session string is one line, not a malformed record (143.99886ms)
|
||||
✔ no sessions is an empty table; symlink source refuses (201.557445ms)
|
||||
✔ reads only refactor even when another branch is checked out (147.979135ms)
|
||||
✔ invalid dates, reverse dates and duplicate options refuse (104.18468ms)
|
||||
✔ T3 agent assignments do not count as human in Table 2 (139.66148ms)
|
||||
✔ preamble parsing and issue number boundaries (0.444337ms)
|
||||
✔ T3 header: agent, or board from control-board; anything short of the full header is human (0.142854ms)
|
||||
✔ no closed issues with human messages means undefined ratio, not invented zero (0.193379ms)
|
||||
✔ T3: seat, archived, unmapped and Researcher threads count; imported, deleted and other-project threads do not (457.934061ms)
|
||||
✔ T3: the default path is read from HOME and prints no path line; --no-t3 says so (779.897354ms)
|
||||
✔ T3: a HOME with no database exits 1 and names --no-t3 (120.676721ms)
|
||||
✔ T3: a file that is not a database exits 1 and names --no-t3 (135.224239ms)
|
||||
✔ T3: a seat thread renamed to another seat exits 1 naming thread, title and roles (408.387539ms)
|
||||
✔ T3: an unmapped thread addressed as a seat exits 1 (415.567824ms)
|
||||
✔ T3: a header to another thread id is not cross-checked (389.339817ms)
|
||||
✔ T3: no project, or two, for this root exits 1 (962.619281ms)
|
||||
✔ T3: a removed column exits 1 and names it (230.664945ms)
|
||||
✔ T3: a missing table exits 1 and names it (236.562197ms)
|
||||
✔ T3: a counted row with an unknown role exits 1 without its text (386.410747ms)
|
||||
✔ T3: a counted row with non-text content exits 1 without its text (379.115412ms)
|
||||
✔ T3: a counted row with an unparseable created_at exits 1 without its text (376.855527ms)
|
||||
✔ T3: a missing orchestration_events makes the diagnostic unknown and keeps the counts (667.254109ms)
|
||||
✔ T3: a human message with no event counts in humanWithoutEvent (367.607996ms)
|
||||
✔ T3: an unparseable event makes the diagnostic unknown and keeps the counts (702.872696ms)
|
||||
✔ T3: an event with no string messageId makes the diagnostic unknown and keeps the counts (662.308384ms)
|
||||
✔ T3: an error that is not from SQLite is rethrown, not reported as a database failure (323.727455ms)
|
||||
✔ T3: a symlink at ~/.t3 exits 1 (133.932176ms)
|
||||
✔ T3: a symlink at ~/.t3/userdata exits 1 (129.80237ms)
|
||||
✔ T3: a symlink at ~/.t3/userdata/state.sqlite exits 1 (133.694791ms)
|
||||
✔ T3: with --t3-db, a symlinked file or directory exits 1 (440.763268ms)
|
||||
✔ T3 WAL: the newest message only in -wal, writer attached, is counted (380.189417ms)
|
||||
✔ T3 WAL: stopped cleanly, counts are correct and the main file is unchanged (386.429909ms)
|
||||
✔ T3 WAL: -wal without -shm in a writable directory is read (401.393256ms)
|
||||
✔ T3 WAL: -wal without -shm in a read-only directory exits 1 (417.122341ms)
|
||||
✔ T3 WAL: stopped cleanly in a read-only directory exits 1 (397.701426ms)
|
||||
✔ T3: a lock held past the 5 s busy timeout exits 1 and names --no-t3 (5400.347456ms)
|
||||
✔ a done row whose closing issue is open is a violation; a row that is not done is not (1.746376ms)
|
||||
✔ an issue several rows close is expected closed only once all of them are done (0.490788ms)
|
||||
✔ closure needs positive evidence: unknown is undecided, and so is a skipped or short issue check (0.360754ms)
|
||||
✔ each owner of an in-progress or in-review row gets one liveness class (7.402141ms)
|
||||
✔ a required row not done after 14 days is a violation; a legacy row uses genesis as its lower bound (0.362633ms)
|
||||
✔ an ISO requiredSince, as `set required` writes it, ages from its UTC day; one that does not parse is a violation (0.200523ms)
|
||||
✔ the text section always ends in a count and a result, and never prints a full pass (0.313303ms)
|
||||
✔ pidAlive: a running pid is present, an exited one is gone, and EPERM still means present (21.032604ms)
|
||||
✔ issue states: open list first, then the metric page, then at most 10 lookups (254.059285ms)
|
||||
✔ a full open list: lookups settle what it leaves out, and only an unsettled issue keeps it undecided (345.286693ms)
|
||||
✔ the open list refuses on a failed call or a bad record, and never echoes the helper (161.23077ms)
|
||||
✔ a helper call past the deadline is killed with its child, and the call reports it (2007.289246ms)
|
||||
✔ readQueue loads queue.json through the queue validator and refuses anything else (118.517584ms)
|
||||
✔ protected changes list every in-range entry that changes a required or parked row (479.42873ms)
|
||||
✔ the CLI prints the queue section above the weekly table and under a queue key in --json (496.94536ms)
|
||||
✔ a queue with nothing wrong prints 0 violations and a reduced pass, never a full pass (218.081343ms)
|
||||
✔ --no-issues makes no call and leaves the issue checks undecided; --no-queue skips the section (315.673462ms)
|
||||
✔ the CLI refuses a bad queue before any call, and a failed open list with exit 2 (290.599912ms)
|
||||
✔ --unsupported-runtime repeats once per seat and takes a seat name (322.135509ms)
|
||||
✔ an unreadable config makes every owner invalid instead of passing them (170.446916ms)
|
||||
ℹ tests 78
|
||||
ℹ suites 0
|
||||
ℹ pass 78
|
||||
ℹ fail 0
|
||||
ℹ cancelled 0
|
||||
ℹ skipped 0
|
||||
ℹ todo 0
|
||||
ℹ duration_ms 18792.388342
|
||||
@@ -0,0 +1,77 @@
|
||||
✔ pure resolution selects current default or explicit enrolled account (1.903968ms)
|
||||
✔ scope is explicit, bounded and never inferred (1.579116ms)
|
||||
✔ fork pin is preserved against default change, override, missing account and revocation (0.722036ms)
|
||||
✔ unenrolled account/provider, missing harness, model expansion and native model ceiling refuse (0.780953ms)
|
||||
✔ only explicit synthetic credential forms and internal fixture stores admitted (5.499922ms)
|
||||
✔ two concurrent workspaces of the same agent publish distinct complete private generations (34.273503ms)
|
||||
✔ same execution ID is exclusively claimed and cannot overwrite a published generation (32.581426ms)
|
||||
✔ failed generation after-auth preserves prior files, records failure and refuses blind same-ID retry (46.235529ms)
|
||||
✔ failed generation before-publish preserves prior files, records failure and refuses blind same-ID retry (56.411887ms)
|
||||
✔ credential lock contention refuses without duplicate side effects (11.340162ms)
|
||||
✔ symlinked pre-existing final target is refused and never followed (25.703076ms)
|
||||
✔ invalid registry cannot resolve; no fallback to supplied partial entries (0.261187ms)
|
||||
✔ post-publication failure records uncertainty, preserves complete generation and prevents replay (26.887512ms)
|
||||
✔ expired credentials refresh under transaction and subsequent generation reuses rotation (88.925718ms)
|
||||
✔ refresh failure retains prior generation and store state (65.271084ms)
|
||||
✔ refresh timeout retains prior generation and store state (147.699662ms)
|
||||
✔ refresh malformed retains prior generation and store state (70.526808ms)
|
||||
✔ concurrent refresh on same account refuses contention while unrelated account proceeds (222.081349ms)
|
||||
✔ invalid refresh options refuse before burning claim (31.564424ms)
|
||||
✔ fixed fake process rotates both OAuth fields without mutating caller input (29.312958ms)
|
||||
✔ concurrent isolated processes preserve separate provider credentials (23.915794ms)
|
||||
✔ fake failure is refused with fixed diagnostics (20.865045ms)
|
||||
✔ fake malformed is refused with fixed diagnostics (20.453495ms)
|
||||
✔ fake timeout is refused with fixed diagnostics (102.359452ms)
|
||||
✔ fake unchanged is refused with fixed diagnostics (23.36169ms)
|
||||
✔ caller executable/environment injection is rejected before spawning (0.303784ms)
|
||||
✔ valid fixture tree validates and lists without secrets (60.018745ms)
|
||||
✔ unknown-field refuses (0.371485ms)
|
||||
✔ invalid-id refuses uppercase and traversal shapes (0.267464ms)
|
||||
✔ plain-http baseUrl requires allowInsecureTransport (0.288148ms)
|
||||
✔ native provider rejects allowInsecureTransport (0.127905ms)
|
||||
✔ unsupported credential type and kind refuse (0.156791ms)
|
||||
✔ account provider-path mismatch refuses (0.116271ms)
|
||||
✔ profile account refs must be provider/account shaped (0.212204ms)
|
||||
✔ seat selection accepts fork pin field, validates account refs (0.229377ms)
|
||||
✔ harness manifest id must equal executable (gate 1) (0.190868ms)
|
||||
✔ CLI validate: duplicate provider id across files refuses (26.845818ms)
|
||||
✔ CLI validate: missing referenced provider/account refuse (30.559197ms)
|
||||
✔ CLI validate: broken JSON refuses without secret echo (25.958534ms)
|
||||
✔ CLI usage errors exit 2 (47.026986ms)
|
||||
✔ credential.json sibling presence does not break validation and is never read (62.641979ms)
|
||||
✔ D1 missing, empty and structurally empty roots refuse, no list projection (165.304803ms)
|
||||
✔ D1 required directory auth cannot be absent (53.506377ms)
|
||||
✔ D1 required directory auth/providers cannot be absent (55.79635ms)
|
||||
✔ D1 required directory auth/accounts cannot be absent (59.281125ms)
|
||||
✔ D1 required directory auth/settings cannot be absent (62.350064ms)
|
||||
✔ D1 required directory harnesses cannot be absent (61.348911ms)
|
||||
✔ D1 root file and unreadable metadata refuse (105.732246ms)
|
||||
✔ D2 no symlink traversal at auth/providers/openai-codex.json (51.979822ms)
|
||||
✔ D2 no symlink traversal at auth/accounts/openai-codex/homelab-openai (63.002238ms)
|
||||
✔ D2 no symlink traversal at auth/providers (62.075184ms)
|
||||
✔ D2 no symlink traversal at auth (52.67446ms)
|
||||
✔ D2 root and ancestor symlinks and lexical traversal refuse (149.117176ms)
|
||||
✔ private filesystem modes enforced for root (49.727877ms)
|
||||
✔ private filesystem modes enforced for auth (50.769968ms)
|
||||
✔ private filesystem modes enforced for auth/providers/openai-codex.json (51.318586ms)
|
||||
✔ private filesystem modes enforced for auth/accounts/openai-codex/homelab-openai/account.json (58.355741ms)
|
||||
✔ D3 numeric version 1 only across all record kinds (1.176333ms)
|
||||
✔ D4 nested unknown keys and missing per-kind required fields refuse (56.402093ms)
|
||||
✔ D5 unenrolled default refuses even when account exists (67.323428ms)
|
||||
✔ D6 provider/account credential type must match (61.59385ms)
|
||||
✔ D7 every harness endpoint enforces HTTP opt-in and shape (0.445938ms)
|
||||
✔ D8 URLs reject embedded credentials and unsupported protocols without echo (161.840977ms)
|
||||
✔ D9 malformed JSON diagnostics contain no content excerpt (55.340877ms)
|
||||
✔ D10 missing metadata is missing-path, not invalid-json (60.348108ms)
|
||||
✔ D10 library returns no partial entries on any invalid record (71.907852ms)
|
||||
✔ null/scalar/array metadata refuses without stack or echo (238.69823ms)
|
||||
✔ credential sibling is never opened, even when an unreadable symlink (33.417448ms)
|
||||
✔ oversized metadata refuses before parsing (56.186916ms)
|
||||
ℹ tests 69
|
||||
ℹ suites 0
|
||||
ℹ pass 69
|
||||
ℹ fail 0
|
||||
ℹ cancelled 0
|
||||
ℹ skipped 0
|
||||
ℹ todo 0
|
||||
ℹ duration_ms 2070.961118
|
||||
@@ -0,0 +1,166 @@
|
||||
✔ F1: an ordinary commit after update-ref is refused until step 8; then it commits and the queue stays at C (1206.961486ms)
|
||||
✔ F1: a plain `commit -e` whose guard ran before update-ref fails at its own HEAD update (962.719505ms)
|
||||
ℹ git commit -e: index.lock free during the editor
|
||||
✔ F1: a `commit -e -- path` whose guard ran before update-ref fails at its own HEAD update (960.495929ms)
|
||||
ℹ git commit -e -- src.txt: index.lock held during the editor
|
||||
✔ F1: step 8 with index.lock held exits 3, and ordinary commits stay refused until the printed command runs (1028.951372ms)
|
||||
✔ F1: HEAD moving after the step-7 guard check and before update-ref: refused, nothing published (994.789128ms)
|
||||
✔ F1: H is recorded before the canary, so HEAD moving during the step-1 canary is refused at step 7 (966.065737ms)
|
||||
✔ F1: a queue commit landing after H is recorded: step 1 says HEAD moved, not the guard (1263.147263ms)
|
||||
✔ F1: a queue commit landing between the HEAD check and the canary: the failed clean run is reported as HEAD moved (896.150874ms)
|
||||
✔ F1: a shared-index change during the procedure is not committed (1011.728488ms)
|
||||
✔ F1: a queue path staged after update-ref: step 8 stops and touches nothing (1007.844855ms)
|
||||
✔ F1: a missing or a different hook refuses (741.226791ms)
|
||||
✔ F1: same bytes without the exec bit, a symlinked hook, and core.hooksPath in the local or global scope each refuse before update-ref (1176.206482ms)
|
||||
✔ F1: the canary refuses a hook that git would not run (643.720658ms)
|
||||
✔ F1: the guard deactivated after step 1 is refused at the step-7 recheck (922.85157ms)
|
||||
✔ bootstrap: implementation-only HEAD, the guard, genesis, the --genesis commit, then an extending commit (877.481291ms)
|
||||
✔ bootstrap: --genesis with a base present, no base without --genesis, an op before the first commit, a changed map, another branch (863.506373ms)
|
||||
✔ bootstrap: the archived tests and validator run outside any repository (885.715016ms)
|
||||
✔ general: an unrelated staged file stays staged, and the committed blobs are the snapshot bytes (911.798101ms)
|
||||
✔ general: a queue write after the snapshot is not committed (1226.645772ms)
|
||||
✔ general: a snapshot whose log does not extend the base refuses (988.29749ms)
|
||||
✔ general: install-hook privilege, repair of a same-bytes hook, and its refusals (150.64601ms)
|
||||
✔ general: environment overrides, a linked worktree and usage (574.604128ms)
|
||||
✔ general: a queue path staged before the run refuses at step 1 (647.380431ms)
|
||||
✔ general: HEAD's queue tests failing in the archive refuse (857.507126ms)
|
||||
✔ genesis document serializes deterministically and replays (3.725209ms)
|
||||
✔ a hand edit that stays valid JSON fails replay; a formatting-only edit fails re-serialization (2.14242ms)
|
||||
✔ a tampered result, receipt or viewSha fails replay (1.588753ms)
|
||||
✔ op ids: 8 to 72 characters for callers, 80 in the log for .outcome entries (0.480327ms)
|
||||
✔ add: defaults for an ordinary seat, privileged extras, refusals (3.417708ms)
|
||||
✔ matrix: queued→briefed privileged; briefed→in-progress owner with after satisfied (6.029186ms)
|
||||
✔ matrix: release, review round, changes requested and waiting-on-jason (12.370498ms)
|
||||
✔ matrix J5: in-review→done by the gate owner with evidence naming the current round (10.86933ms)
|
||||
✔ review issue, lead decision 23: none refuses, one is used, several need --issue, later rounds keep it (20.316905ms)
|
||||
✔ the row schema refuses a round with a null issue, and the A1 review shape (P2) (2.107039ms)
|
||||
✔ matrix R1: every state × target × actor class matches 8.7, gate owner jason or not, required or not (1288.962944ms)
|
||||
✔ matrix: blocked keeps the claim and returns only to previousState (3.747304ms)
|
||||
✔ matrix J4: parking is Jason's, refused while required; unpark returns to queued (2.300519ms)
|
||||
✔ field edits: who may change what (4.358408ms)
|
||||
✔ set issues keeps a logged narrowing of closes (N10) (2.604857ms)
|
||||
✔ text the table shows refuses \ and <, everywhere it enters (N8) (1.647073ms)
|
||||
✔ every accepted text renders to nine cells on every row (N8) (20.488587ms)
|
||||
✔ genesis: the map refuses an owner among its row's reviewers; replay doesn't (2026-10-04) (0.510234ms)
|
||||
✔ times and dates must be calendar values, not just the shape (2026-10-04) (1.507289ms)
|
||||
✔ replay holds every op id to the caller's rule (N11) (4.668896ms)
|
||||
✔ note: owner, listed reviewer or privileged; empty clears (1.099476ms)
|
||||
✔ assign moves the claim with the owner; done clears it (2.484097ms)
|
||||
✔ render is byte-stable and escapes pipes (0.465586ms)
|
||||
✔ view classification: current, genuine stale, edited stale marker, changed current body, markers (0.534222ms)
|
||||
✔ next: resume, then review, then start, then wait, then nothing; lowest id first (15.635193ms)
|
||||
✔ canonical args make a retry's identity independent of list order (0.249821ms)
|
||||
✔ manifests, headings and blob ids (0.378352ms)
|
||||
✔ the migration map: one queue-map block, exact keys (0.514488ms)
|
||||
✔ every call but `queue` reaches the seat CLI exactly as before A2 (512.255278ms)
|
||||
✔ no argument reaches the app entry in packages/cli, and only that call (68.264167ms)
|
||||
✔ `queue` reaches the queue CLI with the rest of the arguments (180.170008ms)
|
||||
✔ the pre-A2 fixture is the script A2 changed (0.277621ms)
|
||||
✔ an old privileged add with no gate owner still replays through loadDoc, and gets jason (7.198364ms)
|
||||
✔ a non-privileged add gets jason through the replay path and can't name a gate owner (2.064441ms)
|
||||
✔ the CLI refuses a privileged add without --gate-owner, for jason and sage, and writes nothing (302.810273ms)
|
||||
✔ the CLI add with --gate-owner carries the named owner and records a log entry (275.907584ms)
|
||||
✔ the CLI add by a non-privileged seat gets jason and can't name a gate owner (311.22677ms)
|
||||
✔ a retried sage add recorded with no gate owner answers (already recorded at rev N) and writes nothing (277.763118ms)
|
||||
✔ acquire publishes the record by link; release removes only its own lock (9.723142ms)
|
||||
✔ a kill between the temp write and the link leaves no lock (61.4248ms)
|
||||
✔ a short or failed temp write refuses and leaves no lock and no temp (10.249309ms)
|
||||
✔ a link error other than EEXIST refuses (6.283332ms)
|
||||
✔ an error after the link releases the lock: unreadable gate, failing temp stat (28.173772ms)
|
||||
✔ a release that fails on a gate path is reported, never a stack trace (P1) (37.17611ms)
|
||||
✔ a paused holder: another writer waits 10 s, then refuses naming it live (10093.484861ms)
|
||||
✔ two concurrent unlockers: the second refuses on the gate (34.605564ms)
|
||||
✔ a writer publishing during an unlock, lock first: unlock sees it live and refuses (10.770892ms)
|
||||
✔ a writer publishing during an unlock, gate first: the writer releases and refuses (20.391598ms)
|
||||
✔ a gate swapped while held is left in place and reported, on success and on refusal (N1) (40.808406ms)
|
||||
✔ a reused pid within one boot is mismatch; unlock removes the lock and never signals the process (22.451175ms)
|
||||
✔ the same pid and start on a different boot is mismatch (0.408242ms)
|
||||
✔ a foreign host is unknown whatever the local pid says; unlock refuses (58.42168ms)
|
||||
✔ unreadable /proc: classification is unknown and acquire refuses (0.620623ms)
|
||||
✔ invalid records: empty, unparsable, wrong keys, bad start or boot (0.18928ms)
|
||||
✔ a stale gate blocks writers; --check-gate says mismatch for a reused pid (20.903274ms)
|
||||
✔ a delayed release by a dead owner, after unlock and a new owner: the inode check keeps the new lock (28.612508ms)
|
||||
✔ release checks the inode too: a byte-identical lock file with a new inode is left in place (10.697305ms)
|
||||
✔ unlock refuses a live, unknown or invalid lock, and does nothing without one (27.679157ms)
|
||||
✔ the migration map validates and renders the golden genesis table (4.558961ms)
|
||||
✔ the marked QUEUE.md holds every row and parked item between its markers (1.764555ms)
|
||||
✔ map-check reports each kind of drift (4.248928ms)
|
||||
✔ a request posts once as the requester; a retry sends nothing (797.797115ms)
|
||||
✔ each transport answer maps to posted, failed or uncertain (8.9 step 3) (4326.688448ms)
|
||||
✔ the pre-send checks: GET user must name the requester, under the deadline (1510.829096ms)
|
||||
✔ the lead's request refuses a token for login sage (892.263682ms)
|
||||
✔ the credential file: the seat's own, 0600, no symlink, never the shared default (827.32458ms)
|
||||
✔ an unresolved request blocks a new request, a new round, waiting-on-jason and done (2211.088719ms)
|
||||
✔ a same-op retry after a kill sends nothing, even with a stale view (3017.117377ms)
|
||||
✔ a held lock at the outcome exits 3 and names what the transport said (659.798454ms)
|
||||
✔ late outcomes: after an abandon, and after a resolve with the same or another id (2223.931042ms)
|
||||
✔ resolve checks the comment: issue, markers, round, candidate and author (1668.601373ms)
|
||||
✔ the lead resolves a seat's request: the comment's author is the requester, fetched with the lead's token (656.832729ms)
|
||||
✔ validateRow checks a request round's shape, which every replayed entry must keep (526.945557ms)
|
||||
✔ request, changes, a new candidate, approval: every round pinned; no review files (1681.06922ms)
|
||||
✔ a row with no reviewers opens a round that posts nothing (1169.321718ms)
|
||||
✔ verify-commit: a prospective tree must hold exactly the candidate's paths (1308.607903ms)
|
||||
✔ semantics: v1 entries replay as before; review entries need v2 (471.647002ms)
|
||||
✔ set reviewers refuses the row's owner (2026-09-28) (346.758938ms)
|
||||
✔ the owner records no verdict, even as a listed reviewer (478.794165ms)
|
||||
✔ a request comment over the length limit is not sent (553.01425ms)
|
||||
✔ a late POST on a closed row leaves a conflict nothing can resolve, and resolve asks nothing (649.403422ms)
|
||||
✔ a Jason-gated row reaches waiting-on-jason only on every reviewer's approval (2411.697213ms)
|
||||
✔ genesis: refusals before anything is written (421.127484ms)
|
||||
✔ genesis: the map must be committed, well formed, with committed briefs and seats (582.474095ms)
|
||||
✔ genesis: markers, a stray witness, once only; a retry returns the receipt (520.232299ms)
|
||||
✔ genesis: a file holding genesis alone with no witness is confirmed by sync or a retry (383.319466ms)
|
||||
✔ canonical checks: worktree, second clone, detached HEAD, wrong branch, GIT_DIR, foreign code; a symlink works (602.349578ms)
|
||||
✔ --by that differs from MOSAIC_AGENT_NAME warns on stderr and logs nothing more (N12) (796.504542ms)
|
||||
✔ op ids: missing, too long, reserved; a retry answers; another payload refuses (723.357643ms)
|
||||
✔ a retried add returns the id it first allocated, after reassignment and after done (941.208245ms)
|
||||
✔ Rocko's S4 schedule: a lost result, another writer, then the retry opens no second round (642.500055ms)
|
||||
✔ the review issue and the evidence round through the CLI (lead decision 23, 8.7) (1189.303948ms)
|
||||
✔ claims and add defaults through the CLI; candidates are manifests or reachable commits (818.711901ms)
|
||||
✔ add, set reviewers and assign refuse the row's owner as a reviewer (516.880245ms)
|
||||
✔ the working-brief check: a changed working copy refuses the start and flags next (638.664892ms)
|
||||
✔ next: resume first, then nothing for an idle seat; needs a seat (295.928873ms)
|
||||
✔ view stale: new ops and verify refuse naming the unshown op; retries answer; reads warn; render fixes (624.666747ms)
|
||||
✔ view unknown: a hand edit, an old marker over an edited body, missing or duplicate markers (1102.085241ms)
|
||||
✔ a hand edit to queue.json refuses every verb, reads included (568.488617ms)
|
||||
✔ verify and render --check leave bytes and mtimes unchanged (408.455933ms)
|
||||
✔ render is byte-stable across runs and repositories (282.624967ms)
|
||||
✔ snapshot and verify --snapshot (745.966373ms)
|
||||
✔ usage errors exit 4 (530.715795ms)
|
||||
✔ a short write, ENOSPC or a file fsync failure: nothing visible, temp removed (285.648758ms)
|
||||
✔ a rename failure: nothing visible, temp removed (179.354727ms)
|
||||
✔ a directory fsync failure: uncertain, exit 3, no receipt; the tail refuses new ops; a retry confirms (272.355679ms)
|
||||
✔ a directory fsync failure, then sync names the op (361.251798ms)
|
||||
✔ a witness write failure: uncertain, durable, exit 3; the view is untouched (210.746047ms)
|
||||
✔ the .git fsync after the witness rename fails: uncertain, exit 3, the witness says so (180.852101ms)
|
||||
✔ confirming a tail fsyncs queue.json and docs/plans before the witness; either failure changes nothing (228.645533ms)
|
||||
✔ the docs/plans fsync after the view rename fails: the op stands, the view is written, a warning says so (195.931219ms)
|
||||
✔ a lock swapped while held is left in place and reported, on a receipt and on a refusal (209.570209ms)
|
||||
✔ a lock that cannot be released after an op is reported, on a receipt and on a refusal (226.950096ms)
|
||||
✔ unlock prints a swapped gate's warning on stderr, the result on stdout (152.543655ms)
|
||||
✔ a view write that fails keeps the op and reports a stale view (204.727322ms)
|
||||
✔ SIGKILL before the rename: nothing recorded; the retry removes the leftover temp (644.065758ms)
|
||||
✔ SIGKILL after the rename, before the witness: the tail refuses new ops and sync names the op (677.984401ms)
|
||||
✔ SIGKILL after the witness, before the view: the stale refusal names the op (650.546922ms)
|
||||
✔ SIGKILL after the view, before the receipt: the retry returns the receipt (652.619074ms)
|
||||
✔ git checkout between steps 1 and 7: step 7 refuses and nothing is written (234.67271ms)
|
||||
✔ git stash restoring an older valid pair: history lost; accept-history needs privilege, a reason and --yes (1014.002313ms)
|
||||
✔ a deleted witness: refused after the locked recheck; accept-history records it absent (349.921353ms)
|
||||
✔ a header edit during a write: the op stands, the view write is skipped with a warning (207.597191ms)
|
||||
✔ a reader paused between the witness and the file while a writer finishes: no lost-history report (219.46486ms)
|
||||
✔ rows: every row as show prints it, in list order; writes nothing; same notes and refusals as list (257.353174ms)
|
||||
✔ file-then-witness order forced by a hook: the locked recheck prevents a false report (206.007718ms)
|
||||
✔ a writer paused before and after the witness rename: readers see a tail, then a match (209.805495ms)
|
||||
✔ a true rollback is reported only after the locked recheck; a held lock names its holder instead (582.511177ms)
|
||||
✔ an accept-history in progress: an unlocked reader waits on the lock and never reports lost history (595.455761ms)
|
||||
✔ the platform check refuses other filesystems (128.39315ms)
|
||||
✔ tmpfs passes only a test layer that allows it (N5) (217.484176ms)
|
||||
✔ unlock keeps a multi-line lock record on stdout (P3) (209.645459ms)
|
||||
ℹ tests 156
|
||||
ℹ suites 0
|
||||
ℹ pass 156
|
||||
ℹ fail 0
|
||||
ℹ cancelled 0
|
||||
ℹ skipped 0
|
||||
ℹ todo 0
|
||||
ℹ duration_ms 28460.561057
|
||||
@@ -0,0 +1,51 @@
|
||||
✔ isRunId accepts the run id shape and nothing else (0.863509ms)
|
||||
✔ a missing data root or runs directory lists nothing (1.315942ms)
|
||||
✔ runs as a regular file lists nothing, as before (0.512729ms)
|
||||
✔ listRunIds keeps r- names only, sorted oldest first (0.647165ms)
|
||||
✔ listRunRecords returns each result, or null for an incomplete or unreadable one (1.63768ms)
|
||||
✔ readRunRecord returns documents and artifacts in directory order (0.855624ms)
|
||||
✔ readRunRecord is null for a missing run, a dangling link or a file (0.584011ms)
|
||||
✔ readRunRecord and readRunDocument refuse an invalid run id before touching the disk (0.556905ms)
|
||||
✔ readRunDocument reads only the three run documents (0.452147ms)
|
||||
✔ a link inside the data root is followed (0.830678ms)
|
||||
✔ a data root that is itself a link is trusted as configured (0.537931ms)
|
||||
✔ a run directory linked out of the data root is never read (0.595781ms)
|
||||
✔ a document linked out of the data root reads as null (0.768773ms)
|
||||
✔ a runs directory linked out of the data root refuses (0.491077ms)
|
||||
✔ a relative link that climbs out of the data root refuses (0.368928ms)
|
||||
✔ a sibling whose name starts with the data root's name is outside it (0.42093ms)
|
||||
✔ a link loop refuses instead of reading as missing (0.547875ms)
|
||||
✔ an unreadable runs directory refuses instead of listing nothing (0.358977ms)
|
||||
✔ an unreadable run directory refuses instead of reading as missing (0.424536ms)
|
||||
✔ a link to the data root's parent is outside it (0.329775ms)
|
||||
✔ listRunIds sorts whatever order the directory returns (0.647239ms)
|
||||
✔ listRunRecords lists an r- name that isn't a valid run id, as before (0.403148ms)
|
||||
✔ the readers write nothing (1.652631ms)
|
||||
✔ no pointer is null (1.457283ms)
|
||||
✔ the pointer release.sh writes reads back (0.92443ms)
|
||||
✔ a pointer that isn't the version 1 shape refuses with exit 2 (1.454231ms)
|
||||
✔ a pointer that is a directory refuses with exit 4 (0.922375ms)
|
||||
✔ a state file linked out of the data root refuses (0.766772ms)
|
||||
✔ a state directory linked out of the data root refuses (0.463947ms)
|
||||
✔ a state directory linked out to nothing reads as no release and an empty log (0.395966ms)
|
||||
✔ a missing log is empty (0.312858ms)
|
||||
✔ the log reads oldest first and counts malformed lines (0.800071ms)
|
||||
✔ last must be a positive integer (0.388501ms)
|
||||
✔ the state readers write nothing (0.689762ms)
|
||||
✔ list prints each run in the established format (64.386034ms)
|
||||
✔ list prints a damaged result.json as unknown and keeps listing the runs after it (63.716492ms)
|
||||
✔ list prints each result field that isn't a string as unknown or - (60.920678ms)
|
||||
✔ show prints the run in the established format (60.526474ms)
|
||||
✔ show of a missing run and an invalid id exit 4 as before (93.524632ms)
|
||||
✔ list and show refuse a runs directory linked out of the data root (122.088216ms)
|
||||
✔ show refuses a run linked out of the data root; list reports it unknown (125.528587ms)
|
||||
✔ list shows an r- name that isn't a valid run id, as before (64.166703ms)
|
||||
✔ show refuses an invalid id before reading the config (36.668367ms)
|
||||
ℹ tests 43
|
||||
ℹ suites 0
|
||||
ℹ pass 43
|
||||
ℹ fail 0
|
||||
ℹ cancelled 0
|
||||
ℹ skipped 0
|
||||
ℹ todo 0
|
||||
ℹ duration_ms 741.001299
|
||||
@@ -0,0 +1,132 @@
|
||||
✔ a first launch clones the root at its branch, with the root's path as origin (32.811284ms)
|
||||
✔ the clone is not at its place until git clone has finished (23.948831ms)
|
||||
✔ a later launch fast-forwards a clean clone after a commit to the root, then finds it current (67.233751ms)
|
||||
✔ a clone ahead of the root keeps its commits and counts as current (43.666115ms)
|
||||
▶ a dirty clone, one on another branch and one diverged are kept as they are
|
||||
✔ a modified file (33.077005ms)
|
||||
✔ an untracked file (38.414761ms)
|
||||
✔ a staged file (34.171849ms)
|
||||
✔ another branch (35.366927ms)
|
||||
✔ a detached HEAD (36.240137ms)
|
||||
✔ a diverged branch (47.675496ms)
|
||||
✔ a dirty clone, one on another branch and one diverged are kept as they are (226.143534ms)
|
||||
▶ a clone with user.name or user.email in its config is kept, and git runs nothing in it
|
||||
✔ user.name (25.190655ms)
|
||||
✔ user.email (25.992347ms)
|
||||
✔ a clone with user.name or user.email in its config is kept, and git runs nothing in it (51.62715ms)
|
||||
✔ a commit git can't read after the fast-forward is null (49.234616ms)
|
||||
✔ a new clone whose commit can't be read refuses the launch and leaves nothing behind (22.417472ms)
|
||||
▶ a clone the session rigged is kept, and git runs nothing and writes nothing outside it
|
||||
✔ core.fsmonitor (37.6971ms)
|
||||
✔ core.hooksPath (39.13204ms)
|
||||
✔ a hook file (30.65272ms)
|
||||
✔ include.path (32.412762ms)
|
||||
✔ includeIf (36.14086ms)
|
||||
✔ a filter driver (36.042022ms)
|
||||
✔ origin moved (32.67077ms)
|
||||
✔ a second origin url (32.399848ms)
|
||||
✔ the origin url twice, both the root (38.520872ms)
|
||||
✔ an ext:: origin (36.99087ms)
|
||||
✔ a branch tracking another remote (36.981016ms)
|
||||
✔ a branch merging a tag (34.359795ms)
|
||||
✔ a fetch line onto local branches (36.467933ms)
|
||||
✔ no origin (37.018911ms)
|
||||
✔ a config file over the size cap (32.738565ms)
|
||||
✔ config a symlink to /dev/zero (33.208159ms)
|
||||
✔ the clone a symlink to the bait (31.859668ms)
|
||||
✔ the clone a file (30.826692ms)
|
||||
✔ the clone a plain directory (28.943176ms)
|
||||
✔ .git a gitdir: file (29.79982ms)
|
||||
✔ .git a symlink (29.1941ms)
|
||||
✔ .git/logs a symlink (32.80446ms)
|
||||
✔ .git/refs/heads a symlink (35.486024ms)
|
||||
✔ .git/objects a symlink (32.200231ms)
|
||||
✔ a reflog hard-linked to a file outside (30.874334ms)
|
||||
✔ HEAD a fifo (30.273546ms)
|
||||
✔ a commondir file (31.210921ms)
|
||||
✔ an alternates file (33.652412ms)
|
||||
✔ a gitlink staged, no .gitmodules (42.537313ms)
|
||||
✔ a gitlink and .gitmodules staged (47.369242ms)
|
||||
✔ a gitlink to a repository with its own gitlink (55.261351ms)
|
||||
✔ a gitlink and .gitmodules committed (48.030149ms)
|
||||
✔ a gitlink and submodule.active in config (46.900495ms)
|
||||
✔ a .git/modules with no gitlink (38.474372ms)
|
||||
✔ a clone the session rigged is kept, and git runs nothing and writes nothing outside it (1223.53712ms)
|
||||
▶ a root that can't be cloned refuses with the project id and leaves nothing behind
|
||||
✔ missing (11.640849ms)
|
||||
✔ not a repository (9.952353ms)
|
||||
✔ a plain directory inside another repository (14.292936ms)
|
||||
✔ unreadable (9.572178ms)
|
||||
✔ on no branch (11.455639ms)
|
||||
✔ missing an object (19.229975ms)
|
||||
✔ a root that can't be cloned refuses with the project id and leaves nothing behind (76.985894ms)
|
||||
✔ a clone whose root is on no branch, or gone, is kept and named (32.325638ms)
|
||||
✔ no credential or forge remote of the root reaches a clone (49.915285ms)
|
||||
✔ every project in the business file gets its own clone at its own branch (40.31377ms)
|
||||
✔ a clone past its time gets SIGTERM and refuses the launch, with nothing left in the workspace (513.007959ms)
|
||||
✔ a clone that ignores SIGTERM gets SIGKILL once the grace period is over (1017.472728ms)
|
||||
✔ a timeout signals git's process group, so a process git started dies with it (513.491469ms)
|
||||
✔ once a signalled git has closed, its group gets SIGKILL, so a process that ignored SIGTERM dies too (513.116787ms)
|
||||
✔ RUNNER_FILES is exactly what the runner and the harness sources it starts import (1.65651ms)
|
||||
✔ sandboxProblem names a missing bwrap and a bwrap that fails (8.796141ms)
|
||||
✔ every bind says why; the bundle and session file stay read-only over the run directory (1.476658ms)
|
||||
✔ the host's git socket is bound only when given, read-only and before the writable binds (1.382251ms)
|
||||
✔ the git identity is the instance's, at <instance>@<business>.mosaic.invalid, and sandboxArgv refuses without it (1.080836ms)
|
||||
✔ a confined session's shell reaches its own workspace and run directory and nothing of the founder's or another session's (1766.121443ms)
|
||||
✔ a bind that exposes a stand-in, or a missing workspace bind, fails the matching check (2460.186984ms)
|
||||
✔ a confined session that runs tester-cli.mjs is refused as tester, and the same shim outside the sandbox binds (262.780721ms)
|
||||
✔ resolveSeat: by name under --repo resolves the repo layout (1.257933ms)
|
||||
✔ resolveSeat: by path resolves the fleet layout (0.30939ms)
|
||||
✔ resolveSeat: refusals for missing dir, missing launch.sh, non-executable launch.sh, invalid name, and unknown layout (0.719252ms)
|
||||
✔ tmuxContext: outside tmux, default socket, custom socket, and exec failure (0.656086ms)
|
||||
✔ makeRegistration produces a record that validates; each shape violation throws SeatError (0.776544ms)
|
||||
✔ writeRegistration/readRegistration: round trip, permissions, absence, and malformed records (1.316042ms)
|
||||
✔ updateTask: changes task, taskSetBy and updatedAt only, and refuses appropriately (1.137296ms)
|
||||
✔ CLI launch: registers, execs the fake launch script, and passes args through (28.302245ms)
|
||||
✔ CLI launch: --harness lands in the record (27.480381ms)
|
||||
✔ CLI launch: the launch script's own exit code passes through (25.733907ms)
|
||||
✔ CLI launch: relaunching a seat rewrites the one registration record (59.301398ms)
|
||||
✔ CLI launch: omitting --task records an empty string, not null (27.121894ms)
|
||||
✔ CLI seat task: updates only the task after a launch, and refuses on an unlaunched seat (79.893523ms)
|
||||
✔ CLI refusals: no args, unknown flag, missing config, already-registered env, and exec failure (131.637893ms)
|
||||
✔ samePath: equal paths, symlinked dirs, distinct dirs, and non-strings (0.41976ms)
|
||||
✔ resolveSetBy: explicit --by wins over the environment; absent or empty environment gives unknown; invalid explicit or environment values refuse with exit 4 (0.571295ms)
|
||||
✔ validateRegistration/readRegistration: taskSetBy is optional; a record without it (written before #1511) still loads unchanged; an invalid one is refused; the version does not change (0.71967ms)
|
||||
✔ updateTask: records setBy, preserves startedAt and every unrelated field, upgrades an old record in place only when the task is set, and replaces a previous attribution (1.227595ms)
|
||||
✔ CLI seat task: --by beats MOSAIC_AGENT_NAME, the environment beats nothing, empty environment is unknown, invalid --by or environment refuses with exit 4 and leaves the record byte for byte (266.860827ms)
|
||||
✔ family: exactly one launch.max key in the model name, else null (0.666245ms)
|
||||
✔ sessionEnv passes only the allowlist, the repo's bin on PATH, and the run id (0.735265ms)
|
||||
✔ sessionExit gives a runner's signal death back as the signal; a session credential is one whole set of nonempty variables (0.283812ms)
|
||||
✔ newRun: short ids, 0700 directories, and a refusal when the socket path won't fit (1.933043ms)
|
||||
✔ session file and launch log: 0600, the session file written once (1.268976ms)
|
||||
✔ endReason maps the runner's exit codes; a signal is killed (0.121045ms)
|
||||
✔ Registry mirrors to sessions.json; readSessions marks live entries; bad files refuse (1.217199ms)
|
||||
✔ stopSession refuses an unknown run, reports a stale one, and won't signal a pid that isn't a runner (0.946114ms)
|
||||
✔ a session runs in its sandbox as pid 1 of its namespace, claims, answers, and stops on mosaic stop (362.553648ms)
|
||||
✔ workRef and validBranch take only what a ref name can hold (1.193075ms)
|
||||
✔ a push lands as the task's work ref, a fast-forward lands after it, and main is the root's (157.434023ms)
|
||||
✔ a rewritten branch refuses non-fast-forward and the work ref stays (143.590227ms)
|
||||
✔ a branch with no history in common with main refuses unrelated-history (82.447645ms)
|
||||
✔ a bundle that needs commits the working repository lacks refuses missing-prerequisite (41.210774ms)
|
||||
✔ main is fetched from the root again before each push, so a clone of a newer root pushes (137.881064ms)
|
||||
✔ a root moved back after the clone leaves the push's base unknown: missing-prerequisite (100.23025ms)
|
||||
✔ a commit with a broken object refuses invalid-bundle, and only because fsck is on (73.330925ms)
|
||||
✔ only a v2 bundle with the named branch is read (45.409465ms)
|
||||
✔ a branch named main, or named for another task, moves only this task's work ref (101.937462ms)
|
||||
✔ a record that throws moves the work ref back, or removes a new one (143.941065ms)
|
||||
✔ a work ref that can't go back refuses rollback-failed, naming the ref and the commit (401.044212ms)
|
||||
✔ a root that can't be read refuses root-unreadable (30.862793ms)
|
||||
✔ a git past its time gets SIGTERM, leaves no ref lock, and the next push lands (2159.816302ms)
|
||||
✔ a git that ignores SIGTERM gets SIGKILL once the grace period is over (1028.560955ms)
|
||||
✔ a working repository path that isn't a directory refuses (24.616223ms)
|
||||
✔ review bundles a pushed commit as refs/mosaic/review, leaving out what the reviewer has (112.474671ms)
|
||||
✔ review of a project with no working repository refuses unknown-commit (12.986057ms)
|
||||
✔ the runner's git tools hold the host's limits and branch rule (1.158241ms)
|
||||
ℹ tests 120
|
||||
ℹ suites 0
|
||||
ℹ pass 120
|
||||
ℹ fail 0
|
||||
ℹ cancelled 0
|
||||
ℹ skipped 0
|
||||
ℹ todo 0
|
||||
ℹ duration_ms 4860.236596
|
||||
@@ -0,0 +1,100 @@
|
||||
✔ the boot config is checked before anything starts (76.642131ms)
|
||||
✔ a business with no tracker entry refuses task verbs (136.480774ms)
|
||||
✔ credential.expiring and .expired are recorded once per instance (222.309019ms)
|
||||
✔ a token file that changes on disk records credential.changed (111.026318ms)
|
||||
✔ autostart polls, reconciles and retries a startup the tracker was down for (144.460007ms)
|
||||
✔ a refusal a restart must clear is not retried by the poll (121.81241ms)
|
||||
✔ a poll that fires while two are queued is dropped (97.327052ms)
|
||||
✔ close waits for a running verb and refuses one that has not started (219.991744ms)
|
||||
✔ the bundled Vikunja is the pinned upstream image the runbook names (0.714492ms)
|
||||
✔ every published port is on 127.0.0.1, and no secret is in the file (0.292707ms)
|
||||
✔ the committed table loads frozen; its verbs are the broker task verbs (1.559952ms)
|
||||
✔ a write names a verb the table has as its own key, not one it inherits (0.515594ms)
|
||||
✔ the schema refuses any other shape (2.781069ms)
|
||||
✔ the Field table in the slice 1 plan names the verbs each field is written by (0.599055ms)
|
||||
✔ each verb refuses, asks for a decision and writes as the table and the role files say (1290.533252ms)
|
||||
✔ the list holds the PRD's requirement ids, in its order, at its version (5.175504ms)
|
||||
✔ the committed list loads frozen (0.617003ms)
|
||||
✔ the schema refuses any other shape (2.456394ms)
|
||||
✔ the fake answers each route with the statuses and shapes Vikunja v2.7.0 sent (375.91726ms)
|
||||
✔ the project list holds recorded project objects in the recorded list envelope (98.349749ms)
|
||||
✔ the recorded task bodies pass the checks S3 applies to every read (0.465571ms)
|
||||
✔ the client works against the fake over real HTTP with the platform fetch (257.475ms)
|
||||
✔ a correct install starts, and the first reconcile records tasks that already exist (111.027066ms)
|
||||
✔ verbs refuse while a business is starting and after startup refused it (143.398723ms)
|
||||
✔ startup refuses a token that can do more than its role needs (427.638855ms)
|
||||
✔ startup refuses an unsupported version and flags an untested one (291.245365ms)
|
||||
✔ startup refuses a board that the runbook did not install (374.154977ms)
|
||||
✔ startup refuses a project the sync bot cannot read (100.926081ms)
|
||||
✔ startup refuses a configured label the pm bot cannot see (95.170714ms)
|
||||
✔ startup refuses an expired credential and a missing sync credential (210.674224ms)
|
||||
✔ an unreachable tracker refuses with tracker-unavailable (86.293635ms)
|
||||
✔ an edit in the UI is recorded once, with the fields that changed (207.946439ms)
|
||||
✔ a move between open buckets is seen on the board, though updated does not change (183.964029ms)
|
||||
✔ a person's comment is counted and a bot's is not (295.450835ms)
|
||||
✔ the hourly reconcile catches a comment through comment_count (247.482853ms)
|
||||
✔ a task closed in the UI leaves the open view with its done bucket (417.292816ms)
|
||||
✔ a task that leaves the board is recorded as deleted, moved or out of reach (290.829607ms)
|
||||
✔ a poll that read before a verb wrote does not overwrite the verb (170.457691ms)
|
||||
✔ a tracker fault during a tick is reported and the next tick catches up (191.258491ms)
|
||||
✔ a malformed answer refuses the tick with tracker-shape (136.180273ms)
|
||||
✔ no token value reaches the database, the log or a refusal (284.187141ms)
|
||||
✔ the first look at a task counts only comments inside the window (158.185463ms)
|
||||
✔ with descendants off the adapter reads no project list and no child, as in v0 (125.611241ms)
|
||||
✔ a three-level tree is read per project, and a project outside it is not (122.157949ms)
|
||||
✔ a watch-only child keeps the business ready, and a failed child read refuses as transient (224.572824ms)
|
||||
✔ a root without the installed buckets refuses with descendants on (101.956066ms)
|
||||
✔ the project list refuses without read_all, and skips archived projects and saved filters (222.417405ms)
|
||||
✔ descendants must be true or false (97.310723ms)
|
||||
✔ a move inside the tree is a moved tombstone plus a snapshot under the new ref (173.305422ms)
|
||||
✔ a child made after startup joins the tree at the next reconcile (164.680791ms)
|
||||
✔ a child that leaves the tree has its tasks recorded missing, and the ledger keeps their history (189.124853ms)
|
||||
✔ a child deleted or hidden between reconciles drops out at the next tick, and the root still refuses (209.00009ms)
|
||||
✔ a reconcile that fails keeps the previous tree (140.037974ms)
|
||||
✔ a closed task the cursor missed takes its own project's done bucket (141.154408ms)
|
||||
✔ closed tasks read at startup take their own project's done bucket (105.563067ms)
|
||||
✔ a closed task the cursor returns takes its own project's done bucket (136.92305ms)
|
||||
✔ a task moved out of a child that leaves the tree at the same time is moved, not no-access (129.284183ms)
|
||||
✔ a task in a bucket outside the five keeps its raw bucket id (108.143123ms)
|
||||
✔ bucket 0 on a watch-only task never becomes a bucket or a state (202.769344ms)
|
||||
✔ a child whose kanban view is deleted still refuses the tick, and the reconcile reads it watch-only (146.988907ms)
|
||||
✔ a child dropped at a tick comes back at the next reconcile once the bot reads it again (150.665056ms)
|
||||
✔ task.create needs a recorded human request and a requirement id (199.539682ms)
|
||||
✔ task.create takes only an approved requirement id (221.900736ms)
|
||||
✔ task.create cites the request a received message carries, not the message's id (185.050922ms)
|
||||
✔ only labels named in the business file can be written (262.528603ms)
|
||||
✔ task.schedule sets and clears a due date and relations (296.149247ms)
|
||||
✔ assign and reassign move the role bots and record task.assigned (327.871314ms)
|
||||
✔ task.update.assigned is for the assignee and records task.state (321.940093ms)
|
||||
✔ a wrong expected digest records task.conflict and writes nothing (204.338148ms)
|
||||
✔ a cross-role verb needs a resolved decision, used once (249.893912ms)
|
||||
✔ task.close needs a verdict; after it every verb refuses with task-done (212.693779ms)
|
||||
✔ a task with pushed work closes only citing an approve verdict for its latest push, and a refusal writes nothing (290.01193ms)
|
||||
✔ a lost answer is settled by a re-read and never retried (198.105853ms)
|
||||
✔ a create whose answer is lost is reported uncertain, and the poll finds the task (114.057935ms)
|
||||
✔ a task the sync bot cannot read refuses and records nothing (101.58535ms)
|
||||
✔ verbs and polls for one business run one at a time (164.603975ms)
|
||||
✔ a due date with milliseconds is written to the second (166.562306ms)
|
||||
✔ every write landed and the final read failed: the verb succeeds and records what it wrote (126.58288ms)
|
||||
✔ some writes landed and the final read failed: write-uncertain, and nothing is recorded (84.782764ms)
|
||||
✔ a create whose final read fails succeeds and records task.created (97.408681ms)
|
||||
✔ an edit between the last write and the final read shows as external on the next poll (111.64477ms)
|
||||
✔ task.created is recorded when a later label write fails (77.201145ms)
|
||||
✔ every verb acts in an installed child, on the child's own board (269.18855ms)
|
||||
✔ task.create refuses a project outside the tree, a watch-only one and one that is not an id (163.809333ms)
|
||||
✔ with descendants off, task.create takes only the root project (88.311416ms)
|
||||
✔ a verb on a watch-only task, or a relation to one, refuses with project-not-installed (123.722613ms)
|
||||
✔ a related task is read before authority: in another project than its ref names, task-moved; missing, task-not-found (157.643683ms)
|
||||
✔ a task moved to another project of the tree refuses its old ref with task-moved and takes its new one (104.913093ms)
|
||||
✔ a verb and a create in the child record the final read of the child's board (94.010221ms)
|
||||
✔ a create in the child whose final read fails records the child's todo bucket (99.921912ms)
|
||||
✔ a lost answer in the child is settled by a re-read of the child's board (122.43079ms)
|
||||
✔ every related task is read before authority, not only the first (102.008182ms)
|
||||
ℹ tests 92
|
||||
ℹ suites 0
|
||||
ℹ pass 92
|
||||
ℹ fail 0
|
||||
ℹ cancelled 0
|
||||
ℹ skipped 0
|
||||
ℹ todo 0
|
||||
ℹ duration_ms 5434.915646
|
||||
@@ -0,0 +1,117 @@
|
||||
✔ the Board: held roles with no machine sessions, a claim with no address, the toggle kept across a reload, and Message addressed to the role (5885.399836ms)
|
||||
✔ a redirect answer on a Board read, a bus read, a conversation read, a board POST, a module read and a send signs the page out: one line, nothing stale, no further reads (7298.818167ms)
|
||||
✔ browser edge states: loading, empty, malformed, stale, hostile/long values, in-flight reply and appearance fallback (5306.121922ms)
|
||||
Rendered contrast: {"failures":[],"count":330,"lowest":4.504658476260286}
|
||||
✔ served Console browser: real board fixtures, keyboard, drafts, receipts, themes, 320px and failures (5327.981101ms)
|
||||
✔ the README says a refused field table names the file or the key at fault, never a path (1.336106ms)
|
||||
✔ S5 pages in a browser: real broker rows, inert text, Copy, routes, failures, layout and no writes (8017.184146ms)
|
||||
✔ bus routes serve the four reads from the reader view, unescaped JSON for the page to escape (314.948348ms)
|
||||
✔ decisions route serves the roles and recent lists from the reader view (397.874461ms)
|
||||
✔ credentials route serves the broker credential status from the reader view (211.644191ms)
|
||||
✔ replies route serves the replies to one message from the reader view (688.833472ms)
|
||||
✔ bus routes are GET-only, have no write verb, and keep the same-origin checks (255.139584ms)
|
||||
✔ bus refusals map to statuses and never carry a path or a stack (169.581559ms)
|
||||
✔ humanCall speaks the human transport protocol without blocking the server (1971.704846ms)
|
||||
✔ busReader takes --business, else the live bus host, else refuses (296.039739ms)
|
||||
✔ humanCall kills a transport that runs past its timeout (530.602442ms)
|
||||
✔ serve refuses a --business value that is not a business id (208.229866ms)
|
||||
✔ conversation view: full history, collapsed tools, hidden thinking, inert hostile content, malformed and reconcile markers (5395.583831ms)
|
||||
✔ conversation view: a fork keeps the open branch, says so, and opens the new one on request (3386.272574ms)
|
||||
✔ conversation view: a newer session with no readable history keeps the marker (1845.846259ms)
|
||||
✔ conversation view: seats without history say so and offer no reply (1819.496517ms)
|
||||
✔ Discord row through real board/WebUI: independent brake/liveness, no Reply, literal content (5591.402891ms)
|
||||
✔ return flow through the conversation view: send, tool call, delayed result, peer message, exact long answers, relaunch (54357.545284ms)
|
||||
✔ messages HTTP read pages and preserves metadata; bad arguments refuse before a read; other reads unchanged (360.562391ms)
|
||||
✔ Console Messages lists late unsolicited messages on polling with sender class time read state and trail; nav count includes beyond the page; reads never deliver (9586.2972ms)
|
||||
✔ Messages nav count polls on Tasks and Board and clears on a Board refusal; messages 403 refuses local pages (3042.446427ms)
|
||||
✔ copy writes the Console paths at the full id, read-only, and leaves an existing copy alone (2651.354854ms)
|
||||
✔ copy refuses a commit off refactor, an unknown one, a worktree and a bad copies directory (3993.069348ms)
|
||||
✔ copy writes nothing when an archived path is missing at the commit (588.712539ms)
|
||||
✔ a copy that loses the rename race leaves no read-only temp directory behind (1139.700172ms)
|
||||
✔ a copy whose rename fails with no target refuses, writes no copy and leaves no temp directory (631.661298ms)
|
||||
✔ a copy that meets a target holding files says exists: and moves nothing into it (785.213833ms)
|
||||
✔ a copy that meets an empty target directory replaces it by rename, not by moving into it (504.839881ms)
|
||||
✔ serve runs from a copy: /healthz 200, the config check, roles and RELEASE from the copy, the queue from --checkout (1129.659173ms)
|
||||
✔ serve refuses a --checkout that is not an absolute directory path (227.185644ms)
|
||||
✔ consoleReads runs the queue read under queueRoot and reads RELEASE under root (104.306117ms)
|
||||
✔ preview options are all three or none, and each is checked (9.759077ms)
|
||||
✔ the CLI refuses a partial or invalid preview flag set at start (2430.749385ms)
|
||||
✔ the CLI with all three flags serves the preview mode (203.281323ms)
|
||||
✔ a request for the public name needs the header equal to the user id, else 401 and nothing read (18.353402ms)
|
||||
✔ the header name matches without regard to case, and the value compare is exact (4.168242ms)
|
||||
✔ the public name with the header reads the bus views, and the Host and Origin are checked first (49.794863ms)
|
||||
✔ in preview mode every write answers 403 read-only, from the public name and loopback, and reaches nothing (19.597793ms)
|
||||
✔ loopback requests are unchanged in preview mode, and the bus views work with the board down (8.752628ms)
|
||||
✔ a proxy that rewrites Host to the loopback port gets 403 on its first request, whatever the user (33.676104ms)
|
||||
✔ a repeated Host is 400 before any other check, in preview mode and without it (7.455122ms)
|
||||
✔ without the preview flags the public name is refused and writes still reach the board (6.979433ms)
|
||||
✔ redactor: config, dataRoot, any absolute path, Discord ids and the JSON parser quote (1.938007ms)
|
||||
✔ credential state from the date alone, as the bus states it (0.241777ms)
|
||||
✔ queue: rows, one row, ids and methods, notes; no path or id from a row (1717.752702ms)
|
||||
✔ queue: a refused read fails closed with the store's text, redacted (1329.622472ms)
|
||||
✔ queue: a store that fails to load, or a child that dies, gives a fixed message with no path or stack (4363.088183ms)
|
||||
✔ business: names, vars on the allowlist, authority, credential metadata only (13.586009ms)
|
||||
✔ business: missing or invalid file refuses with the module's text, redacted (79.817675ms)
|
||||
✔ business without --business: the live bus host, else no-bus-host; no system config: not-configured (12.033707ms)
|
||||
✔ settings: release, board, loopback address, notifier binding name only (546.11547ms)
|
||||
✔ task fields: the committed table with no reads configured, GET only; a bad table refuses with no path (90.610044ms)
|
||||
✔ both presentations replace old activity with relaunch notice, label retained history, then resume after new activity (5597.687494ms)
|
||||
✔ reported return flow and relative Age: reply sent from the inspector, then the new answer appears there without manual refresh (22749.742891ms)
|
||||
✔ the send box: held roles, the draft and caret across a refresh, each receipt, one POST per Send, and the trail link; the board Reply on a 4xx and a 5xx (6778.129084ms)
|
||||
✔ a sent receipt lists its replies as the page refreshes, oldest first and capped, and marks none delivered or read (13141.581201ms)
|
||||
✔ startServer takes send only as a function and only with the preview mode (7.050914ms)
|
||||
✔ the CLI refuses --send without the preview flags, and without a configured bus (416.359879ms)
|
||||
✔ the CLI with --send and a configured bus sends through the human transport and logs one line (117.699407ms)
|
||||
✔ without --send the route is read-only like every other write, and config says no send (6.529225ms)
|
||||
✔ with --send every other write is still 403 read-only, and nothing reaches the board or bus (4.834549ms)
|
||||
✔ config says send only on the public name with --send on (1.581171ms)
|
||||
✔ a send without the signed-in user is 401 and is neither sent nor logged, loopback included (3.139268ms)
|
||||
✔ each request check refuses before anything is sent, and logs the refusal without the body (11.357171ms)
|
||||
✔ a send passes exactly to, body and in_reply_to, answers the id, and logs it (3.27015ms)
|
||||
✔ a crafted role name cannot forge a log line (2.260408ms)
|
||||
✔ a bus refusal is not sent, with its status; an outcome the bus could not confirm is unknown and never retried (6.277647ms)
|
||||
✔ through the human transport the request is exactly the business, message.send and the keys (81.892239ms)
|
||||
✔ against a real broker: the message is written once as a human send, and its refusals map (2000.607183ms)
|
||||
✔ loopback host and board origin fail closed (4.967648ms)
|
||||
✔ real board fixture passes through WebUI; assets and isolated seen/reply work (256.252754ms)
|
||||
✔ proxy preserves exact request bytes, status and receipt, rejects forms and malformed JSON, never follows redirect (56.776932ms)
|
||||
✔ unreachable board reports URL; CLI rejects unsupported options (715.672874ms)
|
||||
✔ render prints the unit without the preview flags (16.671023ms)
|
||||
✔ render carries every serve flag, the preview set included (8.948057ms)
|
||||
✔ a partial preview set is refused (71.158549ms)
|
||||
✔ a value the unit cannot carry is refused, for every flag (712.245554ms)
|
||||
✔ the rendered unit names none of the human proof agent markers and sets no agent variable (45.563199ms)
|
||||
✔ install into --dir with --no-reload writes the file whole, then reports unchanged (152.255012ms)
|
||||
✔ install without --no-reload runs daemon-reload and nothing that starts the unit (45.567561ms)
|
||||
✔ uninstall refuses while the unit is running, with or without --no-reload, and removes it once it is not (314.5105ms)
|
||||
✔ status prints the unit state and GET /healthz on the installed port (322.077789ms)
|
||||
✔ usage: an unknown verb or flag, and start or enable, exit 4 (191.272846ms)
|
||||
✔ without --send, render is the template with its placeholders filled, byte for byte (56.664395ms)
|
||||
✔ render --send with the preview set ends ExecStart with --send and changes nothing else (98.544019ms)
|
||||
✔ --send without the whole preview set is refused and writes nothing (256.654595ms)
|
||||
✔ install --send into --dir with --no-reload writes the sending unit, and status says whether it sends (323.086448ms)
|
||||
✔ uninstall and status take no --send (45.240051ms)
|
||||
✔ install refuses while the unit is running, in any state but inactive or failed, and changes nothing (458.018257ms)
|
||||
✔ when the state read fails, install and uninstall refuse with one line and change nothing (65.65361ms)
|
||||
✔ install needs --copy naming a copy that exists, and refuses outside the canonical checkout without --allow-worktree (62.233918ms)
|
||||
✔ install refuses unless the bus unit runs from the same copy, naming both paths, and changes nothing (345.361819ms)
|
||||
✔ render --copy runs the copy and reads the queue in the checkout; without --copy the unit is unchanged (27.093096ms)
|
||||
✔ the uid header takes only an Authentik uid; the username header still takes a username (535.746614ms)
|
||||
✔ a preview value cannot start with - or . (51.030119ms)
|
||||
✔ status names the copy and whether refactor has moved past it, and says when the unit runs from no copy (599.839825ms)
|
||||
✔ tokens.css and icons.svg are byte copies of docs/design (0.861407ms)
|
||||
✔ JetBrains Mono files match the digests in their sources file, with the OFL text (1.557109ms)
|
||||
✔ shipped strings are unchanged, and the web keeps its write boundary (1.922485ms)
|
||||
✔ shell in a browser: System default, seeded states, 400px, focus and the Ctrl+K palette (7329.929573ms)
|
||||
✔ five states on Board, Inbox, Tasks, Agents and Trail (2514.064602ms)
|
||||
✔ Queue, Business and Settings in their five states, with no move button (4780.666996ms)
|
||||
✔ seeded secrets, Discord ids and paths reach no response and no page; business refusals in the module's words (2517.80111ms)
|
||||
✔ the server log names no config path when the system config refuses (316.736639ms)
|
||||
ℹ tests 108
|
||||
ℹ suites 0
|
||||
ℹ pass 108
|
||||
ℹ fail 0
|
||||
ℹ cancelled 0
|
||||
ℹ skipped 0
|
||||
ℹ todo 0
|
||||
ℹ duration_ms 54783.596269
|
||||
@@ -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,55 @@
|
||||
Note: switching to 'badfa52b9d5b956392bd9cb141ff1f4ce9f402f0'.
|
||||
|
||||
You are in 'detached HEAD' state. You can look around, make experimental
|
||||
changes and commit them, and you can discard any commits you make in this
|
||||
state without impacting any branches by switching back to a branch.
|
||||
|
||||
If you want to create a new branch to retain commits you create, you may
|
||||
do so (now or later) by using -c with the switch command. Example:
|
||||
|
||||
git switch -c <new-branch-name>
|
||||
|
||||
Or undo this operation with:
|
||||
|
||||
git switch -
|
||||
|
||||
Turn off this advice by setting config variable advice.detachedHead to false
|
||||
|
||||
Not currently on any branch.
|
||||
nothing to commit, working tree clean
|
||||
Note: switching to 'badfa52b9d5b956392bd9cb141ff1f4ce9f402f0'.
|
||||
|
||||
You are in 'detached HEAD' state. You can look around, make experimental
|
||||
changes and commit them, and you can discard any commits you make in this
|
||||
state without impacting any branches by switching back to a branch.
|
||||
|
||||
If you want to create a new branch to retain commits you create, you may
|
||||
do so (now or later) by using -c with the switch command. Example:
|
||||
|
||||
git switch -c <new-branch-name>
|
||||
|
||||
Or undo this operation with:
|
||||
|
||||
git switch -
|
||||
|
||||
Turn off this advice by setting config variable advice.detachedHead to false
|
||||
|
||||
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: 2038860 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,36 @@
|
||||
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/gate-owner.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 156
|
||||
ℹ pass 156
|
||||
ℹ 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/r110/wt) is not the queue's canonical root (/mnt/storage/src/mosaic-stack)
|
||||
|
||||
queue suite: 28 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,101 @@
|
||||
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)
|
||||
OK mock adapter: gate passes on matching mock response (exit 0)
|
||||
OK mock adapter: expect-mismatch recorded (exit 1)
|
||||
OK mismatch reason recorded
|
||||
OK unknown adapter fails closed (exit 1)
|
||||
OK mission task runs via mock adapter (exit 0)
|
||||
OK mission section injected into generated prompt
|
||||
OK retry of mission run succeeds (exit 0)
|
||||
OK retriedFrom lineage recorded
|
||||
OK mission section present after retry (relative path resolved)
|
||||
OK retry of missing run exits 4 (exit 4)
|
||||
OK skill install bundled ms-tools (exit 0)
|
||||
OK installed to skills-available
|
||||
OK double install refuses (exit 1)
|
||||
OK activate enables skill (exit 0)
|
||||
OK enabled dir populated
|
||||
OK uninstall while enabled refuses (exit 1)
|
||||
OK deactivate moves back to available (exit 0)
|
||||
OK uninstall removes from available (exit 0)
|
||||
OK seat with enabled skill launches
|
||||
OK skill path delivered to adapter
|
||||
OK shipped researcher contract resolves (ceiling + network)
|
||||
OK resolve-role refuses a non-role document (conductor policy) (exit 2)
|
||||
OK resolve-role refuses name/filename mismatch (exit 2)
|
||||
OK resolve-role refuses unknown network declaration (exit 2)
|
||||
OK resolve-role refuses duplicate role tool (exit 2)
|
||||
OK resolve-role refuses unsupported tool (exit 2)
|
||||
OK resolve-role refuses missing contract file (exit 4)
|
||||
OK version 1 role prints no contract line
|
||||
OK shipped version 2 roles resolve with their contracts (pm, cto, coder, reviewer)
|
||||
OK resolve-role accepts a minimal version 2 role (exit 0)
|
||||
OK resolve-role refuses an action outside the vocabulary (exit 2)
|
||||
OK resolve-role refuses a gated-only action in a role file (exit 2)
|
||||
OK resolve-role refuses a Vikunja verb no role may hold (exit 2)
|
||||
OK resolve-role refuses a version 2 role whose contract is missing (exit 2)
|
||||
OK resolve-role refuses a contract that is a symbolic link (exit 2)
|
||||
OK seat launches under role ceiling
|
||||
OK role ceiling narrows seat tools (read,write,bash -> read,bash)
|
||||
OK narrowing recorded loudly
|
||||
OK missing role contract refuses launch (exit 2)
|
||||
OK missing-contract refusal names the role
|
||||
OK empty ceiling intersection -> tool-free seat
|
||||
OK tool-free outcome recorded loudly
|
||||
OK overridden agents dir without seat definition refuses (exit 4)
|
||||
OK seat-resolution refusal names the seat
|
||||
OK policy: mission only -> mission tools
|
||||
OK policy: task only -> task tools
|
||||
OK policy: both -> intersection (task narrowed)
|
||||
OK policy: empty intersection -> tool-free
|
||||
OK invalid mission capabilities rejected (exit 2)
|
||||
mosaic-task: unsupported mission tool: "sudo" (supported: read, write, edit, bash, grep, find, ls)
|
||||
OK task run with user layer present (exit 0)
|
||||
OK user context dispatched into generated prompt
|
||||
OK workspace+tools task runs via mock (exit 0)
|
||||
OK workspace path + tools delivered to adapter
|
||||
OK persistent workspace created on host
|
||||
OK plain task still runs (no workspace/tools) (exit 0)
|
||||
OK workspace var present but empty when absent
|
||||
OK tools empty when absent
|
||||
OK unknown tool exits 2 (exit 2)
|
||||
OK workspace traversal exits 2 (exit 2)
|
||||
OK live hello task succeeds with exact marker
|
||||
OK result.json written in run dir
|
||||
OK result.json contents are correct
|
||||
OK wrong expectExact fails with exit 1 (reason: expect-mismatch)
|
||||
OK each run gets a distinct run dir (no clobber)
|
||||
OK list shows both runs
|
||||
OK fork base: teach succeeds (exit 0)
|
||||
OK fork child recalls ancestor context (exit 0)
|
||||
OK forked child recalled ancestor code word
|
||||
OK ancestor session untouched by fork
|
||||
OK child session dir has its own branch file
|
||||
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
|
||||
OK user recall run succeeds
|
||||
OK recalled user name (response: Jason)
|
||||
OK no agent identity on headless run
|
||||
|
||||
selftest: 98 passed, 0 failed
|
||||
@@ -0,0 +1,155 @@
|
||||
✔ agents: a held role with nothing on the bus is idle; a role session gets the claim rows unchanged (138.020751ms)
|
||||
✔ agents: working counts what the holder received and has not read; routed alone is not working (195.259497ms)
|
||||
✔ agents: input needed is the role's open decisions routed to the human, gated first, then oldest (332.171145ms)
|
||||
✔ agents: working wins over waiting, and Input needed still shows (163.268103ms)
|
||||
✔ agents: a new holder doesn't inherit what the last one left unread; decisions stay with the role (208.11145ms)
|
||||
✔ agents: state reads only its own business, even for a run name both use (193.679047ms)
|
||||
✔ agents: a message the holder released unread is stranded; the next holder gets nothing and cannot read it (206.126346ms)
|
||||
✔ agents: a message the holder read is not stranded; one the current holder has not read is not listed (205.00062ms)
|
||||
✔ agents: stranded is oldest first and capped at 50 for a role (835.857882ms)
|
||||
✔ agents: stranded is per role and per business (288.881804ms)
|
||||
✔ agents: a message routed to a held role and not yet received is not stranded (113.813059ms)
|
||||
✔ launch identity is stamped, payload identity is refused and stale holder cannot send (157.028727ms)
|
||||
✔ decision classes route from policy; gated resolution is human-only, choice and target must match (243.963503ms)
|
||||
✔ claim exclusion, holder release, gated revoke and rerouting to a new holder are atomic (279.311212ms)
|
||||
✔ launch events require a human CLI capability; generic emit cannot forge authority events (174.490757ms)
|
||||
✔ within-role decisions close atomically and invalid options or blocking omissions refuse (177.661047ms)
|
||||
✔ observer capabilities read human inbox but cannot mutate or forge launch identity (176.722541ms)
|
||||
✔ task action subjects and linked decision trail are complete and ordered (186.972945ms)
|
||||
✔ launch binding is durable and reconnecting requires the identical trusted record (110.775321ms)
|
||||
✔ business isolation includes inherited object names and cross-business message references (177.253193ms)
|
||||
✔ authority never transfers between action, run, target, unresolved or replaced role holder (310.418882ms)
|
||||
✔ task projection uses schema current view, skipping earlier and equal-start polls (147.985624ms)
|
||||
✔ revocation permanently bars the old run from reclaiming first, including after broker restart (214.535741ms)
|
||||
✔ empty message references refuse before storage; refusal-evidence failure stays a typed error (135.291412ms)
|
||||
✔ both arbiters require human resolution when their cross-role route is themselves (201.400263ms)
|
||||
✔ the shipped coder role raises task.reassign as gated to the human; task.scope.change stays cross-role (135.501667ms)
|
||||
✔ S1 adapter takes resolved limits and refs, rejects mismatched instance, never mutates input (2.201112ms)
|
||||
✔ credentials gives human and reader sessions service, account, role, date and state, and nothing else (102.468456ms)
|
||||
✔ credentials refuses a role session and any argument (156.737361ms)
|
||||
✔ credentials reads the status on each request, and a broker without one lists none (222.464436ms)
|
||||
✔ the broker process answers credentials without a token, path or variable in its output, log or store (181.146872ms)
|
||||
✔ only validated broker references load; returned data and exceptions cannot expose a known token (4.572004ms)
|
||||
✔ bad file modes, symlinks, repository/data paths, malformed tokens and missing dates refuse (3.048566ms)
|
||||
✔ expiry refuses use and env references never become client data (0.977687ms)
|
||||
✔ S1 parsed service refs work, service mismatch refuses, Gitea rotation due is a warning state (1.76097ms)
|
||||
✔ opaque tokens shorter than 16 characters refuse before use (0.406372ms)
|
||||
✔ roles lists open decisions routed to a role, and never the human inbox (210.594645ms)
|
||||
✔ recent lists closed decisions of every route, newest close first, with how each closed (277.635217ms)
|
||||
✔ recent gives the closing event as closed when the decision was seen first (289.213147ms)
|
||||
✔ recent stops at RECENT rows (798.760776ms)
|
||||
✔ every row is what trail already gives the same session for that id (284.795382ms)
|
||||
✔ a role session can't list decisions, so it can't read another role's open decisions (386.748085ms)
|
||||
✔ decisions is a read: one business, strict arguments, nothing written (334.027208ms)
|
||||
✔ endLaunch writes session.ended, releases the run claim and kills its capabilities (127.277858ms)
|
||||
✔ endLaunch refuses an unknown run, a second end and a rebind of the ended run (126.251011ms)
|
||||
✔ a restarted broker refuses to rebind an ended run; a refused rebind leaves the run unbound (145.747979ms)
|
||||
✔ endLaunch leaves a claim another run took alone (189.953625ms)
|
||||
✔ refuse records action.refused against the caller with the code only (134.320109ms)
|
||||
✔ launches off refuses role.launch with launch-revoked until launches on (197.826287ms)
|
||||
✔ broker process: launch ops authorize role.launch, record refusals and end runs (267.730222ms)
|
||||
✔ human proof binds CLI entry, process start and nonce; agents and incomplete ancestry refuse (3.225122ms)
|
||||
✔ process reader gets own kernel identity without exposing environment values (0.774249ms)
|
||||
✔ EACCES ancestor environments skip only markers; commands and registered launches still refuse (0.939342ms)
|
||||
✔ real pid 1 remains inspectable when its environment is protected (0.383178ms)
|
||||
✔ within-role sends cite an open gated launch decision without spending it or naming it in grants (190.707105ms)
|
||||
✔ missing and foreign-business citations refuse and roll back message and grant (172.256748ms)
|
||||
✔ cross-role sends still need a matching resolved decision and consume it once (247.74663ms)
|
||||
✔ messages pages all human messages newest first without writes; read state is history (2520.124928ms)
|
||||
✔ messages refuses role callers, invalid paging and cursors outside this human feed (219.955323ms)
|
||||
✔ views messages passes only its paging arguments through (0.34904ms)
|
||||
✔ messages isolates businesses even with a valid foreign cursor (139.10302ms)
|
||||
✔ broker process binds trusted launches, offers reader capabilities, refuses human mutation, closes cleanly (182.320684ms)
|
||||
✔ startup token refusal returns safe code without value or partial listening broker (38.756026ms)
|
||||
✔ loaded fixture token is absent from socket replies and SQLite, including refusal evidence (164.321481ms)
|
||||
✔ killed broker leaves an explicit stale lock; another process cannot silently reclaim it (189.971556ms)
|
||||
✔ trusted host registers later launches; socket clients never have a registration verb (192.93966ms)
|
||||
✔ runtime excludes declared project roots even when host supplies no repoRoots (37.124919ms)
|
||||
✔ a refused launch binding leaves the broker and existing capabilities alive; bad protocol stops it (166.115828ms)
|
||||
✔ a reply is listed for human and reader sessions, and its delivery rows are unchanged (210.81233ms)
|
||||
✔ the reply still reaches the human message.receive (mosaic talk) after the Console showed it (188.962826ms)
|
||||
✔ a message with no reply gives an empty list (172.591786ms)
|
||||
✔ replies come oldest first and stop at REPLIES (760.253033ms)
|
||||
✔ an id that names no message in the business refuses message-not-found, and the refusal is recorded (294.056449ms)
|
||||
✔ strict arguments; a role session gets human-required (379.351581ms)
|
||||
✔ views passes replies through as one call with the id (0.289216ms)
|
||||
✔ requirements returns the committed list to a holding role session, and no one else (203.812425ms)
|
||||
✔ the PM's prd.approve with no task, when no id fits, reaches the human's Inbox (142.858637ms)
|
||||
✔ the PM's non-blocking role.revoke with no task reaches the human's Inbox (179.256156ms)
|
||||
✔ a push is recorded as the action.allowed for git.push.working on the task, and only the host records one (229.490383ms)
|
||||
✔ review.request names the latest pushed commit and sends the reviewer a REVIEW-REQUEST (240.990608ms)
|
||||
✔ review.request says which run holds the reviewer role, or null, and a null request waits for a claim (302.383803ms)
|
||||
✔ review.request refuses a commit that isn't the latest push, a non-reviewer and a role without the action (388.946637ms)
|
||||
✔ reviewFetch answers only the addressed reviewer, for the request it cites (244.486295ms)
|
||||
✔ review.verdict answers one request once, to the role that asked (293.331733ms)
|
||||
✔ task.close on a pushed task needs an approve verdict for the latest push (396.862644ms)
|
||||
✔ a refusal the host decided is filed on the task it names, and only a task ref counts (234.248493ms)
|
||||
✔ v3b prototype refusals, views and append-only mutations (1062.077061ms)
|
||||
✔ gated approval authorizes once, survives store reopen, and fresh approval works (249.800129ms)
|
||||
✔ another run cannot consume an approval; a failed check leaves it usable (226.927597ms)
|
||||
✔ two scheduled callers have exactly one grant and one consumed refusal (166.783264ms)
|
||||
✔ failed commit rolls consumption back; cross-role consumes and within-role stays reusable (342.183752ms)
|
||||
✔ class drift gated to cross-role refuses before consumption (225.579895ms)
|
||||
✔ class drift cross-role to gated refuses before consumption (250.303942ms)
|
||||
✔ class drift gated to within-role refuses before consumption (218.791088ms)
|
||||
✔ class drift cross-role to within-role refuses before consumption (269.196624ms)
|
||||
✔ class drift within-role to gated refuses before consumption (203.48143ms)
|
||||
✔ class drift within-role to cross-role refuses before consumption (183.015783ms)
|
||||
✔ message.send consumes approval and prevents a later send or authorize (195.402229ms)
|
||||
✔ role.revoke consumes approval and prevents a later revoke or authorize (223.840849ms)
|
||||
✔ a business that never switched launches reads allowed with no time and no event (148.982834ms)
|
||||
✔ launches revoked and then restored read allowed, with the restore time and event id (148.49073ms)
|
||||
✔ a task closed on the tracker leaves the open list, and total counts the open ones (162.002882ms)
|
||||
✔ a tombstone is not open (144.180013ms)
|
||||
✔ the open list stops at the cap and says so; total still counts every open task (150.682203ms)
|
||||
✔ a role and a reader session read state; arguments, and an unbound cap, refuse (139.240508ms)
|
||||
✔ a human session reads the same state, with the same cap (154.963925ms)
|
||||
✔ reading state writes nothing: every table keeps its row count (155.236814ms)
|
||||
✔ creates private WAL store and excludes a second writer until explicit close (129.799975ms)
|
||||
✔ rollback is atomic and schema metadata is checked against trusted DDL, not just itself (168.964562ms)
|
||||
✔ existing empty database and symlink runtime directory refuse, never initialize over damage (184.858036ms)
|
||||
✔ crash during a transaction recovers no partial event after explicit fixture-only lock removal (177.855541ms)
|
||||
✔ writer refuses mixed at/read_at forms atomically, even through trusted SQL helpers (122.427622ms)
|
||||
✔ async transactions refuse before invoking their function (98.821466ms)
|
||||
✔ the session default fits under the reply cap and pages to the end with next (307.517006ms)
|
||||
✔ the short form: no description, assignees by role, every listed key (251.858396ms)
|
||||
✔ each filter, alone and together, and total counts every match (320.543148ms)
|
||||
✔ task_ref reads one task in full, as the whole board has it (287.750214ms)
|
||||
✔ no arguments is the read the Console and mosaic make, unchanged (262.795777ms)
|
||||
✔ an unknown argument or a bad value refuses invalid-request (884.855283ms)
|
||||
✔ tasks rows carry priority and requirement for every session that reads tasks (314.473115ms)
|
||||
✔ the read model rebuilds from the ledger: a reopened bus and each trail give the same values (339.963358ms)
|
||||
✔ an empty business and one with snapshots but no task.created read cleanly (121.80021ms)
|
||||
✔ recordTask keeps sync reads and a role write apart (184.623343ms)
|
||||
✔ read_at must be one canonical UTC format, so the projection compares strings safely (109.070373ms)
|
||||
✔ a bad entry refuses the whole record (120.283852ms)
|
||||
✔ taskView reads the projection for one business (133.341323ms)
|
||||
✔ requestTask hands only a holder and a task verb to the handler, and records refusals (239.383233ms)
|
||||
✔ the server sends task verbs to the adapter with its own timeout; other verbs stay synchronous (394.831557ms)
|
||||
✔ without an adapter the server refuses every task verb (207.776799ms)
|
||||
✔ the runtime refuses an invalid adapter and closes a valid one (203.305317ms)
|
||||
✔ the process loads the S3 adapter from plain-data trackers (229.157418ms)
|
||||
✔ the tester binds only when the business sets bus.tester to exactly true (809.539089ms)
|
||||
✔ a business may not name a role tester, so the tester address is never a role (267.201061ms)
|
||||
✔ every verb outside the tester list refuses tester-refused and is recorded against the tester (513.603015ms)
|
||||
✔ a tester message is from tester, starts [mos test], and writes no human.input or authority (204.206904ms)
|
||||
✔ a role answers a tester only in reply to a tester message, and the answer never reaches the human (178.23666ms)
|
||||
✔ the tester reads the human views a reader reads (74.267744ms)
|
||||
✔ tester proof binds its own CLI, start time and nonce, refuses a managed run and allows an agent (1.298467ms)
|
||||
✔ neither CLI's nonce proves the other: each proof is bound to its own CLI and nonce variable (0.396332ms)
|
||||
✔ testerNonce reads only the tester's variable and gives null on any read error (0.215849ms)
|
||||
✔ an envelope carries exactly one of cap, human and tester; no tester authenticator refuses (44.388103ms)
|
||||
✔ tester-cli.mjs binds through the runtime when the switch is on, and refuses off or under a run marker (711.673517ms)
|
||||
✔ socket capability stamps launch identity; shared views use wire, no SQL client (173.568864ms)
|
||||
✔ two wire claims serialize; a lost reply never automatically retries (172.473904ms)
|
||||
✔ malformed, oversized and identity-forging envelopes refuse without echoing input (114.494276ms)
|
||||
✔ client preserves UTF-8 when a response divides a multibyte character (12.068849ms)
|
||||
✔ committed mutation followed by dropped reply reports unknown and is never retried (137.386715ms)
|
||||
ℹ tests 147
|
||||
ℹ suites 0
|
||||
ℹ pass 147
|
||||
ℹ fail 0
|
||||
ℹ cancelled 0
|
||||
ℹ skipped 0
|
||||
ℹ todo 0
|
||||
ℹ duration_ms 3866.934393
|
||||
@@ -0,0 +1,181 @@
|
||||
✔ agents: a held role with nothing on the bus is idle; a role session gets the claim rows unchanged (132.032708ms)
|
||||
✔ agents: working counts what the holder received and has not read; routed alone is not working (194.523362ms)
|
||||
✔ agents: input needed is the role's open decisions routed to the human, gated first, then oldest (343.402878ms)
|
||||
✔ agents: working wins over waiting, and Input needed still shows (170.924425ms)
|
||||
✔ agents: a new holder doesn't inherit what the last one left unread; decisions stay with the role (208.3784ms)
|
||||
✔ agents: state reads only its own business, even for a run name both use (197.85966ms)
|
||||
✔ agents: a message the holder released unread is stranded; the next holder gets nothing and cannot read it (227.652886ms)
|
||||
✔ agents: a message the holder read is not stranded; one the current holder has not read is not listed (211.724927ms)
|
||||
✔ agents: stranded is oldest first and capped at 50 for a role (877.068064ms)
|
||||
✔ agents: stranded is per role and per business (335.450155ms)
|
||||
✔ agents: a message routed to a held role and not yet received is not stranded (124.265758ms)
|
||||
✔ launch identity is stamped, payload identity is refused and stale holder cannot send (148.565749ms)
|
||||
✔ decision classes route from policy; gated resolution is human-only, choice and target must match (267.632174ms)
|
||||
✔ claim exclusion, holder release, gated revoke and rerouting to a new holder are atomic (283.853783ms)
|
||||
✔ launch events require a human CLI capability; generic emit cannot forge authority events (186.732586ms)
|
||||
✔ within-role decisions close atomically and invalid options or blocking omissions refuse (184.445751ms)
|
||||
✔ observer capabilities read human inbox but cannot mutate or forge launch identity (186.216715ms)
|
||||
✔ task action subjects and linked decision trail are complete and ordered (207.239917ms)
|
||||
✔ launch binding is durable and reconnecting requires the identical trusted record (109.119427ms)
|
||||
✔ business isolation includes inherited object names and cross-business message references (227.076135ms)
|
||||
✔ authority never transfers between action, run, target, unresolved or replaced role holder (290.50025ms)
|
||||
✔ task projection uses schema current view, skipping earlier and equal-start polls (154.122835ms)
|
||||
✔ revocation permanently bars the old run from reclaiming first, including after broker restart (226.447413ms)
|
||||
✔ empty message references refuse before storage; refusal-evidence failure stays a typed error (144.6397ms)
|
||||
✔ both arbiters require human resolution when their cross-role route is themselves (224.335879ms)
|
||||
✔ the shipped coder role raises task.reassign as gated to the human; task.scope.change stays cross-role (172.023152ms)
|
||||
✔ S1 adapter takes resolved limits and refs, rejects mismatched instance, never mutates input (2.226522ms)
|
||||
✔ credentials gives human and reader sessions service, account, role, date and state, and nothing else (85.52517ms)
|
||||
✔ credentials refuses a role session and any argument (173.418681ms)
|
||||
✔ credentials reads the status on each request, and a broker without one lists none (225.775612ms)
|
||||
✔ the broker process answers credentials without a token, path or variable in its output, log or store (185.742864ms)
|
||||
✔ only validated broker references load; returned data and exceptions cannot expose a known token (5.070023ms)
|
||||
✔ bad file modes, symlinks, repository/data paths, malformed tokens and missing dates refuse (2.181309ms)
|
||||
✔ expiry refuses use and env references never become client data (0.798138ms)
|
||||
✔ S1 parsed service refs work, service mismatch refuses, Gitea rotation due is a warning state (1.620347ms)
|
||||
✔ opaque tokens shorter than 16 characters refuse before use (0.339593ms)
|
||||
✔ roles lists open decisions routed to a role, and never the human inbox (190.562577ms)
|
||||
✔ recent lists closed decisions of every route, newest close first, with how each closed (294.333118ms)
|
||||
✔ recent gives the closing event as closed when the decision was seen first (313.69939ms)
|
||||
✔ recent stops at RECENT rows (813.956636ms)
|
||||
✔ every row is what trail already gives the same session for that id (277.469725ms)
|
||||
✔ a role session can't list decisions, so it can't read another role's open decisions (426.368497ms)
|
||||
✔ decisions is a read: one business, strict arguments, nothing written (370.802094ms)
|
||||
✔ endLaunch writes session.ended, releases the run claim and kills its capabilities (117.802803ms)
|
||||
✔ endLaunch refuses an unknown run, a second end and a rebind of the ended run (138.700301ms)
|
||||
✔ a restarted broker refuses to rebind an ended run; a refused rebind leaves the run unbound (151.368629ms)
|
||||
✔ endLaunch leaves a claim another run took alone (184.268821ms)
|
||||
✔ refuse records action.refused against the caller with the code only (143.740364ms)
|
||||
✔ launches off refuses role.launch with launch-revoked until launches on (194.662581ms)
|
||||
✔ broker process: launch ops authorize role.launch, record refusals and end runs (253.837743ms)
|
||||
✔ human proof binds CLI entry, process start and nonce; agents and incomplete ancestry refuse (2.634756ms)
|
||||
✔ process reader gets own kernel identity without exposing environment values (0.674954ms)
|
||||
✔ EACCES ancestor environments skip only markers; commands and registered launches still refuse (0.987154ms)
|
||||
✔ real pid 1 remains inspectable when its environment is protected (0.459116ms)
|
||||
✔ within-role sends cite an open gated launch decision without spending it or naming it in grants (179.440016ms)
|
||||
✔ missing and foreign-business citations refuse and roll back message and grant (191.707756ms)
|
||||
✔ cross-role sends still need a matching resolved decision and consume it once (260.250425ms)
|
||||
✔ messages pages all human messages newest first without writes; read state is history (2609.522879ms)
|
||||
✔ messages refuses role callers, invalid paging and cursors outside this human feed (270.406578ms)
|
||||
✔ views messages passes only its paging arguments through (0.244128ms)
|
||||
✔ messages isolates businesses even with a valid foreign cursor (159.596819ms)
|
||||
✔ broker process binds trusted launches, offers reader capabilities, refuses human mutation, closes cleanly (185.430659ms)
|
||||
✔ startup token refusal returns safe code without value or partial listening broker (41.893543ms)
|
||||
✔ loaded fixture token is absent from socket replies and SQLite, including refusal evidence (181.253096ms)
|
||||
✔ killed broker leaves an explicit stale lock; another process cannot silently reclaim it (182.738395ms)
|
||||
✔ trusted host registers later launches; socket clients never have a registration verb (196.402962ms)
|
||||
✔ runtime excludes declared project roots even when host supplies no repoRoots (39.808295ms)
|
||||
✔ a refused launch binding leaves the broker and existing capabilities alive; bad protocol stops it (175.334531ms)
|
||||
✔ a reply is listed for human and reader sessions, and its delivery rows are unchanged (224.306915ms)
|
||||
✔ the reply still reaches the human message.receive (mosaic talk) after the Console showed it (210.454911ms)
|
||||
✔ a message with no reply gives an empty list (176.382149ms)
|
||||
✔ replies come oldest first and stop at REPLIES (771.060432ms)
|
||||
✔ an id that names no message in the business refuses message-not-found, and the refusal is recorded (316.193905ms)
|
||||
✔ strict arguments; a role session gets human-required (401.377497ms)
|
||||
✔ views passes replies through as one call with the id (0.253698ms)
|
||||
✔ requirements returns the committed list to a holding role session, and no one else (205.181144ms)
|
||||
✔ the PM's prd.approve with no task, when no id fits, reaches the human's Inbox (151.022179ms)
|
||||
✔ the PM's non-blocking role.revoke with no task reaches the human's Inbox (191.748612ms)
|
||||
✔ a push is recorded as the action.allowed for git.push.working on the task, and only the host records one (231.976589ms)
|
||||
✔ review.request names the latest pushed commit and sends the reviewer a REVIEW-REQUEST (267.164995ms)
|
||||
✔ review.request says which run holds the reviewer role, or null, and a null request waits for a claim (301.91025ms)
|
||||
✔ review.request refuses a commit that isn't the latest push, a non-reviewer and a role without the action (383.2094ms)
|
||||
✔ reviewFetch answers only the addressed reviewer, for the request it cites (255.895787ms)
|
||||
✔ review.verdict answers one request once, to the role that asked (336.233822ms)
|
||||
✔ task.close on a pushed task needs an approve verdict for the latest push (370.799858ms)
|
||||
✔ a refusal the host decided is filed on the task it names, and only a task ref counts (252.342304ms)
|
||||
✔ v3b prototype refusals, views and append-only mutations (1081.652091ms)
|
||||
✔ gated approval authorizes once, survives store reopen, and fresh approval works (246.692635ms)
|
||||
✔ another run cannot consume an approval; a failed check leaves it usable (239.639278ms)
|
||||
✔ two scheduled callers have exactly one grant and one consumed refusal (156.213764ms)
|
||||
✔ failed commit rolls consumption back; cross-role consumes and within-role stays reusable (328.594084ms)
|
||||
✔ class drift gated to cross-role refuses before consumption (242.763989ms)
|
||||
✔ class drift cross-role to gated refuses before consumption (263.062992ms)
|
||||
✔ class drift gated to within-role refuses before consumption (264.845804ms)
|
||||
✔ class drift cross-role to within-role refuses before consumption (259.796346ms)
|
||||
✔ class drift within-role to gated refuses before consumption (204.167061ms)
|
||||
✔ class drift within-role to cross-role refuses before consumption (200.244047ms)
|
||||
✔ message.send consumes approval and prevents a later send or authorize (222.970693ms)
|
||||
✔ role.revoke consumes approval and prevents a later revoke or authorize (264.184112ms)
|
||||
✔ a business that never switched launches reads allowed with no time and no event (131.89778ms)
|
||||
✔ launches revoked and then restored read allowed, with the restore time and event id (153.265884ms)
|
||||
✔ a task closed on the tracker leaves the open list, and total counts the open ones (165.009402ms)
|
||||
✔ a tombstone is not open (155.618706ms)
|
||||
✔ the open list stops at the cap and says so; total still counts every open task (145.876663ms)
|
||||
✔ a role and a reader session read state; arguments, and an unbound cap, refuse (146.729966ms)
|
||||
✔ a human session reads the same state, with the same cap (146.166352ms)
|
||||
✔ reading state writes nothing: every table keeps its row count (158.683289ms)
|
||||
✔ creates private WAL store and excludes a second writer until explicit close (132.239817ms)
|
||||
✔ rollback is atomic and schema metadata is checked against trusted DDL, not just itself (167.636659ms)
|
||||
✔ existing empty database and symlink runtime directory refuse, never initialize over damage (197.792271ms)
|
||||
✔ crash during a transaction recovers no partial event after explicit fixture-only lock removal (178.190488ms)
|
||||
✔ writer refuses mixed at/read_at forms atomically, even through trusted SQL helpers (111.710995ms)
|
||||
✔ async transactions refuse before invoking their function (98.657879ms)
|
||||
✔ the session default fits under the reply cap and pages to the end with next (298.182244ms)
|
||||
✔ the short form: no description, assignees by role, every listed key (243.972333ms)
|
||||
✔ each filter, alone and together, and total counts every match (335.446399ms)
|
||||
✔ task_ref reads one task in full, as the whole board has it (303.565554ms)
|
||||
✔ no arguments is the read the Console and mosaic make, unchanged (235.570418ms)
|
||||
✔ an unknown argument or a bad value refuses invalid-request (911.899826ms)
|
||||
✔ tasks rows carry priority and requirement for every session that reads tasks (315.006096ms)
|
||||
✔ the read model rebuilds from the ledger: a reopened bus and each trail give the same values (347.332902ms)
|
||||
✔ an empty business and one with snapshots but no task.created read cleanly (120.110796ms)
|
||||
✔ recordTask keeps sync reads and a role write apart (172.220037ms)
|
||||
✔ read_at must be one canonical UTC format, so the projection compares strings safely (116.581167ms)
|
||||
✔ a bad entry refuses the whole record (111.464003ms)
|
||||
✔ taskView reads the projection for one business (126.545546ms)
|
||||
✔ requestTask hands only a holder and a task verb to the handler, and records refusals (261.344985ms)
|
||||
✔ the server sends task verbs to the adapter with its own timeout; other verbs stay synchronous (399.051898ms)
|
||||
✔ without an adapter the server refuses every task verb (247.58855ms)
|
||||
✔ the runtime refuses an invalid adapter and closes a valid one (227.725356ms)
|
||||
✔ the process loads the S3 adapter from plain-data trackers (260.5554ms)
|
||||
✔ the tester binds only when the business sets bus.tester to exactly true (810.310325ms)
|
||||
✔ a business may not name a role tester, so the tester address is never a role (253.919147ms)
|
||||
✔ every verb outside the tester list refuses tester-refused and is recorded against the tester (556.247343ms)
|
||||
✔ a tester message is from tester, starts [mos test], and writes no human.input or authority (229.266188ms)
|
||||
✔ a role answers a tester only in reply to a tester message, and the answer never reaches the human (233.813615ms)
|
||||
✔ the tester reads the human views a reader reads (83.522443ms)
|
||||
✔ tester proof binds its own CLI, start time and nonce, refuses a managed run and allows an agent (1.145632ms)
|
||||
✔ neither CLI's nonce proves the other: each proof is bound to its own CLI and nonce variable (0.380863ms)
|
||||
✔ testerNonce reads only the tester's variable and gives null on any read error (0.201693ms)
|
||||
✖ an envelope carries exactly one of cap, human and tester; no tester authenticator refuses (55.167181ms)
|
||||
✔ tester-cli.mjs binds through the runtime when the switch is on, and refuses off or under a run marker (720.277048ms)
|
||||
✔ socket capability stamps launch identity; shared views use wire, no SQL client (153.645311ms)
|
||||
✔ two wire claims serialize; a lost reply never automatically retries (203.032036ms)
|
||||
✔ malformed, oversized and identity-forging envelopes refuse without echoing input (113.778094ms)
|
||||
✔ client preserves UTF-8 when a response divides a multibyte character (12.431331ms)
|
||||
✔ committed mutation followed by dropped reply reports unknown and is never retried (137.931081ms)
|
||||
ℹ tests 147
|
||||
ℹ suites 0
|
||||
ℹ pass 146
|
||||
ℹ fail 1
|
||||
ℹ cancelled 0
|
||||
ℹ skipped 0
|
||||
ℹ todo 0
|
||||
ℹ duration_ms 4006.018324
|
||||
|
||||
✖ failing tests:
|
||||
|
||||
test at tests/tester.test.mjs:308:1
|
||||
✖ an envelope carries exactly one of cap, human and tester; no tester authenticator refuses (55.167181ms)
|
||||
AssertionError [ERR_ASSERTION]: tester+cap
|
||||
+ actual - expected
|
||||
|
||||
{
|
||||
+ ok: true,
|
||||
+ result: []
|
||||
- error: 'invalid-envelope',
|
||||
- ok: false
|
||||
}
|
||||
|
||||
at TestContext.<anonymous> (file:///home/jwoltje/darkwing-scratch/r110/wt/packages/bus/tests/tester.test.mjs:337:12)
|
||||
at process.processTicksAndRejections (node:internal/process/task_queues:104:5)
|
||||
at async Test.run (node:internal/test_runner/test:1409:7)
|
||||
at async Test.processPendingSubtests (node:internal/test_runner/test:974:7) {
|
||||
generatedMessage: false,
|
||||
code: 'ERR_ASSERTION',
|
||||
actual: { ok: true, result: [] },
|
||||
expected: { ok: false, error: 'invalid-envelope' },
|
||||
operator: 'deepStrictEqual',
|
||||
diff: 'simple'
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
mutant n1 rc=0 ℹ pass 147 ℹ fail 0 ℹ skipped 0 restored=True
|
||||
SURVIVED
|
||||
--- round 2 candidate
|
||||
mutant n1 rc=1 ℹ pass 146 ℹ fail 1 ℹ skipped 0 restored=True
|
||||
✖ an envelope carries exactly one of cap, human and tester; no tester authenticator refuses (55.167181ms)
|
||||
KILLED
|
||||
@@ -0,0 +1,46 @@
|
||||
#!/usr/bin/env python3
|
||||
# Row 110 round 2 (#1604), Sage's ask in comment 28355: the narrowing mutant.
|
||||
# server.mjs:50 refuses an envelope that carries more than one of cap, human and
|
||||
# tester. The mutant narrows the conflict check so a cap counts only when it is
|
||||
# alone or is a reader cap (role null, not human). A role cap sent with a tester
|
||||
# proof then passes the envelope and the request runs as the tester.
|
||||
# Runs the FULL bus suite (node --test tests/*.test.mjs in packages/bus), not
|
||||
# one named test. Restores server.mjs from the .orig copy whatever happens.
|
||||
import os, shutil, subprocess, sys, signal
|
||||
|
||||
WT = os.path.expanduser('~/darkwing-scratch/r110/wt')
|
||||
SRC = os.path.join(WT, 'packages/bus/src/server.mjs')
|
||||
LOG = sys.argv[1]
|
||||
OLD = " [r.cap, r.human, r.tester].filter(Boolean).length !== 1\n"
|
||||
NEW = (" [r.cap && (!(r.human || r.tester) || ((c) => { try { const i = broker.identity(c); "
|
||||
"return i.role === null && !i.human; } catch { return true; } })(r.cap)), r.human, r.tester]"
|
||||
".filter(Boolean).length !== 1\n")
|
||||
|
||||
text = open(SRC).read()
|
||||
if text.count(OLD) != 1:
|
||||
sys.exit('STOP the line at server.mjs:50 is not unique or has moved')
|
||||
if os.path.exists(SRC + '.orig'):
|
||||
sys.exit('STOP server.mjs.orig already exists')
|
||||
shutil.copy2(SRC, SRC + '.orig')
|
||||
try:
|
||||
open(SRC, 'w').write(text.replace(OLD, NEW))
|
||||
env = dict(os.environ, TMPDIR=os.path.expanduser('~/darkwing-scratch/r110/tmp'))
|
||||
with open(LOG, 'w') as out:
|
||||
p = subprocess.Popen('node --test tests/*.test.mjs', shell=True, cwd=os.path.join(WT, 'packages/bus'),
|
||||
stdout=out, stderr=subprocess.STDOUT, env=env, start_new_session=True)
|
||||
try:
|
||||
rc = p.wait(timeout=600)
|
||||
except subprocess.TimeoutExpired:
|
||||
os.killpg(p.pid, signal.SIGKILL)
|
||||
p.wait()
|
||||
rc = 'timeout'
|
||||
finally:
|
||||
shutil.move(SRC + '.orig', SRC)
|
||||
restored = open(SRC).read() == text
|
||||
log = open(LOG).read()
|
||||
counts = [l for l in log.splitlines() if l.startswith(('ℹ pass', 'ℹ fail', 'ℹ skipped'))]
|
||||
failed = [l.strip() for l in log.splitlines() if l.lstrip().startswith('✖') and 'failing tests' not in l]
|
||||
print(f'mutant n1 rc={rc} {" ".join(counts)} restored={restored}')
|
||||
for f in sorted(set(failed)):
|
||||
print(' ' + f)
|
||||
print('KILLED' if rc != 0 else 'SURVIVED')
|
||||
@@ -0,0 +1,13 @@
|
||||
diff -ru a/packages/bus/tests/tester.test.mjs b/packages/bus/tests/tester.test.mjs
|
||||
--- a/packages/bus/tests/tester.test.mjs 2026-10-13 20:22:19.270339252 -0500
|
||||
+++ b/packages/bus/tests/tester.test.mjs 2026-10-13 20:22:19.272520482 -0500
|
||||
@@ -332,7 +332,8 @@
|
||||
});
|
||||
const p = { business: 'demo', pid: 2, startTime: '1', nonce };
|
||||
const cap = b.bindReader({ business: 'demo' });
|
||||
- for (const r of [{ tester: p, cap }, { tester: p, human: p }, { tester: p, human: p, cap }])
|
||||
+ const role = b.bindLaunch({ business: 'demo', role: 'pm', run: 'pm-run', harness: 'pi' });
|
||||
+ for (const r of [{ tester: p, cap }, { tester: p, cap: role }, { tester: p, human: p }, { tester: p, human: p, cap }])
|
||||
assert.deepEqual(await raw(path, { ...r, verb: 'agents' }), { ok: false, error: 'invalid-envelope' }, Object.keys(r).join('+'));
|
||||
assert.equal(calls, 0);
|
||||
assert.deepEqual(await raw(bare, { tester: p, verb: 'agents' }), { ok: false, error: 'tester-required' });
|
||||
Reference in New Issue
Block a user