agents: Sage row 70 round 1 packet (#1564, docs for mosaic on PATH)
Candidate 1126314bd522de838aad03689bed41f6b12a33c2d7f30b2eff1b38653f559ae0, 4 files on48b893871. The first gate stopped at script-queue: the pre-A2 fixture check pinned the old header comment. The check now skips comment lines (brief amended in06e964026). Second gate: 24 suites, 0 failed. Co-Authored-By: Claude Opus 5.5 <[email protected]>
This commit is contained in:
1 parent
b6a588cd20
commit
5dc0c7de74
31 files changed
+2483
No files matched your search
@@ -0,0 +1,92 @@
|
||||
# Row 70 round 1 (#1564): docs for `mosaic` on PATH
|
||||
|
||||
Sage, 2026-10-15. Three doc files and one test. Reviewer: Filbert.
|
||||
|
||||
- Brief: `docs/plans/2026-10-11_mosaic-on-path-docs.md`, pinned at blob
|
||||
d56345b76ffd (rev 1314; first pinned at f04bcaddb529, rev 1306). I updated it in 4c1b11d93 before building,
|
||||
because rows 85 (#1579) and 130 (#1625) changed the facts after it was
|
||||
written on 10-11. The update paragraph sits under "Problem"; "What
|
||||
ships" now names the stack-copy wrapper and the human/queue split.
|
||||
After the first gate I amended it again in 06e964026: the acceptance
|
||||
list now allows this row to narrow the pre-A2 fixture check, and no
|
||||
other test or source change.
|
||||
- Base: 48b893871 (`base.txt`), which has rows 49 and 123 in it. The
|
||||
patch is the same diff I first built on 4c1b11d93; it applies cleanly
|
||||
on 48b893871 and no commit between them touches a manifest path.
|
||||
- Candidate: `build.patch`, `candidate-manifest.sha256` (4 files),
|
||||
manifest file sha256 `1126314bd522de838aad03689bed41f6b12a33c2d7f30b2eff1b38653f559ae0`.
|
||||
The first build (3 files, `c8649f2e…`) failed the gate; see "Test change".
|
||||
|
||||
## What changed
|
||||
|
||||
| File | Change |
|
||||
|------|--------|
|
||||
| `scripts/mosaic` | header comment lines 10-11 replaced by seven comment lines; no code line moved or changed |
|
||||
| `docs/TOOLS.md` | seat-launch paragraph: "Not the npm-global `mosaic` CLI; run by path." replaced; links "Stack copies" in the same file |
|
||||
| `packages/seat/README.md` | "Commands" first paragraph replaced; links packages/cli/README.md "One pinned copy" |
|
||||
| `packages/queue/tests/dispatch.test.mjs` | the pre-A2 fixture check skips comment lines; see "Test change" |
|
||||
|
||||
Each place now says:
|
||||
|
||||
1. `scripts/mosaic` is the repository wrapper, run by path from the checkout.
|
||||
2. A host may put `mosaic` on PATH as a wrapper around a pinned copy's
|
||||
`scripts/mosaic` (`scripts/stack-copy.sh wrapper`). The seat README also
|
||||
names `mosaic-old` as the estate CLI it replaced on Jason's host.
|
||||
3. On a pinned host the human commands go through `mosaic` on PATH. The
|
||||
header also says `--tester`, and that the checkout exits 3 for them.
|
||||
4. Queue commands stay on `scripts/mosaic queue` in the canonical
|
||||
checkout, because a copy refuses them.
|
||||
|
||||
## Evidence for the claims
|
||||
|
||||
- Point 4: from the canonical checkout, a read-only
|
||||
`bash ~/.local/share/mosaic-stack/copies/2f57e3c5…/scripts/mosaic queue show 70`
|
||||
printed `queue: this queue code (…/copies/2f57e3c5…/packages/queue/src/store.mjs) is not under the canonical root /mnt/storage/src/mosaic-stack`
|
||||
and exited 2. The refusal is `packages/queue/src/store.mjs:85`.
|
||||
- Point 3: packages/cli/README.md "One pinned copy" (row 130 text) and
|
||||
docs/TOOLS.md "Pinned stack update", which this row links rather than
|
||||
restates.
|
||||
- Point 2: docs/TOOLS.md "Stack copies" and `scripts/stack-copy.sh`
|
||||
lines 67-71, which keep the lease line to `mosaic-old` and exec the
|
||||
copy's `scripts/mosaic`.
|
||||
- The two anchors resolve: `#stack-copies-scriptsstack-copysh` is the
|
||||
slug of TOOLS.md "## Stack copies (`scripts/stack-copy.sh`)", and
|
||||
`#one-pinned-copy-row-85-1579` is the anchor TOOLS.md already uses for
|
||||
the CLI README section.
|
||||
|
||||
## Checks
|
||||
|
||||
- `bash -n scripts/mosaic` passes. `git diff --check` is clean. The added
|
||||
lines have no em dash, curly quote or control character.
|
||||
- Gate, second run on 48b893871: `gate-run.log` and `gate/`. 24 suites,
|
||||
0 failed. 14 package suites passed 1611 tests (cli 195, seat 132,
|
||||
bus 191). The 10 script suites passed, queue 156/0 with the test
|
||||
change. No flake of #258, #261, #262 or #263 in either run.
|
||||
|
||||
## Test change
|
||||
|
||||
My first build claimed here that `dispatch.test.mjs` compares behaviour,
|
||||
not header text. That was wrong. Its test "the pre-A2 fixture is the
|
||||
script A2 changed" checks that every line of
|
||||
`packages/queue/tests/fixtures/mosaic-pre-a2.sh` still appears, in order,
|
||||
in `scripts/mosaic`. The fixture carries the two old header lines
|
||||
("Not the npm-global `mosaic` CLI ... only reachable as scripts/mosaic"),
|
||||
so the first gate stopped at script-queue with 155/1 (log kept as
|
||||
`gate-r1-run.log`).
|
||||
|
||||
Keeping those lines isn't an option: "only reachable as scripts/mosaic" is
|
||||
false on a pinned host, which is the point of this row. Editing the
|
||||
fixture would make it no longer the pre-A2 script. So the check now
|
||||
compares lines that don't start with `# `. The shebang (`#!`) stays in.
|
||||
The pin protects what the script runs, and a header rewrite can't
|
||||
change that.
|
||||
|
||||
Evidence, single file in the candidate tree: `dispatch.test.mjs` 4/0.
|
||||
Three mutants on `scripts/mosaic`, each run against the pre-A2 test,
|
||||
each fails it (fail 1): drop `set -euo pipefail`, drop the `REPO=` line,
|
||||
drop the shebang.
|
||||
|
||||
## Out of scope
|
||||
|
||||
The Q81 script, packaging for other hosts, and the queue's canonical-root
|
||||
refusal, as the brief says.
|
||||
@@ -0,0 +1 @@
|
||||
48b8938719d9809bcca8e7948b899687be2719c3
|
||||
@@ -0,0 +1,81 @@
|
||||
diff --git a/docs/TOOLS.md b/docs/TOOLS.md
|
||||
index bba405c3f..621d41412 100644
|
||||
--- a/docs/TOOLS.md
|
||||
+++ b/docs/TOOLS.md
|
||||
@@ -171,8 +171,12 @@ leaves one registration record at `<dataRoot>/seats/<layout>/<seat>/registration
|
||||
that the control board reads: seat, project, task, workspace, tmux session,
|
||||
harness, start time, pid. Rewritten on every launch. `seat task` changes the
|
||||
task field only. The four repository seats (`agents/*/launch.sh`) register
|
||||
-themselves through this path unless called with `--check`. Not the
|
||||
-npm-global `mosaic` CLI; run by path. Exit codes: the launch script's own
|
||||
+themselves through this path unless called with `--check`. Run it by
|
||||
+path from the checkout. On a pinned host, `mosaic` on PATH is a wrapper
|
||||
+around a pinned copy's `scripts/mosaic`
|
||||
+([Stack copies](#stack-copies-scriptsstack-copysh)) and carries the human
|
||||
+commands. Queue commands stay on `scripts/mosaic queue` in the canonical
|
||||
+checkout, because a copy refuses them. Exit codes: the launch script's own
|
||||
once it runs; before that 1 could not start, 2 invalid config or seat, 4
|
||||
usage. Details and the record's fields: `packages/seat/README.md`.
|
||||
|
||||
diff --git a/packages/queue/tests/dispatch.test.mjs b/packages/queue/tests/dispatch.test.mjs
|
||||
index 2280cdb2e..154e056a2 100644
|
||||
--- a/packages/queue/tests/dispatch.test.mjs
|
||||
+++ b/packages/queue/tests/dispatch.test.mjs
|
||||
@@ -102,10 +102,12 @@ test("`queue` reaches the queue CLI with the rest of the arguments", (t) => {
|
||||
test("the pre-A2 fixture is the script A2 changed", () => {
|
||||
const fixture = readFileSync(join(HERE, "fixtures", "mosaic-pre-a2.sh"), "utf8");
|
||||
const now = readFileSync(join(SCRIPTS, "mosaic"), "utf8");
|
||||
- // A2 adds lines and removes none.
|
||||
- const lines = now.split("\n");
|
||||
+ // A2 adds lines and removes none. Comment lines are left out: later rows
|
||||
+ // rewrite the header (row 70), and the pin is on what the script runs.
|
||||
+ const code = (s) => s.split("\n").filter((l) => !l.startsWith("# "));
|
||||
+ const lines = code(now);
|
||||
let i = 0;
|
||||
- for (const l of fixture.split("\n")) {
|
||||
+ for (const l of code(fixture)) {
|
||||
while (i < lines.length && lines[i] !== l) i++;
|
||||
assert.ok(i < lines.length, `pre-A2 line missing: ${l}`);
|
||||
i++;
|
||||
diff --git a/packages/seat/README.md b/packages/seat/README.md
|
||||
index 7796d8887..bc8a2b2a2 100644
|
||||
--- a/packages/seat/README.md
|
||||
+++ b/packages/seat/README.md
|
||||
@@ -10,9 +10,14 @@ Issue #1504. Plain ESM, no dependencies, Node 24 or newer.
|
||||
|
||||
## Commands
|
||||
|
||||
-The repository wrapper is `scripts/mosaic`. It is not on PATH and is not
|
||||
-the npm-global `mosaic` CLI from the estate tooling, which has no `launch`
|
||||
-or `seat` command; run it by path.
|
||||
+The repository wrapper is `scripts/mosaic`; run it by path from the
|
||||
+checkout. A host may also put `mosaic` on PATH as a wrapper around a
|
||||
+pinned copy's `scripts/mosaic` (`scripts/stack-copy.sh wrapper`). On
|
||||
+Jason's host the estate CLI it replaced is `mosaic-old`, which has no
|
||||
+`launch` or `seat` command. On a pinned host the human commands go
|
||||
+through `mosaic` on PATH, and queue commands stay on `scripts/mosaic
|
||||
+queue` in the canonical checkout, because a copy refuses them. Details:
|
||||
+[packages/cli/README.md, One pinned copy](../cli/README.md#one-pinned-copy-row-85-1579).
|
||||
|
||||
```
|
||||
scripts/mosaic launch <seat|seat-dir> [--task TEXT] [--project NAME] [--workspace PATH]
|
||||
diff --git a/scripts/mosaic b/scripts/mosaic
|
||||
index 010e1a7e6..7f9998a5a 100755
|
||||
--- a/scripts/mosaic
|
||||
+++ b/scripts/mosaic
|
||||
@@ -7,8 +7,13 @@
|
||||
# `mosaic inbox|decide|tasks|agents|trail|talk|stop|launches` and
|
||||
# `mosaic bus start|stop|status`: the human commands, managed sessions and
|
||||
# the bus host. See packages/cli/README.md.
|
||||
-# Not the npm-global `mosaic` CLI from the estate tooling; this one is
|
||||
-# repository-local and only reachable as scripts/mosaic.
|
||||
+# This is the repository wrapper; run it by path from the checkout. A host
|
||||
+# may also put `mosaic` on PATH as a wrapper that runs a pinned copy's
|
||||
+# scripts/mosaic (scripts/stack-copy.sh wrapper, docs/TOOLS.md "Stack
|
||||
+# copies"). On a pinned host the human commands and --tester go through
|
||||
+# that `mosaic`, and from a checkout they exit 3. Queue commands stay on
|
||||
+# scripts/mosaic queue in the canonical checkout, because a copy refuses
|
||||
+# them. See packages/cli/README.md "One pinned copy".
|
||||
set -euo pipefail
|
||||
REPO="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
||||
# No argument: the full-screen app on a terminal (packages/cli/README.md).
|
||||
@@ -0,0 +1,4 @@
|
||||
4aa7d08a85b7bf6440f72a61eabc13ed6540c68ee20301924610712b3bbc2484 docs/TOOLS.md
|
||||
c8b2c6bf08bc5c83e4a068aff990941730e8e2cb2cce850075a602da1649b118 packages/queue/tests/dispatch.test.mjs
|
||||
0840e4b9ebe227ac2296e9f737a51481113ff39645ee952eb22c8bcd001858c3 packages/seat/README.md
|
||||
3e6f30008155bfed29394844ec2bd41fec2e35d9b3d6fffbdea0a28029eb5a1c scripts/mosaic
|
||||
@@ -0,0 +1,24 @@
|
||||
base 4c1b11d93 dirty 3 DOCKER_HOST=unset
|
||||
pkg-bus rc=0 ℹ pass 171 ℹ fail 0 | ℹ duration_ms 1409.904509
|
||||
pkg-business rc=0 ℹ pass 92 ℹ fail 0 | ℹ duration_ms 1933.259198
|
||||
pkg-cli rc=0 ℹ pass 195 ℹ fail 0 | ℹ duration_ms 14621.468176
|
||||
pkg-control-board rc=0 ℹ pass 124 ℹ fail 0 | ℹ duration_ms 674.24669
|
||||
pkg-conversation rc=0 ℹ pass 182 ℹ fail 0 | ℹ duration_ms 35520.025592
|
||||
pkg-discord rc=0 ℹ pass 183 ℹ fail 0 | ℹ duration_ms 2619.991492
|
||||
pkg-harness rc=0 ℹ pass 84 ℹ fail 0 | ℹ duration_ms 10806.821882
|
||||
pkg-ledger rc=0 ℹ pass 86 ℹ fail 0 | ℹ duration_ms 13433.99526
|
||||
pkg-mosaic rc=0 ℹ pass 69 ℹ fail 0 | ℹ duration_ms 2167.947474
|
||||
pkg-plugins rc=0 ℹ pass 21 ℹ fail 0 | ℹ duration_ms 222.286632
|
||||
pkg-runs rc=0 ℹ pass 43 ℹ fail 0 | ℹ duration_ms 733.225916
|
||||
pkg-seat rc=0 ℹ pass 132 ℹ fail 0 | ℹ duration_ms 9238.521712
|
||||
pkg-tasks rc=0 ℹ pass 92 ℹ fail 0 | ℹ duration_ms 1224.551161
|
||||
pkg-webui rc=0 ℹ pass 112 ℹ fail 0 | ℹ duration_ms 57280.309096
|
||||
script-auth rc=0 | selftest: 15 passed, 0 failed
|
||||
script-conductor rc=0 | selftest: 17 passed, 0 failed
|
||||
script-config rc=0 | selftest: 24 passed, 0 failed
|
||||
script-discord rc=0 | discord suite: 78 passed, 0 failed
|
||||
script-extension-package rc=0 | extension package selftest: 18 passed, 0 failed
|
||||
script-foundation rc=0 | selftest: 44 passed, 0 failed
|
||||
script-land-work rc=0 | land-work selftests: 84 passed, 0 failed
|
||||
script-queue rc=1 ℹ pass 155 ℹ fail 1 | queue suite: 27 passed, 1 failed
|
||||
STOP script-queue
|
||||
@@ -0,0 +1,26 @@
|
||||
base 48b893871 dirty 4 DOCKER_HOST=unset
|
||||
pkg-bus rc=0 ℹ pass 191 ℹ fail 0 | ℹ duration_ms 1559.590174
|
||||
pkg-business rc=0 ℹ pass 92 ℹ fail 0 | ℹ duration_ms 2703.426842
|
||||
pkg-cli rc=0 ℹ pass 195 ℹ fail 0 | ℹ duration_ms 15856.640798
|
||||
pkg-control-board rc=0 ℹ pass 124 ℹ fail 0 | ℹ duration_ms 675.20037
|
||||
pkg-conversation rc=0 ℹ pass 182 ℹ fail 0 | ℹ duration_ms 37479.617696
|
||||
pkg-discord rc=0 ℹ pass 183 ℹ fail 0 | ℹ duration_ms 2711.481971
|
||||
pkg-harness rc=0 ℹ pass 84 ℹ fail 0 | ℹ duration_ms 10652.286247
|
||||
pkg-ledger rc=0 ℹ pass 86 ℹ fail 0 | ℹ duration_ms 17728.316114
|
||||
pkg-mosaic rc=0 ℹ pass 69 ℹ fail 0 | ℹ duration_ms 2246.198087
|
||||
pkg-plugins rc=0 ℹ pass 21 ℹ fail 0 | ℹ duration_ms 229.032854
|
||||
pkg-runs rc=0 ℹ pass 43 ℹ fail 0 | ℹ duration_ms 777.029734
|
||||
pkg-seat rc=0 ℹ pass 132 ℹ fail 0 | ℹ duration_ms 5083.812107
|
||||
pkg-tasks rc=0 ℹ pass 97 ℹ fail 0 | ℹ duration_ms 1138.01294
|
||||
pkg-webui rc=0 ℹ pass 112 ℹ fail 0 | ℹ duration_ms 54897.597429
|
||||
script-auth rc=0 | selftest: 15 passed, 0 failed
|
||||
script-conductor rc=0 | selftest: 17 passed, 0 failed
|
||||
script-config rc=0 | selftest: 24 passed, 0 failed
|
||||
script-discord rc=0 | discord suite: 78 passed, 0 failed
|
||||
script-extension-package rc=0 | extension package selftest: 18 passed, 0 failed
|
||||
script-foundation rc=0 | selftest: 44 passed, 0 failed
|
||||
script-land-work rc=0 | land-work selftests: 84 passed, 0 failed
|
||||
script-queue rc=0 ℹ pass 156 ℹ fail 0 | queue suite: 28 passed, 0 failed
|
||||
script-release rc=0 | selftest: 14 passed, 0 failed
|
||||
script-task rc=0 | selftest: 98 passed, 0 failed
|
||||
GATE_PASSED
|
||||
@@ -0,0 +1,199 @@
|
||||
✔ agents: a held role with nothing on the bus is idle; a role session gets the claim rows unchanged (28.067785ms)
|
||||
✔ agents: working counts what the holder received and has not read; routed alone is not working (26.453359ms)
|
||||
✔ agents: input needed is the role's open decisions routed to the human, gated first, then oldest (51.422452ms)
|
||||
✔ agents: working wins over waiting, and Input needed still shows (19.539458ms)
|
||||
✔ agents: a new holder doesn't inherit what the last one left unread; decisions stay with the role (23.41594ms)
|
||||
✔ agents: state reads only its own business, even for a run name both use (24.565002ms)
|
||||
✔ agents: a message the holder released unread is stranded; the next holder gets nothing and cannot read it (30.168439ms)
|
||||
✔ agents: a message the holder read is not stranded; one the current holder has not read is not listed (27.767143ms)
|
||||
✔ agents: stranded is oldest first and capped at 50 for a role (123.006928ms)
|
||||
✔ agents: stranded is per role and per business (50.523319ms)
|
||||
✔ agents: a message routed to a held role and not yet received is not stranded (17.219554ms)
|
||||
✔ launch identity is stamped, payload identity is refused and stale holder cannot send (29.006172ms)
|
||||
✔ decision classes route from policy; gated resolution is human-only, choice and target must match (36.148326ms)
|
||||
✔ claim exclusion, holder release, gated revoke and rerouting to a new holder are atomic (36.508171ms)
|
||||
✔ launch events require a human CLI capability; generic emit cannot forge authority events (20.76218ms)
|
||||
✔ within-role decisions close atomically and invalid options or blocking omissions refuse (21.514272ms)
|
||||
✔ a recommendation that is not exactly one option key refuses invalid-recommendation after the options (37.472628ms)
|
||||
✔ observer capabilities read human inbox but cannot mutate or forge launch identity (24.960147ms)
|
||||
✔ task action subjects and linked decision trail are complete and ordered (38.870658ms)
|
||||
✔ launch binding is durable and reconnecting requires the identical trusted record (17.359537ms)
|
||||
✔ business isolation includes inherited object names and cross-business message references (25.496232ms)
|
||||
✔ authority never transfers between action, run, target, unresolved or replaced role holder (36.358989ms)
|
||||
✔ task projection uses schema current view, skipping earlier and equal-start polls (19.996703ms)
|
||||
✔ revocation permanently bars the old run from reclaiming first, including after broker restart (36.778949ms)
|
||||
✔ empty message references refuse before storage; refusal-evidence failure stays a typed error (24.360267ms)
|
||||
✔ both arbiters require human resolution when their cross-role route is themselves (37.685334ms)
|
||||
✔ the shipped coder role raises task.reassign as gated to the human; task.scope.change stays cross-role (27.954779ms)
|
||||
✔ S1 adapter takes resolved limits and refs, rejects mismatched instance, never mutates input (2.511939ms)
|
||||
✔ REQ-COS-1, REQ-ROLE-2: the chief of staff ends a run with a reason and no decision, and the reason is logged (33.752839ms)
|
||||
✔ REQ-ROLE-2: a role without role.end still needs a resolved decision to revoke (43.254215ms)
|
||||
✔ REQ-ROLE-2: storage accepts a revoke with a reason or a decision and aborts one with neither (24.2001ms)
|
||||
✔ REQ-ROLE-2: the chief of staff may still cite a decision, and a decision alone is the old path (24.988236ms)
|
||||
✔ REQ-ROLE-2: a role without role.end still ends a run through a resolved decision, which is recorded and has no reason (29.642895ms)
|
||||
✔ REQ-ROLE-2: a reason is required, bounded, and never sent with a decision (32.309147ms)
|
||||
✔ REQ-COS-1: the chief of staff cannot end its own run or the assistant's through role.end (31.28498ms)
|
||||
✔ REQ-LIMIT-1, REQ-COS-1: organization.set from the chief of staff reaches the shared writer, named as the actor (28.952573ms)
|
||||
✔ REQ-LIMIT-1, REQ-COS-1: a refused organization.set logs action.refused and no action.allowed, like a refused limits.set (35.34328ms)
|
||||
✔ REQ-LIMIT-1: organization.set is refused for every caller whose role file lacks organization.change (35.376971ms)
|
||||
✔ REQ-LIMIT-1: the chief of staff is logged as allowed, and organization.set takes only the human's keys (46.45152ms)
|
||||
✔ REQ-ROLE-3, REQ-LIMIT-1: a chief-of-staff change is validated, written and logged like a human's; an invalid one is refused (92.429545ms)
|
||||
✔ REQ-ROLE-2, REQ-COS-1: the 3c→3d migration lets the chief of staff end a held role with a reason; neither is still refused (81.403732ms)
|
||||
✔ comment cursors isolate businesses, move only forward and remain append-only (24.98887ms)
|
||||
✔ only trusted sync writes validated cursors; events and cursor share rollback (15.065434ms)
|
||||
✔ explicit 3b copy migration preserves evidence and opens as 3d; normal startup never migrates (88.247872ms)
|
||||
✔ migration refuses active writers and drifted legacy schemas, with no partial upgrade (137.270254ms)
|
||||
✔ named migration registry chains 3b through 3d to a fixture-only future version in order (91.083961ms)
|
||||
✔ migration refuses mismatched source metadata without changing schema or evidence (81.833582ms)
|
||||
✔ offline command migrates only the supplied fixture root and reports usage errors (176.01797ms)
|
||||
✔ credentials gives human and reader sessions service, account, role, date and state, and nothing else (22.674695ms)
|
||||
✔ credentials refuses a role session and any argument (21.916526ms)
|
||||
✔ credentials reads the status on each request, and a broker without one lists none (30.874822ms)
|
||||
✔ the broker process answers credentials without a token, path or variable in its output, log or store (96.671613ms)
|
||||
✔ only validated broker references load; returned data and exceptions cannot expose a known token (4.074758ms)
|
||||
✔ bad file modes, symlinks, repository/data paths, malformed tokens and missing dates refuse (2.876085ms)
|
||||
✔ expiry refuses use and env references never become client data (0.881236ms)
|
||||
✔ S1 parsed service refs work, service mismatch refuses, Gitea rotation due is a warning state (1.919445ms)
|
||||
✔ opaque tokens shorter than 16 characters refuse before use (0.597242ms)
|
||||
✔ a human resolve sends the raising role one message from the human, and its request is the answer (45.960979ms)
|
||||
✔ a cross-role decision resolved by its arbiter comes from that role and run, with no request (33.510055ms)
|
||||
✔ a human withdraw sends one message with the note (29.032256ms)
|
||||
✔ a human seen before the close leaves the request on the close's answer, not the seen (34.710429ms)
|
||||
✔ the raising run's withdraw, a supersede, a within-role raise and seen send nothing (40.6562ms)
|
||||
✔ refused closes send nothing: invalid-choice, decision-closed, not-resolver (40.488037ms)
|
||||
✔ a raising role nobody holds at the close gets the message on the next claim (34.151803ms)
|
||||
✔ a human DECISION send keeps its own instruction as the request, not a close's answer (30.72508ms)
|
||||
✔ roles lists open decisions routed to a role, and never the human inbox (33.142959ms)
|
||||
✔ recent lists closed decisions of every route, newest close first, with how each closed (44.428015ms)
|
||||
✔ recent gives the closing event as closed when the decision was seen first (36.081644ms)
|
||||
✔ recent stops at RECENT rows (123.724583ms)
|
||||
✔ every row is what trail already gives the same session for that id (51.557192ms)
|
||||
✔ a role session can't list decisions, so it can't read another role's open decisions (59.726332ms)
|
||||
✔ decisions is a read: one business, strict arguments, nothing written (58.504551ms)
|
||||
✔ endLaunch writes session.ended, releases the run claim and kills its capabilities (24.204326ms)
|
||||
✔ endLaunch refuses an unknown run, a second end and a rebind of the ended run (18.362557ms)
|
||||
✔ a restarted broker refuses to rebind an ended run; a refused rebind leaves the run unbound (20.494177ms)
|
||||
✔ endLaunch leaves a claim another run took alone (24.330505ms)
|
||||
✔ refuse records action.refused against the caller with the code only (15.142932ms)
|
||||
✔ launches off refuses role.launch with launch-revoked until launches on (21.814041ms)
|
||||
✔ broker process: launch ops authorize role.launch, record refusals and end runs (130.546923ms)
|
||||
✔ human proof binds CLI entry, process start and nonce; agents and incomplete ancestry refuse (4.396649ms)
|
||||
✔ process reader gets own kernel identity without exposing environment values (0.712523ms)
|
||||
✔ EACCES ancestor environments skip only markers; commands and registered launches still refuse (1.025314ms)
|
||||
✔ real pid 1 remains inspectable when its environment is protected (0.424489ms)
|
||||
✔ limits binds actor from human proof and rejects role, reader, tester and forged actor (27.845675ms)
|
||||
✔ runtime wires verified human settings to the actual shared file writer and actor receipt (61.342522ms)
|
||||
✔ uncertain organization receipts leave bus startup and reads alone but refuse writes until reconciled (50.156554ms)
|
||||
✔ within-role sends cite an open gated launch decision without spending it or naming it in grants (33.748572ms)
|
||||
✔ missing and foreign-business citations refuse and roll back message and grant (26.507855ms)
|
||||
✔ cross-role sends still need a matching resolved decision and consume it once (33.408388ms)
|
||||
✔ messages pages all human messages newest first without writes; read state is history (438.908947ms)
|
||||
✔ messages refuses role callers, invalid paging and cursors outside this human feed (32.330445ms)
|
||||
✔ views messages passes only its paging arguments through (0.431361ms)
|
||||
✔ messages isolates businesses even with a valid foreign cursor (22.138374ms)
|
||||
✔ the assistant sends to the user and to the chief of staff only (44.058172ms)
|
||||
✔ an assistant send to any other role, or to the tester, is refused and logged (41.006248ms)
|
||||
✔ only the user and the chief of staff may send to the assistant; the tester is refused by default (44.476419ms)
|
||||
✔ a sender-side list alone refuses a send to a role that has no list (37.777612ms)
|
||||
✔ a target-side list alone refuses a send from a role that has no list (41.744617ms)
|
||||
✔ the sender list is checked before the target list (35.681589ms)
|
||||
✔ roles without the field behave exactly as before (49.497225ms)
|
||||
✔ a business with no role definitions at all is unlimited (41.331756ms)
|
||||
✔ a malformed messaging field refuses at boot (19.159441ms)
|
||||
✔ the assistant's review.request to a reviewer is refused and logged, even with an approved decision (46.469246ms)
|
||||
✔ a review.verdict to the assistant from a role outside its receiveFrom is refused and logged (44.478685ms)
|
||||
✔ review requests and verdicts between roles without the field are unchanged (31.401506ms)
|
||||
✔ broker process binds trusted launches, offers reader capabilities, refuses human mutation, closes cleanly (98.493646ms)
|
||||
✔ startup token refusal returns safe code without value or partial listening broker (71.537153ms)
|
||||
✔ loaded fixture token is absent from socket replies and SQLite, including refusal evidence (97.846946ms)
|
||||
✔ killed broker leaves an explicit stale lock; another process cannot silently reclaim it (154.006789ms)
|
||||
✔ trusted host registers later launches; socket clients never have a registration verb (79.906096ms)
|
||||
✔ runtime excludes declared project roots even when host supplies no repoRoots (49.574724ms)
|
||||
✔ a refused launch binding leaves the broker and existing capabilities alive; bad protocol stops it (66.774262ms)
|
||||
✔ a reply is listed for human and reader sessions, and its delivery rows are unchanged (41.706348ms)
|
||||
✔ the reply still reaches the human message.receive (mosaic talk) after the Console showed it (26.397986ms)
|
||||
✔ a message with no reply gives an empty list (19.194805ms)
|
||||
✔ replies come oldest first and stop at REPLIES (104.405804ms)
|
||||
✔ an id that names no message in the business refuses message-not-found, and the refusal is recorded (38.02522ms)
|
||||
✔ strict arguments; a role session gets human-required (49.380321ms)
|
||||
✔ views passes replies through as one call with the id (0.405789ms)
|
||||
✔ requirements returns the committed list to a holding role session, and no one else (31.12386ms)
|
||||
✔ the PM's prd.approve with no task, when no id fits, reaches the human's Inbox (21.013698ms)
|
||||
✔ the PM's non-blocking role.revoke with no task reaches the human's Inbox (23.529477ms)
|
||||
✔ a push is recorded as the action.allowed for git.push.working on the task, and only the host records one (35.682733ms)
|
||||
✔ review.request names the latest pushed commit and sends the reviewer a REVIEW-REQUEST (32.725562ms)
|
||||
✔ review.request says which run holds the reviewer role, or null, and a null request waits for a claim (38.611832ms)
|
||||
✔ review.request refuses a commit that isn't the latest push, a non-reviewer and a role without the action (48.619672ms)
|
||||
✔ reviewFetch answers only the addressed reviewer, for the request it cites (36.805365ms)
|
||||
✔ review.verdict answers one request once, to the role that asked (44.396542ms)
|
||||
✔ task.close on a pushed task needs an approve verdict for the latest push (54.502798ms)
|
||||
✔ a refusal the host decided is filed on the task it names, and only a task ref counts (31.233601ms)
|
||||
✔ v3b prototype refusals, views and append-only mutations (109.262563ms)
|
||||
✔ gated approval authorizes once, survives store reopen, and fresh approval works (42.584564ms)
|
||||
✔ another run cannot consume an approval; a failed check leaves it usable (30.377293ms)
|
||||
✔ two scheduled callers have exactly one grant and one consumed refusal (21.829364ms)
|
||||
✔ failed commit rolls consumption back; cross-role consumes and within-role stays reusable (49.227372ms)
|
||||
✔ class drift gated to cross-role refuses before consumption (34.772291ms)
|
||||
✔ class drift cross-role to gated refuses before consumption (35.102094ms)
|
||||
✔ class drift gated to within-role refuses before consumption (32.899048ms)
|
||||
✔ class drift cross-role to within-role refuses before consumption (30.712908ms)
|
||||
✔ class drift within-role to gated refuses before consumption (22.398805ms)
|
||||
✔ class drift within-role to cross-role refuses before consumption (20.268256ms)
|
||||
✔ message.send consumes approval and prevents a later send or authorize (20.263017ms)
|
||||
✔ role.revoke consumes approval and prevents a later revoke or authorize (20.426937ms)
|
||||
✔ a business that never switched launches reads allowed with no time and no event (25.323162ms)
|
||||
✔ launches revoked and then restored read allowed, with the restore time and event id (22.384089ms)
|
||||
✔ a task closed on the tracker leaves the open list, and total counts the open ones (22.004952ms)
|
||||
✔ a tombstone is not open (20.590711ms)
|
||||
✔ the open list stops at the cap and says so; total still counts every open task (26.000304ms)
|
||||
✔ a role and a reader session read state; arguments, and an unbound cap, refuse (22.012346ms)
|
||||
✔ a human session reads the same state, with the same cap (24.71103ms)
|
||||
✔ reading state writes nothing: every table keeps its row count (25.043088ms)
|
||||
✔ creates private WAL store and excludes a second writer until explicit close (24.768796ms)
|
||||
✔ rollback is atomic and schema metadata is checked against trusted DDL, not just itself (21.696845ms)
|
||||
✔ existing empty database and symlink runtime directory refuse, never initialize over damage (25.11103ms)
|
||||
✔ crash during a transaction recovers no partial event after explicit fixture-only lock removal (71.918076ms)
|
||||
✔ writer refuses mixed at/read_at forms atomically, even through trusted SQL helpers (16.703623ms)
|
||||
✔ async transactions refuse before invoking their function (10.606814ms)
|
||||
✔ the session default fits under the reply cap and pages to the end with next (188.33606ms)
|
||||
✔ the short form: no description, assignees by role, every listed key (77.774946ms)
|
||||
✔ each filter, alone and together, and total counts every match (176.438061ms)
|
||||
✔ task_ref reads one task in full, as the whole board has it (109.474745ms)
|
||||
✔ no arguments is the read the Console and mosaic make, unchanged (98.424675ms)
|
||||
✔ an unknown argument or a bad value refuses invalid-request (381.902996ms)
|
||||
✔ tasks rows carry priority and requirement for every session that reads tasks (55.161926ms)
|
||||
✔ the read model rebuilds from the ledger: a reopened bus and each trail give the same values (54.942401ms)
|
||||
✔ an empty business and one with snapshots but no task.created read cleanly (14.549408ms)
|
||||
✔ recordTask keeps sync reads and a role write apart (29.546254ms)
|
||||
✔ read_at must be one canonical UTC format, so the projection compares strings safely (14.363921ms)
|
||||
✔ a bad entry refuses the whole record (12.42493ms)
|
||||
✔ taskView reads the projection for one business (16.353759ms)
|
||||
✔ requestTask hands only a holder and a task verb to the handler, and records refusals (26.618468ms)
|
||||
✔ the server sends task verbs to the adapter with its own timeout; other verbs stay synchronous (275.792315ms)
|
||||
✔ without an adapter the server refuses every task verb (18.929423ms)
|
||||
✔ the runtime refuses an invalid adapter and closes a valid one (20.357389ms)
|
||||
✔ the process loads the S3 adapter from plain-data trackers (136.758006ms)
|
||||
✔ the tester binds only when the business sets bus.tester to exactly true (82.751545ms)
|
||||
✔ a business may not name a role tester, so the tester address is never a role (17.137016ms)
|
||||
✔ every verb outside the tester list refuses tester-refused and is recorded against the tester (34.7707ms)
|
||||
✔ a tester message is from tester, starts [mos test], and writes no human.input or authority (14.032979ms)
|
||||
✔ a role answers a tester only in reply to a tester message, and the answer never reaches the human (17.909866ms)
|
||||
✔ the tester reads the human views a reader reads (20.137198ms)
|
||||
✔ tester proof binds its own CLI, start time and nonce, refuses a managed run and allows an agent (1.289728ms)
|
||||
✔ neither CLI's nonce proves the other: each proof is bound to its own CLI and nonce variable (0.405847ms)
|
||||
✔ testerNonce reads only the tester's variable and gives null on any read error (0.247727ms)
|
||||
✔ an envelope carries exactly one of cap, human and tester; no tester authenticator refuses (18.864971ms)
|
||||
✔ tester-cli.mjs binds through the runtime when the switch is on, and refuses off or under a run marker (757.367338ms)
|
||||
✔ socket capability stamps launch identity; shared views use wire, no SQL client (32.023523ms)
|
||||
✔ two wire claims serialize; a lost reply never automatically retries (22.292329ms)
|
||||
✔ malformed, oversized and identity-forging envelopes refuse without echoing input (12.460044ms)
|
||||
✔ client preserves UTF-8 when a response divides a multibyte character (11.680008ms)
|
||||
✔ committed mutation followed by dropped reply reports unknown and is never retried (11.684186ms)
|
||||
ℹ tests 191
|
||||
ℹ suites 0
|
||||
ℹ pass 191
|
||||
ℹ fail 0
|
||||
ℹ cancelled 0
|
||||
ℹ skipped 0
|
||||
ℹ todo 0
|
||||
ℹ duration_ms 1559.590174
|
||||
@@ -0,0 +1,100 @@
|
||||
✔ the role file loads and resolves through loadBusiness (8.249303ms)
|
||||
✔ the shipped file is exactly the approved list (1.076052ms)
|
||||
✔ the assistant's authority is message.send within role and nothing else (2.841432ms)
|
||||
✔ the assistant talks to the user and the chief of staff only, and no other shipped role is limited (0.972263ms)
|
||||
✔ a business grant through limits does not widen the role (2.293792ms)
|
||||
✔ a launch block whose by is the assistant is refused (2.417502ms)
|
||||
✔ the instance takes no credential references and no tracker (3.64661ms)
|
||||
✔ the contract passes work to the chief of staff and directs no one (0.787815ms)
|
||||
✔ config directory and file path follow MOSAIC_CONFIG (2.230454ms)
|
||||
✔ the fixture business validates and comes back frozen (7.303144ms)
|
||||
✔ two instances may share a definition (2.901772ms)
|
||||
✔ top-level refusals (6.590059ms)
|
||||
✔ arbiters and projects (11.643519ms)
|
||||
✔ role instances (4.154108ms)
|
||||
✔ Vikunja bots (11.86028ms)
|
||||
✔ a role without Vikunja takes no tracker block (3.337853ms)
|
||||
✔ credential references match the definition's services (4.147753ms)
|
||||
✔ launch (12.98169ms)
|
||||
✔ loadBusiness: file checks (2.570057ms)
|
||||
✔ loadBusiness: not a regular file (44.026396ms)
|
||||
✔ loading writes nothing (1.242314ms)
|
||||
✔ names that are Object.prototype properties don't count as declared (3.106382ms)
|
||||
✔ the shipped example refuses as written and validates once filled in (1.028843ms)
|
||||
✔ the role file loads and resolves through loadBusiness (11.190626ms)
|
||||
✔ the shipped file is exactly the approved list (0.371752ms)
|
||||
✔ the resolved record matches the approved list, so a widened file fails (3.305857ms)
|
||||
✔ the chief of staff holds role.launch, message.send, role.end and organization.change; every other action is gated (2.325708ms)
|
||||
✔ the pm does not hold role.launch (2.554339ms)
|
||||
✔ a business grant of a gated action through limits does not widen the role (2.335106ms)
|
||||
✔ a limit that drops role.launch leaves the chief of staff with no launch block (2.045776ms)
|
||||
✔ a launch block whose by is the pm instance is refused (2.021139ms)
|
||||
✔ the contract names no duty to talk to the human and the pm contract no launch (0.681828ms)
|
||||
✔ the instance takes no credential references and no tracker (2.903702ms)
|
||||
✔ usage errors exit 4 (311.011509ms)
|
||||
✔ validate: a good business exits 0 and prints instance digests (71.559247ms)
|
||||
✔ validate: project files (330.205583ms)
|
||||
✔ validate: missing files and a broken system config (261.861821ms)
|
||||
✔ validate: credential reference problems exit 2 and name each one (150.246685ms)
|
||||
✔ validate: a token file inside the repository is refused (115.509175ms)
|
||||
✔ validate: role definitions come from MOSAIC_ROLES_DIR (295.298749ms)
|
||||
✔ resolve: prints one instance's record (464.132936ms)
|
||||
✔ resolve: refusals (591.155305ms)
|
||||
✔ parse: exactly one of file or env, plus the service's date (3.963491ms)
|
||||
✔ check: a good file has no problems (1.18861ms)
|
||||
✔ check never opens the file: a write-only token passes (0.53341ms)
|
||||
✔ check: file problems (1.288577ms)
|
||||
✔ check: token files can't live in the repository or dataRoot, even through a linked directory (1.294226ms)
|
||||
✔ check: dates and environment references (0.619647ms)
|
||||
✔ REQ-LIMIT-1 schema: every scope unset or zero, adjustable above four, bad values and unknown models refuse (25.388797ms)
|
||||
✔ REQ-LIMIT-1 counts each exact scope at/over limit, unlimited and old tuple after model reassignment (1.314793ms)
|
||||
✔ REQ-ROLE-3 writer: atomic validated revision, immutable previous bytes and authenticated actor/old/new receipt (38.822428ms)
|
||||
✔ REQ-ROLE-3 writer: pre-rename fault leaves old file, post-rename recovery records commit, conflicting recovery refuses (20.676777ms)
|
||||
✔ REQ-LIMIT-1 / decision145 migration prints only, preserves values, rejects ambiguity, and is idempotent (3.349368ms)
|
||||
✔ REQ-LIMIT-1 shared settings validates the complete proposed selection and logs exact actor (20.664866ms)
|
||||
✔ REQ-LIMIT-1 provider is table data: another provider routes separately and aliases share exact counts (1.868796ms)
|
||||
✔ REQ-ROLE-3 refuses live writer locks, symlink targets and uncertain recovery without changing bytes (9.047791ms)
|
||||
✔ REQ-PLUG-1 schema: plugins is optional, names known kinds and checked module names (15.563917ms)
|
||||
✔ REQ-PLUG-1 REQ-ROLE-3 the setter writes plugins through the organization writer and logs old and new names (37.260247ms)
|
||||
✔ REQ-PLUG-1 REQ-ROLE-3 the writer checks the bytes it writes: a Date in plugins is refused there too (5.011955ms)
|
||||
✔ REQ-PLUG-1 REQ-ROLE-3 a refused setter write throws a code, changes nothing and logs nothing (1.844561ms)
|
||||
✔ path and load (3.19451ms)
|
||||
✔ refusals (3.012456ms)
|
||||
✔ systemVars flattens the validated config (2.903554ms)
|
||||
✔ precedence: system, business, project, project role, agent (7.791127ms)
|
||||
✔ limits narrow the definition and never widen it (2.992751ms)
|
||||
✔ role.launch stays within-role only for the instance the launch block names (8.023441ms)
|
||||
✔ limits.authority without role.launch leaves the launcher with no launch block (2.830167ms)
|
||||
✔ limits.authority narrows cross-role actions too (1.227966ms)
|
||||
✔ classify (1.297062ms)
|
||||
✔ the record carries what the broker and launcher need (1.343695ms)
|
||||
✔ digest: key order doesn't matter, any value change does (7.081219ms)
|
||||
✔ refusals (2.434611ms)
|
||||
✔ the four shipped version 2 roles load (4.622523ms)
|
||||
✔ shipped role scopes match addendum B section 2 and the SR runbook (1.687494ms)
|
||||
✔ shipped authority follows the note's table (1.026534ms)
|
||||
✔ every role file in roles/ loads through the validator, and a v2 contract exists (0.874202ms)
|
||||
✔ version 1 files keep loading with no authority (1.077054ms)
|
||||
✔ the conductor policy isn't a role (0.248389ms)
|
||||
✔ a missing role file is exit 4, a symbolic link too (0.546342ms)
|
||||
✔ version 2 refusals (2.094139ms)
|
||||
✔ authority: closed vocabulary, no gated-only action, no overlap (3.499953ms)
|
||||
✔ messaging: optional, role definitions and human, never the tester (2.805507ms)
|
||||
✔ credentials: Gitea scopes (1.467193ms)
|
||||
✔ credentials: Vikunja scopes are a group-to-verbs map from the grantable list (1.546314ms)
|
||||
✔ credentials: services (3.913246ms)
|
||||
✔ contract: a non-empty regular Markdown file beside the role file (0.866764ms)
|
||||
✔ every key names known layers and a merge rule (1.292624ms)
|
||||
✔ unknown keys and wrong layers refuse (1.053135ms)
|
||||
✔ types (2.782478ms)
|
||||
✔ merge: defaults, then the most specific layer wins (0.442491ms)
|
||||
✔ merge: limits only narrow, and provenance lists each source (0.519525ms)
|
||||
✔ merge doesn't change its inputs (0.208357ms)
|
||||
ℹ tests 92
|
||||
ℹ suites 0
|
||||
ℹ pass 92
|
||||
ℹ fail 0
|
||||
ℹ cancelled 0
|
||||
ℹ skipped 0
|
||||
ℹ todo 0
|
||||
ℹ duration_ms 2703.426842
|
||||
@@ -0,0 +1,205 @@
|
||||
✔ every screen and overlay fits 120x36, 80x24 and the narrow layout, row for row (2173.773088ms)
|
||||
✔ width counts terminal cells: wide CJK and emoji 2, U+FE0F after a one-cell base 1, marks 0 (97.248765ms)
|
||||
✔ broker text is shown, never interpreted: no raw control character or bidi override reaches the terminal (207.921392ms)
|
||||
✔ NO_COLOR: no escape codes, except reverse video on the selected row (381.325292ms)
|
||||
✔ 1-9 shows the decide flow's own lines and question; only y resolves (52.483686ms)
|
||||
✔ the app shows the same decide text as mosaic decide (21.546904ms)
|
||||
✔ any key but y or Y at the question answers No; nothing is sent (240.411362ms)
|
||||
✔ below 40x10 the question is hidden, so every key answers it No, y included (Filbert R3) (106.53196ms)
|
||||
✔ under 40 columns a tall terminal shows the too-small screen even where the decide box would fit; y sends nothing (Filbert N10) (412.795439ms)
|
||||
✔ a hidden decide box names the way out, and the status line under it does not promise y (Filbert N12) (46.817756ms)
|
||||
✔ a throw while answering the question goes to crash, sends nothing and leaves no job pending (27.810377ms)
|
||||
✔ type-ahead never answers the question: a paste, keys typed before it is drawn, keys inside askMs (484.831087ms)
|
||||
✔ quitting at the question answers No and sends nothing, armed or not (53.306133ms)
|
||||
✔ a timer tick reads nothing under an overlay, never answers the question, and keeps a resolved status (54.498095ms)
|
||||
✔ a long question is cut inside the question; the action, target, choice and prompt stay (Dewey B1) (88.764252ms)
|
||||
✔ a decide box too big for the terminal says so, and every key answers it No, y included (Dewey B1, round 3) (510.866766ms)
|
||||
✔ at every size the decide box shows each line y depends on, or says it is hidden (10122.88395ms)
|
||||
✔ outcome unknown: the CLI's sentence, no retry, then the trail or a re-read (71.276937ms)
|
||||
✔ refused and failed reads keep the last list, marked stale; a refused decide says refused (39.050434ms)
|
||||
✔ help or a detail box cut to fit keeps its last line, Esc included (Dewey) (16.603961ms)
|
||||
✔ keys: views, moving, trail of a selection, the command line, help and quit (80.550092ms)
|
||||
✔ one option: the status line says Press 1, not 1-1 (15.611907ms)
|
||||
✔ no business: the app says so and :business picks one (8.668548ms)
|
||||
✔ mosaic with no argument: exit 3 in an agent run, exit 4 with the usage off a terminal, the app on one (1.809169ms)
|
||||
✔ scripts/mosaic with no argument reaches the app entry; the seat usage it printed before has not drifted (192.51792ms)
|
||||
✔ strings to keep exactly live once, in the decide flow and the transport; the app does not restate them (0.471804ms)
|
||||
✔ decide with --note still asks; a No sends nothing (10.338491ms)
|
||||
✔ the app's business comes from a live bus host only (1.077585ms)
|
||||
✔ 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.913017ms)
|
||||
✔ draw is pure: same state, same frame (0.910347ms)
|
||||
✔ inbox lists only decisions routed to the human, with what approving authorizes and how to decide (64.881614ms)
|
||||
✔ decide resolves by id prefix with --yes, and the trail shows it in broker order with the task to follow (91.559652ms)
|
||||
✔ decide refuses without a terminal or --yes, on an unknown option and on a short reference (42.35212ms)
|
||||
✔ decide prints a declining choice as declining (45.501778ms)
|
||||
✔ an unknown outcome is reported once and never resent (39.987472ms)
|
||||
✔ a decision closed before the answer arrives exits 2 and points at its trail (28.162797ms)
|
||||
✔ a prefix that matches two open decisions exits 2 and resolves neither (26.783084ms)
|
||||
✔ without --business a command uses the live host's business, and a stale host.json is not a host (28.001206ms)
|
||||
✔ every human command refuses inside an agent run before it touches the bus (31.01867ms)
|
||||
✔ usage errors exit 4; no business and no host is a usage error (40.564278ms)
|
||||
✔ agents and tasks print through the broker (31.567663ms)
|
||||
✔ notify.json: missing, loose, malformed and extra keys refuse; a binding or null passes (3.153893ms)
|
||||
✔ bootConfig builds the broker's boot message for one business, with no trackers key when no project names one (148.907103ms)
|
||||
✔ trackers come from the tracker.* variables of the one project that names a tracker project (245.904446ms)
|
||||
✔ with two projects, the one that sets tracker.project supplies the tracker and the other is no conflict (72.937675ms)
|
||||
✔ two projects that each name a tracker project refuse, since the boot shape holds one (54.830103ms)
|
||||
✔ a business without tracker.baseUrl gets no trackers entry (74.39283ms)
|
||||
✔ an unknown business and a broken system config refuse with exit 3 (118.431257ms)
|
||||
✔ follow prints an unsolicited message after plain talk returned (121.841495ms)
|
||||
✔ talk --follow continues after reply and prints a later unsolicited message (76.393098ms)
|
||||
✔ inbox lists all unread human messages under decisions, does not deliver or read them (546.864225ms)
|
||||
✔ follow refuses agent context and bad arguments (13.409509ms)
|
||||
✔ follow Ctrl-C wakes the poll sleep and removes its handler; transport errors also clean up (14.533935ms)
|
||||
✔ plain talk leaves the process SIGINT handlers unchanged (14.212808ms)
|
||||
✔ empty views say so (10.033195ms)
|
||||
✔ the trail keeps the broker's order and names a decision's task without its rows (1.991834ms)
|
||||
✔ tasks print the tracker fields the snapshot carries (0.369089ms)
|
||||
✔ the review loop: the coder pushes and asks, the reviewer fetches and approves, and the task's trail holds each step (849.680921ms)
|
||||
✔ refusals: no action, non-fast-forward, an unknown commit or request, and a push for another task moves only that task's ref (440.306985ms)
|
||||
✔ the size cap refuses a push before any byte (222.628864ms)
|
||||
✔ a fetch over the cap refuses before any byte reaches the reviewer (496.566882ms)
|
||||
✔ the socket refuses a request with no capability, an unknown one, and a bad request line (213.316348ms)
|
||||
✔ a rigged clone runs nothing: hooks, fsmonitor, a filter, a gitlink and a symlinked .git/config leave no marker (356.693385ms)
|
||||
✔ an identity with no role, or of another business, refuses not-a-role and moves no ref (328.157975ms)
|
||||
✔ a review of a project the business no longer has refuses unknown-project (199.473421ms)
|
||||
✔ past maxActive connections the socket refuses git-busy, and a hung-up connection frees its slot (186.775973ms)
|
||||
✔ close() answers host-closing to a connection still sending its request line (212.40302ms)
|
||||
✔ a bundle that trickles in refuses body-timeout at its deadline and frees its slot (640.517153ms)
|
||||
✔ a reviewer gone before its bundle was sent is filed fetch-aborted, not as the host's fault (619.745309ms)
|
||||
✔ a reviewer that hangs up the moment it has the whole bundle was served, not aborted (810.422078ms)
|
||||
✔ send: a close after every byte left is served; one that fails a queued write is fetch-aborted (42.569597ms)
|
||||
✔ a bundle with no bytes for bodyIdle refuses body-timeout long before its deadline (564.165052ms)
|
||||
✔ send: a close once the loop is past its last chunk is served, and a failed write there is fetch-aborted (47.78708ms)
|
||||
✔ letGo waits for an answer to leave for at most its cap, then destroys the socket (301.059444ms)
|
||||
✔ a record that fails and can't be undone refuses rollback-failed, which the host logs (411.609063ms)
|
||||
✔ the host boots the broker, binds a launch in process, and the notifier DMs a blocking decision exactly once (1138.239189ms)
|
||||
✔ a notifier that dies takes the host down with exit 1, so the unit restarts the pair (234.097562ms)
|
||||
✔ a second host for the same data root refuses with exit 3 while the first runs (124.336445ms)
|
||||
✔ two bus starts racing for one data root leave one host running; the other refuses that a host already runs (634.945611ms)
|
||||
✔ a host takes over a host.lock its dead host left, and puts back a live claim that replaced it meanwhile (2.474548ms)
|
||||
✔ a host.lock that goes between the check and the read is tried again; one that can't be parsed still refuses as unreadable (1.341588ms)
|
||||
✔ a live host's lock stays, however old the file is (0.814715ms)
|
||||
✔ a host.lock left by a host killed with SIGKILL doesn't stop the next bus start (922.811047ms)
|
||||
✔ while a host boots, a reader sees no host: the human commands still ask for --business, stop and status find none (316.515609ms)
|
||||
✔ a host started before host.lock existed, holding only host.json, still refuses a second start, which leaves no lock (44.043297ms)
|
||||
✔ a broker fork that throws leaves no host.lock and no host.json (53.750365ms)
|
||||
✔ a broker send that throws leaves no host.lock and no host.json (47.959785ms)
|
||||
✔ a broker that misses its boot wait leaves no host.lock and no host.json (51.343827ms)
|
||||
✔ a notifier fork that throws stops the broker and leaves no host.lock and no host.json (134.934876ms)
|
||||
✔ a notifier send that throws stops the broker and leaves no host.lock and no host.json (108.747819ms)
|
||||
✔ 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 (150.80167ms)
|
||||
✔ 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 (115.444931ms)
|
||||
✔ a host.json that can't be written stops both children and leaves no host.lock (296.560014ms)
|
||||
✔ an error event on the broker before it replies fails the start at once, and leaves no host.lock and no live child (58.077592ms)
|
||||
✔ an error event on the notifier before it replies fails the start at once, and leaves no host.lock and no live child (148.56551ms)
|
||||
✔ an error event on the broker while the notifier starts fails the start once the notifier replies (300.230295ms)
|
||||
✔ 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 (349.12169ms)
|
||||
✔ 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 (353.843573ms)
|
||||
✔ a broker reply that misses the wait breaks the channel: the late reply answers nothing, later requests refuse, the host exits 1 (1154.585808ms)
|
||||
✔ a broker reply with another request's id, or none, breaks the channel and the host exits 1 (294.361167ms)
|
||||
✔ a broker reply with no request waiting breaks the channel and the host exits 1 (100.892956ms)
|
||||
✔ a notifier that refuses stops the broker and the host refuses with exit 3 (156.395117ms)
|
||||
✔ a notifier that refuses after the broker died still refuses with exit 3, without a send to the dead broker (184.781646ms)
|
||||
✔ a notifier that dies before it replies, after the broker died, still refuses, without a send to the dead broker (123.085178ms)
|
||||
✔ a close send that fails with EPIPE after the notifier refuses still gives the notifier's refusal, exit 3 (185.090261ms)
|
||||
✔ a close send that fails with EPIPE after the notifier dies unanswered still gives the notifier's error (100.383823ms)
|
||||
✔ close() whose stop and close sends fail with EPIPE still finishes, with exit 1 (173.047749ms)
|
||||
✔ watchChildren reports a child that died before it was called, and one that dies later (31.426835ms)
|
||||
✔ bus stop refuses to signal a live pid that is not a bus host (203.336445ms)
|
||||
✔ bus start refuses with exit 3 and the code when the broker refuses to boot; bus status names the lock (266.564643ms)
|
||||
✔ bus start refuses with exit 3 without a notifier config (117.875864ms)
|
||||
✔ bus start runs until bus stop; status reports it while it runs (769.236275ms)
|
||||
✔ bus-service.sh renders the unit and installs it into a given directory (69.670039ms)
|
||||
✔ bus-service.sh pins the claude binary, refuses one the unit can't carry, and renders without one (93.263508ms)
|
||||
✔ bus-service.sh refuses a directory pin and keeps a symlink default unresolved (14.521611ms)
|
||||
✔ bus-service.sh refuses a default claude without the execute bit, or in a PATH dir with a space (10.045269ms)
|
||||
✔ each launch rereads limits and model selections; lowered limits keep sessions and log exact hit (104.430865ms)
|
||||
✔ bus start --pm: the launcher instance (the chief of staff) runs under its own PID namespace, registered, with a manifest (1511.080056ms)
|
||||
✔ the chief of staff launches a coder through its launch tool; the coder answers; refusals name their code (725.960363ms)
|
||||
✔ each launch makes sure the workspace has a clone of the project: cloned, fast-forwarded, or fetched and logged (3779.026688ms)
|
||||
✔ a project clone that can't be made refuses checkout-failed, from --chief and from a session, with no run and no clone left (206.539203ms)
|
||||
✔ a runner that stops at once ends its launch with the runner's reason (237.995262ms)
|
||||
✔ a Claude Code launch with no session credential refuses before it starts (147.489944ms)
|
||||
✔ a pi launch needs no session credential: pi logs in through its own ENV_ALLOW variables (267.818875ms)
|
||||
✔ a runner killed from the host ends its launch as killed, not failed (279.584618ms)
|
||||
✔ a missing or failing bwrap refuses sandbox-unavailable before anything starts; there is no unconfined fallback (143.547861ms)
|
||||
✔ a real Claude Code coder's shell can't read a stand-in secret or anything else its sandbox doesn't hold (1219.812449ms)
|
||||
✔ 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 (704.368544ms)
|
||||
✔ 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 (3632.037109ms)
|
||||
✔ a malformed sessions.json refuses the host start with exit 3 and stays as it was (113.805107ms)
|
||||
✔ an over-long launch request is refused at once, not at the 10 s idle timeout (104.722694ms)
|
||||
✔ a launch client that never closes its side doesn't hold the host's close (103.218033ms)
|
||||
✔ a runner that ignores SIGTERM is killed when the host closes (1233.1751ms)
|
||||
✔ claudeVersion asks MOSAIC_CLAUDE_BIN when set, else claude on PATH (5.037612ms)
|
||||
✔ migration needs no bus/config, prints whole converted document, is idempotent and never writes (38.359408ms)
|
||||
✔ limits view/set use the human bus transport and pass only the explicit patch (10.968238ms)
|
||||
✔ post-119 example and Jason fixture migrate with checked inherited models; wrong or absent config refuses (450.169987ms)
|
||||
✔ zoned uses the IANA zone across DST (53.76669ms)
|
||||
✔ each open blocking decision is DM'd once, across polls and a restart; non-blocking ones are not (71.480947ms)
|
||||
✔ two blocking decisions get two DMs with different nonces (43.345638ms)
|
||||
✔ the digest nonce differs per business and per day and fits Discord's 25 characters (0.403967ms)
|
||||
✔ a failed DM is journaled, backs off, and is retried until it lands (40.475709ms)
|
||||
✔ five definite refusals stop a DM: one gave-up line, one log line, and a restart keeps the count (42.267637ms)
|
||||
✔ 429s, 5xx-style unknowns and refusals without a status never count toward the limit (39.852712ms)
|
||||
✔ a crash between the fifth refusal and its gave-up line: the next poll appends it and sends nothing (28.717839ms)
|
||||
✔ 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 (162.001908ms)
|
||||
✔ a restart after the second refusal does not send before that refusal's 30 min are up (76.762444ms)
|
||||
✔ the digest goes at 08:00 Chicago once a day, with blocking ones marked as DM'd (19.244097ms)
|
||||
✔ a late start with no digest for the day sends one at once; an empty inbox gets one line (14.856466ms)
|
||||
✔ an inbox read failure is logged and the next poll retries (0.907963ms)
|
||||
✔ no Discord id reaches the journal or the log (14.935675ms)
|
||||
✔ the journal: a torn tail is copied out and truncated, so an append after it reopens cleanly (3.351207ms)
|
||||
✔ the journal: a crash between the copy and the truncate leaves a tail the next open repairs (3.346844ms)
|
||||
✔ the journal: a whole file that is one torn line truncates to empty (1.900982ms)
|
||||
✔ the journal: a malformed complete line refuses and leaves the file and any torn tail alone (0.909679ms)
|
||||
✔ the journal: a loose file mode, a loose directory or a symlinked journal refuses (3.662134ms)
|
||||
✔ the journal: a line with a wrong type refuses with exit 3 and names the field (3.96456ms)
|
||||
✔ the journal: a symlinked directory refuses and says it is a link (0.602646ms)
|
||||
✔ 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.862414ms)
|
||||
✔ the journal: an append after the file was swapped for a symlink refuses and writes nothing through it (0.688796ms)
|
||||
✔ the journal: a directory it cannot write or create refuses with exit 3 and names the path (0.968655ms)
|
||||
✔ digest content stays within Discord's 2000 characters (0.54217ms)
|
||||
✔ runLoop never overlaps ticks and stops after the one in flight (111.836121ms)
|
||||
✔ a human command refused with human-required prints the other-tree line and exits 3 (3.736631ms)
|
||||
✔ a --tester command refused with tester-required prints the other-tree line and exits 3 (2.730063ms)
|
||||
✔ other exit-3 refusals print no other-tree line (5.977698ms)
|
||||
✔ the line names no path (0.222376ms)
|
||||
✔ shared copy: npm ci, pushed reachability, readonly/existing copy, webui delegation (861.05824ms)
|
||||
✔ wrapper preserves Q81 lease and marker, rejects foreign/symlink; check uses effective roots (584.962766ms)
|
||||
✔ bus unit copy/pm pins, install refuses all busy states and failed reads, drop-in refusal (613.708602ms)
|
||||
✔ missing package manifest refuses before npm; npm failure removes the unpublished temp copy (386.478084ms)
|
||||
✔ real pinned install loads bus and Console; same-copy mosaic talk passes proof, other trees refuse (4931.049667ms)
|
||||
✔ bus status reads effective copy/pm/drop-ins without writes; check refuses an overridden ExecStart (586.398503ms)
|
||||
✔ drop-ins in either override field and malformed metadata refuse; uninstall also checks --no-reload (185.840175ms)
|
||||
✔ install never shows a template name on systemd 261; template drop-ins still refuse (186.012962ms)
|
||||
✔ unlisted instance drop-in is found in another unit lookup directory; path discovery errors refuse (163.755417ms)
|
||||
✔ inherited mosaic-.service.d override refuses without changing the unit (195.106197ms)
|
||||
✔ inherited service.d override refuses without changing the unit (202.647958ms)
|
||||
✔ inherited drop-in unreadable directory or file refuses before writing (196.247471ms)
|
||||
✔ talk --tester sends a [mos test] message as tester from inside an agent and prints the role's reply (76.144496ms)
|
||||
✔ talk --tester refuses text without the [mos test] prefix before any call (25.003386ms)
|
||||
✔ the read verbs take --tester inside an agent, and the trail shows the tester as the sender (52.437029ms)
|
||||
✔ decide, stop and launches take no --tester (35.419981ms)
|
||||
✔ the tester transport runs tester-cli.mjs, and the tester's refusals exit 3 (0.72582ms)
|
||||
✔ with no stand-in transport, --tester runs tester-cli.mjs on the data root's socket (1.057652ms)
|
||||
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 (530.155001ms)
|
||||
✔ the transport writes {business, verb, args} to the child and reads its JSON (98.36645ms)
|
||||
✔ a bus code on stderr becomes the exit code; garbage and timeouts are outcome problems (2348.753472ms)
|
||||
✔ busExit and refuseInsideAgent (0.899264ms)
|
||||
✔ talk sends a REQUEST, prints and reads everything that arrives, and stops at the reply (81.651605ms)
|
||||
✔ talk --wait 0 only sends; no reply within --wait exits 1 and says where it will show (1047.23713ms)
|
||||
✔ a late reply the Console has listed still reaches the next talk (2034.570851ms)
|
||||
✔ talk, stop and launches off/on refuse inside an agent run; bad arguments are usage errors (15.79745ms)
|
||||
✔ launches off and on go to the broker and change the business's launch state (16.705713ms)
|
||||
✔ launches list reads sessions.json and marks a stale entry; stop reports it and refuses a non-runner (18.872699ms)
|
||||
ℹ tests 195
|
||||
ℹ suites 0
|
||||
ℹ pass 195
|
||||
ℹ fail 0
|
||||
ℹ cancelled 0
|
||||
ℹ skipped 0
|
||||
ℹ todo 0
|
||||
ℹ duration_ms 15856.640798
|
||||
@@ -0,0 +1,132 @@
|
||||
✔ explicit request, Seen, ordinary completion and a new request have distinct attention states (4.363084ms)
|
||||
✔ attention convention ignores reasoning/quoted examples and permits leading blank lines (0.265627ms)
|
||||
✔ completed smoke replies and ordinary questions are idle, not human blockers (0.892845ms)
|
||||
✔ only an explicit first-line input request makes a finished reply waiting (0.211035ms)
|
||||
✔ tool activity, user text, errors and unfinished turns override attention text (0.220624ms)
|
||||
✔ STOP access failure is unknown, not absence, under a non-root identity (37.560625ms)
|
||||
✔ connector Task never inherits Discord routing envelopes; ordinary Task still uses user text (4.297053ms)
|
||||
✔ connector discovery keeps only safe identity; rejects modes, mismatches, links and traversal (2.023137ms)
|
||||
✔ canonical owner identity and STOP are independent; no tmux fallback or forged registration (7.581773ms)
|
||||
✔ connector reply refusal precedes forged live tmux registration; ordinary agent still sends (0.869408ms)
|
||||
✔ server rescans connector discovery and refuses HTTP reply without transport (37.144076ms)
|
||||
✔ connector session links and linked directories are not read (1.085384ms)
|
||||
✔ newer live matching launch marks old activity, preserves history/attention/attribution, then clears on new activity (3.571343ms)
|
||||
✔ CLI print uses the relaunch notice instead of old current preview (63.613777ms)
|
||||
✔ connector owner and fixed task never inherit a native relaunch notice (2.802665ms)
|
||||
✔ equality, stale/unknown/offline, mismatched registration and unknown activity do not assert relaunch (1.699989ms)
|
||||
✔ loadConfig: missing file throws ConfigError (2.48094ms)
|
||||
✔ loadConfig: invalid JSON throws ConfigError (0.460668ms)
|
||||
✔ loadConfig: missing dataRoot throws ConfigError (0.359646ms)
|
||||
✔ loadConfig: relative dataRoot throws ConfigError (0.337366ms)
|
||||
✔ loadConfig: valid config returns dataRoot (1.013249ms)
|
||||
✔ findNewestSession: picks the newest by mtime among two files (0.736242ms)
|
||||
✔ findNewestSession: finds files in nested subdirectories (0.343677ms)
|
||||
✔ findNewestSession: returns null for a missing dir (0.134314ms)
|
||||
✔ readSession: extracts fields, collapses/truncates text, counts a truncated final line (0.683895ms)
|
||||
✔ readSession: model and provider follow the latest model_change entry or assistant turn; null when the log names neither; scanAgent carries them (1.295723ms)
|
||||
✔ readSession: lastError carries the assistant errorMessage only when the last assistant turn errored (0.515166ms)
|
||||
✔ findNewestSession/scan: never read sibling auth or secrets next to a sessions dir (1.150396ms)
|
||||
✔ deriveState: full state table (0.205112ms)
|
||||
✔ rule: newest entry is an assistant message with a tool call, after a question-looking text, is working (0.351336ms)
|
||||
✔ rule: newest entry is a tool result with no assistant text after it is working (0.305227ms)
|
||||
✔ rule: a finished ordinary turn is idle, even if it says your move (0.277945ms)
|
||||
✔ task: the first user message of the session, from text blocks (0.274217ms)
|
||||
✔ task: a plain-string user content is accepted, whitespace collapsed and long text capped (0.278162ms)
|
||||
✔ task: no user message in the log means null (shown as unknown), never a guess (0.277092ms)
|
||||
✔ workspace: the live tmux pane path wins; the session cwd is the fallback; neither means null (0.382395ms)
|
||||
✔ activeProject: basename of the nearest .git directory or .git file above the workspace; none means null (0.542236ms)
|
||||
✔ scan: the written record carries task, workspace and activeProject (0.519455ms)
|
||||
✔ registration: overrides task, project and workspace; every source says registration; registered carries the launch fields; the grouping column is untouched (0.612872ms)
|
||||
✔ registration: empty task and null project/workspace leave the derived values in place; registered is still non-null (0.369423ms)
|
||||
✔ 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 (1.014261ms)
|
||||
✔ registration: no registration leaves the Gate A fields exactly as before, and registered is null (0.421866ms)
|
||||
✔ loadRegistrations: a missing seatsDir gives empty lists (0.192237ms)
|
||||
✔ loadRegistrations: one good record, one malformed JSON, one with an unknown field; a stray file under seatsDir is ignored (0.768343ms)
|
||||
✔ 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.337653ms)
|
||||
✔ scan: writes the registration override to disk; index.json carries registered and registrationErrors (0.720672ms)
|
||||
✔ scan: a relative seatsDir throws ConfigError; an omitted seatsDir behaves as before (0.752406ms)
|
||||
✔ scanAgent: waitingOnYou is true for waiting/error and false otherwise (0.488762ms)
|
||||
✔ scanAgent: ageSeconds is computed from the injected now (0.275121ms)
|
||||
✔ scanAgent: sessionFile null and state idle when sessions dir is empty but alive (0.152ms)
|
||||
✔ discoverRepoAgents: finds agents with a sessions dir, skips those without, sorted by name (0.333742ms)
|
||||
✔ discoverFleetAgents: finds agents with a sessions dir, sorted by name, fleet tmux fields (0.460963ms)
|
||||
✔ scan: writes per-agent files and index.json, rerun overwrites, no leftover tmp files (0.902937ms)
|
||||
✔ scan: relative boardDir throws ConfigError (0.091177ms)
|
||||
✔ CLI: scan with assume-alive liveness exits 0, prints board summary, writes board files (62.105206ms)
|
||||
✔ CLI: missing config exits 2 with a refused: message (55.972311ms)
|
||||
✔ CLI: unknown command exits 2 (54.9918ms)
|
||||
✔ CLI: unknown --liveness value exits 2 (53.329072ms)
|
||||
✔ panesRunPi: true when any trimmed line equals 'pi' (0.22984ms)
|
||||
✔ panesRunPi: false for bash-only, claude, empty, or node-pi-style lines (0.081033ms)
|
||||
✔ tmuxIsAlive: a pane running pi is alive (0.180296ms)
|
||||
✔ tmuxIsAlive: session exists but pi has exited is not alive (0.077549ms)
|
||||
✔ tmuxIsAlive: no such tmux session is not alive (0.048531ms)
|
||||
✔ tmuxIsAlive: tmux could not be run at all is unknown (null), never assumed alive (0.059258ms)
|
||||
✔ tmuxIsAlive: passes -L <socket> only when a socket is given (0.098667ms)
|
||||
✔ parsePanes: one pane per line, command and optional tab-separated path (0.085408ms)
|
||||
✔ tmuxInspect: reports the path of the pane running pi, not of a shell pane (0.076136ms)
|
||||
✔ tmuxInspect: no pi pane, no session, or no tmux gives no workspace and the matching liveness (0.095716ms)
|
||||
✔ loadSeen: missing file returns {} (0.178982ms)
|
||||
✔ loadSeen: invalid JSON throws ConfigError (0.215305ms)
|
||||
✔ loadSeen: a JSON array throws ConfigError (0.159767ms)
|
||||
✔ loadSeen: a non-string value throws ConfigError (0.179985ms)
|
||||
✔ markSeen: seen true adds the key and writes seen.json mode 0600, no leftover tmp files (0.351677ms)
|
||||
✔ markSeen: seen false deletes the key (0.268498ms)
|
||||
✔ markSeen: missing, empty, or non-string fields throw ConfigError (0.208456ms)
|
||||
✔ markSeen: project containing '/' throws ConfigError (0.130603ms)
|
||||
✔ markSeen: non-boolean seen throws ConfigError (0.145423ms)
|
||||
✔ scanAgent: a seen mark matching the waiting session's lastTimestamp clears waitingOnYou (0.322876ms)
|
||||
✔ scanAgent: a stale mark (agent wrote something newer) is not seen and waitingOnYou is true (0.272682ms)
|
||||
✔ scanAgent: a working session with a matching mark is not seen (marks only apply to waiting/error) (0.261673ms)
|
||||
✔ scanAgent: an error-state session with a matching mark is seen (0.22895ms)
|
||||
✔ scan: index.seen and waitingOnYou reflect seen.json, which scan never rewrites or deletes (0.520533ms)
|
||||
✔ scan: a corrupt seen.json makes scan throw ConfigError (fail closed) (0.200827ms)
|
||||
✔ 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.688498ms)
|
||||
✔ 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.622947ms)
|
||||
✔ 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.023734ms)
|
||||
✔ isLoopbackHost: recognizes loopback hosts (1.30155ms)
|
||||
✔ isLoopbackHost: rejects non-loopback hosts (4.702415ms)
|
||||
✔ startServer: refuses a non-loopback host with ConfigError, never opens a socket (3.21449ms)
|
||||
✔ startServer: serves page, healthz, and a rescanning /api/board (36.088534ms)
|
||||
✔ startServer: a seatsDir registration overrides the row and index.registered reflects it (9.571521ms)
|
||||
✔ startServer: /api/board returns 500 JSON with an error field when scan throws (3.323713ms)
|
||||
✔ CLI: serve refuses a non-loopback host with exit 2 and a refused: message (61.561158ms)
|
||||
✔ CLI: serve rejects a non-numeric --port with exit 2 (57.511102ms)
|
||||
✔ CLI: scan still works after the async cli refactor (52.742618ms)
|
||||
✔ CLI: live serve prints its URL and answers /healthz (66.825813ms)
|
||||
✔ page.html: esc() escapes every HTML-significant character (0.684593ms)
|
||||
✔ POST /api/seen marks a row; GET /api/board still shows it seen; seen:false clears it (8.858909ms)
|
||||
✔ POST /api/seen without a JSON content-type returns 400 and does not write a mark (2.085639ms)
|
||||
✔ POST /api/seen with invalid JSON returns 400 (3.04174ms)
|
||||
✔ POST /api/seen with a body over 4096 bytes returns 400 (or resets the connection) and writes no mark (2.351489ms)
|
||||
✔ POST /api/seen with a missing agent returns 400 (1.606449ms)
|
||||
✔ POST /api/board returns 405; PUT /api/seen returns 405 (1.946138ms)
|
||||
✔ CLI: scan --print marks a seen row with 's' and the summary line ends with 'N seen)' (57.835561ms)
|
||||
✔ page.html: seenControl() escapes rec.project/agent/lastActivity, and the POST uses a JSON content-type (0.322889ms)
|
||||
✔ page.html: has a collapsed Seen section that lists seen rows with the shared row builder (0.282622ms)
|
||||
✔ page.html: each project has a Hide seen checkbox (default on) beside Hide offline, with a hidden-count note (0.172408ms)
|
||||
✔ page.html: a project header reads "N of N" only while a checkbox hides rows (0.147984ms)
|
||||
✔ page.html: every row shows Task and Active project, derived or the word unknown, with the workspace in the detail (0.343463ms)
|
||||
✔ 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.618427ms)
|
||||
✔ 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 (29.086477ms)
|
||||
✔ POST /api/reply: a registration with a tmux socket adds -L <socket> (28.979951ms)
|
||||
✔ POST /api/reply: a non-zero tool exit is a 200 with delivered false, the exit code and the stderr verbatim (31.535611ms)
|
||||
✔ 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 (15.743942ms)
|
||||
✔ POST /api/reply: a missing agent-send.sh is a 500 with the path in the error, not a crash (4.970411ms)
|
||||
✔ replyToRow: DEFAULT_AGENT_SEND is the repository's tools/tmux/agent-send.sh and it is executable (0.148699ms)
|
||||
✔ 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.624562ms)
|
||||
✔ startServer: /api/board carries taskSetBy from a live registration and null for the derived rows (2.395698ms)
|
||||
✔ 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.309961ms)
|
||||
✔ 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 (8.483045ms)
|
||||
✔ Host/Origin guard: loopback names on this port are accepted, with or without a same-origin Origin (32.749835ms)
|
||||
✔ conversation routes (F16): a foreign Host, a wrong port and a cross-origin Origin get 403 before the reader runs, with no CORS headers (6.204471ms)
|
||||
✔ every refusal code the reader can raise has an HTTP status (0.978883ms)
|
||||
✔ conversation routes: catalogue, first page, next page and follow over HTTP; refusals map to 4xx with their code; nothing is written (47.287386ms)
|
||||
ℹ tests 124
|
||||
ℹ suites 0
|
||||
ℹ pass 124
|
||||
ℹ fail 0
|
||||
ℹ cancelled 0
|
||||
ℹ skipped 0
|
||||
ℹ todo 0
|
||||
ℹ duration_ms 675.20037
|
||||
@@ -0,0 +1,190 @@
|
||||
✔ W1: two processes acquire the same pair at once; exactly one claim (138.150631ms)
|
||||
✔ W1: two writers publish the same revision at once: one wins, the other gets null, the winner's record stays (11.179751ms)
|
||||
✔ W1: a revision name appears only after its bytes are synced; before that, only a temp file exists (5.956882ms)
|
||||
✔ W2: acquire while a claim is reserved or active refuses already-active (120.205568ms)
|
||||
✔ W3: acquire while stopping, uncertain, or stopped without proof refuses unsafe-replacement (196.425179ms)
|
||||
✔ W4: same session with another seat tuple, and the reverse, both refuse; a loser on the seat key closes it no-unit (153.700959ms)
|
||||
✔ W4: a hard link of one session under another seat is the same session: the second controller refuses already-active and launches nothing (24.773276ms)
|
||||
✔ W4: a copy of one session under another seat is the same session: the second controller refuses already-active and launches nothing (25.96912ms)
|
||||
✔ W4: a session header ID that changes after construction refuses target; nothing is claimed or launched (5.76592ms)
|
||||
✔ W5: SIGKILL between every publication barrier of acquire and transition; restart never finds two holders or a lost claim (7276.677406ms)
|
||||
✔ W5: SIGKILL between every publication barrier of release; restart finishes or holds the release (25323.541997ms)
|
||||
✔ W6: controller killed mid-turn while the engine lives; restart is uncertain, no launch, prompts refuse (138.694995ms)
|
||||
✔ W12: a live owner paused with SIGSTOP; a second controller refuses already-active and changes nothing (88.33774ms)
|
||||
✔ W13: crash after the engine spawns, before active; restart finds the live unit: uncertain, no second spawn, force stop only (279.732276ms)
|
||||
✔ W14: crash after reservation, before the spawn marker: stopped with a no-unit observation; the pair is free (184.513961ms)
|
||||
✔ W20: crash after the spawn marker, scope collected; uncertain in both runs, the marker is copied, no launch until a boot proof (204.910825ms)
|
||||
✔ W15: crash between the two keys during release; restart finishes it under the same claim ID (32.342029ms)
|
||||
✔ W7: recorded boot ID differs on the same machine: stopped with a boot proof; open tool calls become uncertain (117.227473ms)
|
||||
✔ W8: resume after a proven stop with the same pins: new claim ID, generation +1, same conversation, branch and leaf (31.553705ms)
|
||||
✔ W9: resume with a changed binary, argv digest, branch or leaf is refused and the claim is unchanged (85.402385ms)
|
||||
✔ W11: the controller writes no session file; only the fake engine's own appends appear (22.044848ms)
|
||||
✔ W16: a highest revision that won't parse holds the pair uncertain; the older stopped revision is not reused (54.67204ms)
|
||||
✔ W17: a claim root copied from another host refuses foreign-host and promotes nothing (51.05823ms)
|
||||
✔ G1: a session path or claim root under .pi/state, ~/.claude, the data root or a registration refuses at construction (3.434142ms)
|
||||
✔ G2: a symlink inside the fixture root to a live session file is refused by the real-path check (1.067285ms)
|
||||
✔ G3: a fixture path swapped for a live path after construction is refused at bind (1.767203ms)
|
||||
✔ no shim from this file's tests is left running for the after hook (16.129291ms)
|
||||
✔ K1: force stop kills a tool child that called setsid; stopped with a verified proof (346.363458ms)
|
||||
✔ K2: K1 on the process-group fallback ends uncertain, never stopped (173.233355ms)
|
||||
✔ K3: SIGTERM acknowledged while a member lives: stopping until the kill phase, never stopped from TERM (338.272903ms)
|
||||
✔ K4: two engines; force stop one; the other survives by independent observation (2422.449231ms)
|
||||
✔ K5: a stop during a tool call leaves the effect uncertain, and it is shown (221.249465ms)
|
||||
✔ K12: a member forking in a loop: the freeze stops it, enumeration is complete, populated 0 after cgroup.kill (325.30133ms)
|
||||
✔ K13: a member writing its pid into another cgroup is refused by the namespace; the kill is complete (236.053103ms)
|
||||
✔ K15: the shim gone, engine/cgroup.events unreadable, or the engine cgroup missing: evidence unavailable, not empty; uncertain (4438.845685ms)
|
||||
✔ K10: controller killed between the TERM and kill phases: restart checks the invocation ID and re-runs from TERM for the same stop (428.297784ms)
|
||||
✔ K11: controller killed after the confirmation is recorded, before TERM: restart checks the invocation ID and re-runs from TERM for the same stop (357.9965ms)
|
||||
✔ K14: a unit with the recorded name but another invocation ID: evidence unavailable, no signals, uncertain (247.375213ms)
|
||||
✔ K6: recover without proof, without confirmation, or with changed pins is refused (61.512441ms)
|
||||
✔ K7: recover after proof, then launch: new claim and execution, generation +1, same leaf; the cancelled prompt is not replayed (31.106823ms)
|
||||
✔ K8: an engine that loads another leaf on resume is refused before admission; it stays claimed until a proven stop (44.083691ms)
|
||||
✔ K9: an interrupt that never settles stays uncertain; force stop stays available; takeover is refused while fenced (3038.942072ms)
|
||||
✔ K16: a claim from another machine ID refuses foreign-host; no boot proof is issued (4.818403ms)
|
||||
✔ K17: two launcher calls with one eligibility record: one launch, the other refuses, no second engine (31.932004ms)
|
||||
✔ K18: the leaf changes after eligibility: launch refused; the reservation stays until released with proof (27.345576ms)
|
||||
✔ K19: a scope launched with only the engine environment still reaches the user manager; the engine sees no other names (82.689073ms)
|
||||
✔ R1: after a proven force stop the controller releases the scope; the shim and the unit are gone (200.059861ms)
|
||||
✔ R2: close releases the scope of a binding proven stopped, once, when the stop's own release hasn't run (304.379048ms)
|
||||
✔ R3: no release after an unavailable stop or an unverified proof: the scope and its shim stay as evidence (697.804685ms)
|
||||
✔ E1: a natural engine exit with no member left: an engine-exit stop, stopped on a proof naming the engine, and the scope released (189.569096ms)
|
||||
✔ E2: an engine exit with a tool child alive: the stop ends uncertain and nothing is released; a force stop then ends it (2268.284716ms)
|
||||
✔ E3: an engine exit and a force stop take one escalation slot, in either order (657.711622ms)
|
||||
✔ E4: a shim that doesn't answer: the engine-exit stop misses its deadline, ends uncertain and frees the slot (704.921809ms)
|
||||
✔ E5: an engine exit after a force stop ended uncertain starts no engine-exit stop; the force stop stays current (2447.815747ms)
|
||||
✔ E6: the engine-exit observation refuses a reaped process that isn't the recorded engine: another PID or start ticks (201.88798ms)
|
||||
✔ 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) (437.478898ms)
|
||||
✔ R6: releaseCohort releases nothing while the engine runs, for another invocation ID, or for a cohort without a scope (139.709062ms)
|
||||
✔ R7: controller killed after the claim is stopped, before the release: the restart releases the scope (2357.489461ms)
|
||||
✔ R8: controller killed between the two keys' stopped revisions: the restart releases the scope (243.224591ms)
|
||||
✔ R9: a release that ended unavailable is tried again on a confirmed recover, not a refused one (217.999263ms)
|
||||
✔ R10: close releases and signals nothing for a stopped binding whose proof no longer verifies (Filbert N3 on #1536) (194.615525ms)
|
||||
✔ R11: close({ killEngine: true }) after a proven stop sends no signal to the recorded engine PID; a restart releases nothing more (2292.385741ms)
|
||||
✔ R12: a restart onto a claim stopped on a boot proof releases nothing, though its scope is listed (184.23266ms)
|
||||
✔ no shim from this file's tests is left running for the after hook (17.525407ms)
|
||||
✔ S1: `/goal x`, with leading spaces or a tab, refuses text-policy at admission; zero engine bytes (41.57313ms)
|
||||
✔ S2: every prefix pinned Pi interprets is refused, from the list the code uses; the rest reach the engine exactly (32.562134ms)
|
||||
✔ S3: `/goal` on the second line is pinned from the source: Pi checks only index 0, so it is admitted and sent exactly (23.208692ms)
|
||||
✔ S4: a `/` left in the composer is cleared when control transfers and returns; the next submit sends only the new text (45.855649ms)
|
||||
✔ S5: an observer terminal gets a paste then Enter, as send-message.sh does: not admitted: controller, nothing sent (20.593987ms)
|
||||
✔ S6: a mediated-shaped registration (no tmux) passed to the board's replyToRow: 409 no tmux session; exec never runs (0.845694ms)
|
||||
✔ S7: ESC, bracketed-paste markers and U+2028/U+2029 travel as one JSON string; the engine receives the exact text in one record (21.184611ms)
|
||||
✔ P3: a Pi confirm, select, input or editor dialog is shown disabled with a reason and never answered (125.040201ms)
|
||||
✔ E1: send, ack, user, toolCall, toolResult, final answer: shown once, no refresh, draft and reading position kept (36.629756ms)
|
||||
✔ E2: U+2028, U+2029 inside JSON strings and CRLF line ends each parse as one record, on the splitter and through the controller (20.126516ms)
|
||||
✔ E3: a multipart final, two blocks, null request correlation and duplicate delivery (31.059827ms)
|
||||
✔ 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 (31.515036ms)
|
||||
✔ E4: a gap or a new epoch also reconciles; nothing is concatenated across a gap (11.50735ms)
|
||||
✔ E5: an unknown native event gives no client event; evidence records its type and bytes; the terminal count goes up (28.519815ms)
|
||||
✔ E6: a tool result delayed across a pause and a reconnect is reconciled without a manual refresh (46.087027ms)
|
||||
✔ E7: the terminal renders the same stream as the library client, as observer and then as controller, and submits only as controller (39.012862ms)
|
||||
✔ terminal: engine control characters are made visible; a lost connection refuses submit (26.166899ms)
|
||||
✔ terminal: outcome unknown is shown as such, with no resend offer, and nothing is resent (0.611145ms)
|
||||
✔ terminal: text after Enter in the same input chunk starts the next message; it never joins the one submitted (0.439239ms)
|
||||
✔ terminal: a paste-start marker split right after its ESC still opens the paste; the Enter inside it never submits (0.498238ms)
|
||||
✔ 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.570652ms)
|
||||
✔ terminal: input held behind Ctrl-T waits for that takeover while an earlier action is still pending (Darkwing T1 on #1522) (31.990374ms)
|
||||
✔ 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.646024ms)
|
||||
✔ terminal: an action that throws still releases the input held behind it, in order, then rethrows (5.066823ms)
|
||||
✔ terminal: after an action throws, later input still runs; input() puts the error in the status line (Filbert N1 on #1522) (0.883697ms)
|
||||
✔ 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.670662ms)
|
||||
✔ terminal: invisible and bidi characters are made visible; head, status and notice lines stay one line (0.188098ms)
|
||||
✔ every record these fixtures produced is a valid CHAT-01 record (E5: no record fails the schema) (353.332163ms)
|
||||
✔ H1: two takeovers with the same expected generation: one wins, +1; the other refuses generation (55.948445ms)
|
||||
✔ H2: the old controller's prompt after a takeover commits is refused with zero engine bytes (85.992875ms)
|
||||
✔ H3: a takeover while a prompt holds the dispatch lock: written under the old actor, or refused; never both (136.223131ms)
|
||||
✔ H4: self-takeover is refused (16.819338ms)
|
||||
✔ H9: Interrupt racing a prompt's dispatch: before the write, dispatch-refused and no-turn; after, §3 rules (89.63477ms)
|
||||
✔ H10: Interrupt and force stop together: one stop chain, force stop supersedes (83.827151ms)
|
||||
✔ H10: an overlap during the pause before the abort: no abort, the stop ends uncertain (24.487141ms)
|
||||
✔ H10: a no-turn Interrupt lifts only its own fence; admission stays closed under force stop, overlap or revocation (82.531857ms)
|
||||
✔ H11: the controller disconnects mid-turn: work continues, the claim is unchanged, control stays put (132.25551ms)
|
||||
✔ H12: an exact retry after reconnecting to the same incarnation returns the same receipt; one dispatch (20.81694ms)
|
||||
✔ H13: a retry with the same request ID and different text is refused (23.586558ms)
|
||||
✔ H14: late stdout from the old engine after a replacement is dropped by incarnation, counted, never rendered (143.137981ms)
|
||||
✔ H15: a revoked connection's command is refused; the revocation fence holds (68.770581ms)
|
||||
✔ H16: a second controller for the same session refuses already-active; the first is untouched (22.152975ms)
|
||||
✔ H10: a second force stop while the first escalation runs refuses fenced; one escalation, and the claim records only the first stop's phases (66.062752ms)
|
||||
✔ a force stop whose fence throws leaves no escalation flag behind, so the next force stop runs (Darkwing F2, #1507) (25.705185ms)
|
||||
✔ H17: a confirmation reused, answered from another connection, or used after the stop changed is refused (93.774513ms)
|
||||
✔ H18: two prompts before any native output: the second refuses busy; one engine write (38.54247ms)
|
||||
✔ H19: the pipe fails mid-line under a large prompt: delivery-unknown transport-unknown, poisoned, no later write (127.571437ms)
|
||||
✔ H19: the link itself never writes again after an unknown outcome, whoever calls it (0.921566ms)
|
||||
✔ H19: the controller dies mid-write of a large line: after restart the outcome is unknown and nothing is resent (556.28349ms)
|
||||
✔ H20: the line is written but the ack is lost when the controller dies: orphan, outcome unknown, nothing resent (397.442413ms)
|
||||
✔ H21: a retry of the exact request with the old token after a crash is stale-incarnation; no second write (369.017779ms)
|
||||
✔ H22: after H21 and a valid recovery, a new request with the new token is admitted (2475.767375ms)
|
||||
✔ H23: requests pending at a restart are not resent; each shows outcome unknown (469.56075ms)
|
||||
✔ no shim from this file's tests is left running for the after hook (16.798311ms)
|
||||
✔ a plain conversation: catalogue row, one page, CHAT-01 records (7.32825ms)
|
||||
✔ native entries map to blocks: tools, thinking, bash, notices, ids that do not fit (1.671577ms)
|
||||
✔ F1: a malformed line is an unavailable part at its position, and reading continues (1.797267ms)
|
||||
✔ F1: a missing parent stops the history with a notice that names the unreadable lines (3.823873ms)
|
||||
✔ F1: an unreadable fork is never merged into another branch's history (2.131738ms)
|
||||
✔ F1: a follow stays on its branch when the next entry's parent is unreadable (3.08525ms)
|
||||
✔ F1: a file whose entries are all unreadable shows a notice per line (1.261016ms)
|
||||
✔ F2: a truncated trailing line marks the view incomplete, not an error (2.050202ms)
|
||||
✔ pagination: 100 parts, then the rest; parts concatenate to the whole branch (4.374478ms)
|
||||
✔ F3: a replaced file (new inode) refuses old cursors with reconcile (5.93408ms)
|
||||
✔ F4: a same-inode rewrite of the prefix refuses old cursors with reconcile (7.262726ms)
|
||||
✔ F5: growth between pages keeps the epoch and the page stops at the pinned length (8.17482ms)
|
||||
✔ F6: unknown, foreign and expired cursors refuse and leave the cursor usable (7.751472ms)
|
||||
✔ F7: a symlinked file and a symlinked directory component are refused, never opened (9.779172ms)
|
||||
✔ F8: a file swapped for a symlink after the catalogue is refused (2.259139ms)
|
||||
✔ F9: registrations never add or redirect a root (1.647066ms)
|
||||
✔ F10: a header cwd naming another project is refused (3.862289ms)
|
||||
✔ F11: parentSession renders with a marker and the parent is never opened (0.861955ms)
|
||||
✔ F12: two leaves: the default leaf is shown and the other branch reads alone (6.113849ms)
|
||||
✔ F12: a follow refuses when an appended duplicate id changes the branch's earlier parts (3.802909ms)
|
||||
✔ F12: a second root (Pi's resetLeaf) starts its own branch (2.232241ms)
|
||||
✔ F13: compaction is a marker in place, then the retained content (1.204849ms)
|
||||
✔ F14: long strings split into fragments and parts, reassemble exactly, and pages respect the byte cap (594.925675ms)
|
||||
✔ fragments never cut a surrogate pair and keep an empty string (5.340767ms)
|
||||
✔ F15: a Claude seat is an unsupported-harness placeholder whose directory is never read (1.832182ms)
|
||||
✔ unknown conversations, empty files and non-Pi files refuse (3.187358ms)
|
||||
✔ an unreadable file or root inside the roots is refused per row, not a failed catalogue (1.376523ms)
|
||||
✔ a seat directory without search permission refuses that root, not the catalogue (2.360616ms)
|
||||
✔ every page and cursor is a valid CHAT-01 record (1158.906087ms)
|
||||
✔ the engine pin holds for the installed package (1.805789ms)
|
||||
✔ pinned Pi, sealed and without credentials, answers the controller's commands with the shapes the fake models (338.482911ms)
|
||||
✔ 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) (622.587826ms)
|
||||
✔ pinned Pi appends thinking_level_change at start when the branch lacks one, so the leaf moves (K8 then fails closed) (382.850074ms)
|
||||
✔ N25: ordinary Interrupt reconciles; a non-empty queue_update in the window is O5 (78.124653ms)
|
||||
✔ N1: an extension's follow-up queued after the fence is cleared before any abort; O5, Unknown (56.004012ms)
|
||||
✔ N1: a follow-up queued before the fence is O5 at once; the Interrupt refuses fenced (27.989722ms)
|
||||
✔ N2: with abort first, the fake runs the external item (the ordering guard has teeth) (23.453382ms)
|
||||
✔ N3: the fence lands in preflight, preflight errors, no run: failed, No run, uncertain (34.752374ms)
|
||||
✔ N4: the ack arrives after the first abort and a run starts: clear and abort again; Interrupted (21.075286ms)
|
||||
✔ N5: an input handler takes the prompt: ack, no run, delivery-unknown handled-without-run (117.766065ms)
|
||||
✔ N6: an extension queues between clear_queue and abort: O5 and O6, Unknown (46.493948ms)
|
||||
✔ N7: clear_queue times out: no abort, nativeQueue unknown, force stop still ends it (1533.381108ms)
|
||||
✔ N7: clear_queue answers an error: no abort, nativeQueue unknown, the link not poisoned (14.706066ms)
|
||||
✔ N8: an extension prompt starts a run during Mosaic preflight; the losing settle is O3 (72.545436ms)
|
||||
✔ N9: a run that started before the fence and ends aborted: failed interrupted, Interrupted (18.028075ms)
|
||||
✔ N9: decision 34: a run that ends aborted with no stop in progress: aborted-without-stop, uncertain, outcome unknown (17.335107ms)
|
||||
✔ N9: an aborted that lands after the fence but before any abort is written: aborted-without-stop, Unknown (33.264748ms)
|
||||
✔ N10: fake conformance (32.60117ms)
|
||||
✔ N11: the run fails before any user message_start: delivery-unknown ack-without-start, never failed (32.220626ms)
|
||||
✔ N12: input that starts a run after the final empty clear is O1 and not part of the stop's proof (20.547692ms)
|
||||
✔ N13: agent_start with no slot held is O1; a later prompt refuses with zero engine bytes (66.377667ms)
|
||||
✔ N14: the run completes while clear_queue is in flight: finished, Completed first, uncertain (32.879935ms)
|
||||
✔ N14: the run completes after the abort is written, before Pi applies it: finished, never relabelled (31.06225ms)
|
||||
✔ N15: the fence lands in preflight, then an input handler takes it: handled-without-run, No run (22.296217ms)
|
||||
✔ N16: Interrupt with no slot and no run refuses no-turn: no stop, no bytes, admission open (13.679293ms)
|
||||
✔ N17: the run fails on its own during the exchange: failed, Failed on its own (28.830586ms)
|
||||
✔ N18: no final assistant message_end, or a lost line: working stays working; before working, transport-unknown (119.202622ms)
|
||||
✔ N19: a losing extension prompt settles inside the Mosaic run before its user message: O3, run-overlap (137.861973ms)
|
||||
✔ N20: an extension triggerTurn during Mosaic preflight starts first; while streaming it queues with no signal (84.077105ms)
|
||||
✔ N21: a losing settle after the receipt settled finished is O2; the receipt stays finished (15.997283ms)
|
||||
✔ N22: an agent-level custom message is dropped by the clear with no signal; evidence names the seal (16.070786ms)
|
||||
✔ N23: a nextTurn message survives clear and abort and attaches to the next prompt, with no signal (14.68961ms)
|
||||
✔ 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 (42.31339ms)
|
||||
✔ 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 (30.636779ms)
|
||||
ℹ tests 182
|
||||
ℹ suites 0
|
||||
ℹ pass 182
|
||||
ℹ fail 0
|
||||
ℹ cancelled 0
|
||||
ℹ skipped 0
|
||||
ℹ todo 0
|
||||
ℹ duration_ms 37479.617696
|
||||
@@ -0,0 +1,191 @@
|
||||
✔ approvals: a request is validated before anything is posted; the rendering shows names and never ids (6.803327ms)
|
||||
✔ approvals: the ledger is appended and folded into open requests with bind and approval states (1.503325ms)
|
||||
✔ approvals: a reply approves only when it points at a request, says exactly approve, and comes from a listed approver once (0.587709ms)
|
||||
✔ approvals: a button approves only on its own request message with the matching custom id (0.499045ms)
|
||||
✔ approvals flow: a turn that opened a request posts the message with the button, records it, binds it, and both approvers approve (15.626121ms)
|
||||
✔ approvals flow: a non-approver, a repeat, a wrong custom id and a service refusal each get their fixed line and a drop entry (8.238502ms)
|
||||
✔ approvals flow: an invalid request from the model, a refused post, and no api client are recorded and post nothing (6.407881ms)
|
||||
✔ approvals flow: start retries a bind and an approval left as unknown, under their original keys (2.652972ms)
|
||||
✔ authorize: open channel, listed user (2.08313ms)
|
||||
✔ authorize: wrong guild (0.212104ms)
|
||||
✔ authorize: no guild (DM) (0.17287ms)
|
||||
✔ authorize: unlisted channel (0.196632ms)
|
||||
✔ authorize: unknown channel, no info (0.270646ms)
|
||||
✔ authorize: thread of listed parent (0.202701ms)
|
||||
✔ authorize: thread of unlisted parent (0.284735ms)
|
||||
✔ authorize: text channel that is not a thread and not listed (0.146396ms)
|
||||
✔ authorize: unlisted user (1.051808ms)
|
||||
✔ authorize: no author (0.272498ms)
|
||||
✔ authorize: bot author (listed id, bot flag) (0.247539ms)
|
||||
✔ authorize: system author (0.121567ms)
|
||||
✔ authorize: the bot itself (0.081936ms)
|
||||
✔ authorize: webhook (0.086619ms)
|
||||
✔ authorize: mention channel without mention (0.148158ms)
|
||||
✔ authorize: mention channel with bot mention (0.144816ms)
|
||||
✔ authorize: mention channel with @everyone only (0.09966ms)
|
||||
✔ authorize: mention channel mentioning someone else (0.063962ms)
|
||||
✔ authorize: mention channel, content says @bot but mentions empty (0.081421ms)
|
||||
✔ authorize: private thread under mention channel, mentioned (0.082017ms)
|
||||
✔ authorize: private thread under mention channel, not mentioned (0.062717ms)
|
||||
✔ authorize: thread in another guild per channel info (0.952473ms)
|
||||
✔ authorize: not an object (0.076654ms)
|
||||
✔ authorize: no id (0.041326ms)
|
||||
✔ authorize: oversize content is accepted and flagged (0.043636ms)
|
||||
✔ authorize: exactly the limit is not oversize (0.056194ms)
|
||||
✔ authorize: a user's channel allowlist drops them outside it, threads count as the parent, others are unaffected (0.493223ms)
|
||||
✔ authorize: order puts wrong guild before user, and user before channel (no channel lookup for strangers) (0.16626ms)
|
||||
✔ binding: a complete binding validates and is frozen (2.916255ms)
|
||||
✔ binding: unknown key, missing field, wrong type refuse with exit 2 (1.364972ms)
|
||||
✔ binding: empty allowlists refuse (0.422034ms)
|
||||
✔ binding: a user's channel allowlist must be non-empty, listed and unique; absent means every listed channel (2.290728ms)
|
||||
✔ reloadDiff: reloadable keys are summarised by id; every fixed key refuses with exit 2 (2.40113ms)
|
||||
✔ binding: dmRecipient is optional, a snowflake, one of the listed users, and a fixed key (0.674105ms)
|
||||
✔ binding: file must be 0600, regular, not a symlink (1.718117ms)
|
||||
✔ binding: token file mode, symlink, emptiness and shape are checked; token never appears in errors (1.509502ms)
|
||||
✔ cli: check refuses a non-0600 token file with exit 2 before any network use (114.498363ms)
|
||||
✔ context files: absolute paths, traversal, symlinks and out-of-repo targets refuse; in-repo files resolve (3.478517ms)
|
||||
✔ cli: check refuses a missing context file and a missing binding with exit 2; usage is exit 4 (360.663917ms)
|
||||
✔ cli: reload validates the file first (exit 2), then needs a live owner (exit 1); usage is exit 4 (185.921135ms)
|
||||
✔ cli: run refuses when STOP is present, before any network use (125.594308ms)
|
||||
✔ binding: tools is optional, validated strictly, a fixed key for reload, and its roots are resolved against the data root (1.897438ms)
|
||||
✔ binding: a git key is validated at load and reaches the extension whole, and only on a writable root (1.264173ms)
|
||||
✔ 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 (20.543912ms)
|
||||
✔ delivery: refused and unknown outcomes are journaled; a later chunk is not sent after a failure (17.891089ms)
|
||||
✔ delivery: restart with an unknown entry re-sends the same nonce once and reconciles before accepting traffic (1.76059ms)
|
||||
✔ delivery: an unknown entry older than the dedupe window is marked refused, not re-sent; a still-unknown one refuses start (1.630809ms)
|
||||
✔ delivery: repeated unknown reconciliations never refresh the dedupe window; the original intent time decides (1.382004ms)
|
||||
✔ turn: a failed engine turn posts the fixed line, never model output, and writes a failed record (3.431852ms)
|
||||
✔ turn: a second message during a turn is held by the engine, both get their own reply and record (34.639966ms)
|
||||
✔ turn: a thread under a listed channel is answered in the thread; an unknown thread is looked up once (3.181378ms)
|
||||
✔ drop: an unlisted user gets silence and one drop line; no inbox entry, no REST call, no engine call (1.075773ms)
|
||||
✔ drop: an oversize message is accepted into the inbox, answered with the fixed line and journaled as a drop (1.349932ms)
|
||||
✔ restart: an inbox with three ids and a replay of the same three produces zero turns (44.027978ms)
|
||||
✔ stop: STOP present refuses start; STOP written while running refuses new turns and the current one finishes (33.845189ms)
|
||||
✔ ceiling: the ceiling plus one is refused and journaled; one fixed line per UTC day; a new day accepts again (8.161326ms)
|
||||
✔ ceiling: a burst arriving while turns are still running cannot queue past the ceiling (5.061618ms)
|
||||
✔ ceiling: a turn interrupted by a crash still counts after restart; admissions are durable (2.10529ms)
|
||||
✔ ceiling: the daily notice survives a same-day restart; one delivery attempt in total, even when the first attempt crashed mid-flight (4.530971ms)
|
||||
✔ duplicate: the same event delivered twice while the thread lookup is held yields one prompt, one admission and one reply (2.929839ms)
|
||||
✔ journal: no token-shaped string and no model output on the drop path reaches disk (0.765246ms)
|
||||
✔ 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 (2.00127ms)
|
||||
✔ receipt: Discord refusing the reaction leaves the turn intact and records ok false (3.055757ms)
|
||||
✔ reload: a new user is silent before and answered after; a removed channel goes silent; a lower ceiling applies at once (4.761877ms)
|
||||
✔ reload: a fixed key refuses with exit 2 and the old binding stays in force (2.76971ms)
|
||||
✔ tools: with a tools binding the turn record lists every read and its outcome; without one the field is null (4.884151ms)
|
||||
✔ context: the Discord block names the server, channels and modes, and states the rules from Q15 and Q16 (2.574995ms)
|
||||
✔ context: with tools the block names the roots, keeps file content as data, and says to state refusals plainly (0.80106ms)
|
||||
✔ context: a writable root adds the write rules and says a write is real only once Jason commits (2.075766ms)
|
||||
✔ context: the envelope is one bracketed line then the text; names cannot break the line (1.248815ms)
|
||||
✔ context: a git root swaps the terminal-commit line for the git verbs, and a vault root adds the id protocol (1.556924ms)
|
||||
✔ context: assembleContext concatenates files in launcher format and appends the block; sha256 is stable (1.485863ms)
|
||||
✔ context: splitReply keeps paragraphs together under the limit and splits long ones at lines, spaces, then hard (0.941675ms)
|
||||
✔ pinned Discord render/install and missing prerequisites; checkout bytes unchanged (149.267764ms)
|
||||
✔ Discord status reports effective WorkingDirectory from a fake user manager (65.444636ms)
|
||||
✔ engine: buildPiArgs carries the fixed flags, engine settings, session dir and prompt file (2.84504ms)
|
||||
✔ engine: with tools, buildPiArgs turns pi's own tools off, loads the extension explicitly and allowlists exactly our three (0.44355ms)
|
||||
✔ engine: a run with tool turns settles once, on the answer, with every tool call in the result (64.96546ms)
|
||||
✔ engine: a run that ends on a tool-only turn fails the prompt as empty; a retried run settles on the real end (67.103849ms)
|
||||
✔ engine: one prompt, one turn, text and usage come back (50.383506ms)
|
||||
✔ engine: a prompt while streaming is held until pi settles, then sent as its own run, and answered in order (336.876708ms)
|
||||
✔ engine: a held prompt that times out before pi settles fails on its own and is never sent (233.322574ms)
|
||||
✔ engine: timeout sends abort and fails only that turn; the process stays (104.920883ms)
|
||||
✔ engine: tool events from a run that outlived its timeout never land in the next prompt's record (223.370784ms)
|
||||
✔ engine: a prompt after a turn that timed out before its agent_start waits for pi to settle instead of being refused (127.731305ms)
|
||||
✔ 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 (213.93457ms)
|
||||
✔ engine: a timed-out turn pi starts only after the grace never answers a later prompt (613.725525ms)
|
||||
✔ engine: late events of a run past its grace, before pi exits, answer nothing and nothing more is sent (early prompt response) (1.460483ms)
|
||||
✔ engine: late events of a run past its grace, before pi exits, answer nothing and nothing more is sent (late prompt response) (0.625371ms)
|
||||
✔ engine: a timed-out run pi did start outlives the grace; the next prompt goes out when it ends (426.236129ms)
|
||||
✔ engine: a malformed JSONL line fails the turn, not the process (24.228471ms)
|
||||
✔ engine: a turn that ends in error rejects with the error code; process exit fails pending turns (45.309487ms)
|
||||
✔ gateway: hello -> identify with intents, ready, heartbeat with jitter, ack (3.084366ms)
|
||||
✔ gateway: missed ack closes the socket and resumes with the last sequence (1.473896ms)
|
||||
✔ gateway: op 7 reconnect resumes; op 9 non-resumable re-identifies (0.452681ms)
|
||||
✔ gateway: op 9 resumable resumes (0.31575ms)
|
||||
✔ gateway: close 4014 is fatal, reports the missing intent, never reconnects (0.803115ms)
|
||||
✔ gateway: 4004 and 4013 are fatal too; 1006 reconnects with identify when no session (0.485026ms)
|
||||
✔ gateway: close() is final and unparseable frames are ignored (0.41958ms)
|
||||
✔ git: config validation is strict, needs write: true, a work tree and a private token file (104.284235ms)
|
||||
✔ git: the child environment drops every host git config, names one helper, and carries the token path only for origin (71.311503ms)
|
||||
✔ git: status reports the branch, ahead/behind and changed paths, and refuses off the named branch or mid-merge (96.657776ms)
|
||||
✔ git: parseStatus reads porcelain v2 including renames and conflicts (0.32419ms)
|
||||
✔ git: a commit stages exactly the named files, carries the seat author and the requester trailer, and pushes at once (89.539983ms)
|
||||
✔ git: commit refusals: message, paths, requester, nothing to commit, and an index that already holds other work (94.146798ms)
|
||||
✔ git: a commit whose push fails is still a commit, says so, and the next commit's push carries both (D6) (78.290835ms)
|
||||
✔ git: pull is fast-forward only; a diverged origin or dirty local files refuse with nothing merged (197.406561ms)
|
||||
✔ git: push pushes the named branch only and reports up to date (63.210276ms)
|
||||
✔ git: no token value or token path ever reaches a git argument list; outputs are masked and capped (81.539991ms)
|
||||
✔ git: the credential helper answers get over https from a private file and nothing else (188.960765ms)
|
||||
✔ git: the vault protocol validates before a commit, honours another owner's lock, reserves ids, and locks around writes (705.497553ms)
|
||||
✔ lock: the claim is exclusive; a second start against a live owner refuses (5.301776ms)
|
||||
✔ lock: a stale lock (dead owner, reused pid, or record without start) refuses run and is never signaled; only unlock clears it (113.242882ms)
|
||||
✔ lock: an incomplete claim (directory without owner record) is busy and refuses run; unlock clears it (1.19267ms)
|
||||
✔ lock: an owner record that exists but cannot be read is invalid: never signaled, never removed, never claimed over (4.066148ms)
|
||||
✔ lock: legacy upgrade; a live connector holding a {pid, start} record is unknown, unlock refuses and nothing changes; after it exits, unlock clears it (59.602498ms)
|
||||
✔ 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 (324.073705ms)
|
||||
✔ lock: identity syntax; only canonical unsigned decimal start ticks and lowercase boot uuids are identities (0.456936ms)
|
||||
✔ lock: a process whose start marker or boot id cannot be read refuses to claim (21.480782ms)
|
||||
✔ lock: a live pid whose identity cannot be read right now is unknown: never signaled, never removed, never claimed over (0.96481ms)
|
||||
✔ lock: four processes racing for the same binding; exactly one claims it and the others refuse (31.767544ms)
|
||||
✔ lock: stale handoff; concurrent starts over a stale lock all refuse, nothing reclaims, one unlock then exactly one live owner (139.819451ms)
|
||||
✔ lock: four-party schedule; claims landing inside an unlock's gap never survive, one unlock leaves no owner and no residue (83.723685ms)
|
||||
✔ notices: a kind is recorded per UTC day and found again (0.674976ms)
|
||||
✔ notify: the DM channel opens once, every send carries the nonce, and only the message id comes back (5.380147ms)
|
||||
✔ notify: refusals and unknowns surface as RestOutcome without ids; a refused send reopens the channel next time (2.098667ms)
|
||||
✔ notify: no dmRecipient, a non-0600 binding or token, or a missing binding refuse before any network use (1.781719ms)
|
||||
✔ check --offline validates local fixture without REST or gateway calls (234.650379ms)
|
||||
✔ check --offline refuses token mode and missing context under the selected repo (132.484268ms)
|
||||
✔ run --offline is a usage refusal before reading config or networking (64.731696ms)
|
||||
✔ recover: nothing to do is clean; a lock whose owner is gone or that has no record is cleared and STOP ends up absent (62.410957ms)
|
||||
✔ recover: an operator STOP refuses with exit 3 and is never removed, whatever the lock says (58.053121ms)
|
||||
✔ recover: a brake written during the unlock wins; STOP stays with both lines and the start is refused (43.996203ms)
|
||||
✔ recover: a held binding refuses with exit 3 and writes no STOP: live owner, alive pid without verifiable identity, unreadable record (84.057158ms)
|
||||
✔ cli: recover exits 0 when ready, 3 behind a brake or a held binding, and run's own STOP refusal is 3 (635.667287ms)
|
||||
✔ rest: createMessage sends nonce, enforce_nonce, empty allowed_mentions and a soft reply reference (2.591213ms)
|
||||
✔ rest: 429 waits retry_after and retries; 4xx is refused; 5xx and socket errors are unknown (3.024713ms)
|
||||
✔ rest: content and nonce limits are enforced locally; typing never throws (0.656847ms)
|
||||
✔ rest: react PUTs the encoded emoji on the inbound message for @me; 2xx is true, anything else is false and never throws (0.924726ms)
|
||||
✔ rest: createDm posts the recipient and resolves the channel id; 4xx refused, 5xx unknown, bad id never sent (1.483626ms)
|
||||
✔ setspark config: a bare https or loopback origin, a private key file, a principal (6.664919ms)
|
||||
✔ setspark config: reaches the tools config and the binding as a fixed key (3.061079ms)
|
||||
✔ setspark config: the binding's key survives resolveToolRoots and the engine's JSON hand-off to the extension (1.496314ms)
|
||||
✔ setspark config: approvers come from the binding's users, never from the binding's setspark key (1.878755ms)
|
||||
✔ setspark verbs: required_approvers go out as discord ids from names and come back as names (30.749849ms)
|
||||
✔ setspark verbs: no Discord user id reaches tool text, whatever shape the service returns it in (14.265047ms)
|
||||
✔ setspark contract: a decision made with names opens a request the connector accepts; names stored by an old record still refuse (9.062074ms)
|
||||
✔ setspark keys: read per call, one printable token per file, rotation without a restart (4.733272ms)
|
||||
✔ setspark idempotency keys: principal, turn id, call index; connector keys name a step (0.776584ms)
|
||||
✔ setspark http core: json in and out, bearer header, idempotency header, fixed user agent, no key anywhere else (3.474422ms)
|
||||
✔ setspark http core: error bodies become fixed refusals with code and the 409 fields; server text is data, cut (1016.710312ms)
|
||||
✔ setspark verbs: a setspark key enables the eight verbs and no counters (0.458927ms)
|
||||
✔ 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 (6.299028ms)
|
||||
✔ setspark verbs: no turn refuses every write before any request; bad arguments refuse before any request; reads still work (1.418772ms)
|
||||
✔ setspark verbs: renderRecord caps long output and hides the accepted snapshot (0.232995ms)
|
||||
✔ setspark api: bind, add_approval (button and reply) and get use integer request ids and the connector's keys (2.528152ms)
|
||||
✔ tools: config refuses a missing, symlinked, dotted, non-directory or duplicate root and bad limits (4.150182ms)
|
||||
✔ tools: every escape is refused with a fixed reason and nothing outside the root is read (3.833281ms)
|
||||
✔ tools: happy paths list, read a window, and search case-insensitively; dotfiles and symlinks never appear (5.143806ms)
|
||||
✔ tools: the tool set renders text for the model, records details for the journal, and enforces the per-run budget (4.315637ms)
|
||||
✔ tools: listing and search caps hold (12.230843ms)
|
||||
✔ tools: credential shapes are caught; ordinary prose and ids are not (1.532659ms)
|
||||
✔ 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 (11.468616ms)
|
||||
✔ tools: an unreadable file under the root is skipped by search and refused by read (2.193103ms)
|
||||
✔ tools: config accepts write: true only as a boolean, and enables the write tools only then (1.457073ms)
|
||||
✔ tools: every write outside the fence is refused before any byte lands, and no temp file remains (7.358864ms)
|
||||
✔ tools: write_file leaves the exact bytes, edit_file replaces one exact match, and the set renders the change as uncommitted (2.9623ms)
|
||||
✔ tools: a target that changed between the check and the rename is refused and the temp file is removed (1.618327ms)
|
||||
✔ web: config takes an https or loopback-http SearXNG base url and a bounded fetch cap (3.657914ms)
|
||||
✔ web: address rules refuse every private, loopback, link-local, mapped and multicast form (2.950055ms)
|
||||
✔ web: web_fetch refuses bad urls, private hosts, rebinding names, non-https redirects, too many hops, error status, non-text bodies, and times out (1035.194935ms)
|
||||
✔ 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.303015ms)
|
||||
✔ web: html to text drops scripts, styles and comments, decodes entities and keeps block breaks (0.248421ms)
|
||||
✔ 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.635785ms)
|
||||
✔ 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.29529ms)
|
||||
ℹ tests 183
|
||||
ℹ suites 0
|
||||
ℹ pass 183
|
||||
ℹ fail 0
|
||||
ℹ cancelled 0
|
||||
ℹ skipped 0
|
||||
ℹ todo 0
|
||||
ℹ duration_ms 2711.481971
|
||||
@@ -0,0 +1,92 @@
|
||||
✔ sessionModel: agent vars win, then the system's execution settings (14.765749ms)
|
||||
✔ a pi bundle: prompt, policy, tools and manifest, 0600 in a 0700 directory (5.731538ms)
|
||||
✔ a chief-of-staff bundle lists launch among the bus tools and the manifest's typed tools (2.457181ms)
|
||||
✔ a claude-code bundle adds the wrapped gate hook and the MCP config (2.403953ms)
|
||||
✔ the prompt's built-in tools line is the harness's own names, as the manifest lists them (8.448232ms)
|
||||
✔ the prompt names the project clones in the workspace, and none when there are none (6.323868ms)
|
||||
✔ the prompt explains the review loop to a role with push_working, and reviews to one with fetch_review (8.992296ms)
|
||||
✔ a bundle is written once: an existing file refuses (2.179129ms)
|
||||
✔ a path with a single quote can't go into the hook command (2.05533ms)
|
||||
✔ allow exits 0, a deny exits 2 with the reason on stderr (122.949032ms)
|
||||
✔ a missing or wrong policy, or a bad event, exits 2 (82.270114ms)
|
||||
✔ the bundle's wrapped command: a missing gate or node still blocks (1090.345033ms)
|
||||
✔ claude: typed tools through MCP, the hook blocks, builtins outside --tools don't exist (780.922125ms)
|
||||
✔ claude: the hook alone blocks a path outside the workspace (470.826308ms)
|
||||
✔ claude: a second turn resumes the first turn's session (756.433206ms)
|
||||
✔ claude adapter: --restricted is always passed (6.071969ms)
|
||||
✔ claude adapter: MOSAIC_CLAUDE_BIN wins over PATH and must be an absolute executable (19.245536ms)
|
||||
✔ claude adapter: a directory pin exits 2 before it runs; a pin with a space runs (7.19483ms)
|
||||
✔ claude: CLAUDE.md files and auto-memory don't reach the model; without --restricted they do (728.360818ms)
|
||||
✔ claude: a missing hook or MCP file refuses before claude starts (7.267484ms)
|
||||
✔ pi: policy tools and typed tools pass, anything else is blocked (3.318107ms)
|
||||
✔ claude: builtins map from pi names, typed tools need the mcp prefix (0.659734ms)
|
||||
✔ file tool paths must resolve inside the workspace (1.068427ms)
|
||||
✔ pi's own path normalisation can't be used to step out (0.885685ms)
|
||||
✔ a symlink inside the workspace that points out is outside (0.821539ms)
|
||||
✔ a dangling symlink is refused at any depth, in both harnesses (2.534074ms)
|
||||
✔ read is checked under every spelling pi's read would open, in both harnesses (14.776276ms)
|
||||
✔ other spellings cover directories, dangling links and pi's cwd (1.587952ms)
|
||||
✔ a relative path climbs from the workspace's real path, in both harnesses (4.285242ms)
|
||||
✔ claude path fields per tool (0.819818ms)
|
||||
✔ glob patterns stay inside the workspace (0.805017ms)
|
||||
✔ a path that can't be checked is blocked (0.524914ms)
|
||||
✔ fetch_review's have lists the working refs before origin's, and no more than HAVE_MAX (222.698272ms)
|
||||
✔ fetch_review's have orders the working refs newest first within each clone, one clone after another (133.247163ms)
|
||||
✔ a runner git past its time: SIGTERM reaches its process group, and SIGKILL follows after the grace period (1809.941407ms)
|
||||
✔ once a signalled runner git has closed, its group gets SIGKILL, so a process that ignored SIGTERM dies too (304.233894ms)
|
||||
✔ initialize, ping and tools/list (47.535777ms)
|
||||
✔ tools/call goes through the tool socket; a refusal is an isError result (34.256518ms)
|
||||
✔ replies are compact JSON; over the cap a tool error names the size and the cap (38.269859ms)
|
||||
✔ unknown tools and methods are JSON-RPC errors and never reach the socket (30.336371ms)
|
||||
✔ a missing argument is a usage error (28.004897ms)
|
||||
✔ pi: typed tools reach the socket, the gate blocks, agent_end writes the marker (380.895506ms)
|
||||
✔ pi: a reply over the cap is a tool error naming the size and the cap; list_tasks asks for open tasks (351.643182ms)
|
||||
✔ pi: a write through a dangling symlink is blocked, and nothing appears outside (388.300224ms)
|
||||
✔ pi: a read is refused when pi would open another spelling outside (335.479106ms)
|
||||
✔ pi: a relative path climbs from the real path of a workspace behind a symlink (345.378553ms)
|
||||
✔ pi: a relative path climbs from the real path of a dataRoot behind a symlink (342.28412ms)
|
||||
✔ pi: a missing extension refuses before any model call (6.179747ms)
|
||||
✔ pi: an extension without its configuration fails pi's start (257.559933ms)
|
||||
✔ founderCheck: founder variables, then a needed service without a usable token (1.439894ms)
|
||||
✔ turnRequest names the sender, class, reply and decision (0.191121ms)
|
||||
✔ turnRequest says a tester's message is a test and never Jason's instruction (0.195149ms)
|
||||
✔ turnRequest shows the message's own sent time, a day old as well as new (0.123592ms)
|
||||
✔ stateBlock names the launch state with its time and event, and the open tasks with the cap (0.230878ms)
|
||||
✔ a message becomes a turn, the answer goes back as a RESULT, SIGTERM releases and exits 0 (184.993444ms)
|
||||
✔ launches revoked and then restored: the turn's block reads allowed and gives the restore time (196.150497ms)
|
||||
✔ a task closed on the tracker is missing from the next turn's open list (195.117059ms)
|
||||
✔ a message read a day after it was sent shows the time it was sent (114.595794ms)
|
||||
✔ a decision the human resolves becomes a turn citing the decision and the answer (140.337806ms)
|
||||
✔ a failed state read fails the turn: no adapter runs, the sender hears why, the message is read (64.373497ms)
|
||||
✔ a SIGTERM before the claim stops the runner with exit 0 and no claim (65.06167ms)
|
||||
✔ typed tools carry the runner's capability; launch goes to the host's launch socket (275.816021ms)
|
||||
✔ a RESULT gets no automatic reply; failed turns reply with the reason (1189.5088ms)
|
||||
✔ SIGTERM during a turn kills the turn's process group and still exits 0 (92.973134ms)
|
||||
✔ founder credentials stop before the claim (20) (98.36162ms)
|
||||
✔ a refused claim exits 21; an ended run's capability exits 22 (93.909697ms)
|
||||
✔ the launch ending under a running session exits 22 (98.032738ms)
|
||||
✔ a broker that stays unreachable exits 23 after brokerRetries polls (198.928496ms)
|
||||
✔ a broker that is down at the claim exits 23, not 21 (47.669801ms)
|
||||
✔ no capability, or a malformed one, on stdin exits 2 (148.954023ms)
|
||||
✔ a missing or malformed policy exits 2 before the claim (81.932002ms)
|
||||
✔ the PM gets its task verbs and the reads, and no launch (7.990909ms)
|
||||
✔ the chief of staff gets launch and send_message, and no task verbs (2.911115ms)
|
||||
✔ the assistant gets send_message and the reads, no launch (3.231081ms)
|
||||
✔ list_requirements reads the requirement ids, for a task.create holder with a tracker (4.537834ms)
|
||||
✔ a coder gets no launch, no resolve_decision, and no task tools without a tracker (1.479233ms)
|
||||
✔ a coder with a tracker gets its own task tools and no task_reassign (1.217942ms)
|
||||
✔ launch only when the business's launch block names the instance as launcher (2.412717ms)
|
||||
✔ an action outside the instance's authority has no tool (1.724385ms)
|
||||
✔ the review loop: push_working and request_review for the coder, fetch_review and review_verdict for the reviewer, none for the PM (4.429008ms)
|
||||
✔ callTool: one JSON line out, the result back, a refusal rejects (8.564741ms)
|
||||
✔ list_tasks takes the broker's filters and pages; get_task takes a task_ref (2.707563ms)
|
||||
✔ callTool sends list_tasks done false unless the session names done; other tools as given (2.671271ms)
|
||||
✔ replyText: compact JSON up to the cap, an error naming the size and the cap over it (0.839773ms)
|
||||
ℹ tests 84
|
||||
ℹ suites 0
|
||||
ℹ pass 84
|
||||
ℹ fail 0
|
||||
ℹ cancelled 0
|
||||
ℹ skipped 0
|
||||
ℹ todo 0
|
||||
ℹ duration_ms 10652.286247
|
||||
@@ -0,0 +1,94 @@
|
||||
✔ a raw token file, with or without one trailing newline, reaches curl only through the config stream (231.324327ms)
|
||||
✔ the raw path accepts nothing else, and refuses before curl runs (1924.439161ms)
|
||||
✔ the file checks still apply on the raw path: mode, symlink, missing, directory (738.547711ms)
|
||||
✔ the raw path base URL has no override (130.02302ms)
|
||||
✔ the JSON path is unchanged, and JSON never falls through to the raw path (358.228042ms)
|
||||
✔ a file that changes between the two reads refuses before curl runs, with or without a body (1090.397106ms)
|
||||
✔ the token reaches no child environment, even with an inherited CFG or SHELLOPTS=allexport (1776.885418ms)
|
||||
✔ real helper GET HTTP 200 preserves exit 0 without credentials (21.581152ms)
|
||||
✔ real helper POST HTTP 201 preserves exit 0 without credentials (15.997032ms)
|
||||
✔ real helper GET HTTP 403 preserves exit 1 without credentials (15.692494ms)
|
||||
✔ fixture git subjects only, follow-ups and three session kinds (217.066332ms)
|
||||
✔ text and JSON carry same numbers, open and truncated title (777.700079ms)
|
||||
✔ missing credentials exit 2, no-issues never calls API and shows unknown (566.260049ms)
|
||||
✔ empty range gives no rows and zero totals (824.741049ms)
|
||||
✔ inclusive UTC dates, first-line preamble only, role and seat boundaries (260.176734ms)
|
||||
✔ close-only issue included, even median, missing metadata stays unknown (215.746601ms)
|
||||
✔ unique commits but per-issue links count multiple tags once each (225.06664ms)
|
||||
✔ issue pages of 50, 50 and 7 read 107 issues in 3 pages (416.257793ms)
|
||||
✔ one short page reads 1 page; --no-issues reads none and says so (288.385777ms)
|
||||
✔ a short page 10 is read; a full page 10 refuses at the ceiling (1649.290896ms)
|
||||
✔ an issue number on two pages refuses (265.109433ms)
|
||||
✔ a failing page 2 refuses the whole read without its stderr (198.755351ms)
|
||||
✔ page 2 with a malformed record refuses (160.017184ms)
|
||||
✔ page 2 with more than 50 rows refuses (173.584478ms)
|
||||
✔ bad API payload not JSON refuses (148.925795ms)
|
||||
✔ bad API payload {} refuses (107.715123ms)
|
||||
✔ bad API payload [{"number":1}] refuses (96.156802ms)
|
||||
✔ partial or malformed session log refuses with location, not content (115.456959ms)
|
||||
✔ a U+2028 or U+2029 inside a session string is one line, not a malformed record (106.309601ms)
|
||||
✔ no sessions is an empty table; symlink source refuses (177.005039ms)
|
||||
✔ reads only refactor even when another branch is checked out (113.389965ms)
|
||||
✔ invalid dates, reverse dates and duplicate options refuse (71.131067ms)
|
||||
✔ T3 agent assignments do not count as human in Table 2 (112.489382ms)
|
||||
✔ preamble parsing and issue number boundaries (0.504241ms)
|
||||
✔ T3 header: agent, or board from control-board; anything short of the full header is human (0.164069ms)
|
||||
✔ no closed issues with human messages means undefined ratio, not invented zero (0.251719ms)
|
||||
✔ T3: seat, archived, unmapped and Researcher threads count; imported, deleted and other-project threads do not (219.630792ms)
|
||||
✔ T3: the default path is read from HOME and prints no path line; --no-t3 says so (723.130006ms)
|
||||
✔ T3: a HOME with no database exits 1 and names --no-t3 (213.617719ms)
|
||||
✔ T3: a file that is not a database exits 1 and names --no-t3 (122.440817ms)
|
||||
✔ T3: a seat thread renamed to another seat exits 1 naming thread, title and roles (112.393021ms)
|
||||
✔ T3: an unmapped thread addressed as a seat exits 1 (133.776694ms)
|
||||
✔ T3: a header to another thread id is not cross-checked (138.784024ms)
|
||||
✔ T3: no project, or two, for this root exits 1 (304.217809ms)
|
||||
✔ T3: a removed column exits 1 and names it (105.582429ms)
|
||||
✔ T3: a missing table exits 1 and names it (127.224138ms)
|
||||
✔ T3: a counted row with an unknown role exits 1 without its text (149.899839ms)
|
||||
✔ T3: a counted row with role reasoning without the reasoning: id prefix exits 1 without its text (116.717834ms)
|
||||
✔ T3: a counted row with non-text content exits 1 without its text (162.013763ms)
|
||||
✔ T3: a counted row with an unparseable created_at exits 1 without its text (179.586973ms)
|
||||
✔ T3: a reasoning summary is skipped before any check and counted as excluded (387.580732ms)
|
||||
✔ T3: a missing orchestration_events makes the diagnostic unknown and keeps the counts (235.161169ms)
|
||||
✔ T3: a human message with no event counts in humanWithoutEvent (126.365015ms)
|
||||
✔ T3: an unparseable event makes the diagnostic unknown and keeps the counts (210.904788ms)
|
||||
✔ T3: an event with no string messageId makes the diagnostic unknown and keeps the counts (221.404409ms)
|
||||
✔ T3: an error that is not from SQLite is rethrown, not reported as a database failure (83.069155ms)
|
||||
✔ T3: a symlink at ~/.t3 exits 1 (109.699913ms)
|
||||
✔ T3: a symlink at ~/.t3/userdata exits 1 (99.363632ms)
|
||||
✔ T3: a symlink at ~/.t3/userdata/state.sqlite exits 1 (89.484625ms)
|
||||
✔ T3: with --t3-db, a symlinked file or directory exits 1 (180.78262ms)
|
||||
✔ T3 WAL: the newest message only in -wal, writer attached, is counted (137.130585ms)
|
||||
✔ T3 WAL: stopped cleanly, counts are correct and the main file is unchanged (125.765282ms)
|
||||
✔ T3 WAL: -wal without -shm in a writable directory is read (145.613648ms)
|
||||
✔ T3 WAL: -wal without -shm in a read-only directory exits 1 (139.028844ms)
|
||||
✔ T3 WAL: stopped cleanly in a read-only directory exits 1 (121.631129ms)
|
||||
✔ T3: a lock held past the 5 s busy timeout exits 1 and names --no-t3 (5127.476351ms)
|
||||
✔ a done row whose closing issue is open is a violation; a row that is not done is not (3.437406ms)
|
||||
✔ an issue several rows close is expected closed only once all of them are done (0.813215ms)
|
||||
✔ closure needs positive evidence: unknown is undecided, and so is a skipped or short issue check (0.541976ms)
|
||||
✔ each owner of an in-progress or in-review row gets one liveness class (13.114675ms)
|
||||
✔ a required row not done after 14 days is a violation; a legacy row uses genesis as its lower bound (0.817411ms)
|
||||
✔ an ISO requiredSince, as `set required` writes it, ages from its UTC day; one that does not parse is a violation (0.963939ms)
|
||||
✔ the text section always ends in a count and a result, and never prints a full pass (0.636028ms)
|
||||
✔ pidAlive: a running pid is present, an exited one is gone, and EPERM still means present (46.120699ms)
|
||||
✔ issue states: open list first, then the metric page, then at most 10 lookups (993.773519ms)
|
||||
✔ a full open list: lookups settle what it leaves out, and only an unsettled issue keeps it undecided (1608.668701ms)
|
||||
✔ the open list refuses on a failed call or a bad record, and never echoes the helper (380.692351ms)
|
||||
✔ a helper call past the deadline is killed with its child, and the call reports it (2138.560511ms)
|
||||
✔ readQueue loads queue.json through the queue validator and refuses anything else (284.064045ms)
|
||||
✔ protected changes list every in-range entry that changes a required or parked row (512.777191ms)
|
||||
✔ the CLI prints the queue section above the weekly table and under a queue key in --json (657.590914ms)
|
||||
✔ a queue with nothing wrong prints 0 violations and a reduced pass, never a full pass (221.659297ms)
|
||||
✔ --no-issues makes no call and leaves the issue checks undecided; --no-queue skips the section (291.481037ms)
|
||||
✔ the CLI refuses a bad queue before any call, and a failed open list with exit 2 (266.763721ms)
|
||||
✔ --unsupported-runtime repeats once per seat and takes a seat name (353.472496ms)
|
||||
✔ an unreadable config makes every owner invalid instead of passing them (300.777195ms)
|
||||
ℹ tests 86
|
||||
ℹ suites 0
|
||||
ℹ pass 86
|
||||
ℹ fail 0
|
||||
ℹ cancelled 0
|
||||
ℹ skipped 0
|
||||
ℹ todo 0
|
||||
ℹ duration_ms 17728.316114
|
||||
@@ -0,0 +1,77 @@
|
||||
✔ pure resolution selects current default or explicit enrolled account (2.135692ms)
|
||||
✔ scope is explicit, bounded and never inferred (1.696573ms)
|
||||
✔ fork pin is preserved against default change, override, missing account and revocation (0.751061ms)
|
||||
✔ unenrolled account/provider, missing harness, model expansion and native model ceiling refuse (0.834454ms)
|
||||
✔ only explicit synthetic credential forms and internal fixture stores admitted (5.651779ms)
|
||||
✔ two concurrent workspaces of the same agent publish distinct complete private generations (101.846352ms)
|
||||
✔ same execution ID is exclusively claimed and cannot overwrite a published generation (32.593771ms)
|
||||
✔ failed generation after-auth preserves prior files, records failure and refuses blind same-ID retry (46.139481ms)
|
||||
✔ failed generation before-publish preserves prior files, records failure and refuses blind same-ID retry (60.774131ms)
|
||||
✔ credential lock contention refuses without duplicate side effects (11.113394ms)
|
||||
✔ symlinked pre-existing final target is refused and never followed (30.783662ms)
|
||||
✔ invalid registry cannot resolve; no fallback to supplied partial entries (0.266675ms)
|
||||
✔ post-publication failure records uncertainty, preserves complete generation and prevents replay (26.890403ms)
|
||||
✔ expired credentials refresh under transaction and subsequent generation reuses rotation (123.182237ms)
|
||||
✔ refresh failure retains prior generation and store state (68.053264ms)
|
||||
✔ refresh timeout retains prior generation and store state (154.620414ms)
|
||||
✔ refresh malformed retains prior generation and store state (62.990002ms)
|
||||
✔ concurrent refresh on same account refuses contention while unrelated account proceeds (215.640843ms)
|
||||
✔ invalid refresh options refuse before burning claim (32.04283ms)
|
||||
✔ fixed fake process rotates both OAuth fields without mutating caller input (29.720572ms)
|
||||
✔ concurrent isolated processes preserve separate provider credentials (70.950318ms)
|
||||
✔ fake failure is refused with fixed diagnostics (23.217211ms)
|
||||
✔ fake malformed is refused with fixed diagnostics (24.645594ms)
|
||||
✔ fake timeout is refused with fixed diagnostics (103.595576ms)
|
||||
✔ fake unchanged is refused with fixed diagnostics (26.548106ms)
|
||||
✔ caller executable/environment injection is rejected before spawning (0.311464ms)
|
||||
✔ valid fixture tree validates and lists without secrets (76.274057ms)
|
||||
✔ unknown-field refuses (0.436141ms)
|
||||
✔ invalid-id refuses uppercase and traversal shapes (0.320595ms)
|
||||
✔ plain-http baseUrl requires allowInsecureTransport (0.380754ms)
|
||||
✔ native provider rejects allowInsecureTransport (0.186075ms)
|
||||
✔ unsupported credential type and kind refuse (0.178492ms)
|
||||
✔ account provider-path mismatch refuses (0.138572ms)
|
||||
✔ profile account refs must be provider/account shaped (0.237522ms)
|
||||
✔ seat selection accepts fork pin field, validates account refs (0.245458ms)
|
||||
✔ harness manifest id must equal executable (gate 1) (0.234864ms)
|
||||
✔ CLI validate: duplicate provider id across files refuses (32.865949ms)
|
||||
✔ CLI validate: missing referenced provider/account refuse (32.541351ms)
|
||||
✔ CLI validate: broken JSON refuses without secret echo (28.519109ms)
|
||||
✔ CLI usage errors exit 2 (57.148481ms)
|
||||
✔ credential.json sibling presence does not break validation and is never read (65.239047ms)
|
||||
✔ D1 missing, empty and structurally empty roots refuse, no list projection (184.857134ms)
|
||||
✔ D1 required directory auth cannot be absent (67.860984ms)
|
||||
✔ D1 required directory auth/providers cannot be absent (60.088359ms)
|
||||
✔ D1 required directory auth/accounts cannot be absent (63.468986ms)
|
||||
✔ D1 required directory auth/settings cannot be absent (73.038247ms)
|
||||
✔ D1 required directory harnesses cannot be absent (69.029213ms)
|
||||
✔ D1 root file and unreadable metadata refuse (119.490104ms)
|
||||
✔ D2 no symlink traversal at auth/providers/openai-codex.json (54.86312ms)
|
||||
✔ D2 no symlink traversal at auth/accounts/openai-codex/homelab-openai (65.403539ms)
|
||||
✔ D2 no symlink traversal at auth/providers (56.102928ms)
|
||||
✔ D2 no symlink traversal at auth (57.325378ms)
|
||||
✔ D2 root and ancestor symlinks and lexical traversal refuse (169.358477ms)
|
||||
✔ private filesystem modes enforced for root (56.497545ms)
|
||||
✔ private filesystem modes enforced for auth (53.365766ms)
|
||||
✔ private filesystem modes enforced for auth/providers/openai-codex.json (56.01682ms)
|
||||
✔ private filesystem modes enforced for auth/accounts/openai-codex/homelab-openai/account.json (66.821651ms)
|
||||
✔ D3 numeric version 1 only across all record kinds (1.972247ms)
|
||||
✔ D4 nested unknown keys and missing per-kind required fields refuse (79.552573ms)
|
||||
✔ D5 unenrolled default refuses even when account exists (66.6451ms)
|
||||
✔ D6 provider/account credential type must match (68.767803ms)
|
||||
✔ D7 every harness endpoint enforces HTTP opt-in and shape (0.449312ms)
|
||||
✔ D8 URLs reject embedded credentials and unsupported protocols without echo (168.73083ms)
|
||||
✔ D9 malformed JSON diagnostics contain no content excerpt (59.71486ms)
|
||||
✔ D10 missing metadata is missing-path, not invalid-json (62.072326ms)
|
||||
✔ D10 library returns no partial entries on any invalid record (72.736021ms)
|
||||
✔ null/scalar/array metadata refuses without stack or echo (245.003918ms)
|
||||
✔ credential sibling is never opened, even when an unreadable symlink (34.755858ms)
|
||||
✔ oversized metadata refuses before parsing (57.711837ms)
|
||||
ℹ tests 69
|
||||
ℹ suites 0
|
||||
ℹ pass 69
|
||||
ℹ fail 0
|
||||
ℹ cancelled 0
|
||||
ℹ skipped 0
|
||||
ℹ todo 0
|
||||
ℹ duration_ms 2246.198087
|
||||
@@ -0,0 +1,29 @@
|
||||
✔ REQ-PLUG-1 REQ-LIMIT-4 the registry: usage-source and limit-check at interface version 1, each a list (0.982103ms)
|
||||
✔ REQ-PLUG-1 checkPlugins accepts one key per kind, each a list of names, empty included (0.225131ms)
|
||||
✔ REQ-PLUG-1 checkPlugins refuses bad shapes, unknown kinds, bad names and duplicates (3.76004ms)
|
||||
✔ REQ-PLUG-1 a kind that switches on one module takes one name, not a list (0.149881ms)
|
||||
✔ REQ-PLUG-1 checkPlugins reports through the caller's fail callback (0.141601ms)
|
||||
✔ REQ-PLUG-1 fixture modules of each kind switch on from a test organization file, no core file changed (7.52453ms)
|
||||
✔ REQ-PLUG-1 a name not matching ^[a-z][a-z0-9-]*$ refuses and keeps the old setting (3.440457ms)
|
||||
✔ REQ-PLUG-1 a missing module directory or index.mjs refuses and keeps the old setting (3.353717ms)
|
||||
✔ REQ-PLUG-1 a symlinked module directory or index.mjs refuses and keeps the old setting (2.431583ms)
|
||||
✔ REQ-PLUG-1 a symlinked kind directory refuses and keeps the old setting (2.099217ms)
|
||||
✔ REQ-PLUG-1 a symlinked packages/plugins refuses (run with --preserve-symlinks so the path keeps it) (30.890332ms)
|
||||
✔ REQ-PLUG-1 index.mjs swapped for a symlink after the lstat checks refuses at the realpath check (5.90666ms)
|
||||
✔ REQ-PLUG-1 a path that can't be read refuses as unreadable, is logged and keeps the old setting (2.270321ms)
|
||||
✔ REQ-PLUG-1 a path that becomes unreadable after the lstat checks refuses at either realpath (2.419482ms)
|
||||
✔ REQ-PLUG-1 a module that fails to load refuses and keeps the old setting (3.129865ms)
|
||||
✔ REQ-PLUG-1 a missing export refuses and keeps the old setting (3.231694ms)
|
||||
✔ REQ-PLUG-1 a different interface version refuses and keeps the old setting (2.428837ms)
|
||||
✔ REQ-PLUG-1 a kind refused on its first load has nothing switched on, and the log names kind and name (1.831207ms)
|
||||
✔ REQ-PLUG-1 the setting's shape: unknown kinds refuse alone, a bad whole setting keeps everything (2.957549ms)
|
||||
✔ REQ-PLUG-1 loads run one at a time, so the last requested setting wins (103.223789ms)
|
||||
✔ REQ-PLUG-1 the loader needs a log callback (1.093523ms)
|
||||
ℹ tests 21
|
||||
ℹ suites 0
|
||||
ℹ pass 21
|
||||
ℹ fail 0
|
||||
ℹ cancelled 0
|
||||
ℹ skipped 0
|
||||
ℹ todo 0
|
||||
ℹ duration_ms 229.032854
|
||||
@@ -0,0 +1,51 @@
|
||||
✔ isRunId accepts the run id shape and nothing else (1.121373ms)
|
||||
✔ a missing data root or runs directory lists nothing (2.040924ms)
|
||||
✔ runs as a regular file lists nothing, as before (0.802422ms)
|
||||
✔ listRunIds keeps r- names only, sorted oldest first (1.01328ms)
|
||||
✔ listRunRecords returns each result, or null for an incomplete or unreadable one (2.023471ms)
|
||||
✔ readRunRecord returns documents and artifacts in directory order (1.02768ms)
|
||||
✔ readRunRecord is null for a missing run, a dangling link or a file (0.68065ms)
|
||||
✔ readRunRecord and readRunDocument refuse an invalid run id before touching the disk (0.61014ms)
|
||||
✔ readRunDocument reads only the three run documents (0.48683ms)
|
||||
✔ a link inside the data root is followed (0.879902ms)
|
||||
✔ a data root that is itself a link is trusted as configured (0.494169ms)
|
||||
✔ a run directory linked out of the data root is never read (0.547284ms)
|
||||
✔ a document linked out of the data root reads as null (0.825596ms)
|
||||
✔ a runs directory linked out of the data root refuses (0.4779ms)
|
||||
✔ a relative link that climbs out of the data root refuses (0.380204ms)
|
||||
✔ a sibling whose name starts with the data root's name is outside it (0.557315ms)
|
||||
✔ a link loop refuses instead of reading as missing (0.581986ms)
|
||||
✔ an unreadable runs directory refuses instead of listing nothing (0.381004ms)
|
||||
✔ an unreadable run directory refuses instead of reading as missing (0.446529ms)
|
||||
✔ a link to the data root's parent is outside it (0.353549ms)
|
||||
✔ listRunIds sorts whatever order the directory returns (0.771468ms)
|
||||
✔ listRunRecords lists an r- name that isn't a valid run id, as before (0.444891ms)
|
||||
✔ the readers write nothing (1.851707ms)
|
||||
✔ no pointer is null (1.753725ms)
|
||||
✔ the pointer release.sh writes reads back (1.488479ms)
|
||||
✔ a pointer that isn't the version 1 shape refuses with exit 2 (2.22052ms)
|
||||
✔ a pointer that is a directory refuses with exit 4 (0.528115ms)
|
||||
✔ a state file linked out of the data root refuses (0.856126ms)
|
||||
✔ a state directory linked out of the data root refuses (0.51002ms)
|
||||
✔ a state directory linked out to nothing reads as no release and an empty log (0.421ms)
|
||||
✔ a missing log is empty (0.327214ms)
|
||||
✔ the log reads oldest first and counts malformed lines (0.859957ms)
|
||||
✔ last must be a positive integer (0.4193ms)
|
||||
✔ the state readers write nothing (0.709293ms)
|
||||
✔ list prints each run in the established format (89.490317ms)
|
||||
✔ list prints a damaged result.json as unknown and keeps listing the runs after it (64.353948ms)
|
||||
✔ list prints each result field that isn't a string as unknown or - (65.094677ms)
|
||||
✔ show prints the run in the established format (62.731842ms)
|
||||
✔ show of a missing run and an invalid id exit 4 as before (96.879093ms)
|
||||
✔ list and show refuse a runs directory linked out of the data root (124.506728ms)
|
||||
✔ show refuses a run linked out of the data root; list reports it unknown (124.152021ms)
|
||||
✔ list shows an r- name that isn't a valid run id, as before (60.458178ms)
|
||||
✔ show refuses an invalid id before reading the config (35.481873ms)
|
||||
ℹ tests 43
|
||||
ℹ suites 0
|
||||
ℹ pass 43
|
||||
ℹ fail 0
|
||||
ℹ cancelled 0
|
||||
ℹ skipped 0
|
||||
ℹ todo 0
|
||||
ℹ duration_ms 777.029734
|
||||
@@ -0,0 +1,145 @@
|
||||
✔ a first launch clones the root at its branch, with the root's path as origin (39.772011ms)
|
||||
✔ the clone is not at its place until git clone has finished (32.335596ms)
|
||||
✔ a later launch fast-forwards a clean clone after a commit to the root, then finds it current (87.001717ms)
|
||||
✔ a clone ahead of the root keeps its commits and counts as current (51.653227ms)
|
||||
▶ a dirty clone and one on another branch are fetched, and nothing but refs/remotes moves
|
||||
✔ a modified file (49.381191ms)
|
||||
✔ an untracked file (48.221081ms)
|
||||
✔ a staged file (49.729305ms)
|
||||
✔ another branch (47.087374ms)
|
||||
✔ a detached HEAD (52.693466ms)
|
||||
✔ a dirty clone and one on another branch are fetched, and nothing but refs/remotes moves (248.589113ms)
|
||||
✔ a diverged clone gets the fetch into refs/remotes and is kept, not-fast-forward (45.155924ms)
|
||||
▶ a clone kept before the branch check, or for a failed status, gets no fetch
|
||||
✔ not-a-clone (23.914125ms)
|
||||
✔ hooks-present (27.307087ms)
|
||||
✔ submodule (33.29949ms)
|
||||
✔ config-changed (31.994237ms)
|
||||
✔ root-unreadable (30.56357ms)
|
||||
✔ root-detached (35.303699ms)
|
||||
✔ status-failed, on the root's branch and clean (46.218079ms)
|
||||
✔ status-failed, dirty (37.546152ms)
|
||||
✔ a clone kept before the branch check, or for a failed status, gets no fetch (267.461496ms)
|
||||
✔ a dirty clone whose fetch fails is kept, fetch-failed, exactly as it was (41.255422ms)
|
||||
✔ every git call the host makes, for a new clone and an existing one, carries maintenance.auto=false and gc.auto=0 (54.726734ms)
|
||||
✔ a clone with loose objects enough for gc --auto gets no maintenance or gc from the fetch and the merge (85.552833ms)
|
||||
▶ a clone with user.name or user.email in its config is kept, and git runs nothing in it
|
||||
✔ user.name (24.633563ms)
|
||||
✔ user.email (25.697094ms)
|
||||
✔ a clone with user.name or user.email in its config is kept, and git runs nothing in it (50.763432ms)
|
||||
✔ a commit git can't read after the fast-forward is null (46.225022ms)
|
||||
✔ a new clone whose commit can't be read refuses the launch and leaves nothing behind (18.4942ms)
|
||||
▶ a clone the session rigged is kept, and git runs nothing and writes nothing outside it
|
||||
✔ core.fsmonitor (32.683874ms)
|
||||
✔ core.hooksPath (32.572912ms)
|
||||
✔ a hook file (31.302377ms)
|
||||
✔ include.path (33.605319ms)
|
||||
✔ includeIf (33.393035ms)
|
||||
✔ a filter driver (32.813265ms)
|
||||
✔ origin moved (35.093304ms)
|
||||
✔ a second origin url (32.556626ms)
|
||||
✔ the origin url twice, both the root (36.266154ms)
|
||||
✔ an ext:: origin (33.929553ms)
|
||||
✔ a branch tracking another remote (32.547921ms)
|
||||
✔ a branch merging a tag (33.694877ms)
|
||||
✔ a fetch line onto local branches (37.16716ms)
|
||||
✔ no origin (37.77053ms)
|
||||
✔ a config file over the size cap (42.045321ms)
|
||||
✔ config a symlink to /dev/zero (38.037512ms)
|
||||
✔ the clone a symlink to the bait (32.715091ms)
|
||||
✔ the clone a file (32.793149ms)
|
||||
✔ the clone a plain directory (31.237924ms)
|
||||
✔ .git a gitdir: file (30.740631ms)
|
||||
✔ .git a symlink (30.312995ms)
|
||||
✔ .git/logs a symlink (32.223434ms)
|
||||
✔ .git/refs/heads a symlink (32.512991ms)
|
||||
✔ .git/objects a symlink (31.446598ms)
|
||||
✔ a reflog hard-linked to a file outside (30.591194ms)
|
||||
✔ HEAD a fifo (31.487405ms)
|
||||
✔ a commondir file (31.125514ms)
|
||||
✔ an alternates file (31.006488ms)
|
||||
✔ a gitlink staged, no .gitmodules (45.069955ms)
|
||||
✔ a gitlink and .gitmodules staged (46.743754ms)
|
||||
✔ a gitlink to a repository with its own gitlink (52.530127ms)
|
||||
✔ a gitlink and .gitmodules committed (49.17773ms)
|
||||
✔ a gitlink and submodule.active in config (46.305351ms)
|
||||
✔ a .git/modules with no gitlink (33.620995ms)
|
||||
✔ a clone the session rigged is kept, and git runs nothing and writes nothing outside it (1211.733055ms)
|
||||
▶ a root that can't be cloned refuses with the project id and leaves nothing behind
|
||||
✔ missing (10.244099ms)
|
||||
✔ not a repository (10.016345ms)
|
||||
✔ a plain directory inside another repository (14.566017ms)
|
||||
✔ unreadable (9.865997ms)
|
||||
✔ on no branch (12.343232ms)
|
||||
✔ missing an object (15.608287ms)
|
||||
✔ a root that can't be cloned refuses with the project id and leaves nothing behind (73.489717ms)
|
||||
✔ a clone whose root is on no branch, or gone, is kept and named (27.62217ms)
|
||||
✔ no credential or forge remote of the root reaches a clone (47.992381ms)
|
||||
✔ every project in the business file gets its own clone at its own branch (34.541155ms)
|
||||
✔ a clone past its time gets SIGTERM and refuses the launch, with nothing left in the workspace (514.423558ms)
|
||||
✔ a clone that ignores SIGTERM gets SIGKILL once the grace period is over (1017.774196ms)
|
||||
✔ a timeout signals git's process group, so a process git started dies with it (515.838823ms)
|
||||
✔ once a signalled git has closed, its group gets SIGKILL, so a process that ignored SIGTERM dies too (513.510965ms)
|
||||
✔ RUNNER_FILES is exactly what the runner and the harness sources it starts import (1.7273ms)
|
||||
✔ sandboxProblem names a missing bwrap and a bwrap that fails (10.310699ms)
|
||||
✔ every bind says why; the bundle and session file stay read-only over the run directory (1.443061ms)
|
||||
✔ the host's git socket is bound only when given, read-only and before the writable binds (1.097371ms)
|
||||
✔ the git identity is the instance's, at <instance>@<business>.mosaic.invalid, and sandboxArgv refuses without it (1.109827ms)
|
||||
✔ a confined session's shell reaches its own workspace and run directory and nothing of the founder's or another session's (1784.29457ms)
|
||||
✔ a bind that exposes a stand-in, or a missing workspace bind, fails the matching check (2637.09657ms)
|
||||
✔ a confined session that runs tester-cli.mjs is refused as tester, and the same shim outside the sandbox binds (207.388776ms)
|
||||
✔ resolveSeat: by name under --repo resolves the repo layout (1.417139ms)
|
||||
✔ resolveSeat: by path resolves the fleet layout (0.356629ms)
|
||||
✔ resolveSeat: refusals for missing dir, missing launch.sh, non-executable launch.sh, invalid name, and unknown layout (0.79931ms)
|
||||
✔ tmuxContext: outside tmux, default socket, custom socket, and exec failure (0.734029ms)
|
||||
✔ makeRegistration produces a record that validates; each shape violation throws SeatError (1.032687ms)
|
||||
✔ writeRegistration/readRegistration: round trip, permissions, absence, and malformed records (1.771458ms)
|
||||
✔ updateTask: changes task, taskSetBy and updatedAt only, and refuses appropriately (1.314166ms)
|
||||
✔ CLI launch: registers, execs the fake launch script, and passes args through (33.507078ms)
|
||||
✔ CLI launch: --harness lands in the record (40.62423ms)
|
||||
✔ CLI launch: the launch script's own exit code passes through (39.392375ms)
|
||||
✔ CLI launch: relaunching a seat rewrites the one registration record (75.044725ms)
|
||||
✔ CLI launch: omitting --task records an empty string, not null (34.694846ms)
|
||||
✔ CLI seat task: updates only the task after a launch, and refuses on an unlaunched seat (86.603819ms)
|
||||
✔ CLI refusals: no args, unknown flag, missing config, already-registered env, and exec failure (158.295204ms)
|
||||
✔ samePath: equal paths, symlinked dirs, distinct dirs, and non-strings (0.406614ms)
|
||||
✔ resolveSetBy: explicit --by wins over the environment; absent or empty environment gives unknown; invalid explicit or environment values refuse with exit 4 (0.608075ms)
|
||||
✔ 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.714541ms)
|
||||
✔ 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 (0.875481ms)
|
||||
✔ 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 (283.491731ms)
|
||||
✔ REQ-LIMIT-1: family substring matching is no longer exported (0.902606ms)
|
||||
✔ sessionEnv passes only the allowlist, the repo's bin on PATH, and the run id (0.886444ms)
|
||||
✔ sessionExit gives a runner's signal death back as the signal; a session credential is one whole set of nonempty variables (0.283725ms)
|
||||
✔ newRun: short ids, 0700 directories, and a refusal when the socket path won't fit (1.815205ms)
|
||||
✔ session file and launch log: 0600, the session file written once (1.106937ms)
|
||||
✔ endReason maps the runner's exit codes; a signal is killed (0.126759ms)
|
||||
✔ Registry mirrors to sessions.json; readSessions marks live entries; bad files refuse (1.362936ms)
|
||||
✔ stopSession refuses an unknown run, reports a stale one, and won't signal a pid that isn't a runner (1.104882ms)
|
||||
✔ a session runs in its sandbox as pid 1 of its namespace, claims, answers, and stops on mosaic stop (246.233821ms)
|
||||
✔ workRef and validBranch take only what a ref name can hold (1.25048ms)
|
||||
✔ a push lands as the task's work ref, a fast-forward lands after it, and main is the root's (135.562175ms)
|
||||
✔ a rewritten branch refuses non-fast-forward and the work ref stays (108.438193ms)
|
||||
✔ a branch with no history in common with main refuses unrelated-history (49.086761ms)
|
||||
✔ a bundle that needs commits the working repository lacks refuses missing-prerequisite (45.024698ms)
|
||||
✔ main is fetched from the root again before each push, so a clone of a newer root pushes (105.841259ms)
|
||||
✔ a root moved back after the clone leaves the push's base unknown: missing-prerequisite (65.559194ms)
|
||||
✔ a commit with a broken object refuses invalid-bundle, and only because fsck is on (63.972346ms)
|
||||
✔ only a v2 bundle with the named branch is read (50.991704ms)
|
||||
✔ a branch named main, or named for another task, moves only this task's work ref (84.249039ms)
|
||||
✔ a record that throws moves the work ref back, or removes a new one (108.909753ms)
|
||||
✔ a work ref that can't go back refuses rollback-failed, naming the ref and the commit (358.97344ms)
|
||||
✔ a root that can't be read refuses root-unreadable (27.843378ms)
|
||||
✔ a git past its time gets SIGTERM, leaves no ref lock, and the next push lands (2115.606722ms)
|
||||
✔ a git that ignores SIGTERM gets SIGKILL once the grace period is over (1027.697152ms)
|
||||
✔ a working repository path that isn't a directory refuses (25.302856ms)
|
||||
✔ review bundles a pushed commit as refs/mosaic/review, leaving out what the reviewer has (105.173408ms)
|
||||
✔ review of a project with no working repository refuses unknown-commit (14.137517ms)
|
||||
✔ the runner's git tools hold the host's limits and branch rule (1.143727ms)
|
||||
ℹ tests 132
|
||||
ℹ suites 0
|
||||
ℹ pass 132
|
||||
ℹ fail 0
|
||||
ℹ cancelled 0
|
||||
ℹ skipped 0
|
||||
ℹ todo 0
|
||||
ℹ duration_ms 5083.812107
|
||||
@@ -0,0 +1,105 @@
|
||||
✔ the boot config is checked before anything starts (15.637709ms)
|
||||
✔ a business with no tracker entry refuses task verbs (15.275818ms)
|
||||
✔ credential.expiring and .expired are recorded once per instance (30.769243ms)
|
||||
✔ a token file that changes on disk records credential.changed (16.010049ms)
|
||||
✔ autostart polls, reconciles and retries a startup the tracker was down for (38.374827ms)
|
||||
✔ a refusal a restart must clear is not retried by the poll (11.015358ms)
|
||||
✔ a poll that fires while two are queued is dropped (11.988184ms)
|
||||
✔ close waits for a running verb and refuses one that has not started (44.448755ms)
|
||||
✔ stopped bus resumes an empty task cursor after a long outage, exactly once (38.507194ms)
|
||||
✔ persisted IDs avoid replay, exclude bots and bound rows to observed new comments (30.480909ms)
|
||||
✔ commit followed by crash before cache advance neither loses nor duplicates a comment (21.147601ms)
|
||||
✔ failed event insert rolls back cursor and a retry still emits the comment once (17.522247ms)
|
||||
✔ initial cursor accepts a comment within the stated one-minute clock skew margin (15.289078ms)
|
||||
✔ the bundled Vikunja is the pinned upstream image the runbook names (1.140398ms)
|
||||
✔ every published port is on 127.0.0.1, and no secret is in the file (0.419232ms)
|
||||
✔ the committed table loads frozen; its verbs are the broker task verbs (2.576046ms)
|
||||
✔ a write names a verb the table has as its own key, not one it inherits (0.740514ms)
|
||||
✔ the schema refuses any other shape (2.593089ms)
|
||||
✔ the Field table in the slice 1 plan names the verbs each field is written by (0.652489ms)
|
||||
✔ each verb refuses, asks for a decision and writes as the table and the role files say (180.565923ms)
|
||||
✔ the list holds the PRD's requirement ids, in its order, at its version (1.445453ms)
|
||||
✔ the committed list loads frozen (0.66807ms)
|
||||
✔ the schema refuses any other shape (3.647621ms)
|
||||
✔ the fake answers each route with the statuses and shapes Vikunja v2.7.0 sent (94.729092ms)
|
||||
✔ the project list holds recorded project objects in the recorded list envelope (11.190116ms)
|
||||
✔ the recorded task bodies pass the checks S3 applies to every read (0.505574ms)
|
||||
✔ the client works against the fake over real HTTP with the platform fetch (101.837735ms)
|
||||
✔ a correct install starts, and the first reconcile records tasks that already exist (30.287949ms)
|
||||
✔ verbs refuse while a business is starting and after startup refused it (15.540215ms)
|
||||
✔ startup refuses a token that can do more than its role needs (43.794392ms)
|
||||
✔ startup refuses an unsupported version and flags an untested one (32.793739ms)
|
||||
✔ startup refuses a board that the runbook did not install (39.228866ms)
|
||||
✔ startup refuses a project the sync bot cannot read (10.074429ms)
|
||||
✔ startup refuses a configured label the pm bot cannot see (8.938457ms)
|
||||
✔ startup refuses an expired credential and a missing sync credential (14.510064ms)
|
||||
✔ an unreachable tracker refuses with tracker-unavailable (5.96249ms)
|
||||
✔ an edit in the UI is recorded once, with the fields that changed (78.451377ms)
|
||||
✔ a move between open buckets is seen on the board, though updated does not change (42.345585ms)
|
||||
✔ a person's comment is counted and a bot's is not (70.913102ms)
|
||||
✔ the hourly reconcile catches a comment through comment_count (78.598591ms)
|
||||
✔ a task closed in the UI leaves the open view with its done bucket (197.908769ms)
|
||||
✔ a task that leaves the board is recorded as deleted, moved or out of reach (66.814511ms)
|
||||
✔ a poll that read before a verb wrote does not overwrite the verb (39.057024ms)
|
||||
✔ a tracker fault during a tick is reported and the next tick catches up (37.170687ms)
|
||||
✔ a malformed answer refuses the tick with tracker-shape (13.499732ms)
|
||||
✔ no token value reaches the database, the log or a refusal (49.294058ms)
|
||||
✔ the first look at a task counts only comments inside the window (34.806971ms)
|
||||
✔ with descendants off the adapter reads no project list and no child, as in v0 (47.624084ms)
|
||||
✔ a three-level tree is read per project, and a project outside it is not (22.374132ms)
|
||||
✔ a watch-only child keeps the business ready, and a failed child read refuses as transient (27.740919ms)
|
||||
✔ a root without the installed buckets refuses with descendants on (9.111876ms)
|
||||
✔ the project list refuses without read_all, and skips archived projects and saved filters (23.372933ms)
|
||||
✔ descendants must be true or false (9.506324ms)
|
||||
✔ a move inside the tree is a moved tombstone plus a snapshot under the new ref (62.402417ms)
|
||||
✔ a child made after startup joins the tree at the next reconcile (54.051666ms)
|
||||
✔ a child that leaves the tree has its tasks recorded missing, and the ledger keeps their history (147.893188ms)
|
||||
✔ a child deleted or hidden between reconciles drops out at the next tick, and the root still refuses (84.556758ms)
|
||||
✔ a reconcile that fails keeps the previous tree (34.069582ms)
|
||||
✔ a closed task the cursor missed takes its own project's done bucket (35.365029ms)
|
||||
✔ closed tasks read at startup take their own project's done bucket (12.455727ms)
|
||||
✔ a closed task the cursor returns takes its own project's done bucket (38.626347ms)
|
||||
✔ a task moved out of a child that leaves the tree at the same time is moved, not no-access (33.047055ms)
|
||||
✔ a task in a bucket outside the five keeps its raw bucket id (30.227555ms)
|
||||
✔ bucket 0 on a watch-only task never becomes a bucket or a state (77.479473ms)
|
||||
✔ a child whose kanban view is deleted still refuses the tick, and the reconcile reads it watch-only (54.638098ms)
|
||||
✔ a child dropped at a tick comes back at the next reconcile once the bot reads it again (35.251518ms)
|
||||
✔ task.create needs a recorded human request and a requirement id (40.649064ms)
|
||||
✔ task.create takes only an approved requirement id (25.977387ms)
|
||||
✔ task.create cites the request a received message carries, not the message's id (19.700175ms)
|
||||
✔ only labels named in the business file can be written (28.199312ms)
|
||||
✔ task.schedule sets and clears a due date and relations (34.244094ms)
|
||||
✔ assign and reassign move the role bots and record task.assigned (54.996761ms)
|
||||
✔ task.update.assigned is for the assignee and records task.state (46.477918ms)
|
||||
✔ a wrong expected digest records task.conflict and writes nothing (41.645288ms)
|
||||
✔ a cross-role verb needs a resolved decision, used once (106.837584ms)
|
||||
✔ task.close needs a verdict; after it every verb refuses with task-done (24.192974ms)
|
||||
✔ a task with pushed work closes only citing an approve verdict for its latest push, and a refusal writes nothing (28.975424ms)
|
||||
✔ a lost answer is settled by a re-read and never retried (30.245357ms)
|
||||
✔ a create whose answer is lost is reported uncertain, and the poll finds the task (36.354664ms)
|
||||
✔ a task the sync bot cannot read refuses and records nothing (17.502908ms)
|
||||
✔ verbs and polls for one business run one at a time (88.665716ms)
|
||||
✔ a due date with milliseconds is written to the second (65.834076ms)
|
||||
✔ every write landed and the final read failed: the verb succeeds and records what it wrote (35.846001ms)
|
||||
✔ some writes landed and the final read failed: write-uncertain, and nothing is recorded (12.467527ms)
|
||||
✔ a create whose final read fails succeeds and records task.created (30.630244ms)
|
||||
✔ an edit between the last write and the final read shows as external on the next poll (34.655431ms)
|
||||
✔ task.created is recorded when a later label write fails (12.866553ms)
|
||||
✔ every verb acts in an installed child, on the child's own board (41.898674ms)
|
||||
✔ task.create refuses a project outside the tree, a watch-only one and one that is not an id (24.094442ms)
|
||||
✔ with descendants off, task.create takes only the root project (11.753079ms)
|
||||
✔ a verb on a watch-only task, or a relation to one, refuses with project-not-installed (15.706905ms)
|
||||
✔ a related task is read before authority: in another project than its ref names, task-moved; missing, task-not-found (21.870029ms)
|
||||
✔ a task moved to another project of the tree refuses its old ref with task-moved and takes its new one (15.248111ms)
|
||||
✔ a verb and a create in the child record the final read of the child's board (15.368853ms)
|
||||
✔ a create in the child whose final read fails records the child's todo bucket (32.946188ms)
|
||||
✔ a lost answer in the child is settled by a re-read of the child's board (19.810045ms)
|
||||
✔ every related task is read before authority, not only the first (16.98901ms)
|
||||
ℹ tests 97
|
||||
ℹ suites 0
|
||||
ℹ pass 97
|
||||
ℹ fail 0
|
||||
ℹ cancelled 0
|
||||
ℹ skipped 0
|
||||
ℹ todo 0
|
||||
ℹ duration_ms 1138.01294
|
||||
@@ -0,0 +1,122 @@
|
||||
✔ 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 (6262.999221ms)
|
||||
✔ 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 (6217.042996ms)
|
||||
✔ browser edge states: loading, empty, malformed, stale, hostile/long values, in-flight reply and appearance fallback (4334.118634ms)
|
||||
Rendered contrast: {"failures":[],"count":330,"lowest":4.504658476260286}
|
||||
✔ served Console browser: real board fixtures, keyboard, drafts, receipts, themes, 320px and failures (5880.871918ms)
|
||||
✔ the README says a refused field table names the file or the key at fault, never a path (1.524694ms)
|
||||
✔ S5 pages in a browser: real broker rows, inert text, Copy, routes, failures, layout and no writes (8128.263094ms)
|
||||
✔ bus routes serve the four reads from the reader view, unescaped JSON for the page to escape (147.427005ms)
|
||||
✔ decisions route serves the roles and recent lists from the reader view (113.495166ms)
|
||||
✔ credentials route serves the broker credential status from the reader view (102.875707ms)
|
||||
✔ replies route serves the replies to one message from the reader view (148.984773ms)
|
||||
✔ bus routes are GET-only, have no write verb, and keep the same-origin checks (193.880025ms)
|
||||
✔ bus refusals map to statuses and never carry a path or a stack (176.881836ms)
|
||||
✔ humanCall speaks the human transport protocol without blocking the server (2605.875258ms)
|
||||
✔ busReader takes --business, else the live bus host, else refuses (404.98564ms)
|
||||
✔ humanCall kills a transport that runs past its timeout (536.021137ms)
|
||||
✔ serve refuses a --business value that is not a business id (1084.934081ms)
|
||||
✔ conversation view: full history, collapsed tools, hidden thinking, inert hostile content, malformed and reconcile markers (4016.163113ms)
|
||||
✔ conversation view: a fork keeps the open branch, says so, and opens the new one on request (2996.291022ms)
|
||||
✔ conversation view: a newer session with no readable history keeps the marker (1649.97838ms)
|
||||
✔ conversation view: seats without history say so and offer no reply (790.648338ms)
|
||||
✔ Discord row through real board/WebUI: independent brake/liveness, no Reply, literal content (3759.988009ms)
|
||||
✔ return flow through the conversation view: send, tool call, delayed result, peer message, exact long answers, relaunch (54375.900028ms)
|
||||
✔ messages HTTP read pages and preserves metadata; bad arguments refuse before a read; other reads unchanged (198.527381ms)
|
||||
✔ 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 (3981.010754ms)
|
||||
✔ Messages nav count polls on Tasks and Board and clears on a Board refusal; messages 403 refuses local pages (3556.492569ms)
|
||||
✔ copy writes the Console paths at the full id, read-only, and leaves an existing copy alone (3706.163291ms)
|
||||
✔ copy refuses a commit off refactor, an unknown one, a worktree and a bad copies directory (2617.094825ms)
|
||||
✔ copy writes nothing when an archived path is missing at the commit (884.443396ms)
|
||||
✔ a copy that loses the rename race leaves no read-only temp directory behind (1022.167467ms)
|
||||
✔ a copy whose rename fails with no target refuses, writes no copy and leaves no temp directory (497.394887ms)
|
||||
✔ a copy that meets a target holding files says exists: and moves nothing into it (668.667811ms)
|
||||
✔ a copy that meets an empty target directory replaces it by rename, not by moving into it (439.467766ms)
|
||||
✔ serve runs from a copy: /healthz 200, the config check, roles and RELEASE from the copy, the queue from --checkout (797.719771ms)
|
||||
✔ serve refuses a --checkout that is not an absolute directory path (346.818245ms)
|
||||
✔ consoleReads runs the queue read under queueRoot and reads RELEASE under root (78.448607ms)
|
||||
✔ preview options are all three or none, and each is checked (12.36061ms)
|
||||
✔ the CLI refuses a partial or invalid preview flag set at start (2981.894643ms)
|
||||
✔ the CLI with all three flags serves the preview mode (352.625312ms)
|
||||
✔ a request for the public name needs the header equal to the user id, else 401 and nothing read (37.799915ms)
|
||||
✔ the header name matches without regard to case, and the value compare is exact (9.061788ms)
|
||||
✔ the public name with the header reads the bus views, and the Host and Origin are checked first (66.374194ms)
|
||||
✔ in preview mode every write answers 403 read-only, from the public name and loopback, and reaches nothing (25.366823ms)
|
||||
✔ loopback requests are unchanged in preview mode, and the bus views work with the board down (11.737812ms)
|
||||
✔ a proxy that rewrites Host to the loopback port gets 403 on its first request, whatever the user (33.902607ms)
|
||||
✔ a repeated Host is 400 before any other check, in preview mode and without it (15.430846ms)
|
||||
✔ without the preview flags the public name is refused and writes still reach the board (15.658778ms)
|
||||
✔ redactor: config, dataRoot, any absolute path, Discord ids and the JSON parser quote (2.524522ms)
|
||||
✔ credential state from the date alone, as the bus states it (0.298544ms)
|
||||
✔ queue: rows, one row, ids and methods, notes; no path or id from a row (1701.3085ms)
|
||||
✔ queue: a refused read fails closed with the store's text, redacted (1332.674522ms)
|
||||
✔ queue: a store that fails to load, or a child that dies, gives a fixed message with no path or stack (1978.940496ms)
|
||||
✔ business: names, vars on the allowlist, authority, credential metadata only (47.963024ms)
|
||||
✔ business: missing or invalid file refuses with the module's text, redacted (27.874196ms)
|
||||
✔ business without --business: the live bus host, else no-bus-host; no system config: not-configured (17.815064ms)
|
||||
✔ settings: release, board, loopback address, notifier binding name only (512.211609ms)
|
||||
✔ task fields: the committed table with no reads configured, GET only; a bad table refuses with no path (87.339668ms)
|
||||
✔ both presentations replace old activity with relaunch notice, label retained history, then resume after new activity (3530.108514ms)
|
||||
✔ reported return flow and relative Age: reply sent from the inspector, then the new answer appears there without manual refresh (23238.417717ms)
|
||||
✔ 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 (6590.851958ms)
|
||||
✔ a sent receipt lists its replies as the page refreshes, oldest first and capped, and marks none delivered or read (11990.153427ms)
|
||||
✔ startServer takes send only as a function and only with the preview mode (11.478628ms)
|
||||
✔ the CLI refuses --send without the preview flags, and without a configured bus (1097.541976ms)
|
||||
✔ the CLI with --send and a configured bus sends through the human transport and logs one line (396.900718ms)
|
||||
✔ without --send the route is read-only like every other write, and config says no send (19.506037ms)
|
||||
✔ with --send every other write is still 403 read-only, and nothing reaches the board or bus (15.1756ms)
|
||||
✔ config says send only on the public name with --send on (6.433686ms)
|
||||
✔ a send without the signed-in user is 401 and is neither sent nor logged, loopback included (27.433495ms)
|
||||
✔ each request check refuses before anything is sent, and logs the refusal without the body (38.167108ms)
|
||||
✔ a send passes exactly to, body and in_reply_to, answers the id, and logs it (6.161726ms)
|
||||
✔ a crafted role name cannot forge a log line (5.631205ms)
|
||||
✔ a bus refusal is not sent, with its status; an outcome the bus could not confirm is unknown and never retried (19.776024ms)
|
||||
✔ through the human transport the request is exactly the business, message.send and the keys (321.395953ms)
|
||||
✔ against a real broker: the message is written once as a human send, and its refusals map (154.031003ms)
|
||||
✔ loopback host and board origin fail closed (9.15588ms)
|
||||
✔ real board fixture passes through WebUI; assets and isolated seen/reply work (146.021021ms)
|
||||
✔ proxy preserves exact request bytes, status and receipt, rejects forms and malformed JSON, never follows redirect (140.090043ms)
|
||||
✔ unreachable board reports URL; CLI rejects unsupported options (1374.739198ms)
|
||||
✔ render prints the unit without the preview flags (36.697001ms)
|
||||
✔ render carries every serve flag, the preview set included (21.739026ms)
|
||||
✔ a partial preview set is refused (56.042068ms)
|
||||
✔ a value the unit cannot carry is refused, for every flag (1653.921015ms)
|
||||
✔ the rendered unit names none of the human proof agent markers and sets no agent variable (138.665769ms)
|
||||
✔ install into --dir with --no-reload writes the file whole, then reports unchanged (199.04024ms)
|
||||
✔ install without --no-reload runs daemon-reload and nothing that starts the unit (53.167719ms)
|
||||
✔ uninstall refuses while the unit is running, with or without --no-reload, and removes it once it is not (581.811452ms)
|
||||
✔ status prints the unit state and GET /healthz on the installed port (590.00351ms)
|
||||
✔ usage: an unknown verb or flag, and start or enable, exit 4 (340.581675ms)
|
||||
✔ without --send, render is the template with its placeholders filled, byte for byte (144.803476ms)
|
||||
✔ render --send with the preview set ends ExecStart with --send and changes nothing else (195.753549ms)
|
||||
✔ --send without the whole preview set is refused and writes nothing (254.80524ms)
|
||||
✔ install --send into --dir with --no-reload writes the sending unit, and status says whether it sends (230.761365ms)
|
||||
✔ uninstall and status take no --send (53.304614ms)
|
||||
✔ install refuses while the unit is running, in any state but inactive or failed, and changes nothing (350.774053ms)
|
||||
✔ when the state read fails, install and uninstall refuse with one line and change nothing (101.816584ms)
|
||||
✔ install needs --copy naming a copy that exists, and refuses outside the canonical checkout without --allow-worktree (112.280328ms)
|
||||
✔ install refuses unless the bus unit runs from the same copy, naming both paths, and changes nothing (366.194187ms)
|
||||
✔ render --copy runs the copy and reads the queue in the checkout; without --copy the unit is unchanged (26.356628ms)
|
||||
✔ the uid header takes only an Authentik uid; the username header still takes a username (364.716731ms)
|
||||
✔ a preview value cannot start with - or . (30.835097ms)
|
||||
✔ status names the copy and whether refactor has moved past it, and says when the unit runs from no copy (440.830193ms)
|
||||
✔ Settings form shows every scope and held role; gated save, exact payload, retained draft and no automatic retry (2807.79278ms)
|
||||
2026-10-15T17:52:45.422Z settings user="jason" changed
|
||||
✔ REQ-WEB-1 signed-in same-origin Settings write reaches exactly one human operation (35.44091ms)
|
||||
✔ REQ-WEB-1 Settings writes require enabled send, named Host, identity, exact Origin and same-origin Fetch Metadata (18.376333ms)
|
||||
✔ REQ-WEB-1 Settings validates JSON, size, revision and strict writable keys before broker call (15.371265ms)
|
||||
✔ tokens.css and icons.svg are byte copies of docs/design (1.985637ms)
|
||||
✔ JetBrains Mono files match the digests in their sources file, with the OFL text (3.014754ms)
|
||||
✔ shipped strings are unchanged, and the web keeps its write boundary (3.481637ms)
|
||||
✔ shell in a browser: System default, seeded states, 400px, focus and the Ctrl+K palette (6899.657172ms)
|
||||
✔ five states on Board, Inbox, Tasks, Agents and Trail (2253.926205ms)
|
||||
✔ Queue, Business and Settings in their five states, with no move button (4463.867059ms)
|
||||
✔ seeded secrets, Discord ids and paths reach no response and no page; business refusals in the module's words (1393.325612ms)
|
||||
✔ the server log names no config path when the system config refuses (315.224975ms)
|
||||
ℹ tests 112
|
||||
ℹ suites 0
|
||||
ℹ pass 112
|
||||
ℹ fail 0
|
||||
ℹ cancelled 0
|
||||
ℹ skipped 0
|
||||
ℹ todo 0
|
||||
ℹ duration_ms 54897.597429
|
||||
@@ -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 '48b8938719d9809bcca8e7948b899687be2719c3'.
|
||||
|
||||
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 '48b8938719d9809bcca8e7948b899687be2719c3'.
|
||||
|
||||
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,82 @@
|
||||
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/copy-service.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/offline-check.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 183)
|
||||
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
|
||||
OK pinned render runs copy CLI with copy cwd/repo and no checkout executable
|
||||
OK pinned install writes the rendered unit in a temporary unit directory
|
||||
OK pinned install refuses short prerequisite and writes no unit
|
||||
OK pinned install refuses missing prerequisite and writes no unit
|
||||
OK pinned install refuses cli prerequisite and writes no unit
|
||||
OK pinned install refuses pi prerequisite and writes no unit
|
||||
OK status names the copy from fake systemctl effective cwd
|
||||
OK checkout install explicitly recommends --copy
|
||||
OK wrapper check --offline passes local fixture and makes no network call
|
||||
OK wrapper run --offline refuses usage
|
||||
|
||||
discord suite: 78 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: 1679555 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,86 @@
|
||||
OK args: exit 2, target-branch
|
||||
OK args: exit 2, target-branch
|
||||
OK args: exit 4
|
||||
OK args: exit 4
|
||||
OK args: exit 4
|
||||
OK --base next refuses before the config is read (got 2)
|
||||
OK a missing system config exits 3 (got 3)
|
||||
OK nobus: exit 3
|
||||
OK schema-table: exit 2, bus-schema
|
||||
OK schema-meta: exit 2, bus-schema
|
||||
OK openBus opens the database read-only
|
||||
OK other-business: exit 2, decision-not-found
|
||||
OK no-raise: exit 2, decision-not-found
|
||||
OK not-a-merge: exit 2, not-a-merge
|
||||
OK open: exit 2, not-resolved
|
||||
OK withdrawn: exit 2, not-resolved
|
||||
OK by-agent: exit 2, not-human
|
||||
OK via-webui: exit 2, not-human
|
||||
OK no-answer: exit 2, not-human
|
||||
OK answer-role: exit 2, not-human
|
||||
OK answer-run: exit 2, not-human
|
||||
OK answer-choice: exit 2, not-human
|
||||
OK two-answers: exit 2, not-human
|
||||
OK chose-no: exit 2, not-merge-option
|
||||
OK two-refs: exit 2, work-ref
|
||||
OK bad-ref: exit 2, work-ref
|
||||
OK task-ref-disagrees: exit 2, work-ref
|
||||
OK raise-task-disagrees: exit 2, work-ref
|
||||
OK target-main: exit 2, target-branch
|
||||
OK superseded-question: exit 2, superseded
|
||||
OK superseded-task: exit 2, superseded
|
||||
OK superseded-by: exit 2, superseded
|
||||
OK no-verdict: exit 2, no-verdict
|
||||
OK changes-later: exit 2, not-approved
|
||||
OK short-commit: exit 2, not-approved
|
||||
OK wrong-task-body: exit 2, not-approved
|
||||
OK args: exit 2, decision-not-found
|
||||
OK earlier-merge: exit 0
|
||||
OK ref-moved: exit 2, ref-moved
|
||||
OK root-top: exit 2, root-file
|
||||
OK root-newdir: exit 2, root-file
|
||||
OK root-src: exit 2, root-file
|
||||
OK unrelated: exit 2, base-conflict
|
||||
OK empty: exit 2, base-conflict
|
||||
OK an empty reviewed diff says so
|
||||
OK dirty-changed: exit 2, checkout-dirty
|
||||
OK dirty-staged: exit 2, checkout-dirty
|
||||
OK dirty-untracked: exit 2, checkout-dirty
|
||||
OK conflict: exit 2, base-conflict
|
||||
OK a refusal after the worktree is added leaves no worktree or scratch
|
||||
OK cleanup looks for the worktree's Docker network
|
||||
OK dirty-during: exit 2, checkout-dirty
|
||||
OK a reviewed path dirtied during the gate moves no ref
|
||||
OK drift: exit 2, patch-drift
|
||||
OK patch-drift leaves no worktree or scratch
|
||||
OK gate-pkg: exit 2, gate-failed
|
||||
OK gate-failed names the failing step's log, and it exists
|
||||
OK gate-failed leaves no worktree or scratch
|
||||
OK gate-script: exit 2, gate-failed
|
||||
OK a failing scripts/test-*.sh fails the gate
|
||||
OK a failed gate moves no ref
|
||||
OK nomods: exit 4
|
||||
OK a target without node_modules leaves no worktree or scratch
|
||||
OK moved: exit 2, base-moved
|
||||
OK base-moved keeps the base where it moved to
|
||||
OK dry: exit 0
|
||||
OK --dry-run writes no ref
|
||||
OK --dry-run prints the commit with --subject and --attribution
|
||||
OK --dry-run runs the gate
|
||||
OK --dry-run leaves no worktree or scratch
|
||||
OK land: exit 0
|
||||
OK the landing is one commit on the old base
|
||||
OK its diff is the reviewed diff by patch-id
|
||||
OK its tree is the dry run's
|
||||
OK the message has the option's subject, the citations and the attribution last
|
||||
OK it prints the line for the PM
|
||||
OK the checked-out base has the reviewed paths and keeps the unrelated edit
|
||||
OK only refs/heads/refactor exists in the target
|
||||
OK the work ref is unchanged
|
||||
OK the bus database is unchanged
|
||||
OK the gate ran each package suite but queue, and each scripts/test-*.sh
|
||||
OK the landing leaves no worktree or scratch
|
||||
OK the target's node_modules is still there, with its contents
|
||||
OK the worktree's Docker network is removed
|
||||
---
|
||||
land-work selftests: 84 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 (/mnt/storage/scratch/sage-r70/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,30 @@
|
||||
04d4b739c857b63a5148e5dc223ea16d357953e471aa57968979a6ccbcd86638 ./base.txt
|
||||
361afd8a68ddba1e9d05aed1fd233768e501ef49787ab68928343854247c69b0 ./BUILD.md
|
||||
abf65a52322ad086fccf2a8efd1b449901c95b40a5a38de89d8ab1273e9a3feb ./build.patch
|
||||
1126314bd522de838aad03689bed41f6b12a33c2d7f30b2eff1b38653f559ae0 ./candidate-manifest.sha256
|
||||
1646f731320898ec9a54630b99723260d46df455a290a5c05c94ceaf14597552 ./gate/pkg-business.log
|
||||
76914615dae59754c677c7780ed453b014cb79dd3edf4a04c513fe8c7aaf1212 ./gate/pkg-bus.log
|
||||
3b85d8e1b4bf47b163502dab399de6a752bf0bdb0df18cc7299c677af0348224 ./gate/pkg-cli.log
|
||||
1cec2fae3c3ae0e91eda7d6aea3c560863e629f164d4d24a4edf63b1bc464134 ./gate/pkg-control-board.log
|
||||
71e448d69833acfad21be78e8988f4454815c2521a250b8d37bdefb1db6f989f ./gate/pkg-conversation.log
|
||||
554e5c9b763a7db825861e18afc79ffb4dde97d03aeaec8714845184bc582c6b ./gate/pkg-discord.log
|
||||
fa72466191e43789f60c8b6833f5c075f472f289dae9a64cd7651508ea59e290 ./gate/pkg-harness.log
|
||||
d354d6bfdb8dedce4bd5bc1c1f3dc8ad9dfd39b178ea952d1c1951ce2bd51c59 ./gate/pkg-ledger.log
|
||||
750468f66f111e66b8aa220ede98ab075fcd7da8c3250bc7aa24eb3abee78de8 ./gate/pkg-mosaic.log
|
||||
070263eef212a48d9960484b261c67b24344d5f4c6c9cb21e6c8c7c7074e2dd1 ./gate/pkg-plugins.log
|
||||
1e6d1317adc15012c0160f8c5923378ae2ae67c4590f2fa0e1225d4a2b393adf ./gate/pkg-runs.log
|
||||
f791b93ef3f5352d4c0bb9674644760e5c73c5106b6c30664bfed66c5d469063 ./gate/pkg-seat.log
|
||||
22c2814ebff5445a0e0483c676fcd2d8aa5fa2e7f015b2dfc2d48e98c17c16a1 ./gate/pkg-tasks.log
|
||||
b13018992b9b7ceec784a32c8c7a3ef30e5f3211d1700ffbec9a697b74b167b3 ./gate/pkg-webui.log
|
||||
2857c044ac838acd78c6ad0f83caac178c472c7744d84036c3a01e660273fa18 ./gate-r1-run.log
|
||||
3df3e3937bad7738c706d7d5d31e6410cc3aa3e07aaf24d58917532aeb600afe ./gate-run.log
|
||||
f91fc95a173339137f981e24fbf630acfde957e32c3cd71e28304532083c6a77 ./gate/script-auth.log
|
||||
d987c94135bd4102cb8555f48df8c27f9423a269e5d5ee0b0b17953fc23075f7 ./gate/script-conductor.log
|
||||
52d211444489ea729dafc3c9745d4474d9dfd50f41055cbad792424197f04073 ./gate/script-config.log
|
||||
87d2e24ce0be7586e31ea1f2c9ce5618b3127515b3c3cd3893f2a0ed899d5822 ./gate/script-discord.log
|
||||
cb396302cfeddc72043b71a5ed95210f3b9bf018b348fba67313cfbad9d1d684 ./gate/script-extension-package.log
|
||||
83dfaa21c4e4ad941088efab3f69e87ebd2f6bd3cc72ede938e21dc06da8a34f ./gate/script-foundation.log
|
||||
8e2254aa3e1bb9b205ab72c68273d447c0328c9f156ccba2a46f3a0476468b58 ./gate/script-land-work.log
|
||||
b4efb575f1fb54f094f6bf41eb4a0d2cf6482872c1226e8c1627f4b855611ae6 ./gate/script-queue.log
|
||||
fa832794ee35025fb4559d58d01a270f6bff2eb65e863e9075b797f47fa68545 ./gate/script-release.log
|
||||
ccea37f4ffde1f160dcd1a1864988083a645d974e892065c956b338d0f8c853a ./gate/script-task.log
|
||||
Reference in new issue
Block a user