docs(s6): row 41 slice 1 S6 round 1 candidate packet (filbert)
build.patch dc346027…4454, candidate-manifest 5b3a934d…27f3, base915e00e5, 41 files, +4302/-55. Gate at2855e628in out/: every node suite and every test-*.sh green except test-task's live recall (needs Docker; the base fails the same two). The recorded PM launch waits on the Q14 cutover. Co-Authored-By: Claude Opus 5.5 <[email protected]>
This commit is contained in:
@@ -0,0 +1,169 @@
|
||||
# Row 41 (#1523): slice 1 S6, candidate packet, round 1
|
||||
|
||||
Author: Filbert. Reviewer: Darkwing. Brief: `docs/plans/2026-10-04_slice-1.md`,
|
||||
section "Slice 1 S6", blob `72d11de2`. Plan: `PLAN.md` here (b6d2fe2b).
|
||||
Rulings: lead decisions 77 and 78. Base: `915e00e5` (`base.txt`). None of
|
||||
the 41 files changed between the base and `2855e628`, where the gate ran.
|
||||
The candidate source is uncommitted. There are no pushes. No token, private
|
||||
binding or tracker host was read or written, and nothing touched the live
|
||||
`mosaic-bus@mosaic-stack` unit or `~/.mosaic-dev/bus/`.
|
||||
|
||||
## Files (`files.txt`, 41)
|
||||
|
||||
`build.patch` is `git diff --cached --binary 915e00e5` over those files,
|
||||
+4302/−55. It applies cleanly to `2855e628` with `git apply --index`, and
|
||||
`sha256sum -c candidate-manifest.sha256` passes in that tree.
|
||||
|
||||
| Area | Files | What |
|
||||
|---|---|---|
|
||||
| Harness (new) | `packages/harness/` | bundle, typed tools, gate, Pi extension, Claude Code gate and MCP server, runner, tests, README |
|
||||
| Adapters | `adapters/claude/adapter.sh` (new), `adapters/pi/adapter.sh`, `adapters/README.md` | Claude Code adapter; Pi takes `MOSAIC_EXTENSIONS` as `-e`, and `--no-approve` |
|
||||
| Sessions | `packages/seat/src/session.mjs`, `proc.mjs` (new), tests, README | spawn under `unshare`, registry, launch log, `stop` |
|
||||
| Launcher and verbs | `packages/cli/src/launcher.mjs` (new), `host.mjs`, `cli.mjs`, tests, README | the launch socket in the trusted host; `mosaic talk`, `stop`, `launches off\|on\|list`; `bus start --pm` |
|
||||
| Broker | `packages/bus/src/{broker,process,runtime}.mjs`, `tests/end-launch.test.mjs`, README | trusted IPC launch ops; `Broker.endLaunch` |
|
||||
| Other | `docs/TOOLS.md`, `scripts/mosaic`, `scripts/agent-host-dev.sh` | verb reference; host seats pass `--no-approve` (the DEFERRED entry) |
|
||||
|
||||
## Against the brief
|
||||
|
||||
- **Bundles, Pi then Claude Code** (`packages/harness/README.md`, "The
|
||||
bundle"): prompt, policy, typed tools and a manifest from one resolved
|
||||
instance. Each manifest names the S0 lines it relies on (`reliesOn`).
|
||||
Skills: resolution runs without a skills source, so bundles list none
|
||||
(README "Limits").
|
||||
- **S0 lines** (README table): Pi blocks in a `tool_call` handler, a throw
|
||||
blocks, a missing `-e` refuses to start, and a hang is bounded by the
|
||||
runner's wall clock plus the `agent_end` turn marker. Claude Code uses a
|
||||
command hook run as `timeout -k 2 10 <gate> || exit 2` with hook timeout
|
||||
20; `--bare` is never passed. `--restricted` is defence in depth only.
|
||||
Line 5 (`bash`) is the tool limit and is written as a limit.
|
||||
- **The PM launches through the broker, within `launch`**: the host's
|
||||
launch socket checks the instance list, that the instance isn't already
|
||||
running, the model family against `launch.max` (every running session
|
||||
counts, the PM's too), then the broker's own `role.launch` authorization.
|
||||
Every launch, refusal and end is a launch-log line and a broker event.
|
||||
`mosaic launches off` is Jason's one word (`launch.revoke`). `mosaic stop
|
||||
<run>` ends a session.
|
||||
- **Founder credentials (REQ-CRED-2)**: the session environment is an
|
||||
allowlist (`ENV_ALLOW`). The runner exits 20 before claiming if a known
|
||||
founder variable reached it, or if a service the role needs has no usable
|
||||
role token (the broker's credential status, metadata only).
|
||||
- **The PM moves off T3**: `mosaic bus start <business> --pm` launches the
|
||||
business's `launch.by` instance without a window, and `mosaic talk`
|
||||
reaches it. The handover of Sage's T3 thread is the acceptance run below.
|
||||
|
||||
## Changes from the plan
|
||||
|
||||
1. **The capability goes to the runner as one stdin line**, not a 0600
|
||||
file. It is written after the bind and is never on disk, in argv or in
|
||||
the environment.
|
||||
2. **Verbs live in `packages/cli`**, not `packages/seat` (lead decision
|
||||
77). `packages/seat` keeps the session module and the moved `/proc`
|
||||
helpers (`proc.mjs`).
|
||||
3. **`--pm` takes no argument.** The PM is the business file's `launch.by`
|
||||
instance; its harness and model come from resolution.
|
||||
4. **Runner exit 23** covers a `role.claim` with no answer as well as
|
||||
`brokerRetries` failed polls.
|
||||
5. **The adapter runs as `/bin/sh <adapter.sh>`**, because the repository
|
||||
keeps adapters 0644 and only the image sets the mode.
|
||||
|
||||
## Broker surface
|
||||
|
||||
No socket verb, no event kind and no schema change. The new launch ops are
|
||||
trusted IPC from the host to the broker process: `identity`,
|
||||
`authorizeLaunch`, `refuse`, `endLaunch`, `credentialStatus`, beside the
|
||||
existing `bindLaunch`. `bindLaunch` records the run only after its checks,
|
||||
and a rebind of an ended run refuses with `run-ended`, also across a broker
|
||||
restart. The host gains `op(message)` and `beforeClose(fn)`.
|
||||
|
||||
## Process control
|
||||
|
||||
- **Early signals.** The runner installs its SIGTERM and SIGINT handlers
|
||||
before anything else, because pid 1 of a PID namespace ignores a signal
|
||||
with no handler. Node's own startup still leaves a window, so the
|
||||
launcher and `mosaic stop` resend SIGTERM every second.
|
||||
- **`isRunner(pid)`** is `cmdline[1] === RUNNER`, which skips `unshare`'s
|
||||
forked child before its exec (its argv still names the runner).
|
||||
- **Host lost.** A starting host reads the last host's `sessions.json`.
|
||||
Before the launch socket opens, for each entry of this business it
|
||||
SIGKILLs the session if it still runs, rebinds the run with the recorded
|
||||
identity and ends it as `host-lost`, which releases the role. A rebind
|
||||
that refuses with `run-ended` logs "was already ended". An unreadable or
|
||||
malformed `sessions.json` refuses the host start with exit 3.
|
||||
- **PID namespace limits** are in `packages/harness/README.md`, "Limits":
|
||||
same UID, shared broker socket and `/tmp`, network not confined, and a
|
||||
same-UID process can still ask something outside its tree (a systemd user
|
||||
manager, an existing tmux server) to run a command. The README no longer
|
||||
says the namespace blocks `ptrace` in general; it hides other sessions by
|
||||
pid.
|
||||
|
||||
## `--no-approve` (DEFERRED: "Host seats launch Pi with `--approve`")
|
||||
|
||||
`scripts/agent-host-dev.sh` and `adapters/pi/adapter.sh` now pass
|
||||
`--no-approve`. Pi 0.85.1 (`trust-manager.js`) gates project `.pi/`
|
||||
resources on trust: `settings.json`, extensions, skills, prompts, themes,
|
||||
`SYSTEM.md`, `APPEND_SYSTEM.md`. `-e` paths load in the "temporary" scope,
|
||||
which trust doesn't gate. A scratch probe against a workspace saved as
|
||||
trusted, with a mock Messages API, showed:
|
||||
|
||||
| argv | project `SYSTEM.md` | explicit `--skill` | generated prompt | `-e` extension |
|
||||
|---|---|---|---|---|
|
||||
| host-seat, `--approve` | loaded | loaded | loaded | loaded |
|
||||
| host-seat, `--no-approve` | ignored | loaded | loaded | loaded |
|
||||
| `adapters/pi/adapter.sh` | ignored | loaded | loaded | loaded |
|
||||
|
||||
The skill appears only when the session has a tool to read it; the probe's
|
||||
first run used `--no-tools` and showed no skill in any case, the
|
||||
`--approve` control included.
|
||||
|
||||
`agent-host-dev.sh` keeps `--append-system-prompt`, not the entry's
|
||||
"explicit system prompt", because its comment preserves Pi's built-in coding
|
||||
prompt on purpose, and `--no-approve` already closes the project
|
||||
`SYSTEM.md`. I didn't edit `docs/plans/DEFERRED.md`: it holds another
|
||||
seat's uncommitted changes. The entry can close when this lands.
|
||||
`scripts/test-goal-native.py` still passes `--approve` on purpose (it tests
|
||||
project extensions) and is untouched.
|
||||
|
||||
## Gate
|
||||
|
||||
Run at `2855e628` with `build.patch` applied, in a detached worktree,
|
||||
sequentially, each output in `out/` (`out/summary.txt`). `TMPDIR` on the
|
||||
scratch disk; `DOCKER_HOST=unix:///nonexistent.sock`, so nothing reaches
|
||||
Docker.
|
||||
|
||||
| Suite | Pass | Fail |
|
||||
|---|---|---|
|
||||
| node: bus, business, cli, control-board | 74, 60, 77, 124 | 0 |
|
||||
| node: conversation, discord, harness, ledger | 152, 178, 45, 78 | 0 |
|
||||
| node: mosaic, queue, seat, tasks, webui | 69, 148, 27, 51, 14 | 0 |
|
||||
| test-auth, conductor, config, discord | 15, 17, 24, 66 | 0 |
|
||||
| test-extension-package, foundation, queue, release | 18, 44, 27, 4 | 0 |
|
||||
| test-task | 26 | 2 |
|
||||
|
||||
The two `test-task` failures are "user recall run succeeds (exit 1)" and
|
||||
"recalled user name". That check runs a live worker and needs Docker. The
|
||||
unpatched base fails the same two (`out/base-test-task.txt`, identical
|
||||
PASS/FAIL lines), so they are the environment, not this candidate.
|
||||
|
||||
An earlier gate over this candidate without the `--no-approve` edits had
|
||||
`test-queue` fail F1 once ("a plain `commit -e` whose guard ran before
|
||||
update-ref fails at its own HEAD update": only the stderr match). It passed
|
||||
in two patched reruns, in the base run, and in this gate. See the
|
||||
follow-ups below.
|
||||
|
||||
## Acceptance run: waiting
|
||||
|
||||
The recorded run (the host started with `--pm`, `mosaic talk` asks the PM
|
||||
to launch a coder, `mosaic agents` shows both claims) waits on the Vikunja
|
||||
move to tasks.woltje.com (Q14), as Sage ruled: the integration commit and
|
||||
the acceptance run wait for that cutover; build and review continue. It
|
||||
will not run on Astra (R26) or against the live unit. The tracker host
|
||||
comes only from `vars.tracker.baseUrl`; no code, test or launcher config
|
||||
names one, and fixtures use 127.0.0.1 and example.test.
|
||||
|
||||
## Follow-ups (not in this row)
|
||||
|
||||
- `packages/queue/tests/commit.test.mjs`, `pausedCommit`, awaits the
|
||||
child's `exit` rather than `close`, so its stderr can be unread when
|
||||
`test-queue` F1 matches it.
|
||||
- `scripts/test-task.sh` (:514-518): the live user-recall check needs
|
||||
Docker and isn't skipped when Docker is unavailable.
|
||||
@@ -0,0 +1 @@
|
||||
915e00e548439140efc838e2b75aba3ef971cbf4
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,41 @@
|
||||
2a223392268c2ff798a3718ba0386335877ac998c124e48c74c264be71397c53 adapters/claude/adapter.sh
|
||||
962255271e95cb30788e97cd9e86e17f384dd5f74cead435692e8b30e47af9cf adapters/pi/adapter.sh
|
||||
863640ee44598a5bffd6d37d328dcddfd4bdc671f792d029ae2ee18b1055b28b adapters/README.md
|
||||
009059ea86e1d14c5b12ed0fdad64e8cd501e19021ef6f4148a1463d55860125 docs/TOOLS.md
|
||||
8cbbe58045188489932c63d4361c4b0679ae7fbf8a4a7341fc8cad43ec2b26ac packages/bus/README.md
|
||||
aa71088d656455de83d9c1a42745852041ee61e2d5eb6f4c1e48e8ae29623433 packages/bus/src/broker.mjs
|
||||
eb7a281746bacc65da303ff90f7bef709793c82606f177c5f21ff2005a966a3c packages/bus/src/process.mjs
|
||||
12634ff6b6ef5b5a282bb306b30c9f02929d1fb6597e149d47d21069201399b5 packages/bus/src/runtime.mjs
|
||||
1e301c58a562c7d9c1669dc657be708b17d37f26816af755894cc727653f7d6b packages/bus/tests/end-launch.test.mjs
|
||||
667998fd1af37818e65b16bb515f22db73ab6077c5449367e731d16590598783 packages/cli/README.md
|
||||
bd207b81a2b9965b9e46da66ab31ed9a587b87e8669e8293184d4b842e45bff5 packages/cli/src/cli.mjs
|
||||
9e5e08e78be214e2cfe8a1a9a0d310231d53f503e2efc1814ee00efab38fd280 packages/cli/src/host.mjs
|
||||
bf7115a9404a7d3b7406d078c6be409d0d6f726d5b88496a6995ddb1c17a6fa2 packages/cli/src/launcher.mjs
|
||||
9b30a3bfe96a5d7c6956b6c75196c08bc35ceec302859337915ca1cfe2a76b76 packages/cli/tests/fixtures/launch-host.mjs
|
||||
aa705a5bc9de9fa50471b5aba6217fba6c36bc2479590d88a9197c99c8c94cec packages/cli/tests/launcher.test.mjs
|
||||
709bd331bb0d76f28b464e518f4e1fc3bb0b303614e79d7e82479dcd679043cc packages/cli/tests/verbs.test.mjs
|
||||
34ef8212e27f052223443c66830839517f7a54ecb6ef7d85795b3e89c65b4d4c packages/harness/package.json
|
||||
8b3bab21b0725cb19690cde0036a6899bc608911e14446cb32139dfbbcb10c6b packages/harness/README.md
|
||||
22efd0bed7991561920ad29f6bf9a1ab2d7384185fab4267684a037e026d8e85 packages/harness/src/bundle.mjs
|
||||
32b5090760c95eea0e1232ff36ec13a1d9b58335b2eade621196dec1ebdbe784 packages/harness/src/claude-gate.mjs
|
||||
0144ed5591d941689e8cf783daaed445ec728507d43b246cf3fffa317b0f6599 packages/harness/src/gate.mjs
|
||||
71e00113b8e5b55b410c7aae6232f76e972fc77aa68afa893da45c6b78d297e2 packages/harness/src/mcp-server.mjs
|
||||
d19753fa72f0b57e751749359644093713bcb650bfac566f55d2bc05cb817121 packages/harness/src/pi-extension.mjs
|
||||
4065d94c84bc08f570a43071bce1955fed46503352242ebdca806e011ec624d7 packages/harness/src/runner.mjs
|
||||
92153d9e2161ceef4ee76f2ff992014760a8c62b0ea709b51e2ea0ce965d3edb packages/harness/src/tools.mjs
|
||||
96796238b7effab78cc6356ed8ea163f02aa39999d8dfc97fef83d45309d6574 packages/harness/tests/bundle.test.mjs
|
||||
96524da43b212e84d78108cbbf05eef324c934f3ad9e0d4cb2edcb0f3df256d4 packages/harness/tests/claude-gate.test.mjs
|
||||
3846c68c0fda682952fe7d76e8e2006ea2d1b355516897795b4a61f55ba47ea2 packages/harness/tests/claude-session.test.mjs
|
||||
8392172b243356932c974bdca9b4c449a312ae7a042ee7a22e0f22fbb98dbec9 packages/harness/tests/fixtures/fake-adapter.mjs
|
||||
df3225783a1fa35f1084a3c89b5496185901d0bb8e65f13165a5c07ea5047ee6 packages/harness/tests/gate.test.mjs
|
||||
e9b0dfc28f187d58714bfdf57ec7a5f3eb19dbaa8ff6d267e19f2ad10404d073 packages/harness/tests/helpers.mjs
|
||||
793eb11f970e4a8eecddec4dc48c24331e4de795bc04bfadf806a2fc3a15a8b4 packages/harness/tests/mcp-server.test.mjs
|
||||
92529a27ebac228fe2a03a12a0a2ee3b29203af4a26182a0e380a299dc327449 packages/harness/tests/pi-session.test.mjs
|
||||
2fc5a3522297c16f31ced5f31f707277e18e1f860f0a6372faef3e86ea5d2ecd packages/harness/tests/runner.test.mjs
|
||||
ef9130a3cb1ca3e278a8ed370060afd1145d1ceb211a915e83d75c8346b04931 packages/harness/tests/tools.test.mjs
|
||||
4e34456187387b02fa6d996c270dea4076b5d57952cddaa01f7a04843b351a02 packages/seat/README.md
|
||||
382cbf7e0ff336911e288ce858bdbfbec693bc2b69879720d1f0b4c7d8455198 packages/seat/src/proc.mjs
|
||||
5e181204de871d4f1098f5a63b5f80d87a44f5c01bf150101a6690bb1ccdca3d packages/seat/src/session.mjs
|
||||
9a505d255c61034b3be738d359bc4a10acf08916c65675ee14dab2e29c060b04 packages/seat/tests/session.test.mjs
|
||||
482ad6167fc96bf86928e0b467b3e173b174508fd913e297a8164d73f334d031 scripts/agent-host-dev.sh
|
||||
b37d673aa5ce72c10b49018d8ffd9460f393eff7b638f1aa2065eecd608dde77 scripts/mosaic
|
||||
@@ -0,0 +1,41 @@
|
||||
adapters/claude/adapter.sh
|
||||
adapters/pi/adapter.sh
|
||||
adapters/README.md
|
||||
docs/TOOLS.md
|
||||
packages/bus/README.md
|
||||
packages/bus/src/broker.mjs
|
||||
packages/bus/src/process.mjs
|
||||
packages/bus/src/runtime.mjs
|
||||
packages/bus/tests/end-launch.test.mjs
|
||||
packages/cli/README.md
|
||||
packages/cli/src/cli.mjs
|
||||
packages/cli/src/host.mjs
|
||||
packages/cli/src/launcher.mjs
|
||||
packages/cli/tests/fixtures/launch-host.mjs
|
||||
packages/cli/tests/launcher.test.mjs
|
||||
packages/cli/tests/verbs.test.mjs
|
||||
packages/harness/package.json
|
||||
packages/harness/README.md
|
||||
packages/harness/src/bundle.mjs
|
||||
packages/harness/src/claude-gate.mjs
|
||||
packages/harness/src/gate.mjs
|
||||
packages/harness/src/mcp-server.mjs
|
||||
packages/harness/src/pi-extension.mjs
|
||||
packages/harness/src/runner.mjs
|
||||
packages/harness/src/tools.mjs
|
||||
packages/harness/tests/bundle.test.mjs
|
||||
packages/harness/tests/claude-gate.test.mjs
|
||||
packages/harness/tests/claude-session.test.mjs
|
||||
packages/harness/tests/fixtures/fake-adapter.mjs
|
||||
packages/harness/tests/gate.test.mjs
|
||||
packages/harness/tests/helpers.mjs
|
||||
packages/harness/tests/mcp-server.test.mjs
|
||||
packages/harness/tests/pi-session.test.mjs
|
||||
packages/harness/tests/runner.test.mjs
|
||||
packages/harness/tests/tools.test.mjs
|
||||
packages/seat/README.md
|
||||
packages/seat/src/proc.mjs
|
||||
packages/seat/src/session.mjs
|
||||
packages/seat/tests/session.test.mjs
|
||||
scripts/agent-host-dev.sh
|
||||
scripts/mosaic
|
||||
@@ -0,0 +1,33 @@
|
||||
OK valid task validates (exit 0)
|
||||
OK unknown task key exits 2 (exit 2)
|
||||
OK unsupported taskVersion exits 2 (exit 2)
|
||||
OK invalid task id exits 2 (exit 2)
|
||||
OK empty prompt exits 2 (exit 2)
|
||||
OK NUL in expectExact exits 2 (exit 2)
|
||||
OK out-of-range timeout exits 2 (exit 2)
|
||||
OK missing mission file exits 4 (exit 4)
|
||||
OK task with valid mission validates (exit 0)
|
||||
OK invalid mission exits 2 (exit 2)
|
||||
OK validate missing task exits 4 (exit 4)
|
||||
OK validation does not modify the task file
|
||||
OK prune dry-run exits 0 (exit 0)
|
||||
OK dry-run deleted nothing
|
||||
OK prune --keep=2 --yes removes oldest (exit 0)
|
||||
OK kept exactly 2 newest runs
|
||||
OK newest run kept, oldest pruned
|
||||
OK append-only receipt written (3 entries)
|
||||
OK sessions/workspaces untouched by prune
|
||||
OK prune with invalid keep exits 4 (exit 4)
|
||||
skip adapter seam cases (docker daemon unavailable)
|
||||
skip workspace/capability cases (docker daemon unavailable)
|
||||
skip live task cases (docker unavailable)
|
||||
OK onboard without name exits 4 (non-interactive) (exit 4)
|
||||
OK onboard --name renders profile (exit 0)
|
||||
OK profile written
|
||||
OK canon structure: required filled, optional placeholdered
|
||||
OK canon sections present
|
||||
FAIL user recall run succeeds (exit 1)
|
||||
FAIL recalled user name (response: )
|
||||
OK no agent identity on headless run
|
||||
|
||||
selftest: 26 passed, 2 failed
|
||||
@@ -0,0 +1,82 @@
|
||||
✔ launch identity is stamped, payload identity is refused and stale holder cannot send (158.150768ms)
|
||||
✔ decision classes route from policy; gated resolution is human-only, choice and target must match (258.805434ms)
|
||||
✔ claim exclusion, holder release, gated revoke and rerouting to a new holder are atomic (247.340125ms)
|
||||
✔ launch events require a human CLI capability; generic emit cannot forge authority events (154.366458ms)
|
||||
✔ within-role decisions close atomically and invalid options or blocking omissions refuse (141.971144ms)
|
||||
✔ observer capabilities read human inbox but cannot mutate or forge launch identity (131.526953ms)
|
||||
✔ task action subjects and linked decision trail are complete and ordered (132.519219ms)
|
||||
✔ launch binding is durable and reconnecting requires the identical trusted record (94.302493ms)
|
||||
✔ business isolation includes inherited object names and cross-business message references (143.859485ms)
|
||||
✔ authority never transfers between action, run, target, unresolved or replaced role holder (215.909845ms)
|
||||
✔ task projection uses schema current view, skipping earlier and equal-start polls (108.67248ms)
|
||||
✔ revocation permanently bars the old run from reclaiming first, including after broker restart (165.948325ms)
|
||||
✔ empty message references refuse before storage; refusal-evidence failure stays a typed error (103.673551ms)
|
||||
✔ both arbiters require human resolution when their cross-role route is themselves (179.340595ms)
|
||||
✔ S1 adapter takes resolved limits and refs, rejects mismatched instance, never mutates input (3.248662ms)
|
||||
✔ only validated broker references load; returned data and exceptions cannot expose a known token (7.094661ms)
|
||||
✔ bad file modes, symlinks, repository/data paths, malformed tokens and missing dates refuse (5.019312ms)
|
||||
✔ expiry refuses use and env references never become client data (3.278188ms)
|
||||
✔ S1 parsed service refs work, service mismatch refuses, Gitea rotation due is a warning state (2.965278ms)
|
||||
✔ opaque tokens shorter than 16 characters refuse before use (0.609186ms)
|
||||
✔ endLaunch writes session.ended, releases the run claim and kills its capabilities (128.865172ms)
|
||||
✔ endLaunch refuses an unknown run, a second end and a rebind of the ended run (145.44321ms)
|
||||
✔ a restarted broker refuses to rebind an ended run; a refused rebind leaves the run unbound (142.744227ms)
|
||||
✔ endLaunch leaves a claim another run took alone (153.860535ms)
|
||||
✔ refuse records action.refused against the caller with the code only (109.560376ms)
|
||||
✔ launches off refuses role.launch with launch-revoked until launches on (158.905054ms)
|
||||
✔ broker process: launch ops authorize role.launch, record refusals and end runs (203.667266ms)
|
||||
✔ human proof binds CLI entry, process start and nonce; agents and incomplete ancestry refuse (6.466732ms)
|
||||
✔ process reader gets own kernel identity without exposing environment values (1.515607ms)
|
||||
✔ EACCES ancestor environments skip only markers; commands and registered launches still refuse (1.392126ms)
|
||||
✔ real pid 1 remains inspectable when its environment is protected (0.473384ms)
|
||||
✔ within-role sends cite an open gated launch decision without spending it or naming it in grants (182.153792ms)
|
||||
✔ missing and foreign-business citations refuse and roll back message and grant (183.462033ms)
|
||||
✔ cross-role sends still need a matching resolved decision and consume it once (225.561481ms)
|
||||
✔ broker process binds trusted launches, offers reader capabilities, refuses human mutation, closes cleanly (192.194069ms)
|
||||
✔ startup token refusal returns safe code without value or partial listening broker (38.623009ms)
|
||||
✔ loaded fixture token is absent from socket replies and SQLite, including refusal evidence (167.701961ms)
|
||||
✔ killed broker leaves an explicit stale lock; another process cannot silently reclaim it (169.750172ms)
|
||||
✔ trusted host registers later launches; socket clients never have a registration verb (166.888863ms)
|
||||
✔ runtime excludes declared project roots even when host supplies no repoRoots (39.861791ms)
|
||||
✔ a refused launch binding leaves the broker and existing capabilities alive; bad protocol stops it (149.249527ms)
|
||||
✔ v3b prototype refusals, views and append-only mutations (942.242629ms)
|
||||
✔ gated approval authorizes once, survives store reopen, and fresh approval works (222.807767ms)
|
||||
✔ another run cannot consume an approval; a failed check leaves it usable (216.443103ms)
|
||||
✔ two scheduled callers have exactly one grant and one consumed refusal (149.72494ms)
|
||||
✔ failed commit rolls consumption back; cross-role consumes and within-role stays reusable (276.865404ms)
|
||||
✔ class drift gated to cross-role refuses before consumption (170.894519ms)
|
||||
✔ class drift cross-role to gated refuses before consumption (158.250182ms)
|
||||
✔ class drift gated to within-role refuses before consumption (177.520217ms)
|
||||
✔ class drift cross-role to within-role refuses before consumption (185.093462ms)
|
||||
✔ class drift within-role to gated refuses before consumption (144.027639ms)
|
||||
✔ class drift within-role to cross-role refuses before consumption (152.049889ms)
|
||||
✔ message.send consumes approval and prevents a later send or authorize (168.498462ms)
|
||||
✔ role.revoke consumes approval and prevents a later revoke or authorize (198.374954ms)
|
||||
✔ creates private WAL store and excludes a second writer until explicit close (116.738279ms)
|
||||
✔ rollback is atomic and schema metadata is checked against trusted DDL, not just itself (175.406652ms)
|
||||
✔ existing empty database and symlink runtime directory refuse, never initialize over damage (182.907272ms)
|
||||
✔ crash during a transaction recovers no partial event after explicit fixture-only lock removal (154.638962ms)
|
||||
✔ writer refuses mixed at/read_at forms atomically, even through trusted SQL helpers (94.948081ms)
|
||||
✔ async transactions refuse before invoking their function (78.29418ms)
|
||||
✔ recordTask keeps sync reads and a role write apart (155.00518ms)
|
||||
✔ read_at must be one canonical UTC format, so the projection compares strings safely (97.553598ms)
|
||||
✔ a bad entry refuses the whole record (103.483603ms)
|
||||
✔ taskView reads the projection for one business (126.462963ms)
|
||||
✔ requestTask hands only a holder and a task verb to the handler, and records refusals (216.111452ms)
|
||||
✔ the server sends task verbs to the adapter with its own timeout; other verbs stay synchronous (389.096711ms)
|
||||
✔ without an adapter the server refuses every task verb (159.18258ms)
|
||||
✔ the runtime refuses an invalid adapter and closes a valid one (154.938435ms)
|
||||
✔ the process loads the S3 adapter from plain-data trackers (236.701945ms)
|
||||
✔ socket capability stamps launch identity; shared views use wire, no SQL client (138.617926ms)
|
||||
✔ two wire claims serialize; a lost reply never automatically retries (171.818149ms)
|
||||
✔ malformed, oversized and identity-forging envelopes refuse without echoing input (110.874989ms)
|
||||
✔ client preserves UTF-8 when a response divides a multibyte character (11.810021ms)
|
||||
✔ committed mutation followed by dropped reply reports unknown and is never retried (120.128497ms)
|
||||
ℹ tests 74
|
||||
ℹ suites 0
|
||||
ℹ pass 74
|
||||
ℹ fail 0
|
||||
ℹ cancelled 0
|
||||
ℹ skipped 0
|
||||
ℹ todo 0
|
||||
ℹ duration_ms 2336.232793
|
||||
@@ -0,0 +1,68 @@
|
||||
✔ config directory and file path follow MOSAIC_CONFIG (1.341178ms)
|
||||
✔ the fixture business validates and comes back frozen (4.26895ms)
|
||||
✔ two instances may share a definition (1.580704ms)
|
||||
✔ top-level refusals (4.501169ms)
|
||||
✔ arbiters and projects (5.4681ms)
|
||||
✔ role instances (3.487746ms)
|
||||
✔ Vikunja bots (8.02749ms)
|
||||
✔ a role without Vikunja takes no tracker block (10.757229ms)
|
||||
✔ credential references match the definition's services (3.823637ms)
|
||||
✔ launch (9.590847ms)
|
||||
✔ loadBusiness: file checks (1.975951ms)
|
||||
✔ loadBusiness: not a regular file (41.201527ms)
|
||||
✔ loading writes nothing (1.314092ms)
|
||||
✔ names that are Object.prototype properties don't count as declared (2.899494ms)
|
||||
✔ the shipped example refuses as written and validates once filled in (0.575228ms)
|
||||
✔ usage errors exit 4 (318.00225ms)
|
||||
✔ validate: a good business exits 0 and prints instance digests (68.995958ms)
|
||||
✔ validate: project files (349.686854ms)
|
||||
✔ validate: missing files and a broken system config (326.343919ms)
|
||||
✔ validate: credential reference problems exit 2 and name each one (99.678089ms)
|
||||
✔ validate: a token file inside the repository is refused (78.247664ms)
|
||||
✔ validate: role definitions come from MOSAIC_ROLES_DIR (216.675669ms)
|
||||
✔ resolve: prints one instance's record (248.880265ms)
|
||||
✔ resolve: refusals (412.874057ms)
|
||||
✔ parse: exactly one of file or env, plus the service's date (2.325764ms)
|
||||
✔ check: a good file has no problems (0.75954ms)
|
||||
✔ check never opens the file: a write-only token passes (0.311868ms)
|
||||
✔ check: file problems (0.763239ms)
|
||||
✔ check: token files can't live in the repository or dataRoot, even through a linked directory (0.798484ms)
|
||||
✔ check: dates and environment references (0.361131ms)
|
||||
✔ path and load (2.885491ms)
|
||||
✔ refusals (1.515385ms)
|
||||
✔ systemVars flattens the validated config (1.790585ms)
|
||||
✔ precedence: system, business, project, project role, agent (4.547817ms)
|
||||
✔ limits narrow the definition and never widen it (2.16752ms)
|
||||
✔ role.launch stays within-role only for the instance the launch block names (4.55638ms)
|
||||
✔ limits.authority without role.launch leaves the launcher with no launch block (1.912786ms)
|
||||
✔ limits.authority narrows cross-role actions too (1.129226ms)
|
||||
✔ classify (1.062031ms)
|
||||
✔ the record carries what the broker and launcher need (9.084401ms)
|
||||
✔ digest: key order doesn't matter, any value change does (7.179908ms)
|
||||
✔ refusals (2.455766ms)
|
||||
✔ the four shipped version 2 roles load (3.959226ms)
|
||||
✔ shipped role scopes match addendum B section 2 and the SR runbook (1.430244ms)
|
||||
✔ shipped authority follows the note's table (0.81314ms)
|
||||
✔ version 1 files keep loading with no authority (1.244032ms)
|
||||
✔ the conductor policy isn't a role (0.262325ms)
|
||||
✔ a missing role file is exit 4, a symbolic link too (0.547733ms)
|
||||
✔ version 2 refusals (1.428674ms)
|
||||
✔ authority: closed vocabulary, no gated-only action, no overlap (2.035327ms)
|
||||
✔ credentials: Gitea scopes (0.825672ms)
|
||||
✔ credentials: Vikunja scopes are a group-to-verbs map from the grantable list (1.075392ms)
|
||||
✔ credentials: services (8.42904ms)
|
||||
✔ contract: a non-empty regular Markdown file beside the role file (0.830731ms)
|
||||
✔ every key names known layers and a merge rule (0.926442ms)
|
||||
✔ unknown keys and wrong layers refuse (0.796639ms)
|
||||
✔ types (1.829487ms)
|
||||
✔ merge: defaults, then the most specific layer wins (0.289155ms)
|
||||
✔ merge: limits only narrow, and provenance lists each source (0.406369ms)
|
||||
✔ merge doesn't change its inputs (0.149857ms)
|
||||
ℹ tests 60
|
||||
ℹ suites 0
|
||||
ℹ pass 60
|
||||
ℹ fail 0
|
||||
ℹ cancelled 0
|
||||
ℹ skipped 0
|
||||
ℹ todo 0
|
||||
ℹ duration_ms 2187.474485
|
||||
@@ -0,0 +1,87 @@
|
||||
✔ inbox lists only decisions routed to the human, with what approving authorizes and how to decide (118.155092ms)
|
||||
✔ decide resolves by id prefix with --yes, and the trail shows it in broker order with the task to follow (135.340975ms)
|
||||
✔ decide refuses without a terminal or --yes, on an unknown option and on a short reference (110.707482ms)
|
||||
✔ decide prints a declining choice as declining (92.798251ms)
|
||||
✔ an unknown outcome is reported once and never resent (87.28131ms)
|
||||
✔ a decision closed before the answer arrives exits 2 and points at its trail (95.98171ms)
|
||||
✔ a prefix that matches two open decisions exits 2 and resolves neither (88.223671ms)
|
||||
✔ without --business a command uses the live host's business, and a stale host.json is not a host (62.710471ms)
|
||||
✔ every human command refuses inside an agent run before it touches the bus (61.875025ms)
|
||||
✔ usage errors exit 4; no business and no host is a usage error (65.175578ms)
|
||||
✔ agents and tasks print through the broker (68.785667ms)
|
||||
✔ notify.json: missing, loose, malformed and extra keys refuse; a binding or null passes (1.467569ms)
|
||||
✔ bootConfig builds the broker's boot message for one business, with no trackers key when no project names one (59.037059ms)
|
||||
✔ trackers come from the tracker.* variables of the one project that names a tracker project (43.777779ms)
|
||||
✔ with two projects, the one that sets tracker.project supplies the tracker and the other is no conflict (50.148848ms)
|
||||
✔ two projects that each name a tracker project refuse, since the boot shape holds one (53.581893ms)
|
||||
✔ a business without tracker.baseUrl gets no trackers entry (39.300001ms)
|
||||
✔ an unknown business and a broken system config refuse with exit 3 (83.383681ms)
|
||||
✔ empty views say so (1.181626ms)
|
||||
✔ the trail keeps the broker's order and names a decision's task without its rows (1.366794ms)
|
||||
✔ tasks print the tracker fields the snapshot carries (0.251098ms)
|
||||
✔ the host boots the broker, binds a launch in process, and the notifier DMs a blocking decision exactly once (974.34103ms)
|
||||
✔ a notifier that dies takes the host down with exit 1, so the unit restarts the pair (212.356371ms)
|
||||
✔ a second host for the same data root refuses with exit 3 while the first runs (128.8785ms)
|
||||
✔ a notifier that refuses stops the broker and the host refuses with exit 3 (222.476083ms)
|
||||
✔ a notifier that refuses after the broker died still refuses with exit 3, without a send to the dead broker (176.961283ms)
|
||||
✔ a notifier that dies before it replies, after the broker died, still refuses, without a send to the dead broker (135.640371ms)
|
||||
✔ a close send that fails with EPIPE after the notifier refuses still gives the notifier's refusal, exit 3 (198.923159ms)
|
||||
✔ a close send that fails with EPIPE after the notifier dies unanswered still gives the notifier's error (117.161338ms)
|
||||
✔ close() whose stop and close sends fail with EPIPE still finishes, with exit 1 (220.743628ms)
|
||||
✔ watchChildren reports a child that died before it was called, and one that dies later (22.783177ms)
|
||||
✔ bus stop refuses to signal a live pid that is not a bus host (202.440829ms)
|
||||
✔ bus start refuses with exit 3 and the code when the broker refuses to boot; bus status names the lock (259.625833ms)
|
||||
✔ bus start refuses with exit 3 without a notifier config (99.337871ms)
|
||||
✔ bus start runs until bus stop; status reports it while it runs (723.184797ms)
|
||||
✔ bus-service.sh renders the unit and installs it into a given directory (40.188497ms)
|
||||
✔ bus start --pm: the PM runs under its own PID namespace, registered, with a manifest (1298.529746ms)
|
||||
✔ the PM launches a coder through its launch tool; the coder answers; refusals name their code (648.981918ms)
|
||||
✔ a runner that stops at once ends its launch with the runner's reason (218.756086ms)
|
||||
✔ 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 (718.902837ms)
|
||||
✔ 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 (3649.636207ms)
|
||||
✔ a malformed sessions.json refuses the host start with exit 3 and stays as it was (133.892391ms)
|
||||
✔ zoned uses the IANA zone across DST (20.415121ms)
|
||||
✔ each open blocking decision is DM'd once, across polls and a restart; non-blocking ones are not (116.186459ms)
|
||||
✔ two blocking decisions get two DMs with different nonces (120.670182ms)
|
||||
✔ the digest nonce differs per business and per day and fits Discord's 25 characters (0.445474ms)
|
||||
✔ a failed DM is journaled, backs off, and is retried until it lands (120.593616ms)
|
||||
✔ five definite refusals stop a DM: one gave-up line, one log line, and a restart keeps the count (101.787488ms)
|
||||
✔ 429s, 5xx-style unknowns and refusals without a status never count toward the limit (97.104103ms)
|
||||
✔ a crash between the fifth refusal and its gave-up line: the next poll appends it and sends nothing (92.253269ms)
|
||||
✔ 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 (163.676718ms)
|
||||
✔ a restart after the second refusal does not send before that refusal's 30 min are up (110.941061ms)
|
||||
✔ the digest goes at 08:00 Chicago once a day, with blocking ones marked as DM'd (87.725819ms)
|
||||
✔ a late start with no digest for the day sends one at once; an empty inbox gets one line (63.461429ms)
|
||||
✔ an inbox read failure is logged and the next poll retries (0.580971ms)
|
||||
✔ no Discord id reaches the journal or the log (72.972195ms)
|
||||
✔ the journal: a torn tail is copied out and truncated, so an append after it reopens cleanly (11.00011ms)
|
||||
✔ the journal: a crash between the copy and the truncate leaves a tail the next open repairs (16.377097ms)
|
||||
✔ the journal: a whole file that is one torn line truncates to empty (21.36201ms)
|
||||
✔ the journal: a malformed complete line refuses and leaves the file and any torn tail alone (0.604157ms)
|
||||
✔ the journal: a loose file mode, a loose directory or a symlinked journal refuses (0.56872ms)
|
||||
✔ the journal: a line with a wrong type refuses with exit 3 and names the field (1.811585ms)
|
||||
✔ the journal: a symlinked directory refuses and says it is a link (0.322853ms)
|
||||
✔ 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.464501ms)
|
||||
✔ the journal: an append after the file was swapped for a symlink refuses and writes nothing through it (0.375347ms)
|
||||
✔ the journal: a directory it cannot write or create refuses with exit 3 and names the path (0.438934ms)
|
||||
✔ digest content stays within Discord's 2000 characters (0.353809ms)
|
||||
✔ runLoop never overlaps ticks and stops after the one in flight (111.16002ms)
|
||||
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 (444.699888ms)
|
||||
✔ the transport writes {business, verb, args} to the child and reads its JSON (47.864309ms)
|
||||
✔ a bus code on stderr becomes the exit code; garbage and timeouts are outcome problems (2212.180406ms)
|
||||
✔ busExit and refuseInsideAgent (0.532377ms)
|
||||
✔ talk sends a REQUEST, prints and reads everything that arrives, and stops at the reply (210.726131ms)
|
||||
✔ talk --wait 0 only sends; no reply within --wait exits 1 and says where it will show (1134.898685ms)
|
||||
✔ talk, stop and launches off/on refuse inside an agent run; bad arguments are usage errors (98.253828ms)
|
||||
✔ launches off and on go to the broker and change the business's launch state (87.660028ms)
|
||||
✔ launches list reads sessions.json and marks a stale entry; stop reports it and refuses a non-runner (73.549533ms)
|
||||
ℹ tests 77
|
||||
ℹ suites 0
|
||||
ℹ pass 77
|
||||
ℹ fail 0
|
||||
ℹ cancelled 0
|
||||
ℹ skipped 0
|
||||
ℹ todo 0
|
||||
ℹ duration_ms 6786.010947
|
||||
@@ -0,0 +1,132 @@
|
||||
✔ explicit request, Seen, ordinary completion and a new request have distinct attention states (4.641886ms)
|
||||
✔ attention convention ignores reasoning/quoted examples and permits leading blank lines (0.268969ms)
|
||||
✔ completed smoke replies and ordinary questions are idle, not human blockers (1.020682ms)
|
||||
✔ only an explicit first-line input request makes a finished reply waiting (0.289824ms)
|
||||
✔ tool activity, user text, errors and unfinished turns override attention text (0.167445ms)
|
||||
✔ STOP access failure is unknown, not absence, under a non-root identity (54.374685ms)
|
||||
✔ connector Task never inherits Discord routing envelopes; ordinary Task still uses user text (3.166721ms)
|
||||
✔ connector discovery keeps only safe identity; rejects modes, mismatches, links and traversal (1.667541ms)
|
||||
✔ canonical owner identity and STOP are independent; no tmux fallback or forged registration (6.805422ms)
|
||||
✔ connector reply refusal precedes forged live tmux registration; ordinary agent still sends (0.669073ms)
|
||||
✔ server rescans connector discovery and refuses HTTP reply without transport (43.145184ms)
|
||||
✔ connector session links and linked directories are not read (1.27055ms)
|
||||
✔ newer live matching launch marks old activity, preserves history/attention/attribution, then clears on new activity (3.270946ms)
|
||||
✔ CLI print uses the relaunch notice instead of old current preview (68.257724ms)
|
||||
✔ connector owner and fixed task never inherit a native relaunch notice (3.060282ms)
|
||||
✔ equality, stale/unknown/offline, mismatched registration and unknown activity do not assert relaunch (1.70917ms)
|
||||
✔ loadConfig: missing file throws ConfigError (1.787365ms)
|
||||
✔ loadConfig: invalid JSON throws ConfigError (0.347612ms)
|
||||
✔ loadConfig: missing dataRoot throws ConfigError (0.259123ms)
|
||||
✔ loadConfig: relative dataRoot throws ConfigError (0.243686ms)
|
||||
✔ loadConfig: valid config returns dataRoot (0.798268ms)
|
||||
✔ findNewestSession: picks the newest by mtime among two files (0.526496ms)
|
||||
✔ findNewestSession: finds files in nested subdirectories (0.406065ms)
|
||||
✔ findNewestSession: returns null for a missing dir (0.137243ms)
|
||||
✔ readSession: extracts fields, collapses/truncates text, counts a truncated final line (0.770616ms)
|
||||
✔ readSession: model and provider follow the latest model_change entry or assistant turn; null when the log names neither; scanAgent carries them (1.401954ms)
|
||||
✔ readSession: lastError carries the assistant errorMessage only when the last assistant turn errored (0.536856ms)
|
||||
✔ findNewestSession/scan: never read sibling auth or secrets next to a sessions dir (1.449147ms)
|
||||
✔ deriveState: full state table (0.200445ms)
|
||||
✔ rule: newest entry is an assistant message with a tool call, after a question-looking text, is working (0.407812ms)
|
||||
✔ rule: newest entry is a tool result with no assistant text after it is working (0.324887ms)
|
||||
✔ rule: a finished ordinary turn is idle, even if it says your move (0.34233ms)
|
||||
✔ task: the first user message of the session, from text blocks (0.364454ms)
|
||||
✔ task: a plain-string user content is accepted, whitespace collapsed and long text capped (0.306066ms)
|
||||
✔ task: no user message in the log means null (shown as unknown), never a guess (0.341252ms)
|
||||
✔ workspace: the live tmux pane path wins; the session cwd is the fallback; neither means null (0.442806ms)
|
||||
✔ activeProject: basename of the nearest .git directory or .git file above the workspace; none means null (0.797736ms)
|
||||
✔ scan: the written record carries task, workspace and activeProject (0.634047ms)
|
||||
✔ registration: overrides task, project and workspace; every source says registration; registered carries the launch fields; the grouping column is untouched (0.683352ms)
|
||||
✔ registration: empty task and null project/workspace leave the derived values in place; registered is still non-null (0.458167ms)
|
||||
✔ 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.138285ms)
|
||||
✔ registration: no registration leaves the Gate A fields exactly as before, and registered is null (0.509043ms)
|
||||
✔ loadRegistrations: a missing seatsDir gives empty lists (0.193861ms)
|
||||
✔ loadRegistrations: one good record, one malformed JSON, one with an unknown field; a stray file under seatsDir is ignored (1.188153ms)
|
||||
✔ 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.37717ms)
|
||||
✔ scan: writes the registration override to disk; index.json carries registered and registrationErrors (1.460457ms)
|
||||
✔ scan: a relative seatsDir throws ConfigError; an omitted seatsDir behaves as before (0.322685ms)
|
||||
✔ scanAgent: waitingOnYou is true for waiting/error and false otherwise (0.671017ms)
|
||||
✔ scanAgent: ageSeconds is computed from the injected now (0.324056ms)
|
||||
✔ scanAgent: sessionFile null and state idle when sessions dir is empty but alive (0.192104ms)
|
||||
✔ discoverRepoAgents: finds agents with a sessions dir, skips those without, sorted by name (0.429286ms)
|
||||
✔ discoverFleetAgents: finds agents with a sessions dir, sorted by name, fleet tmux fields (0.42239ms)
|
||||
✔ scan: writes per-agent files and index.json, rerun overwrites, no leftover tmp files (1.202797ms)
|
||||
✔ scan: relative boardDir throws ConfigError (0.116583ms)
|
||||
✔ CLI: scan with assume-alive liveness exits 0, prints board summary, writes board files (75.904446ms)
|
||||
✔ CLI: missing config exits 2 with a refused: message (68.3446ms)
|
||||
✔ CLI: unknown command exits 2 (60.818611ms)
|
||||
✔ CLI: unknown --liveness value exits 2 (62.922692ms)
|
||||
✔ panesRunPi: true when any trimmed line equals 'pi' (0.277749ms)
|
||||
✔ panesRunPi: false for bash-only, claude, empty, or node-pi-style lines (0.110431ms)
|
||||
✔ tmuxIsAlive: a pane running pi is alive (0.261019ms)
|
||||
✔ tmuxIsAlive: session exists but pi has exited is not alive (0.088258ms)
|
||||
✔ tmuxIsAlive: no such tmux session is not alive (0.054366ms)
|
||||
✔ tmuxIsAlive: tmux could not be run at all is unknown (null), never assumed alive (0.074409ms)
|
||||
✔ tmuxIsAlive: passes -L <socket> only when a socket is given (0.100801ms)
|
||||
✔ parsePanes: one pane per line, command and optional tab-separated path (0.101551ms)
|
||||
✔ tmuxInspect: reports the path of the pane running pi, not of a shell pane (0.123154ms)
|
||||
✔ tmuxInspect: no pi pane, no session, or no tmux gives no workspace and the matching liveness (0.170557ms)
|
||||
✔ loadSeen: missing file returns {} (0.209322ms)
|
||||
✔ loadSeen: invalid JSON throws ConfigError (0.415243ms)
|
||||
✔ loadSeen: a JSON array throws ConfigError (0.211211ms)
|
||||
✔ loadSeen: a non-string value throws ConfigError (0.231069ms)
|
||||
✔ markSeen: seen true adds the key and writes seen.json mode 0600, no leftover tmp files (0.40028ms)
|
||||
✔ markSeen: seen false deletes the key (0.283061ms)
|
||||
✔ markSeen: missing, empty, or non-string fields throw ConfigError (0.238733ms)
|
||||
✔ markSeen: project containing '/' throws ConfigError (0.134177ms)
|
||||
✔ markSeen: non-boolean seen throws ConfigError (0.123902ms)
|
||||
✔ scanAgent: a seen mark matching the waiting session's lastTimestamp clears waitingOnYou (0.320389ms)
|
||||
✔ scanAgent: a stale mark (agent wrote something newer) is not seen and waitingOnYou is true (0.270898ms)
|
||||
✔ scanAgent: a working session with a matching mark is not seen (marks only apply to waiting/error) (0.244607ms)
|
||||
✔ scanAgent: an error-state session with a matching mark is seen (0.257963ms)
|
||||
✔ scan: index.seen and waitingOnYou reflect seen.json, which scan never rewrites or deletes (0.568729ms)
|
||||
✔ scan: a corrupt seen.json makes scan throw ConfigError (fail closed) (0.20305ms)
|
||||
✔ 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.726555ms)
|
||||
✔ 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.645998ms)
|
||||
✔ 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.045846ms)
|
||||
✔ isLoopbackHost: recognizes loopback hosts (1.386893ms)
|
||||
✔ isLoopbackHost: rejects non-loopback hosts (4.71065ms)
|
||||
✔ startServer: refuses a non-loopback host with ConfigError, never opens a socket (3.447209ms)
|
||||
✔ startServer: serves page, healthz, and a rescanning /api/board (40.64407ms)
|
||||
✔ startServer: a seatsDir registration overrides the row and index.registered reflects it (7.221166ms)
|
||||
✔ startServer: /api/board returns 500 JSON with an error field when scan throws (3.361978ms)
|
||||
✔ CLI: serve refuses a non-loopback host with exit 2 and a refused: message (72.261995ms)
|
||||
✔ CLI: serve rejects a non-numeric --port with exit 2 (59.137725ms)
|
||||
✔ CLI: scan still works after the async cli refactor (64.194896ms)
|
||||
✔ CLI: live serve prints its URL and answers /healthz (70.924896ms)
|
||||
✔ page.html: esc() escapes every HTML-significant character (0.796687ms)
|
||||
✔ POST /api/seen marks a row; GET /api/board still shows it seen; seen:false clears it (9.921107ms)
|
||||
✔ POST /api/seen without a JSON content-type returns 400 and does not write a mark (2.365807ms)
|
||||
✔ POST /api/seen with invalid JSON returns 400 (3.525478ms)
|
||||
✔ POST /api/seen with a body over 4096 bytes returns 400 (or resets the connection) and writes no mark (2.617466ms)
|
||||
✔ POST /api/seen with a missing agent returns 400 (1.613299ms)
|
||||
✔ POST /api/board returns 405; PUT /api/seen returns 405 (2.065397ms)
|
||||
✔ CLI: scan --print marks a seen row with 's' and the summary line ends with 'N seen)' (54.790079ms)
|
||||
✔ page.html: seenControl() escapes rec.project/agent/lastActivity, and the POST uses a JSON content-type (0.36576ms)
|
||||
✔ page.html: has a collapsed Seen section that lists seen rows with the shared row builder (0.343455ms)
|
||||
✔ page.html: each project has a Hide seen checkbox (default on) beside Hide offline, with a hidden-count note (0.206124ms)
|
||||
✔ page.html: a project header reads "N of N" only while a checkbox hides rows (0.160804ms)
|
||||
✔ page.html: every row shows Task and Active project, derived or the word unknown, with the workspace in the detail (0.345793ms)
|
||||
✔ 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.628983ms)
|
||||
✔ 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 (34.931804ms)
|
||||
✔ POST /api/reply: a registration with a tmux socket adds -L <socket> (35.233392ms)
|
||||
✔ POST /api/reply: a non-zero tool exit is a 200 with delivered false, the exit code and the stderr verbatim (45.23328ms)
|
||||
✔ 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 (22.970179ms)
|
||||
✔ POST /api/reply: a missing agent-send.sh is a 500 with the path in the error, not a crash (6.70926ms)
|
||||
✔ replyToRow: DEFAULT_AGENT_SEND is the repository's tools/tmux/agent-send.sh and it is executable (0.244726ms)
|
||||
✔ 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.703156ms)
|
||||
✔ startServer: /api/board carries taskSetBy from a live registration and null for the derived rows (4.279429ms)
|
||||
✔ 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.433195ms)
|
||||
✔ 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 (11.556202ms)
|
||||
✔ Host/Origin guard: loopback names on this port are accepted, with or without a same-origin Origin (33.618842ms)
|
||||
✔ 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.280378ms)
|
||||
✔ every refusal code the reader can raise has an HTTP status (1.042543ms)
|
||||
✔ conversation routes: catalogue, first page, next page and follow over HTTP; refusals map to 4xx with their code; nothing is written (54.030753ms)
|
||||
ℹ tests 124
|
||||
ℹ suites 0
|
||||
ℹ pass 124
|
||||
ℹ fail 0
|
||||
ℹ cancelled 0
|
||||
ℹ skipped 0
|
||||
ℹ todo 0
|
||||
ℹ duration_ms 774.560773
|
||||
@@ -0,0 +1,160 @@
|
||||
✔ W1: two processes acquire the same pair at once; exactly one claim (366.928224ms)
|
||||
✔ W1: two writers publish the same revision at once: one wins, the other gets null, the winner's record stays (39.093165ms)
|
||||
✔ W1: a revision name appears only after its bytes are synced; before that, only a temp file exists (22.903774ms)
|
||||
✔ W2: acquire while a claim is reserved or active refuses already-active (542.049518ms)
|
||||
✔ W3: acquire while stopping, uncertain, or stopped without proof refuses unsafe-replacement (504.802304ms)
|
||||
✔ W4: same session with another seat tuple, and the reverse, both refuse; a loser on the seat key closes it no-unit (322.66265ms)
|
||||
✔ W4: a hard link of one session under another seat is the same session: the second controller refuses already-active and launches nothing (265.106214ms)
|
||||
✔ W4: a copy of one session under another seat is the same session: the second controller refuses already-active and launches nothing (240.449712ms)
|
||||
✔ W4: a session header ID that changes after construction refuses target; nothing is claimed or launched (2.957749ms)
|
||||
✔ W5: SIGKILL between every publication barrier of acquire and transition; restart never finds two holders or a lost claim (12666.098171ms)
|
||||
✔ W5: SIGKILL between every publication barrier of release; restart finishes or holds the release (30953.87984ms)
|
||||
✔ W6: controller killed mid-turn while the engine lives; restart is uncertain, no launch, prompts refuse (266.87467ms)
|
||||
✔ W12: a live owner paused with SIGSTOP; a second controller refuses already-active and changes nothing (145.296641ms)
|
||||
✔ W13: crash after the engine spawns, before active; restart finds the live unit: uncertain, no second spawn, force stop only (410.27827ms)
|
||||
✔ W14: crash after reservation, before the spawn marker: stopped with a no-unit observation; the pair is free (282.414513ms)
|
||||
✔ W20: crash after the spawn marker, scope collected; uncertain in both runs, the marker is copied, no launch until a boot proof (352.278489ms)
|
||||
✔ W15: crash between the two keys during release; restart finishes it under the same claim ID (119.390385ms)
|
||||
✔ W7: recorded boot ID differs on the same machine: stopped with a boot proof; open tool calls become uncertain (185.414736ms)
|
||||
✔ W8: resume after a proven stop with the same pins: new claim ID, generation +1, same conversation, branch and leaf (368.009297ms)
|
||||
✔ W9: resume with a changed binary, argv digest, branch or leaf is refused and the claim is unchanged (813.857743ms)
|
||||
✔ W11: the controller writes no session file; only the fake engine's own appends appear (185.527387ms)
|
||||
✔ W16: a highest revision that won't parse holds the pair uncertain; the older stopped revision is not reused (89.437752ms)
|
||||
✔ W17: a claim root copied from another host refuses foreign-host and promotes nothing (72.840588ms)
|
||||
✔ G1: a session path or claim root under .pi/state, ~/.claude, the data root or a registration refuses at construction (3.634554ms)
|
||||
✔ G2: a symlink inside the fixture root to a live session file is refused by the real-path check (1.002053ms)
|
||||
✔ G3: a fixture path swapped for a live path after construction is refused at bind (1.819507ms)
|
||||
✔ K1: force stop kills a tool child that called setsid; stopped with a verified proof (3075.872051ms)
|
||||
✔ K2: K1 on the process-group fallback ends uncertain, never stopped (550.816208ms)
|
||||
✔ K3: SIGTERM acknowledged while a member lives: stopping until the kill phase, never stopped from TERM (2938.878855ms)
|
||||
✔ K4: two engines; force stop one; the other survives by independent observation (5462.103605ms)
|
||||
✔ K5: a stop during a tool call leaves the effect uncertain, and it is shown (2612.388068ms)
|
||||
✔ K12: a member forking in a loop: the freeze stops it, enumeration is complete, populated 0 after cgroup.kill (2982.619477ms)
|
||||
✔ K13: a member writing its pid into another cgroup is refused by the namespace; the kill is complete (2432.457076ms)
|
||||
✔ K15: the shim gone, engine/cgroup.events unreadable, or the engine cgroup missing: evidence unavailable, not empty; uncertain (5115.442166ms)
|
||||
✔ K10: controller killed between the TERM and kill phases: restart checks the invocation ID and re-runs from TERM for the same stop (761.540049ms)
|
||||
✔ K11: controller killed after the confirmation is recorded, before TERM: restart checks the invocation ID and re-runs from TERM for the same stop (672.791692ms)
|
||||
✔ K14: a unit with the recorded name but another invocation ID: evidence unavailable, no signals, uncertain (615.943344ms)
|
||||
✔ K6: recover without proof, without confirmation, or with changed pins is refused (879.881573ms)
|
||||
✔ K7: recover after proof, then launch: new claim and execution, generation +1, same leaf; the cancelled prompt is not replayed (417.30095ms)
|
||||
✔ K8: an engine that loads another leaf on resume is refused before admission; it stays claimed until a proven stop (585.898732ms)
|
||||
✔ K9: an interrupt that never settles stays uncertain; force stop stays available; takeover is refused while fenced (3559.770078ms)
|
||||
✔ K16: a claim from another machine ID refuses foreign-host; no boot proof is issued (28.327037ms)
|
||||
✔ K17: two launcher calls with one eligibility record: one launch, the other refuses, no second engine (451.142569ms)
|
||||
✔ K18: the leaf changes after eligibility: launch refused; the reservation stays until released with proof (461.907538ms)
|
||||
✔ S1: `/goal x`, with leading spaces or a tab, refuses text-policy at admission; zero engine bytes (352.559273ms)
|
||||
✔ S2: every prefix pinned Pi interprets is refused, from the list the code uses; the rest reach the engine exactly (260.467726ms)
|
||||
✔ S3: `/goal` on the second line is pinned from the source: Pi checks only index 0, so it is admitted and sent exactly (246.161026ms)
|
||||
✔ S4: a `/` left in the composer is cleared when control transfers and returns; the next submit sends only the new text (338.624679ms)
|
||||
✔ S5: an observer terminal gets a paste then Enter, as send-message.sh does: not admitted: controller, nothing sent (240.108119ms)
|
||||
✔ S6: a mediated-shaped registration (no tmux) passed to the board's replyToRow: 409 no tmux session; exec never runs (0.597623ms)
|
||||
✔ S7: ESC, bracketed-paste markers and U+2028/U+2029 travel as one JSON string; the engine receives the exact text in one record (224.445395ms)
|
||||
✔ P3: a Pi confirm, select, input or editor dialog is shown disabled with a reason and never answered (365.969368ms)
|
||||
✔ E1: send, ack, user, toolCall, toolResult, final answer: shown once, no refresh, draft and reading position kept (254.094418ms)
|
||||
✔ E2: U+2028, U+2029 inside JSON strings and CRLF line ends each parse as one record, on the splitter and through the controller (226.861015ms)
|
||||
✔ E3: a multipart final, two blocks, null request correlation and duplicate delivery (223.090316ms)
|
||||
✔ 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 (280.697687ms)
|
||||
✔ E4: a gap or a new epoch also reconciles; nothing is concatenated across a gap (11.533788ms)
|
||||
✔ E5: an unknown native event gives no client event; evidence records its type and bytes; the terminal count goes up (257.59532ms)
|
||||
✔ E6: a tool result delayed across a pause and a reconnect is reconciled without a manual refresh (295.516735ms)
|
||||
✔ E7: the terminal renders the same stream as the library client, as observer and then as controller, and submits only as controller (312.122245ms)
|
||||
✔ terminal: engine control characters are made visible; a lost connection refuses submit (255.96624ms)
|
||||
✔ terminal: outcome unknown is shown as such, with no resend offer, and nothing is resent (0.511256ms)
|
||||
✔ terminal: text after Enter in the same input chunk starts the next message; it never joins the one submitted (0.298392ms)
|
||||
✔ terminal: a paste-start marker split right after its ESC still opens the paste; the Enter inside it never submits (0.366575ms)
|
||||
✔ terminal: invisible and bidi characters are made visible; head, status and notice lines stay one line (0.137018ms)
|
||||
✔ every record these fixtures produced is a valid CHAT-01 record (E5: no record fails the schema) (313.570237ms)
|
||||
✔ H1: two takeovers with the same expected generation: one wins, +1; the other refuses generation (334.013214ms)
|
||||
✔ H2: the old controller's prompt after a takeover commits is refused with zero engine bytes (339.086815ms)
|
||||
✔ H3: a takeover while a prompt holds the dispatch lock: written under the old actor, or refused; never both (963.27244ms)
|
||||
✔ H4: self-takeover is refused (238.984643ms)
|
||||
✔ H9: Interrupt racing a prompt's dispatch: before the write, dispatch-refused and no-turn; after, §3 rules (489.013009ms)
|
||||
✔ H10: Interrupt and force stop together: one stop chain, force stop supersedes (1350.016488ms)
|
||||
✔ H10: an overlap during the pause before the abort: no abort, the stop ends uncertain (277.044896ms)
|
||||
✔ H10: a no-turn Interrupt lifts only its own fence; admission stays closed under force stop, overlap or revocation (903.141931ms)
|
||||
✔ H11: the controller disconnects mid-turn: work continues, the claim is unchanged, control stays put (381.724817ms)
|
||||
✔ H12: an exact retry after reconnecting to the same incarnation returns the same receipt; one dispatch (216.486779ms)
|
||||
✔ H13: a retry with the same request ID and different text is refused (245.581951ms)
|
||||
✔ H14: late stdout from the old engine after a replacement is dropped by incarnation, counted, never rendered (729.986176ms)
|
||||
✔ H15: a revoked connection's command is refused; the revocation fence holds (650.617325ms)
|
||||
✔ H16: a second controller for the same session refuses already-active; the first is untouched (277.608467ms)
|
||||
✔ H10: a second force stop while the first escalation runs refuses fenced; one escalation, and the claim records only the first stop's phases (1252.558084ms)
|
||||
✔ H17: a confirmation reused, answered from another connection, or used after the stop changed is refused (1188.412704ms)
|
||||
✔ H18: two prompts before any native output: the second refuses busy; one engine write (271.516375ms)
|
||||
✔ H19: the pipe fails mid-line under a large prompt: delivery-unknown transport-unknown, poisoned, no later write (396.880958ms)
|
||||
✔ H19: the link itself never writes again after an unknown outcome, whoever calls it (0.859705ms)
|
||||
✔ H19: the controller dies mid-write of a large line: after restart the outcome is unknown and nothing is resent (724.938928ms)
|
||||
✔ H20: the line is written but the ack is lost when the controller dies: orphan, outcome unknown, nothing resent (798.741876ms)
|
||||
✔ H21: a retry of the exact request with the old token after a crash is stale-incarnation; no second write (925.243776ms)
|
||||
✔ H22: after H21 and a valid recovery, a new request with the new token is admitted (2958.527643ms)
|
||||
✔ H23: requests pending at a restart are not resent; each shows outcome unknown (700.652019ms)
|
||||
✔ a plain conversation: catalogue row, one page, CHAT-01 records (6.875614ms)
|
||||
✔ native entries map to blocks: tools, thinking, bash, notices, ids that do not fit (1.974279ms)
|
||||
✔ F1: a malformed line is an unavailable part at its position, and reading continues (2.412892ms)
|
||||
✔ F1: a missing parent stops the history with a notice that names the unreadable lines (3.648476ms)
|
||||
✔ F1: an unreadable fork is never merged into another branch's history (1.996645ms)
|
||||
✔ F1: a follow stays on its branch when the next entry's parent is unreadable (2.144196ms)
|
||||
✔ F1: a file whose entries are all unreadable shows a notice per line (0.973217ms)
|
||||
✔ F2: a truncated trailing line marks the view incomplete, not an error (1.342077ms)
|
||||
✔ pagination: 100 parts, then the rest; parts concatenate to the whole branch (4.217095ms)
|
||||
✔ F3: a replaced file (new inode) refuses old cursors with reconcile (4.361039ms)
|
||||
✔ F4: a same-inode rewrite of the prefix refuses old cursors with reconcile (5.856576ms)
|
||||
✔ F5: growth between pages keeps the epoch and the page stops at the pinned length (5.198373ms)
|
||||
✔ F6: unknown, foreign and expired cursors refuse and leave the cursor usable (8.623027ms)
|
||||
✔ F7: a symlinked file and a symlinked directory component are refused, never opened (9.408628ms)
|
||||
✔ F8: a file swapped for a symlink after the catalogue is refused (2.534355ms)
|
||||
✔ F9: registrations never add or redirect a root (1.900641ms)
|
||||
✔ F10: a header cwd naming another project is refused (5.34781ms)
|
||||
✔ F11: parentSession renders with a marker and the parent is never opened (1.297027ms)
|
||||
✔ F12: two leaves: the default leaf is shown and the other branch reads alone (5.378605ms)
|
||||
✔ F12: a follow refuses when an appended duplicate id changes the branch's earlier parts (2.400711ms)
|
||||
✔ F12: a second root (Pi's resetLeaf) starts its own branch (1.357901ms)
|
||||
✔ F13: compaction is a marker in place, then the retained content (0.791328ms)
|
||||
✔ F14: long strings split into fragments and parts, reassemble exactly, and pages respect the byte cap (869.46465ms)
|
||||
✔ fragments never cut a surrogate pair and keep an empty string (5.322608ms)
|
||||
✔ F15: a Claude seat is an unsupported-harness placeholder whose directory is never read (1.694545ms)
|
||||
✔ unknown conversations, empty files and non-Pi files refuse (2.993734ms)
|
||||
✔ an unreadable file or root inside the roots is refused per row, not a failed catalogue (1.835641ms)
|
||||
✔ a seat directory without search permission refuses that root, not the catalogue (3.409315ms)
|
||||
✔ every page and cursor is a valid CHAT-01 record (1171.522693ms)
|
||||
✔ the engine pin holds for the installed package (2.969351ms)
|
||||
✔ pinned Pi, sealed and without credentials, answers the controller's commands with the shapes the fake models (339.716856ms)
|
||||
✔ pinned Pi appends thinking_level_change at start when the branch lacks one, so the leaf moves (K8 then fails closed) (465.727325ms)
|
||||
✔ N25: ordinary Interrupt reconciles; a non-empty queue_update in the window is O5 (689.187817ms)
|
||||
✔ N1: an extension's follow-up queued after the fence is cleared before any abort; O5, Unknown (514.977427ms)
|
||||
✔ N1: a follow-up queued before the fence is O5 at once; the Interrupt refuses fenced (292.410254ms)
|
||||
✔ N2: with abort first, the fake runs the external item (the ordering guard has teeth) (23.468859ms)
|
||||
✔ N3: the fence lands in preflight, preflight errors, no run: failed, No run, uncertain (443.221347ms)
|
||||
✔ N4: the ack arrives after the first abort and a run starts: clear and abort again; Interrupted (247.308821ms)
|
||||
✔ N5: an input handler takes the prompt: ack, no run, delivery-unknown handled-without-run (322.736492ms)
|
||||
✔ N6: an extension queues between clear_queue and abort: O5 and O6, Unknown (551.870499ms)
|
||||
✔ N7: clear_queue times out: no abort, nativeQueue unknown, force stop still ends it (1961.565503ms)
|
||||
✔ N7: clear_queue answers an error: no abort, nativeQueue unknown, the link not poisoned (244.212591ms)
|
||||
✔ N8: an extension prompt starts a run during Mosaic preflight; the losing settle is O3 (290.90596ms)
|
||||
✔ N9: a run that started before the fence and ends aborted: failed interrupted, Interrupted (246.059572ms)
|
||||
✔ N9: decision 34: a run that ends aborted with no stop in progress: aborted-without-stop, uncertain, outcome unknown (289.589211ms)
|
||||
✔ N9: an aborted that lands after the fence but before any abort is written: aborted-without-stop, Unknown (501.30469ms)
|
||||
✔ N10: fake conformance (34.023867ms)
|
||||
✔ N11: the run fails before any user message_start: delivery-unknown ack-without-start, never failed (831.090384ms)
|
||||
✔ N12: input that starts a run after the final empty clear is O1 and not part of the stop's proof (361.055636ms)
|
||||
✔ N13: agent_start with no slot held is O1; a later prompt refuses with zero engine bytes (358.002753ms)
|
||||
✔ N14: the run completes while clear_queue is in flight: finished, Completed first, uncertain (589.637419ms)
|
||||
✔ N14: the run completes after the abort is written, before Pi applies it: finished, never relabelled (581.06703ms)
|
||||
✔ N15: the fence lands in preflight, then an input handler takes it: handled-without-run, No run (562.073768ms)
|
||||
✔ N16: Interrupt with no slot and no run refuses no-turn: no stop, no bytes, admission open (264.022573ms)
|
||||
✔ N17: the run fails on its own during the exchange: failed, Failed on its own (462.86689ms)
|
||||
✔ N18: no final assistant message_end, or a lost line: working stays working; before working, transport-unknown (860.27459ms)
|
||||
✔ N19: a losing extension prompt settles inside the Mosaic run before its user message: O3, run-overlap (580.591412ms)
|
||||
✔ N20: an extension triggerTurn during Mosaic preflight starts first; while streaming it queues with no signal (480.465854ms)
|
||||
✔ N21: a losing settle after the receipt settled finished is O2; the receipt stays finished (339.574789ms)
|
||||
✔ N22: an agent-level custom message is dropped by the clear with no signal; evidence names the seal (338.952874ms)
|
||||
✔ N23: a nextTurn message survives clear and abort and attaches to the next prompt, with no signal (233.893576ms)
|
||||
✔ 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 (240.739111ms)
|
||||
ℹ tests 152
|
||||
ℹ suites 0
|
||||
ℹ pass 152
|
||||
ℹ fail 0
|
||||
ℹ cancelled 0
|
||||
ℹ skipped 0
|
||||
ℹ todo 0
|
||||
ℹ duration_ms 51185.439363
|
||||
@@ -0,0 +1,186 @@
|
||||
✔ approvals: a request is validated before anything is posted; the rendering shows names and never ids (3.30556ms)
|
||||
✔ approvals: the ledger is appended and folded into open requests with bind and approval states (1.570005ms)
|
||||
✔ approvals: a reply approves only when it points at a request, says exactly approve, and comes from a listed approver once (0.647254ms)
|
||||
✔ approvals: a button approves only on its own request message with the matching custom id (0.487432ms)
|
||||
✔ approvals flow: a turn that opened a request posts the message with the button, records it, binds it, and both approvers approve (17.371847ms)
|
||||
✔ approvals flow: a non-approver, a repeat, a wrong custom id and a service refusal each get their fixed line and a drop entry (9.276715ms)
|
||||
✔ approvals flow: an invalid request from the model, a refused post, and no api client are recorded and post nothing (7.081808ms)
|
||||
✔ approvals flow: start retries a bind and an approval left as unknown, under their original keys (2.09649ms)
|
||||
✔ authorize: open channel, listed user (3.676715ms)
|
||||
✔ authorize: wrong guild (0.208021ms)
|
||||
✔ authorize: no guild (DM) (0.188467ms)
|
||||
✔ authorize: unlisted channel (0.18996ms)
|
||||
✔ authorize: unknown channel, no info (0.40522ms)
|
||||
✔ authorize: thread of listed parent (0.205975ms)
|
||||
✔ authorize: thread of unlisted parent (0.173754ms)
|
||||
✔ authorize: text channel that is not a thread and not listed (0.145406ms)
|
||||
✔ authorize: unlisted user (0.174784ms)
|
||||
✔ authorize: no author (0.408525ms)
|
||||
✔ authorize: bot author (listed id, bot flag) (0.129557ms)
|
||||
✔ authorize: system author (0.168624ms)
|
||||
✔ authorize: the bot itself (0.099418ms)
|
||||
✔ authorize: webhook (0.09274ms)
|
||||
✔ authorize: mention channel without mention (0.117129ms)
|
||||
✔ authorize: mention channel with bot mention (0.133164ms)
|
||||
✔ authorize: mention channel with @everyone only (0.094295ms)
|
||||
✔ authorize: mention channel mentioning someone else (0.074797ms)
|
||||
✔ authorize: mention channel, content says @bot but mentions empty (0.094055ms)
|
||||
✔ authorize: private thread under mention channel, mentioned (0.0797ms)
|
||||
✔ authorize: private thread under mention channel, not mentioned (0.068534ms)
|
||||
✔ authorize: thread in another guild per channel info (0.069749ms)
|
||||
✔ authorize: not an object (0.063066ms)
|
||||
✔ authorize: no id (2.317016ms)
|
||||
✔ authorize: oversize content is accepted and flagged (0.100512ms)
|
||||
✔ authorize: exactly the limit is not oversize (0.06151ms)
|
||||
✔ authorize: a user's channel allowlist drops them outside it, threads count as the parent, others are unaffected (0.514873ms)
|
||||
✔ authorize: order puts wrong guild before user, and user before channel (no channel lookup for strangers) (0.162041ms)
|
||||
✔ binding: a complete binding validates and is frozen (2.678019ms)
|
||||
✔ binding: unknown key, missing field, wrong type refuse with exit 2 (1.292634ms)
|
||||
✔ binding: empty allowlists refuse (0.395772ms)
|
||||
✔ binding: a user's channel allowlist must be non-empty, listed and unique; absent means every listed channel (1.460534ms)
|
||||
✔ reloadDiff: reloadable keys are summarised by id; every fixed key refuses with exit 2 (2.072326ms)
|
||||
✔ binding: dmRecipient is optional, a snowflake, one of the listed users, and a fixed key (0.829546ms)
|
||||
✔ binding: file must be 0600, regular, not a symlink (3.695384ms)
|
||||
✔ binding: token file mode, symlink, emptiness and shape are checked; token never appears in errors (3.125574ms)
|
||||
✔ cli: check refuses a non-0600 token file with exit 2 before any network use (89.43539ms)
|
||||
✔ context files: absolute paths, traversal, symlinks and out-of-repo targets refuse; in-repo files resolve (2.535478ms)
|
||||
✔ cli: check refuses a missing context file and a missing binding with exit 2; usage is exit 4 (501.87896ms)
|
||||
✔ cli: reload validates the file first (exit 2), then needs a live owner (exit 1); usage is exit 4 (351.757699ms)
|
||||
✔ cli: run refuses when STOP is present, before any network use (290.165911ms)
|
||||
✔ binding: tools is optional, validated strictly, a fixed key for reload, and its roots are resolved against the data root (3.754926ms)
|
||||
✔ binding: a git key is validated at load and reaches the extension whole, and only on a writable root (1.998397ms)
|
||||
✔ 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 (18.999258ms)
|
||||
✔ delivery: refused and unknown outcomes are journaled; a later chunk is not sent after a failure (17.07216ms)
|
||||
✔ delivery: restart with an unknown entry re-sends the same nonce once and reconciles before accepting traffic (1.420386ms)
|
||||
✔ delivery: an unknown entry older than the dedupe window is marked refused, not re-sent; a still-unknown one refuses start (1.100597ms)
|
||||
✔ delivery: repeated unknown reconciliations never refresh the dedupe window; the original intent time decides (1.177542ms)
|
||||
✔ turn: a failed engine turn posts the fixed line, never model output, and writes a failed record (2.756928ms)
|
||||
✔ turn: a second message during a turn is held by the engine, both get their own reply and record (33.594306ms)
|
||||
✔ turn: a thread under a listed channel is answered in the thread; an unknown thread is looked up once (3.043242ms)
|
||||
✔ drop: an unlisted user gets silence and one drop line; no inbox entry, no REST call, no engine call (1.119759ms)
|
||||
✔ drop: an oversize message is accepted into the inbox, answered with the fixed line and journaled as a drop (0.915495ms)
|
||||
✔ restart: an inbox with three ids and a replay of the same three produces zero turns (42.686556ms)
|
||||
✔ stop: STOP present refuses start; STOP written while running refuses new turns and the current one finishes (32.574885ms)
|
||||
✔ ceiling: the ceiling plus one is refused and journaled; one fixed line per UTC day; a new day accepts again (7.382548ms)
|
||||
✔ ceiling: a burst arriving while turns are still running cannot queue past the ceiling (4.44967ms)
|
||||
✔ ceiling: a turn interrupted by a crash still counts after restart; admissions are durable (3.296315ms)
|
||||
✔ ceiling: the daily notice survives a same-day restart; one delivery attempt in total, even when the first attempt crashed mid-flight (6.264071ms)
|
||||
✔ duplicate: the same event delivered twice while the thread lookup is held yields one prompt, one admission and one reply (3.449145ms)
|
||||
✔ journal: no token-shaped string and no model output on the drop path reaches disk (0.790787ms)
|
||||
✔ 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 (3.599111ms)
|
||||
✔ receipt: Discord refusing the reaction leaves the turn intact and records ok false (2.515266ms)
|
||||
✔ reload: a new user is silent before and answered after; a removed channel goes silent; a lower ceiling applies at once (4.616309ms)
|
||||
✔ reload: a fixed key refuses with exit 2 and the old binding stays in force (2.647835ms)
|
||||
✔ tools: with a tools binding the turn record lists every read and its outcome; without one the field is null (4.331026ms)
|
||||
✔ context: the Discord block names the server, channels and modes, and states the rules from Q15 and Q16 (2.548305ms)
|
||||
✔ context: with tools the block names the roots, keeps file content as data, and says to state refusals plainly (0.806956ms)
|
||||
✔ context: a writable root adds the write rules and says a write is real only once Jason commits (1.861674ms)
|
||||
✔ context: the envelope is one bracketed line then the text; names cannot break the line (1.359756ms)
|
||||
✔ context: a git root swaps the terminal-commit line for the git verbs, and a vault root adds the id protocol (1.52254ms)
|
||||
✔ context: assembleContext concatenates files in launcher format and appends the block; sha256 is stable (1.414285ms)
|
||||
✔ context: splitReply keeps paragraphs together under the limit and splits long ones at lines, spaces, then hard (0.647646ms)
|
||||
✔ engine: buildPiArgs carries the fixed flags, engine settings, session dir and prompt file (2.406205ms)
|
||||
✔ engine: with tools, buildPiArgs turns pi's own tools off, loads the extension explicitly and allowlists exactly our three (0.407002ms)
|
||||
✔ engine: a run with tool turns settles once, on the answer, with every tool call in the result (62.210504ms)
|
||||
✔ engine: a run that ends on a tool-only turn fails the prompt as empty; a retried run settles on the real end (41.029075ms)
|
||||
✔ engine: one prompt, one turn, text and usage come back (53.820075ms)
|
||||
✔ engine: a prompt while streaming is held until pi settles, then sent as its own run, and answered in order (354.233703ms)
|
||||
✔ engine: a held prompt that times out before pi settles fails on its own and is never sent (264.785114ms)
|
||||
✔ engine: timeout sends abort and fails only that turn; the process stays (121.168917ms)
|
||||
✔ engine: tool events from a run that outlived its timeout never land in the next prompt's record (277.171717ms)
|
||||
✔ engine: a prompt after a turn that timed out before its agent_start waits for pi to settle instead of being refused (158.997315ms)
|
||||
✔ 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 (215.616932ms)
|
||||
✔ engine: a timed-out turn pi starts only after the grace never answers a later prompt (614.286838ms)
|
||||
✔ engine: late events of a run past its grace, before pi exits, answer nothing and nothing more is sent (early prompt response) (1.417736ms)
|
||||
✔ engine: late events of a run past its grace, before pi exits, answer nothing and nothing more is sent (late prompt response) (0.455254ms)
|
||||
✔ engine: a timed-out run pi did start outlives the grace; the next prompt goes out when it ends (427.744815ms)
|
||||
✔ engine: a malformed JSONL line fails the turn, not the process (25.344053ms)
|
||||
✔ engine: a turn that ends in error rejects with the error code; process exit fails pending turns (44.663653ms)
|
||||
✔ gateway: hello -> identify with intents, ready, heartbeat with jitter, ack (2.462075ms)
|
||||
✔ gateway: missed ack closes the socket and resumes with the last sequence (1.806422ms)
|
||||
✔ gateway: op 7 reconnect resumes; op 9 non-resumable re-identifies (0.633551ms)
|
||||
✔ gateway: op 9 resumable resumes (0.539501ms)
|
||||
✔ gateway: close 4014 is fatal, reports the missing intent, never reconnects (0.784361ms)
|
||||
✔ gateway: 4004 and 4013 are fatal too; 1006 reconnects with identify when no session (0.708919ms)
|
||||
✔ gateway: close() is final and unparseable frames are ignored (0.627142ms)
|
||||
✔ git: config validation is strict, needs write: true, a work tree and a private token file (58.774185ms)
|
||||
✔ git: the child environment drops every host git config, names one helper, and carries the token path only for origin (41.09462ms)
|
||||
✔ git: status reports the branch, ahead/behind and changed paths, and refuses off the named branch or mid-merge (128.590736ms)
|
||||
✔ git: parseStatus reads porcelain v2 including renames and conflicts (0.476101ms)
|
||||
✔ git: a commit stages exactly the named files, carries the seat author and the requester trailer, and pushes at once (127.537922ms)
|
||||
✔ git: commit refusals: message, paths, requester, nothing to commit, and an index that already holds other work (221.213798ms)
|
||||
✔ git: a commit whose push fails is still a commit, says so, and the next commit's push carries both (D6) (236.019056ms)
|
||||
✔ git: pull is fast-forward only; a diverged origin or dirty local files refuse with nothing merged (487.643115ms)
|
||||
✔ git: push pushes the named branch only and reports up to date (91.790307ms)
|
||||
✔ git: no token value or token path ever reaches a git argument list; outputs are masked and capped (93.185619ms)
|
||||
✔ git: the credential helper answers get over https from a private file and nothing else (216.017392ms)
|
||||
✔ git: the vault protocol validates before a commit, honours another owner's lock, reserves ids, and locks around writes (737.247015ms)
|
||||
✔ lock: the claim is exclusive; a second start against a live owner refuses (5.952379ms)
|
||||
✔ lock: a stale lock (dead owner, reused pid, or record without start) refuses run and is never signaled; only unlock clears it (75.442042ms)
|
||||
✔ lock: an incomplete claim (directory without owner record) is busy and refuses run; unlock clears it (0.726436ms)
|
||||
✔ lock: an owner record that exists but cannot be read is invalid: never signaled, never removed, never claimed over (2.710958ms)
|
||||
✔ lock: legacy upgrade; a live connector holding a {pid, start} record is unknown, unlock refuses and nothing changes; after it exits, unlock clears it (62.258522ms)
|
||||
✔ 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 (476.231867ms)
|
||||
✔ lock: identity syntax; only canonical unsigned decimal start ticks and lowercase boot uuids are identities (0.810492ms)
|
||||
✔ lock: a process whose start marker or boot id cannot be read refuses to claim (49.81495ms)
|
||||
✔ lock: a live pid whose identity cannot be read right now is unknown: never signaled, never removed, never claimed over (4.254546ms)
|
||||
✔ lock: four processes racing for the same binding; exactly one claims it and the others refuse (98.186867ms)
|
||||
✔ lock: stale handoff; concurrent starts over a stale lock all refuse, nothing reclaims, one unlock then exactly one live owner (261.39297ms)
|
||||
✔ lock: four-party schedule; claims landing inside an unlock's gap never survive, one unlock leaves no owner and no residue (189.602729ms)
|
||||
✔ notices: a kind is recorded per UTC day and found again (0.872726ms)
|
||||
✔ notify: the DM channel opens once, every send carries the nonce, and only the message id comes back (4.954714ms)
|
||||
✔ notify: refusals and unknowns surface as RestOutcome without ids; a refused send reopens the channel next time (1.924999ms)
|
||||
✔ notify: no dmRecipient, a non-0600 binding or token, or a missing binding refuse before any network use (1.597964ms)
|
||||
✔ recover: nothing to do is clean; a lock whose owner is gone or that has no record is cleared and STOP ends up absent (38.82067ms)
|
||||
✔ recover: an operator STOP refuses with exit 3 and is never removed, whatever the lock says (36.200206ms)
|
||||
✔ recover: a brake written during the unlock wins; STOP stays with both lines and the start is refused (53.157519ms)
|
||||
✔ recover: a held binding refuses with exit 3 and writes no STOP: live owner, alive pid without verifiable identity, unreadable record (100.977523ms)
|
||||
✔ cli: recover exits 0 when ready, 3 behind a brake or a held binding, and run's own STOP refusal is 3 (1154.269076ms)
|
||||
✔ rest: createMessage sends nonce, enforce_nonce, empty allowed_mentions and a soft reply reference (2.410433ms)
|
||||
✔ rest: 429 waits retry_after and retries; 4xx is refused; 5xx and socket errors are unknown (3.954227ms)
|
||||
✔ rest: content and nonce limits are enforced locally; typing never throws (1.166406ms)
|
||||
✔ rest: react PUTs the encoded emoji on the inbound message for @me; 2xx is true, anything else is false and never throws (0.771431ms)
|
||||
✔ rest: createDm posts the recipient and resolves the channel id; 4xx refused, 5xx unknown, bad id never sent (0.876233ms)
|
||||
✔ setspark config: a bare https or loopback origin, a private key file, a principal (5.974428ms)
|
||||
✔ setspark config: reaches the tools config and the binding as a fixed key (2.950589ms)
|
||||
✔ setspark config: the binding's key survives resolveToolRoots and the engine's JSON hand-off to the extension (1.409577ms)
|
||||
✔ setspark config: approvers come from the binding's users, never from the binding's setspark key (1.61629ms)
|
||||
✔ setspark verbs: required_approvers go out as discord ids from names and come back as names (19.615454ms)
|
||||
✔ setspark verbs: no Discord user id reaches tool text, whatever shape the service returns it in (7.461312ms)
|
||||
✔ setspark contract: a decision made with names opens a request the connector accepts; names stored by an old record still refuse (5.264184ms)
|
||||
✔ setspark keys: read per call, one printable token per file, rotation without a restart (2.464963ms)
|
||||
✔ setspark idempotency keys: principal, turn id, call index; connector keys name a step (0.491046ms)
|
||||
✔ setspark http core: json in and out, bearer header, idempotency header, fixed user agent, no key anywhere else (3.075832ms)
|
||||
✔ setspark http core: error bodies become fixed refusals with code and the 409 fields; server text is data, cut (1016.278029ms)
|
||||
✔ setspark verbs: a setspark key enables the eight verbs and no counters (0.852283ms)
|
||||
✔ 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 (31.796702ms)
|
||||
✔ setspark verbs: no turn refuses every write before any request; bad arguments refuse before any request; reads still work (6.636421ms)
|
||||
✔ setspark verbs: renderRecord caps long output and hides the accepted snapshot (0.474284ms)
|
||||
✔ setspark api: bind, add_approval (button and reply) and get use integer request ids and the connector's keys (9.083242ms)
|
||||
✔ tools: config refuses a missing, symlinked, dotted, non-directory or duplicate root and bad limits (3.662943ms)
|
||||
✔ tools: every escape is refused with a fixed reason and nothing outside the root is read (3.042075ms)
|
||||
✔ tools: happy paths list, read a window, and search case-insensitively; dotfiles and symlinks never appear (4.050779ms)
|
||||
✔ tools: the tool set renders text for the model, records details for the journal, and enforces the per-run budget (2.896622ms)
|
||||
✔ tools: listing and search caps hold (10.266894ms)
|
||||
✔ tools: credential shapes are caught; ordinary prose and ids are not (0.853382ms)
|
||||
✔ 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 (4.582953ms)
|
||||
✔ tools: an unreadable file under the root is skipped by search and refused by read (0.987122ms)
|
||||
✔ tools: config accepts write: true only as a boolean, and enables the write tools only then (1.046852ms)
|
||||
✔ tools: every write outside the fence is refused before any byte lands, and no temp file remains (4.759934ms)
|
||||
✔ tools: write_file leaves the exact bytes, edit_file replaces one exact match, and the set renders the change as uncommitted (2.765593ms)
|
||||
✔ tools: a target that changed between the check and the rename is refused and the temp file is removed (1.666132ms)
|
||||
✔ web: config takes an https or loopback-http SearXNG base url and a bounded fetch cap (2.483424ms)
|
||||
✔ web: address rules refuse every private, loopback, link-local, mapped and multicast form (1.90685ms)
|
||||
✔ web: web_fetch refuses bad urls, private hosts, rebinding names, non-https redirects, too many hops, error status, non-text bodies, and times out (1024.220555ms)
|
||||
✔ web: web_fetch returns html as text with the title, follows an https redirect, keeps plain text and json, and cuts at the cap (15.168569ms)
|
||||
✔ web: html to text drops scripts, styles and comments, decodes entities and keeps block breaks (0.539613ms)
|
||||
✔ 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 (13.220746ms)
|
||||
✔ web: the tool set enables the web tools only with a web key, counts them in the budget, and records url, status and hits (6.399067ms)
|
||||
ℹ tests 178
|
||||
ℹ suites 0
|
||||
ℹ pass 178
|
||||
ℹ fail 0
|
||||
ℹ cancelled 0
|
||||
ℹ skipped 0
|
||||
ℹ todo 0
|
||||
ℹ duration_ms 2785.874422
|
||||
@@ -0,0 +1,53 @@
|
||||
✔ sessionModel: agent vars win, then the system's execution settings (9.863443ms)
|
||||
✔ a pi bundle: prompt, policy, tools and manifest, 0600 in a 0700 directory (4.904843ms)
|
||||
✔ a claude-code bundle adds the wrapped gate hook and the MCP config (2.507558ms)
|
||||
✔ a bundle is written once: an existing file refuses (2.342637ms)
|
||||
✔ a path with a single quote can't go into the hook command (2.404071ms)
|
||||
✔ allow exits 0, a deny exits 2 with the reason on stderr (119.133617ms)
|
||||
✔ a missing or wrong policy, or a bad event, exits 2 (89.62829ms)
|
||||
✔ the bundle's wrapped command: a missing gate or node still blocks (1090.433581ms)
|
||||
✔ claude: typed tools through MCP, the hook blocks, builtins outside --tools don't exist (764.522718ms)
|
||||
✔ claude: the hook alone blocks a path outside the workspace (429.718725ms)
|
||||
✔ claude: a second turn resumes the first turn's session (697.342346ms)
|
||||
✔ claude: a missing hook or MCP file refuses before claude starts (7.462303ms)
|
||||
✔ pi: policy tools and typed tools pass, anything else is blocked (3.652974ms)
|
||||
✔ claude: builtins map from pi names, typed tools need the mcp prefix (1.053779ms)
|
||||
✔ file tool paths must resolve inside the workspace (1.386549ms)
|
||||
✔ pi's own path normalisation can't be used to step out (1.267113ms)
|
||||
✔ a symlink inside the workspace that points out is outside (1.233345ms)
|
||||
✔ claude path fields per tool (1.085399ms)
|
||||
✔ glob patterns stay inside the workspace (1.257359ms)
|
||||
✔ a path that can't be checked is blocked (0.780884ms)
|
||||
✔ initialize, ping and tools/list (45.612153ms)
|
||||
✔ tools/call goes through the tool socket; a refusal is an isError result (35.942819ms)
|
||||
✔ unknown tools and methods are JSON-RPC errors and never reach the socket (36.869003ms)
|
||||
✔ a missing argument is a usage error (31.167661ms)
|
||||
✔ pi: typed tools reach the socket, the gate blocks, agent_end writes the marker (362.276951ms)
|
||||
✔ pi: a missing extension refuses before any model call (7.449552ms)
|
||||
✔ pi: an extension without its configuration fails pi's start (260.274424ms)
|
||||
✔ founderCheck: founder variables, then a needed service without a usable token (1.668322ms)
|
||||
✔ turnRequest names the sender, class, reply and decision (0.273454ms)
|
||||
✔ a message becomes a turn, the answer goes back as a RESULT, SIGTERM releases and exits 0 (245.265643ms)
|
||||
✔ a SIGTERM before the claim stops the runner with exit 0 and no claim (105.218887ms)
|
||||
✔ typed tools carry the runner's capability; launch goes to the host's launch socket (395.154351ms)
|
||||
✔ a RESULT gets no automatic reply; failed turns reply with the reason (1288.799719ms)
|
||||
✔ SIGTERM during a turn kills the turn's process group and still exits 0 (150.812622ms)
|
||||
✔ founder credentials stop before the claim (20) (163.622892ms)
|
||||
✔ a refused claim exits 21; an ended run's capability exits 22 (193.590834ms)
|
||||
✔ the launch ending under a running session exits 22 (142.09675ms)
|
||||
✔ a broker that stays unreachable exits 23 after brokerRetries polls (280.860796ms)
|
||||
✔ a broker that is down at the claim exits 23, not 21 (88.821778ms)
|
||||
✔ no capability, or a malformed one, on stdin exits 2 (155.374009ms)
|
||||
✔ the PM gets launch, its task verbs and the reads (9.166295ms)
|
||||
✔ a coder gets no launch, no resolve_decision, and no task tools without a tracker (2.866852ms)
|
||||
✔ launch only when the business's launch block names the instance as launcher (2.365004ms)
|
||||
✔ an action outside the instance's authority has no tool (2.33749ms)
|
||||
✔ callTool: one JSON line out, the result back, a refusal rejects (8.138127ms)
|
||||
ℹ tests 45
|
||||
ℹ suites 0
|
||||
ℹ pass 45
|
||||
ℹ fail 0
|
||||
ℹ cancelled 0
|
||||
ℹ skipped 0
|
||||
ℹ todo 0
|
||||
ℹ duration_ms 10300.527757
|
||||
@@ -0,0 +1,86 @@
|
||||
✔ a raw token file, with or without one trailing newline, reaches curl only through the config stream (114.436722ms)
|
||||
✔ the raw path accepts nothing else, and refuses before curl runs (377.041391ms)
|
||||
✔ the file checks still apply on the raw path: mode, symlink, missing, directory (304.201805ms)
|
||||
✔ the raw path base URL has no override (66.337482ms)
|
||||
✔ the JSON path is unchanged, and JSON never falls through to the raw path (253.125376ms)
|
||||
✔ a file that changes between the two reads refuses before curl runs, with or without a body (761.646883ms)
|
||||
✔ the token reaches no child environment, even with an inherited CFG or SHELLOPTS=allexport (687.660023ms)
|
||||
✔ real helper GET HTTP 200 preserves exit 0 without credentials (14.229594ms)
|
||||
✔ real helper POST HTTP 201 preserves exit 0 without credentials (10.603237ms)
|
||||
✔ real helper GET HTTP 403 preserves exit 1 without credentials (8.102968ms)
|
||||
✔ fixture git subjects only, follow-ups and three session kinds (167.878949ms)
|
||||
✔ text and JSON carry same numbers, open and truncated title (229.73911ms)
|
||||
✔ missing credentials exit 2, no-issues never calls API and shows unknown (196.523617ms)
|
||||
✔ empty range gives no rows and zero totals (162.150955ms)
|
||||
✔ inclusive UTC dates, first-line preamble only, role and seat boundaries (162.339817ms)
|
||||
✔ close-only issue included, even median, missing metadata stays unknown (136.236958ms)
|
||||
✔ unique commits but per-issue links count multiple tags once each (153.608813ms)
|
||||
✔ page cap refuses rather than silently undercounting (190.21276ms)
|
||||
✔ bad API payload not JSON refuses (138.853987ms)
|
||||
✔ bad API payload {} refuses (136.396639ms)
|
||||
✔ bad API payload [{"number":1}] refuses (128.488652ms)
|
||||
✔ partial or malformed session log refuses with location, not content (148.218826ms)
|
||||
✔ a U+2028 or U+2029 inside a session string is one line, not a malformed record (152.576029ms)
|
||||
✔ no sessions is an empty table; symlink source refuses (213.349624ms)
|
||||
✔ reads only refactor even when another branch is checked out (158.481962ms)
|
||||
✔ invalid dates, reverse dates and duplicate options refuse (113.994757ms)
|
||||
✔ T3 agent assignments do not count as human in Table 2 (144.192731ms)
|
||||
✔ preamble parsing and issue number boundaries (0.467113ms)
|
||||
✔ T3 header: agent, or board from control-board; anything short of the full header is human (0.151781ms)
|
||||
✔ no closed issues with human messages means undefined ratio, not invented zero (0.195161ms)
|
||||
✔ T3: seat, archived, unmapped and Researcher threads count; imported, deleted and other-project threads do not (488.437473ms)
|
||||
✔ T3: the default path is read from HOME and prints no path line; --no-t3 says so (762.579521ms)
|
||||
✔ T3: a HOME with no database exits 1 and names --no-t3 (138.909365ms)
|
||||
✔ T3: a file that is not a database exits 1 and names --no-t3 (168.711684ms)
|
||||
✔ T3: a seat thread renamed to another seat exits 1 naming thread, title and roles (450.231489ms)
|
||||
✔ T3: an unmapped thread addressed as a seat exits 1 (443.080389ms)
|
||||
✔ T3: a header to another thread id is not cross-checked (443.796967ms)
|
||||
✔ T3: no project, or two, for this root exits 1 (980.381993ms)
|
||||
✔ T3: a removed column exits 1 and names it (258.690726ms)
|
||||
✔ T3: a missing table exits 1 and names it (259.346622ms)
|
||||
✔ T3: a counted row with an unknown role exits 1 without its text (404.02286ms)
|
||||
✔ T3: a counted row with non-text content exits 1 without its text (474.680664ms)
|
||||
✔ T3: a counted row with an unparseable created_at exits 1 without its text (705.621194ms)
|
||||
✔ T3: a missing orchestration_events makes the diagnostic unknown and keeps the counts (776.458741ms)
|
||||
✔ T3: a human message with no event counts in humanWithoutEvent (400.28293ms)
|
||||
✔ T3: an unparseable event makes the diagnostic unknown and keeps the counts (712.325593ms)
|
||||
✔ T3: an event with no string messageId makes the diagnostic unknown and keeps the counts (720.348958ms)
|
||||
✔ T3: an error that is not from SQLite is rethrown, not reported as a database failure (349.418685ms)
|
||||
✔ T3: a symlink at ~/.t3 exits 1 (146.908862ms)
|
||||
✔ T3: a symlink at ~/.t3/userdata exits 1 (135.470026ms)
|
||||
✔ T3: a symlink at ~/.t3/userdata/state.sqlite exits 1 (138.959173ms)
|
||||
✔ T3: with --t3-db, a symlinked file or directory exits 1 (459.255718ms)
|
||||
✔ T3 WAL: the newest message only in -wal, writer attached, is counted (397.757105ms)
|
||||
✔ T3 WAL: stopped cleanly, counts are correct and the main file is unchanged (394.598399ms)
|
||||
✔ T3 WAL: -wal without -shm in a writable directory is read (421.291307ms)
|
||||
✔ T3 WAL: -wal without -shm in a read-only directory exits 1 (414.513572ms)
|
||||
✔ T3 WAL: stopped cleanly in a read-only directory exits 1 (407.146353ms)
|
||||
✔ T3: a lock held past the 5 s busy timeout exits 1 and names --no-t3 (5413.589411ms)
|
||||
✔ a done row whose closing issue is open is a violation; a row that is not done is not (1.794885ms)
|
||||
✔ an issue several rows close is expected closed only once all of them are done (0.499267ms)
|
||||
✔ closure needs positive evidence: unknown is undecided, and so is a skipped or short issue check (0.358303ms)
|
||||
✔ each owner of an in-progress or in-review row gets one liveness class (7.492986ms)
|
||||
✔ a required row not done after 14 days is a violation; a legacy row uses genesis as its lower bound (0.357012ms)
|
||||
✔ an ISO requiredSince, as `set required` writes it, ages from its UTC day; one that does not parse is a violation (0.22952ms)
|
||||
✔ the text section always ends in a count and a result, and never prints a full pass (0.318585ms)
|
||||
✔ pidAlive: a running pid is present, an exited one is gone, and EPERM still means present (30.752549ms)
|
||||
✔ issue states: open list first, then the metric page, then at most 10 lookups (282.692701ms)
|
||||
✔ a full open list: lookups settle what it leaves out, and only an unsettled issue keeps it undecided (402.628517ms)
|
||||
✔ the open list refuses on a failed call or a bad record, and never echoes the helper (198.012007ms)
|
||||
✔ a helper call past the deadline is killed with its child, and the call reports it (2010.177468ms)
|
||||
✔ readQueue loads queue.json through the queue validator and refuses anything else (151.117396ms)
|
||||
✔ protected changes list every in-range entry that changes a required or parked row (448.554587ms)
|
||||
✔ the CLI prints the queue section above the weekly table and under a queue key in --json (551.263507ms)
|
||||
✔ a queue with nothing wrong prints 0 violations and a reduced pass, never a full pass (271.834681ms)
|
||||
✔ --no-issues makes no call and leaves the issue checks undecided; --no-queue skips the section (340.015635ms)
|
||||
✔ the CLI refuses a bad queue before any call, and a failed open list with exit 2 (302.404168ms)
|
||||
✔ --unsupported-runtime repeats once per seat and takes a seat name (368.468703ms)
|
||||
✔ an unreadable config makes every owner invalid instead of passing them (194.237873ms)
|
||||
ℹ tests 78
|
||||
ℹ suites 0
|
||||
ℹ pass 78
|
||||
ℹ fail 0
|
||||
ℹ cancelled 0
|
||||
ℹ skipped 0
|
||||
ℹ todo 0
|
||||
ℹ duration_ms 20056.827353
|
||||
@@ -0,0 +1,77 @@
|
||||
✔ pure resolution selects current default or explicit enrolled account (1.985324ms)
|
||||
✔ scope is explicit, bounded and never inferred (1.73224ms)
|
||||
✔ fork pin is preserved against default change, override, missing account and revocation (0.773137ms)
|
||||
✔ unenrolled account/provider, missing harness, model expansion and native model ceiling refuse (0.816764ms)
|
||||
✔ only explicit synthetic credential forms and internal fixture stores admitted (6.000185ms)
|
||||
✔ two concurrent workspaces of the same agent publish distinct complete private generations (66.448108ms)
|
||||
✔ same execution ID is exclusively claimed and cannot overwrite a published generation (40.55271ms)
|
||||
✔ failed generation after-auth preserves prior files, records failure and refuses blind same-ID retry (49.434978ms)
|
||||
✔ failed generation before-publish preserves prior files, records failure and refuses blind same-ID retry (59.888007ms)
|
||||
✔ credential lock contention refuses without duplicate side effects (12.175935ms)
|
||||
✔ symlinked pre-existing final target is refused and never followed (28.367262ms)
|
||||
✔ invalid registry cannot resolve; no fallback to supplied partial entries (0.309415ms)
|
||||
✔ post-publication failure records uncertainty, preserves complete generation and prevents replay (28.285567ms)
|
||||
✔ expired credentials refresh under transaction and subsequent generation reuses rotation (90.362553ms)
|
||||
✔ refresh failure retains prior generation and store state (65.024119ms)
|
||||
✔ refresh timeout retains prior generation and store state (147.617882ms)
|
||||
✔ refresh malformed retains prior generation and store state (72.635364ms)
|
||||
✔ concurrent refresh on same account refuses contention while unrelated account proceeds (224.359729ms)
|
||||
✔ invalid refresh options refuse before burning claim (32.821353ms)
|
||||
✔ fixed fake process rotates both OAuth fields without mutating caller input (35.083253ms)
|
||||
✔ concurrent isolated processes preserve separate provider credentials (35.340301ms)
|
||||
✔ fake failure is refused with fixed diagnostics (28.741764ms)
|
||||
✔ fake malformed is refused with fixed diagnostics (25.676151ms)
|
||||
✔ fake timeout is refused with fixed diagnostics (104.470792ms)
|
||||
✔ fake unchanged is refused with fixed diagnostics (29.116894ms)
|
||||
✔ caller executable/environment injection is rejected before spawning (0.338944ms)
|
||||
✔ valid fixture tree validates and lists without secrets (91.216095ms)
|
||||
✔ unknown-field refuses (0.431311ms)
|
||||
✔ invalid-id refuses uppercase and traversal shapes (0.296856ms)
|
||||
✔ plain-http baseUrl requires allowInsecureTransport (0.309826ms)
|
||||
✔ native provider rejects allowInsecureTransport (0.13606ms)
|
||||
✔ unsupported credential type and kind refuse (0.154604ms)
|
||||
✔ account provider-path mismatch refuses (0.112673ms)
|
||||
✔ profile account refs must be provider/account shaped (0.273262ms)
|
||||
✔ seat selection accepts fork pin field, validates account refs (0.323896ms)
|
||||
✔ harness manifest id must equal executable (gate 1) (0.257206ms)
|
||||
✔ CLI validate: duplicate provider id across files refuses (35.850199ms)
|
||||
✔ CLI validate: missing referenced provider/account refuse (39.95611ms)
|
||||
✔ CLI validate: broken JSON refuses without secret echo (33.756201ms)
|
||||
✔ CLI usage errors exit 2 (60.031617ms)
|
||||
✔ credential.json sibling presence does not break validation and is never read (70.398482ms)
|
||||
✔ D1 missing, empty and structurally empty roots refuse, no list projection (210.576539ms)
|
||||
✔ D1 required directory auth cannot be absent (55.85294ms)
|
||||
✔ D1 required directory auth/providers cannot be absent (60.124101ms)
|
||||
✔ D1 required directory auth/accounts cannot be absent (75.645966ms)
|
||||
✔ D1 required directory auth/settings cannot be absent (63.682272ms)
|
||||
✔ D1 required directory harnesses cannot be absent (63.842306ms)
|
||||
✔ D1 root file and unreadable metadata refuse (109.803932ms)
|
||||
✔ D2 no symlink traversal at auth/providers/openai-codex.json (72.57022ms)
|
||||
✔ D2 no symlink traversal at auth/accounts/openai-codex/homelab-openai (64.503262ms)
|
||||
✔ D2 no symlink traversal at auth/providers (55.265618ms)
|
||||
✔ D2 no symlink traversal at auth (58.05731ms)
|
||||
✔ D2 root and ancestor symlinks and lexical traversal refuse (167.655041ms)
|
||||
✔ private filesystem modes enforced for root (56.250121ms)
|
||||
✔ private filesystem modes enforced for auth (56.308938ms)
|
||||
✔ private filesystem modes enforced for auth/providers/openai-codex.json (66.924543ms)
|
||||
✔ private filesystem modes enforced for auth/accounts/openai-codex/homelab-openai/account.json (63.934499ms)
|
||||
✔ D3 numeric version 1 only across all record kinds (1.260244ms)
|
||||
✔ D4 nested unknown keys and missing per-kind required fields refuse (69.021863ms)
|
||||
✔ D5 unenrolled default refuses even when account exists (64.812827ms)
|
||||
✔ D6 provider/account credential type must match (62.987986ms)
|
||||
✔ D7 every harness endpoint enforces HTTP opt-in and shape (0.472439ms)
|
||||
✔ D8 URLs reject embedded credentials and unsupported protocols without echo (173.260503ms)
|
||||
✔ D9 malformed JSON diagnostics contain no content excerpt (63.240916ms)
|
||||
✔ D10 missing metadata is missing-path, not invalid-json (61.83338ms)
|
||||
✔ D10 library returns no partial entries on any invalid record (76.193682ms)
|
||||
✔ null/scalar/array metadata refuses without stack or echo (226.566502ms)
|
||||
✔ credential sibling is never opened, even when an unreadable symlink (31.456806ms)
|
||||
✔ oversized metadata refuses before parsing (62.953789ms)
|
||||
ℹ tests 69
|
||||
ℹ suites 0
|
||||
ℹ pass 69
|
||||
ℹ fail 0
|
||||
ℹ cancelled 0
|
||||
ℹ skipped 0
|
||||
ℹ todo 0
|
||||
ℹ duration_ms 2245.097655
|
||||
@@ -0,0 +1,158 @@
|
||||
✔ F1: an ordinary commit after update-ref is refused until step 8; then it commits and the queue stays at C (1315.813425ms)
|
||||
✔ F1: a plain `commit -e` whose guard ran before update-ref fails at its own HEAD update (1081.115352ms)
|
||||
ℹ git commit -e: index.lock free during the editor
|
||||
✔ F1: a `commit -e -- path` whose guard ran before update-ref fails at its own HEAD update (1017.68701ms)
|
||||
ℹ git commit -e -- src.txt: index.lock held during the editor
|
||||
✔ F1: step 8 with index.lock held exits 3, and ordinary commits stay refused until the printed command runs (1051.31222ms)
|
||||
✔ F1: HEAD moving after the step-7 guard check and before update-ref: refused, nothing published (1040.687515ms)
|
||||
✔ F1: H is recorded before the canary, so HEAD moving during the step-1 canary is refused at step 7 (1041.875297ms)
|
||||
✔ F1: a queue commit landing after H is recorded: step 1 says HEAD moved, not the guard (1357.055046ms)
|
||||
✔ F1: a queue commit landing between the HEAD check and the canary: the failed clean run is reported as HEAD moved (967.860816ms)
|
||||
✔ F1: a shared-index change during the procedure is not committed (1109.614ms)
|
||||
✔ F1: a queue path staged after update-ref: step 8 stops and touches nothing (1087.82765ms)
|
||||
✔ F1: a missing or a different hook refuses (744.467811ms)
|
||||
✔ F1: same bytes without the exec bit, a symlinked hook, and core.hooksPath in the local or global scope each refuse before update-ref (1257.445186ms)
|
||||
✔ F1: the canary refuses a hook that git would not run (681.891727ms)
|
||||
✔ F1: the guard deactivated after step 1 is refused at the step-7 recheck (980.199611ms)
|
||||
✔ bootstrap: implementation-only HEAD, the guard, genesis, the --genesis commit, then an extending commit (881.638875ms)
|
||||
✔ bootstrap: --genesis with a base present, no base without --genesis, an op before the first commit, a changed map, another branch (911.897555ms)
|
||||
✔ bootstrap: the archived tests and validator run outside any repository (943.738754ms)
|
||||
✔ general: an unrelated staged file stays staged, and the committed blobs are the snapshot bytes (903.425594ms)
|
||||
✔ general: a queue write after the snapshot is not committed (1305.011012ms)
|
||||
✔ general: a snapshot whose log does not extend the base refuses (1021.887028ms)
|
||||
✔ general: install-hook privilege, repair of a same-bytes hook, and its refusals (154.209801ms)
|
||||
✔ general: environment overrides, a linked worktree and usage (770.148726ms)
|
||||
✔ general: a queue path staged before the run refuses at step 1 (672.104169ms)
|
||||
✔ general: HEAD's queue tests failing in the archive refuse (910.522991ms)
|
||||
✔ genesis document serializes deterministically and replays (4.541225ms)
|
||||
✔ a hand edit that stays valid JSON fails replay; a formatting-only edit fails re-serialization (2.647633ms)
|
||||
✔ a tampered result, receipt or viewSha fails replay (2.377146ms)
|
||||
✔ op ids: 8 to 72 characters for callers, 80 in the log for .outcome entries (0.154114ms)
|
||||
✔ add: defaults for an ordinary seat, privileged extras, refusals (3.687345ms)
|
||||
✔ matrix: queued→briefed privileged; briefed→in-progress owner with after satisfied (7.218915ms)
|
||||
✔ matrix: release, review round, changes requested and waiting-on-jason (12.362106ms)
|
||||
✔ matrix J5: in-review→done by the gate owner with evidence naming the current round (9.377367ms)
|
||||
✔ review issue, lead decision 23: none refuses, one is used, several need --issue, later rounds keep it (19.474585ms)
|
||||
✔ the row schema refuses a round with a null issue, and the A1 review shape (P2) (2.520257ms)
|
||||
✔ matrix R1: every state × target × actor class matches 8.7, gate owner jason or not, required or not (1418.675427ms)
|
||||
✔ matrix: blocked keeps the claim and returns only to previousState (3.604666ms)
|
||||
✔ matrix J4: parking is Jason's, refused while required; unpark returns to queued (2.595486ms)
|
||||
✔ field edits: who may change what (4.724985ms)
|
||||
✔ set issues keeps a logged narrowing of closes (N10) (2.533927ms)
|
||||
✔ text the table shows refuses \ and <, everywhere it enters (N8) (1.673054ms)
|
||||
✔ every accepted text renders to nine cells on every row (N8) (22.975ms)
|
||||
✔ genesis: the map refuses an owner among its row's reviewers; replay doesn't (2026-10-04) (0.519538ms)
|
||||
✔ times and dates must be calendar values, not just the shape (2026-10-04) (1.719428ms)
|
||||
✔ replay holds every op id to the caller's rule (N11) (5.497412ms)
|
||||
✔ note: owner, listed reviewer or privileged; empty clears (1.299237ms)
|
||||
✔ assign moves the claim with the owner; done clears it (2.632329ms)
|
||||
✔ render is byte-stable and escapes pipes (0.526935ms)
|
||||
✔ view classification: current, genuine stale, edited stale marker, changed current body, markers (0.628848ms)
|
||||
✔ next: resume, then review, then start, then wait, then nothing; lowest id first (21.047921ms)
|
||||
✔ canonical args make a retry's identity independent of list order (0.313693ms)
|
||||
✔ manifests, headings and blob ids (0.451512ms)
|
||||
✔ the migration map: one queue-map block, exact keys (0.685253ms)
|
||||
✔ every call but `queue` reaches the seat CLI exactly as before A2 (625.694927ms)
|
||||
✔ `queue` reaches the queue CLI with the rest of the arguments (230.825945ms)
|
||||
✔ the pre-A2 fixture is the script A2 changed (0.371132ms)
|
||||
✔ acquire publishes the record by link; release removes only its own lock (10.852743ms)
|
||||
✔ a kill between the temp write and the link leaves no lock (53.801935ms)
|
||||
✔ a short or failed temp write refuses and leaves no lock and no temp (9.614997ms)
|
||||
✔ a link error other than EEXIST refuses (10.524222ms)
|
||||
✔ an error after the link releases the lock: unreadable gate, failing temp stat (22.084768ms)
|
||||
✔ a release that fails on a gate path is reported, never a stack trace (P1) (38.708582ms)
|
||||
✔ a paused holder: another writer waits 10 s, then refuses naming it live (10100.133413ms)
|
||||
✔ two concurrent unlockers: the second refuses on the gate (39.149916ms)
|
||||
✔ a writer publishing during an unlock, lock first: unlock sees it live and refuses (14.804068ms)
|
||||
✔ a writer publishing during an unlock, gate first: the writer releases and refuses (19.392826ms)
|
||||
✔ a gate swapped while held is left in place and reported, on success and on refusal (N1) (21.070219ms)
|
||||
✔ a reused pid within one boot is mismatch; unlock removes the lock and never signals the process (18.357174ms)
|
||||
✔ the same pid and start on a different boot is mismatch (0.782158ms)
|
||||
✔ a foreign host is unknown whatever the local pid says; unlock refuses (68.810381ms)
|
||||
✔ unreadable /proc: classification is unknown and acquire refuses (0.764468ms)
|
||||
✔ invalid records: empty, unparsable, wrong keys, bad start or boot (0.193095ms)
|
||||
✔ a stale gate blocks writers; --check-gate says mismatch for a reused pid (20.318818ms)
|
||||
✔ a delayed release by a dead owner, after unlock and a new owner: the inode check keeps the new lock (28.304317ms)
|
||||
✔ release checks the inode too: a byte-identical lock file with a new inode is left in place (13.662347ms)
|
||||
✔ unlock refuses a live, unknown or invalid lock, and does nothing without one (29.026503ms)
|
||||
✔ the migration map validates and renders the golden genesis table (3.535676ms)
|
||||
✔ the marked QUEUE.md holds every row and parked item between its markers (1.323144ms)
|
||||
✔ map-check reports each kind of drift (4.081592ms)
|
||||
✔ a request posts once as the requester; a retry sends nothing (855.868743ms)
|
||||
✔ each transport answer maps to posted, failed or uncertain (8.9 step 3) (4503.092654ms)
|
||||
✔ the pre-send checks: GET user must name the requester, under the deadline (1589.810662ms)
|
||||
✔ the lead's request refuses a token for login sage (920.746201ms)
|
||||
✔ the credential file: the seat's own, 0600, no symlink, never the shared default (964.828249ms)
|
||||
✔ an unresolved request blocks a new request, a new round, waiting-on-jason and done (2386.843801ms)
|
||||
✔ a same-op retry after a kill sends nothing, even with a stale view (3202.595418ms)
|
||||
✔ a held lock at the outcome exits 3 and names what the transport said (691.782854ms)
|
||||
✔ late outcomes: after an abandon, and after a resolve with the same or another id (2338.703576ms)
|
||||
✔ resolve checks the comment: issue, markers, round, candidate and author (1687.173607ms)
|
||||
✔ the lead resolves a seat's request: the comment's author is the requester, fetched with the lead's token (725.637294ms)
|
||||
✔ validateRow checks a request round's shape, which every replayed entry must keep (524.098243ms)
|
||||
✔ request, changes, a new candidate, approval: every round pinned; no review files (1820.157622ms)
|
||||
✔ a row with no reviewers opens a round that posts nothing (1223.172232ms)
|
||||
✔ verify-commit: a prospective tree must hold exactly the candidate's paths (1331.172512ms)
|
||||
✔ semantics: v1 entries replay as before; review entries need v2 (490.341261ms)
|
||||
✔ set reviewers refuses the row's owner (2026-09-28) (344.928618ms)
|
||||
✔ the owner records no verdict, even as a listed reviewer (502.322231ms)
|
||||
✔ a request comment over the length limit is not sent (593.527411ms)
|
||||
✔ a late POST on a closed row leaves a conflict nothing can resolve, and resolve asks nothing (759.992641ms)
|
||||
✔ a Jason-gated row reaches waiting-on-jason only on every reviewer's approval (2475.472287ms)
|
||||
✔ genesis: refusals before anything is written (417.116561ms)
|
||||
✔ genesis: the map must be committed, well formed, with committed briefs and seats (679.059586ms)
|
||||
✔ genesis: markers, a stray witness, once only; a retry returns the receipt (558.113068ms)
|
||||
✔ genesis: a file holding genesis alone with no witness is confirmed by sync or a retry (406.475892ms)
|
||||
✔ canonical checks: worktree, second clone, detached HEAD, wrong branch, GIT_DIR, foreign code; a symlink works (682.226948ms)
|
||||
✔ --by that differs from MOSAIC_AGENT_NAME warns on stderr and logs nothing more (N12) (748.523026ms)
|
||||
✔ op ids: missing, too long, reserved; a retry answers; another payload refuses (743.149367ms)
|
||||
✔ a retried add returns the id it first allocated, after reassignment and after done (903.80109ms)
|
||||
✔ Rocko's S4 schedule: a lost result, another writer, then the retry opens no second round (728.329374ms)
|
||||
✔ the review issue and the evidence round through the CLI (lead decision 23, 8.7) (1297.824741ms)
|
||||
✔ claims and add defaults through the CLI; candidates are manifests or reachable commits (850.318323ms)
|
||||
✔ add, set reviewers and assign refuse the row's owner as a reviewer (600.731271ms)
|
||||
✔ the working-brief check: a changed working copy refuses the start and flags next (713.071623ms)
|
||||
✔ next: resume first, then nothing for an idle seat; needs a seat (320.325818ms)
|
||||
✔ view stale: new ops and verify refuse naming the unshown op; retries answer; reads warn; render fixes (703.004529ms)
|
||||
✔ view unknown: a hand edit, an old marker over an edited body, missing or duplicate markers (1199.175189ms)
|
||||
✔ a hand edit to queue.json refuses every verb, reads included (601.872275ms)
|
||||
✔ verify and render --check leave bytes and mtimes unchanged (432.353168ms)
|
||||
✔ render is byte-stable across runs and repositories (300.566987ms)
|
||||
✔ snapshot and verify --snapshot (817.800722ms)
|
||||
✔ usage errors exit 4 (555.243725ms)
|
||||
✔ a short write, ENOSPC or a file fsync failure: nothing visible, temp removed (284.484768ms)
|
||||
✔ a rename failure: nothing visible, temp removed (169.193867ms)
|
||||
✔ a directory fsync failure: uncertain, exit 3, no receipt; the tail refuses new ops; a retry confirms (267.647309ms)
|
||||
✔ a directory fsync failure, then sync names the op (386.599681ms)
|
||||
✔ a witness write failure: uncertain, durable, exit 3; the view is untouched (230.007017ms)
|
||||
✔ the .git fsync after the witness rename fails: uncertain, exit 3, the witness says so (202.930537ms)
|
||||
✔ confirming a tail fsyncs queue.json and docs/plans before the witness; either failure changes nothing (235.406465ms)
|
||||
✔ the docs/plans fsync after the view rename fails: the op stands, the view is written, a warning says so (183.252633ms)
|
||||
✔ a lock swapped while held is left in place and reported, on a receipt and on a refusal (222.500035ms)
|
||||
✔ a lock that cannot be released after an op is reported, on a receipt and on a refusal (195.858452ms)
|
||||
✔ unlock prints a swapped gate's warning on stderr, the result on stdout (175.972928ms)
|
||||
✔ a view write that fails keeps the op and reports a stale view (207.541223ms)
|
||||
✔ SIGKILL before the rename: nothing recorded; the retry removes the leftover temp (689.655744ms)
|
||||
✔ SIGKILL after the rename, before the witness: the tail refuses new ops and sync names the op (696.045038ms)
|
||||
✔ SIGKILL after the witness, before the view: the stale refusal names the op (627.384ms)
|
||||
✔ SIGKILL after the view, before the receipt: the retry returns the receipt (669.367102ms)
|
||||
✔ git checkout between steps 1 and 7: step 7 refuses and nothing is written (254.179057ms)
|
||||
✔ git stash restoring an older valid pair: history lost; accept-history needs privilege, a reason and --yes (1053.58748ms)
|
||||
✔ a deleted witness: refused after the locked recheck; accept-history records it absent (344.005ms)
|
||||
✔ a header edit during a write: the op stands, the view write is skipped with a warning (202.885594ms)
|
||||
✔ a reader paused between the witness and the file while a writer finishes: no lost-history report (201.370104ms)
|
||||
✔ file-then-witness order forced by a hook: the locked recheck prevents a false report (236.401892ms)
|
||||
✔ a writer paused before and after the witness rename: readers see a tail, then a match (239.793838ms)
|
||||
✔ a true rollback is reported only after the locked recheck; a held lock names its holder instead (687.791547ms)
|
||||
✔ an accept-history in progress: an unlocked reader waits on the lock and never reports lost history (645.274879ms)
|
||||
✔ the platform check refuses other filesystems (158.550198ms)
|
||||
✔ tmpfs passes only a test layer that allows it (N5) (226.220123ms)
|
||||
✔ unlock keeps a multi-line lock record on stdout (P3) (205.315769ms)
|
||||
ℹ tests 148
|
||||
ℹ suites 0
|
||||
ℹ pass 148
|
||||
ℹ fail 0
|
||||
ℹ cancelled 0
|
||||
ℹ skipped 0
|
||||
ℹ todo 0
|
||||
ℹ duration_ms 30007.76345
|
||||
@@ -0,0 +1,35 @@
|
||||
✔ resolveSeat: by name under --repo resolves the repo layout (1.245323ms)
|
||||
✔ resolveSeat: by path resolves the fleet layout (0.310504ms)
|
||||
✔ resolveSeat: refusals for missing dir, missing launch.sh, non-executable launch.sh, invalid name, and unknown layout (0.697631ms)
|
||||
✔ tmuxContext: outside tmux, default socket, custom socket, and exec failure (0.632989ms)
|
||||
✔ makeRegistration produces a record that validates; each shape violation throws SeatError (0.786135ms)
|
||||
✔ writeRegistration/readRegistration: round trip, permissions, absence, and malformed records (1.257165ms)
|
||||
✔ updateTask: changes task, taskSetBy and updatedAt only, and refuses appropriately (1.105947ms)
|
||||
✔ CLI launch: registers, execs the fake launch script, and passes args through (31.799286ms)
|
||||
✔ CLI launch: --harness lands in the record (29.529466ms)
|
||||
✔ CLI launch: the launch script's own exit code passes through (29.242989ms)
|
||||
✔ CLI launch: relaunching a seat rewrites the one registration record (55.726663ms)
|
||||
✔ CLI launch: omitting --task records an empty string, not null (30.591302ms)
|
||||
✔ CLI seat task: updates only the task after a launch, and refuses on an unlaunched seat (80.218911ms)
|
||||
✔ CLI refusals: no args, unknown flag, missing config, already-registered env, and exec failure (134.378932ms)
|
||||
✔ samePath: equal paths, symlinked dirs, distinct dirs, and non-strings (0.403602ms)
|
||||
✔ resolveSetBy: explicit --by wins over the environment; absent or empty environment gives unknown; invalid explicit or environment values refuse with exit 4 (0.532978ms)
|
||||
✔ 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.718033ms)
|
||||
✔ 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.848881ms)
|
||||
✔ 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 (268.708629ms)
|
||||
✔ family: exactly one launch.max key in the model name, else null (0.713929ms)
|
||||
✔ sessionEnv passes only the allowlist, the repo's bin on PATH, and the run id (0.664675ms)
|
||||
✔ newRun: short ids, 0700 directories, and a refusal when the socket path won't fit (1.485059ms)
|
||||
✔ session file and launch log: 0600, the session file written once (0.953821ms)
|
||||
✔ endReason maps the runner's exit codes; a signal is killed (0.111125ms)
|
||||
✔ Registry mirrors to sessions.json; readSessions marks live entries; bad files refuse (1.285128ms)
|
||||
✔ stopSession refuses an unknown run, reports a stale one, and won't signal a pid that isn't a runner (1.017973ms)
|
||||
✔ a session runs under unshare as pid 1 of its namespace, claims, answers, and stops on mosaic stop (246.632034ms)
|
||||
ℹ tests 27
|
||||
ℹ suites 0
|
||||
ℹ pass 27
|
||||
ℹ fail 0
|
||||
ℹ cancelled 0
|
||||
ℹ skipped 0
|
||||
ℹ todo 0
|
||||
ℹ duration_ms 719.297907
|
||||
@@ -0,0 +1,59 @@
|
||||
✔ the boot config is checked before anything starts (119.97767ms)
|
||||
✔ a business with no tracker entry refuses task verbs (137.857625ms)
|
||||
✔ credential.expiring and .expired are recorded once per instance (223.935762ms)
|
||||
✔ a token file that changes on disk records credential.changed (120.172495ms)
|
||||
✔ autostart polls, reconciles and retries a startup the tracker was down for (145.800704ms)
|
||||
✔ a refusal a restart must clear is not retried by the poll (113.632769ms)
|
||||
✔ a poll that fires while two are queued is dropped (105.358072ms)
|
||||
✔ close waits for a running verb and refuses one that has not started (249.063575ms)
|
||||
✔ the bundled Vikunja is the pinned upstream image the runbook names (0.71601ms)
|
||||
✔ every published port is on 127.0.0.1, and no secret is in the file (0.256866ms)
|
||||
✔ the fake answers each route with the statuses and shapes Vikunja v2.7.0 sent (418.468613ms)
|
||||
✔ the recorded task bodies pass the checks S3 applies to every read (0.562714ms)
|
||||
✔ the client works against the fake over real HTTP with the platform fetch (269.350239ms)
|
||||
✔ a correct install starts, and the first reconcile records tasks that already exist (130.356365ms)
|
||||
✔ verbs refuse while a business is starting and after startup refused it (145.385943ms)
|
||||
✔ startup refuses a token that can do more than its role needs (406.541994ms)
|
||||
✔ startup refuses an unsupported version and flags an untested one (319.754907ms)
|
||||
✔ startup refuses a board that the runbook did not install (370.242996ms)
|
||||
✔ startup refuses a project the sync bot cannot read (83.884438ms)
|
||||
✔ startup refuses a configured label the pm bot cannot see (103.105883ms)
|
||||
✔ startup refuses an expired credential and a missing sync credential (183.15347ms)
|
||||
✔ an unreachable tracker refuses with tracker-unavailable (87.4792ms)
|
||||
✔ an edit in the UI is recorded once, with the fields that changed (234.03919ms)
|
||||
✔ a move between open buckets is seen on the board, though updated does not change (184.384822ms)
|
||||
✔ a person's comment is counted and a bot's is not (288.788654ms)
|
||||
✔ the hourly reconcile catches a comment through comment_count (237.351272ms)
|
||||
✔ a task closed in the UI leaves the open view with its done bucket (442.960893ms)
|
||||
✔ a task that leaves the board is recorded as deleted, moved or out of reach (271.049202ms)
|
||||
✔ a poll that read before a verb wrote does not overwrite the verb (178.64957ms)
|
||||
✔ a tracker fault during a tick is reported and the next tick catches up (173.029944ms)
|
||||
✔ a malformed answer refuses the tick with tracker-shape (142.07532ms)
|
||||
✔ no token value reaches the database, the log or a refusal (284.205053ms)
|
||||
✔ the first look at a task counts only comments inside the window (182.927221ms)
|
||||
✔ task.create needs a recorded human request and a requirement id (234.003886ms)
|
||||
✔ only labels named in the business file can be written (229.763681ms)
|
||||
✔ task.schedule sets and clears a due date and relations (319.86945ms)
|
||||
✔ assign and reassign move the role bots and record task.assigned (336.838827ms)
|
||||
✔ task.update.assigned is for the assignee and records task.state (338.823149ms)
|
||||
✔ a wrong expected digest records task.conflict and writes nothing (215.122804ms)
|
||||
✔ a cross-role verb needs a resolved decision, used once (278.383423ms)
|
||||
✔ task.close needs a verdict; after it every verb refuses with task-done (255.889778ms)
|
||||
✔ a lost answer is settled by a re-read and never retried (273.373847ms)
|
||||
✔ a create whose answer is lost is reported uncertain, and the poll finds the task (182.244162ms)
|
||||
✔ a task the sync bot cannot read refuses and records nothing (104.189372ms)
|
||||
✔ verbs and polls for one business run one at a time (172.998567ms)
|
||||
✔ a due date with milliseconds is written to the second (171.413102ms)
|
||||
✔ every write landed and the final read failed: the verb succeeds and records what it wrote (117.387915ms)
|
||||
✔ some writes landed and the final read failed: write-uncertain, and nothing is recorded (84.895896ms)
|
||||
✔ a create whose final read fails succeeds and records task.created (93.866128ms)
|
||||
✔ an edit between the last write and the final read shows as external on the next poll (117.570787ms)
|
||||
✔ task.created is recorded when a later label write fails (84.597114ms)
|
||||
ℹ tests 51
|
||||
ℹ suites 0
|
||||
ℹ pass 51
|
||||
ℹ fail 0
|
||||
ℹ cancelled 0
|
||||
ℹ skipped 0
|
||||
ℹ todo 0
|
||||
ℹ duration_ms 3697.915463
|
||||
@@ -0,0 +1,23 @@
|
||||
✔ browser edge states: loading, empty, malformed, stale, hostile/long values, in-flight reply and appearance fallback (3036.471142ms)
|
||||
Rendered contrast: {"failures":[],"count":330,"lowest":4.504658476260286}
|
||||
✔ served Console browser: real board fixtures, keyboard, drafts, receipts, themes, 320px and failures (3034.366279ms)
|
||||
✔ conversation view: full history, collapsed tools, hidden thinking, inert hostile content, malformed and reconcile markers (3015.132576ms)
|
||||
✔ conversation view: a fork keeps the open branch, says so, and opens the new one on request (1653.090341ms)
|
||||
✔ conversation view: a newer session with no readable history keeps the marker (1205.30957ms)
|
||||
✔ conversation view: seats without history say so and offer no reply (746.156155ms)
|
||||
✔ Discord row through real board/WebUI: independent brake/liveness, no Reply, literal content (2896.507237ms)
|
||||
✔ return flow through the conversation view: send, tool call, delayed result, peer message, exact long answers, relaunch (52750.187787ms)
|
||||
✔ both presentations replace old activity with relaunch notice, label retained history, then resume after new activity (3001.025458ms)
|
||||
✔ reported return flow and relative Age: reply sent from the inspector, then the new answer appears there without manual refresh (21708.6077ms)
|
||||
✔ loopback host and board origin fail closed (6.090954ms)
|
||||
✔ real board fixture passes through WebUI; assets and isolated seen/reply work (77.465842ms)
|
||||
✔ proxy preserves exact request bytes, status and receipt, rejects forms and malformed JSON, never follows redirect (55.459641ms)
|
||||
✔ unreachable board reports URL; CLI rejects unsupported options (965.757487ms)
|
||||
ℹ tests 14
|
||||
ℹ suites 0
|
||||
ℹ pass 14
|
||||
ℹ fail 0
|
||||
ℹ cancelled 0
|
||||
ℹ skipped 0
|
||||
ℹ todo 0
|
||||
ℹ duration_ms 53029.312669
|
||||
@@ -0,0 +1,23 @@
|
||||
node-bus exit=0 ℹ pass 74 ℹ fail 0
|
||||
node-business exit=0 ℹ pass 60 ℹ fail 0
|
||||
node-cli exit=0 ℹ pass 77 ℹ fail 0
|
||||
node-control-board exit=0 ℹ pass 124 ℹ fail 0
|
||||
node-conversation exit=0 ℹ pass 152 ℹ fail 0
|
||||
node-discord exit=0 ℹ pass 178 ℹ fail 0
|
||||
node-harness exit=0 ℹ pass 45 ℹ fail 0
|
||||
node-ledger exit=0 ℹ pass 78 ℹ fail 0
|
||||
node-mosaic exit=0 ℹ pass 69 ℹ fail 0
|
||||
node-queue exit=0 ℹ pass 148 ℹ fail 0
|
||||
node-seat exit=0 ℹ pass 27 ℹ fail 0
|
||||
node-tasks exit=0 ℹ pass 51 ℹ fail 0
|
||||
node-webui exit=0 ℹ pass 14 ℹ fail 0
|
||||
test-auth exit=0 selftest: 15 passed, 0 failed
|
||||
test-conductor exit=0 selftest: 17 passed, 0 failed
|
||||
test-config exit=0 selftest: 24 passed, 0 failed
|
||||
test-discord exit=0 discord suite: 66 passed, 0 failed
|
||||
test-extension-package exit=0 extension package selftest: 18 passed, 0 failed
|
||||
test-foundation exit=0 selftest: 44 passed, 0 failed
|
||||
test-queue exit=0 queue suite: 27 passed, 0 failed
|
||||
test-release exit=0 selftest: 4 passed, 0 failed
|
||||
test-task exit=1 selftest: 26 passed, 2 failed
|
||||
GATE-DONE
|
||||
@@ -0,0 +1,17 @@
|
||||
OK status with missing harness credential exits 3 and still lists accounts
|
||||
OK status reports harness credential (read-only) + mosaic accounts
|
||||
OK api key material never reaches output
|
||||
OK oauth token material never reaches output
|
||||
OK unparseable credential file exits 2
|
||||
OK symlinked credential file exits 4
|
||||
OK env-side credential names reported
|
||||
OK env var values never reach output
|
||||
OK accounts without an accounts dir reports none and creates nothing
|
||||
OK accounts lists files and marks the active one
|
||||
OK loose account perms flagged in listing
|
||||
OK agent --auth with missing account file refuses (exit 4)
|
||||
OK agent --auth with non-0600 account file refuses
|
||||
OK agent --auth with invalid account name refuses
|
||||
OK auth.sh without valid config refuses
|
||||
|
||||
selftest: 15 passed, 0 failed
|
||||
@@ -0,0 +1,23 @@
|
||||
On branch refactor
|
||||
Your branch is up to date with 'origin/refactor'.
|
||||
|
||||
nothing to commit, working tree clean
|
||||
OK dry-run: allowed change, exit 0, nothing committed (exit 0)
|
||||
OK dry-run committed nothing
|
||||
OK apply: allowed change exits 0 (exit 0)
|
||||
OK apply: attribution in commit subject
|
||||
OK apply: target tree clean after commit
|
||||
OK disallowed path refused (exit 1)
|
||||
OK disallowed path: target untouched
|
||||
OK syntax gate refused broken .mjs (exit 1)
|
||||
OK syntax gate: target untouched
|
||||
OK suite failure refused (exit 1)
|
||||
OK suite failure: target reverted to clean
|
||||
OK disabled policy refused (exit 2)
|
||||
OK disabled policy: target untouched
|
||||
OK failed run refused (exit 1)
|
||||
OK failed run: target untouched
|
||||
OK missing run exits 4 (exit 4)
|
||||
OK invalid policy exits 2 (exit 2)
|
||||
|
||||
selftest: 17 passed, 0 failed
|
||||
@@ -0,0 +1,26 @@
|
||||
OK absent adapter defaults to pi
|
||||
OK adapter mock validates (exit 0)
|
||||
OK unsupported adapter exits 2 (exit 2)
|
||||
OK env exports adapter
|
||||
OK bootstrap creates default when absent (exit 0)
|
||||
OK bootstrap wrote config file
|
||||
OK bootstrap is idempotent on existing config (exit 0)
|
||||
OK bootstrap did not rewrite existing config
|
||||
OK validate missing config exits 3 (exit 3)
|
||||
OK malformed JSON exits 2 (exit 2)
|
||||
OK unsupported configVersion exits 2 (exit 2)
|
||||
OK unknown top-level key exits 2 (exit 2)
|
||||
OK unknown execution key exits 2 (exit 2)
|
||||
OK unsupported backend exits 2 (exit 2)
|
||||
OK unsupported environment exits 2 (exit 2)
|
||||
OK relative dataRoot exits 2 (exit 2)
|
||||
OK non-canonical dataRoot exits 2 (exit 2)
|
||||
OK filesystem root dataRoot exits 2 (exit 2)
|
||||
OK home directory dataRoot exits 2 (exit 2)
|
||||
OK dataRoot containing config dir exits 2 (exit 2)
|
||||
OK control character in provider exits 2 (exit 2)
|
||||
OK symlinked config file exits 2 (exit 2)
|
||||
OK env exports resolve correctly
|
||||
OK failed validation modified nothing
|
||||
|
||||
selftest: 24 passed, 0 failed
|
||||
@@ -0,0 +1,70 @@
|
||||
toolchain: node v26.8.1
|
||||
|
||||
OK syntax: packages/discord/src/approvals.mjs
|
||||
OK syntax: packages/discord/src/authorize.mjs
|
||||
OK syntax: packages/discord/src/binding.mjs
|
||||
OK syntax: packages/discord/src/cli.mjs
|
||||
OK syntax: packages/discord/src/connector.mjs
|
||||
OK syntax: packages/discord/src/context.mjs
|
||||
OK syntax: packages/discord/src/engine-pi.mjs
|
||||
OK syntax: packages/discord/src/errors.mjs
|
||||
OK syntax: packages/discord/src/gateway.mjs
|
||||
OK syntax: packages/discord/src/git.mjs
|
||||
OK syntax: packages/discord/src/journal.mjs
|
||||
OK syntax: packages/discord/src/notify.mjs
|
||||
OK syntax: packages/discord/src/rest.mjs
|
||||
OK syntax: packages/discord/src/setspark.mjs
|
||||
OK syntax: packages/discord/src/tools.mjs
|
||||
OK syntax: packages/discord/src/web.mjs
|
||||
OK syntax: packages/discord/bin/git-credential.mjs
|
||||
OK syntax: packages/discord/extension/tools.mjs
|
||||
OK syntax: packages/discord/tests/approvals.test.mjs
|
||||
OK syntax: packages/discord/tests/authorize.test.mjs
|
||||
OK syntax: packages/discord/tests/binding.test.mjs
|
||||
OK syntax: packages/discord/tests/connector.test.mjs
|
||||
OK syntax: packages/discord/tests/context.test.mjs
|
||||
OK syntax: packages/discord/tests/engine.test.mjs
|
||||
OK syntax: packages/discord/tests/fake-pi.mjs
|
||||
OK syntax: packages/discord/tests/gateway.test.mjs
|
||||
OK syntax: packages/discord/tests/git.test.mjs
|
||||
OK syntax: packages/discord/tests/helpers.mjs
|
||||
OK syntax: packages/discord/tests/journal.test.mjs
|
||||
OK syntax: packages/discord/tests/notify.test.mjs
|
||||
OK syntax: packages/discord/tests/recover.test.mjs
|
||||
OK syntax: packages/discord/tests/rest.test.mjs
|
||||
OK syntax: packages/discord/tests/setspark.test.mjs
|
||||
OK syntax: packages/discord/tests/tools.test.mjs
|
||||
OK syntax: packages/discord/tests/web.test.mjs
|
||||
OK syntax: packages/discord/fixtures/claim-worker.mjs
|
||||
OK syntax: packages/discord/fixtures/legacy-owner-worker.mjs
|
||||
OK syntax: scripts/discord.sh
|
||||
OK syntax: scripts/discord-service.sh
|
||||
OK packages/discord declares no dependencies
|
||||
OK no bot-token-shaped string in packages/discord
|
||||
OK fixture binding uses placeholder ids only
|
||||
OK fixture binding validates
|
||||
OK real pi with the extension exposes exactly list_dir, read_file, search and no built-in tool
|
||||
OK real pi with a writable root exposes exactly the three reads plus write_file and edit_file, and writes nothing at start
|
||||
OK real pi with a web key exposes the three reads plus web_fetch and web_search, and no write tool without a writable root
|
||||
OK real pi with a git root exposes the reads, writes and the four git verbs, commits nothing at start, and never shows the token
|
||||
OK real pi with protocol vault adds reserve_id to the git verbs
|
||||
OK real pi with a setspark key exposes the reads and the eight record verbs, no counters, and never shows the key
|
||||
OK real pi refuses a git key on a read-only root (fail closed)
|
||||
OK real pi with the pilot flags (--no-tools) exposes no tool at all
|
||||
OK real pi exits non-zero without MOSAIC_DISCORD_TOOLS: no session, no tools (fail closed)
|
||||
OK a failing nested test fails the run under a parent runner's NODE_TEST_CONTEXT
|
||||
OK node --test packages/discord/tests/ (ℹ pass 178)
|
||||
OK scripts/discord.sh --help exits 0
|
||||
OK scripts/discord.sh check without a binding exits 4
|
||||
OK scripts/discord.sh recover without a binding exits 4
|
||||
OK scripts/discord.sh reload without a binding exits 4
|
||||
OK scripts/discord-service.sh without a command exits 4
|
||||
OK service unit renders with the repository path, a supervised run as the main process, exit 3 never retried, and reload as SIGHUP
|
||||
OK service install writes the rendered unit (0644) and leaves no temp file
|
||||
OK service install a second time reports unchanged
|
||||
OK systemd-analyze verify accepts the rendered unit
|
||||
OK service uninstall removes the unit file
|
||||
OK service install with an unknown flag exits 4
|
||||
OK service install with USER unset finishes and names the account for lingering
|
||||
|
||||
discord suite: 66 passed, 0 failed
|
||||
@@ -0,0 +1,21 @@
|
||||
OK initial ordinary-file install
|
||||
OK installed tree matches canonical source
|
||||
OK installed tree has no symlinks
|
||||
OK check detects installation drift
|
||||
OK sync refuses to overwrite installation drift
|
||||
OK check detects an extra destination file
|
||||
OK check detects an extra destination directory
|
||||
OK check rejects a destination symlink
|
||||
OK sync accepts a canonical source update
|
||||
OK updated installation matches canonical source
|
||||
scripts/test-extension-package.sh: line 14: 4103097 Killed "$@" > /dev/null 2>&1
|
||||
OK forced interruption kills the replacing process
|
||||
OK next invocation recovers old consistent installation
|
||||
OK interrupted replacement rolled back
|
||||
OK sync succeeds after interruption recovery
|
||||
OK unlocked stale lock file does not block
|
||||
OK active lock refuses a concurrent sync
|
||||
OK source symlink fails closed
|
||||
OK nested second entrypoint fails closed
|
||||
|
||||
extension package selftest: 18 passed, 0 failed
|
||||
@@ -0,0 +1,53 @@
|
||||
toolchain: node v26.8.1, python 3.12.8, jsonschema 4.26.0
|
||||
|
||||
OK syntax: scripts/foundation-inspect.mjs
|
||||
OK syntax: scripts/foundation/strict-json.mjs
|
||||
OK syntax: scripts/foundation/canonical.mjs
|
||||
OK syntax: scripts/foundation/resolve.mjs
|
||||
OK syntax: scripts/foundation/validate-record.mjs
|
||||
OK syntax: scripts/foundation/fixtures/build-fixtures.mjs
|
||||
OK syntax: scripts/foundation/canonical.test.mjs
|
||||
OK syntax: scripts/foundation/cli.test.mjs
|
||||
OK syntax: scripts/foundation/fixtures.test.mjs
|
||||
OK syntax: scripts/foundation/resolve.test.mjs
|
||||
OK syntax: scripts/foundation/strict-json.test.mjs
|
||||
OK syntax: scripts/foundation/verify-schema.py (ast only; no bytecode written)
|
||||
OK fixture generator runs
|
||||
OK checked-in fixtures/bundles equal a fresh generation
|
||||
OK checked-in fixtures/raw equal a fresh generation
|
||||
OK checked-in fixtures/index.json equal a fresh generation
|
||||
OK checked-in demo bundles equal a fresh generation
|
||||
OK a failing nested test fails the run under a parent runner's NODE_TEST_CONTEXT
|
||||
OK node --test scripts/foundation/ (ℹ pass 80)
|
||||
OK differential schema oracle: PASS: differential schema oracle (finite corpus; compatibility evidence, not equivalence proof)
|
||||
platform witness: strftime('%Y') for year 999 -> '999' (pinned checker refuses years 0001..0999)
|
||||
node v26.8.1; corpus 1568 records (38 pinned fixtures, 478 unique bundle records, 1052 typeCase/mutation/lexical cases)
|
||||
schema column: agree-valid 540, agree-invalid 991, DISAGREEMENTS 0; strict-only (parser-bound) cases: 27; unsupported-kind records not schema-assessed by the inspector: 10
|
||||
profile column (schema-valid records only): profile-valid 510, profile-invalid 30
|
||||
profile refusals asserted: 30 schema-agreed-valid records refused only by the strict typed-string profile (rule profile-pattern-mismatch), 12 declared by name; 73 named probes verified against declared schema/profile columns
|
||||
OK oracle: zero schema-column disagreements with the pinned checker
|
||||
OK oracle: strict-only profile refusals are counted and asserted
|
||||
OK demo: permitted read preview exits 0 (exit 0)
|
||||
OK demo: permitted file.change preview exits 0 (exit 0)
|
||||
OK demo: assignment.change proposal is unresolved (exit 3) (exit 3)
|
||||
OK demo: revoked registration is refused (exit 3) (exit 3)
|
||||
OK demo: message is not authority (exit 3) (exit 3)
|
||||
OK usage: no arguments exits 2 (exit 2)
|
||||
OK io: missing file exits 4 (exit 4)
|
||||
OK io: directory exits 4 (exit 4)
|
||||
OK io: symlink exits 4 (O_NOFOLLOW) (exit 4)
|
||||
OK bound: oversize fixture exits 2 (exit 2)
|
||||
OK profile: one final LF in a typed selection id is refused before admission (exit 2) (exit 2)
|
||||
OK profile: two final LFs fail the schema pattern itself (exit 2) (exit 2)
|
||||
OK profile: escaped newlines in free-form text stay allowed (exit 0) (exit 0)
|
||||
OK profile refusal is invalid-request/profile-pattern-mismatch with selection and operation withheld, value not echoed
|
||||
OK text output starts with the disclaimer
|
||||
OK json output is valid JSON with result allowed and exactly the charter §7 fields
|
||||
OK json golden matches byte-for-byte
|
||||
OK sandboxed bundle run (env -i, PATH=/nonexistent) produced the unresolved proposal
|
||||
OK sandbox inventory (path/type/size/mode/uid/gid/inode/mtime/sha256) unchanged by runs
|
||||
OK canary never printed (bundle run and credential-file run)
|
||||
OK a non-bundle JSON file is refused at the shape gate, not read into output
|
||||
OK no field of the non-bundle file is echoed
|
||||
|
||||
selftest: 44 passed, 0 failed
|
||||
@@ -0,0 +1,35 @@
|
||||
toolchain: node v26.8.1, git version 2.55.0
|
||||
|
||||
OK syntax: packages/queue/src/cli.mjs
|
||||
OK syntax: packages/queue/src/errors.mjs
|
||||
OK syntax: packages/queue/src/io.mjs
|
||||
OK syntax: packages/queue/src/lock.mjs
|
||||
OK syntax: packages/queue/src/queue.mjs
|
||||
OK syntax: packages/queue/src/review.mjs
|
||||
OK syntax: packages/queue/src/store.mjs
|
||||
OK syntax: packages/queue/tests/commit.test.mjs
|
||||
OK syntax: packages/queue/tests/data.test.mjs
|
||||
OK syntax: packages/queue/tests/dispatch.test.mjs
|
||||
OK syntax: packages/queue/tests/helpers.mjs
|
||||
OK syntax: packages/queue/tests/lock.test.mjs
|
||||
OK syntax: packages/queue/tests/migration.test.mjs
|
||||
OK syntax: packages/queue/tests/review.test.mjs
|
||||
OK syntax: packages/queue/tests/store.test.mjs
|
||||
OK syntax: packages/queue/tests/write.test.mjs
|
||||
OK syntax: packages/queue/tests/fixtures/fake-gitea.mjs
|
||||
OK syntax: packages/queue/tests/fixtures/kill-at.mjs
|
||||
OK syntax: packages/queue/tests/fixtures/lock-child.mjs
|
||||
OK syntax: packages/queue/tests/fixtures/mosaic-pre-a2.sh
|
||||
OK syntax: scripts/queue-commit.sh
|
||||
OK syntax: scripts/git-hooks/pre-commit
|
||||
OK syntax: scripts/mosaic
|
||||
OK queue-commit.sh, the guard and scripts/mosaic are executable
|
||||
OK packages/queue declares no dependencies
|
||||
ℹ tests 148
|
||||
ℹ pass 148
|
||||
ℹ fail 0
|
||||
OK node --test packages/queue/tests/
|
||||
OK scripts/mosaic queue help
|
||||
skip queue verify and render --check: this checkout (/home/jwoltje/filbert-scratch/s6-gate2) is not the queue's canonical root (/mnt/storage/src/mosaic-stack)
|
||||
|
||||
queue suite: 27 passed, 0 failed
|
||||
@@ -0,0 +1,7 @@
|
||||
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
|
||||
skip state-machine cases (docker daemon unavailable)
|
||||
|
||||
selftest: 4 passed, 0 failed
|
||||
@@ -0,0 +1,33 @@
|
||||
OK valid task validates (exit 0)
|
||||
OK unknown task key exits 2 (exit 2)
|
||||
OK unsupported taskVersion exits 2 (exit 2)
|
||||
OK invalid task id exits 2 (exit 2)
|
||||
OK empty prompt exits 2 (exit 2)
|
||||
OK NUL in expectExact exits 2 (exit 2)
|
||||
OK out-of-range timeout exits 2 (exit 2)
|
||||
OK missing mission file exits 4 (exit 4)
|
||||
OK task with valid mission validates (exit 0)
|
||||
OK invalid mission exits 2 (exit 2)
|
||||
OK validate missing task exits 4 (exit 4)
|
||||
OK validation does not modify the task file
|
||||
OK prune dry-run exits 0 (exit 0)
|
||||
OK dry-run deleted nothing
|
||||
OK prune --keep=2 --yes removes oldest (exit 0)
|
||||
OK kept exactly 2 newest runs
|
||||
OK newest run kept, oldest pruned
|
||||
OK append-only receipt written (3 entries)
|
||||
OK sessions/workspaces untouched by prune
|
||||
OK prune with invalid keep exits 4 (exit 4)
|
||||
skip adapter seam cases (docker daemon unavailable)
|
||||
skip workspace/capability cases (docker daemon unavailable)
|
||||
skip live task cases (docker unavailable)
|
||||
OK onboard without name exits 4 (non-interactive) (exit 4)
|
||||
OK onboard --name renders profile (exit 0)
|
||||
OK profile written
|
||||
OK canon structure: required filled, optional placeholdered
|
||||
OK canon sections present
|
||||
FAIL user recall run succeeds (exit 1)
|
||||
FAIL recalled user name (response: )
|
||||
OK no agent identity on headless run
|
||||
|
||||
selftest: 26 passed, 2 failed
|
||||
@@ -0,0 +1,29 @@
|
||||
5dc9e67c9ce42b86c40d83c8b1aa8d5ba93d6e8b51bb1f4d2964f4b270ee52aa base.txt
|
||||
ff57f58ab3fea8bd08d44046c66afa3c9dbdbe315af1884fe5f66e1914b870cf BUILD.md
|
||||
dc346027db9b650c70b57ff390b57a8d76d3e2ccd434538cb083fe9a97ea4454 build.patch
|
||||
5b3a934d01eb518e23b842623aeb3cbfc287f1b88110edc1e044d4131b2927f3 candidate-manifest.sha256
|
||||
ec2844698bd087200858279bd145060855374f88bad98a94e2a31d94a34cff61 files.txt
|
||||
8df77c49bf82833efa56d42401a4a06f20931b7a76dd5eed3a73fdec60e92e79 out/base-test-task.txt
|
||||
c6d438dd3ffa913d2e05aeb6fb65052285d06f29fc9acd4c10f58c23515614a6 out/node-business.txt
|
||||
9787d63649bbe350b9f5ce5f4f14fc95e5a2def3269db0124dd992a55c91d789 out/node-bus.txt
|
||||
f8b612d08369d22f4bedc7d026a63a7a9592f652ab7602bab27e98daea36eb90 out/node-cli.txt
|
||||
3d85276fa1675f46c8b26e2600cb8650d50231f8b39b5f142dae6615b73e6ce2 out/node-control-board.txt
|
||||
927a609029f89118e2ce57fe77e2435669c7a6b49d1b69c783f9ff0dcd0fa636 out/node-conversation.txt
|
||||
0d136f1b18657e38974378e579efc79080dc90c7ab94adb56a00d9861b45917c out/node-discord.txt
|
||||
bab5785db84cdd60bccbe263b0c5c692471b8eed539729a9a9596697f2cd110f out/node-harness.txt
|
||||
a43ef2dbaba0f0f8f17ae84296e08e6067b97c0467cbe89337a73f3bce01b8d5 out/node-ledger.txt
|
||||
04ed00245b6c46f554f6d4453ac9455f79e9571f5eaf05021c03f6d03f1276cb out/node-mosaic.txt
|
||||
39bb0c4e1030d5e94f1efdb0de1bc77c5c4baef65b1b39c1fceaa70d6bf9a4e6 out/node-queue.txt
|
||||
97d94f5e9d1a715fab380ca7f1afa5ce1806a700923186bf290036b83656ba1c out/node-seat.txt
|
||||
cd90ab3dc11bc8c457fc7d33ab9aac570d1bc2854025a089e3630bea01bb008f out/node-tasks.txt
|
||||
c0310ed920b5b5b1f1eaa8a51cd5ae2721c0eb404889d024127939faa5c1c497 out/node-webui.txt
|
||||
5a1bb131fdc9f3bb09829fabd9cd3f58cd649db694cd4d69d3e391b5aa1faf9a out/summary.txt
|
||||
f91fc95a173339137f981e24fbf630acfde957e32c3cd71e28304532083c6a77 out/test-auth.txt
|
||||
e1206366f07f3d9029530dcdcb55c0282d3f052adc60fc7fd51642d5d4ec3600 out/test-conductor.txt
|
||||
52d211444489ea729dafc3c9745d4474d9dfd50f41055cbad792424197f04073 out/test-config.txt
|
||||
7d5016ded1994048642f8b9ae845e52e8af4a069ba05c056c8f580a4589bacbe out/test-discord.txt
|
||||
89e5e21216ce3dfbdd4d9ebaaeb2cb1a3f8a03e34e458ffa948c6d478f375dff out/test-extension-package.txt
|
||||
83dfaa21c4e4ad941088efab3f69e87ebd2f6bd3cc72ede938e21dc06da8a34f out/test-foundation.txt
|
||||
75549ab0587c1bb5e06f9acd0e49ca21627fd936e5e2d94121ed8fe1d6e8898b out/test-queue.txt
|
||||
6183e6b9b05edb497e92ffd09a19bba49c6f656c750ecb2acc283021113d1a98 out/test-release.txt
|
||||
8df77c49bf82833efa56d42401a4a06f20931b7a76dd5eed3a73fdec60e92e79 out/test-task.txt
|
||||
Reference in New Issue
Block a user