docs(s6): row 41 round 4 candidate packet, R5 fix (filbert)
Candidate manifest 08a78972 (42 files). Gate at 0a4c8f13.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
This commit is contained in:
+134
-12
@@ -1,15 +1,127 @@
|
||||
# Row 41 (#1523): slice 1 S6, candidate packet, round 3
|
||||
# Row 41 (#1523): slice 1 S6, candidate packet, round 4
|
||||
|
||||
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, Sage's round 2 ruling (fix R1, R2
|
||||
and R3) and round 3 ruling (fix R4). Base: `915e00e5` (`base.txt`). None of
|
||||
the 42 files changed between the base and `66b9e082`, where the round 3 gate
|
||||
ran. The
|
||||
candidate source is uncommitted. There are no pushes. No token, private
|
||||
and R3), round 3 ruling (fix R4) and round 4 ruling (fix R5). Base:
|
||||
`915e00e5` (`base.txt`). None of the 42 files changed between the base and
|
||||
`0a4c8f13`, where the round 4 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/`.
|
||||
|
||||
## Round 4
|
||||
|
||||
Darkwing's round 3 asked for changes (#1523 comment 27032, rev 271, packet
|
||||
`agents/darkwing/work/s6-review/review-r3.md` and `r3/`, 974da6ed). R4 is
|
||||
verified fixed. Sage ruled: fix R5 in this round; leave R1 to R4 alone, no
|
||||
other scope. Four files changed since round 3, and none is new:
|
||||
|
||||
- `packages/harness/src/gate.mjs`, `README.md`
|
||||
- `packages/harness/tests/gate.test.mjs`, `pi-session.test.mjs`
|
||||
|
||||
No source outside `insideWorkspace` in the gate changed.
|
||||
|
||||
### R5: a relative path is resolved from the real path of Pi's cwd
|
||||
|
||||
Both adapters `cd "$MOSAIC_WORKSPACE"` before they start the harness, and
|
||||
a process's cwd is the real path (`getcwd`). Pi 0.85.1 resolves a relative
|
||||
path lexically against `process.cwd()` (`resolveToCwd`), so `..` climbs the
|
||||
real path's parents. Round 3's `insideWorkspace` resolved a relative path
|
||||
against the workspace as given. With the workspace or the dataRoot behind
|
||||
a symlink, the two climb different directories. In Darkwing's layout
|
||||
(`$R/data -> $R/deep/store`, workspace `$R/data/ws`), `../../data/ws/X` is
|
||||
`$R/data/ws/X` to the gate and `$R/deep/data/ws/X` to Pi. A real Pi
|
||||
session read a file there and wrote a new one.
|
||||
|
||||
The fix: a relative path must resolve inside from both the workspace's
|
||||
real path, as Pi resolves it, and the path as given. An absolute path is
|
||||
unchanged. `spellings()` already built from both bases (R4), so `read`'s
|
||||
other-spelling check needed no change.
|
||||
|
||||
The given-path check is a choice, and it is stricter than Pi. A path that
|
||||
is inside for Pi but climbs out of the given path is refused, for example
|
||||
`../../store/ws/x` when the workspace is `$R/a/ws -> $R/deep/store/ws`. I
|
||||
kept it so the gate stays fail-closed whatever directory it runs in, and
|
||||
doesn't rest on the adapter's `cd` alone. The cost is that such a path is
|
||||
refused even though it is safe. A session has no need for it: the plain
|
||||
relative path or the absolute one works.
|
||||
|
||||
### Claude Code isn't affected
|
||||
|
||||
Claude Code makes a `file_path` absolute against its own cwd before the
|
||||
`PreToolUse` hook runs. Its cwd is also the real path, so `claude-gate.mjs`
|
||||
gets the path Claude Code will open, and the absolute branch checks it.
|
||||
Darkwing's `claude-cwd-dotdot.sh` shows this: the hook's refusal names
|
||||
`<R>/deep/data/ws/secret.txt`, not the relative name. My rerun against
|
||||
round 4 gives the same (below). The new check covers a relative path from
|
||||
Claude Code too, if a later version passes one through.
|
||||
|
||||
### Tests
|
||||
|
||||
- `gate.test.mjs`, "a relative path climbs from the workspace's real path,
|
||||
in both harnesses". Two layouts, each in Pi and Claude Code:
|
||||
- workspace behind a symlink: `<dir>/a/ws -> <dir>/deep/store/ws`;
|
||||
- dataRoot behind a symlink: `<dir>/data -> <dir>/deep/store`, workspace
|
||||
`<dir>/data/workspaces/b/i`, the launcher's shape.
|
||||
|
||||
In each, the `../..` escape is inside as given and lands on a planted
|
||||
file outside for Pi (the test asserts both). Read and write of the escape
|
||||
are refused. Read and write of `../ws/...` or `../i/...`, through the
|
||||
symlinked root and back in, are allowed. A path that is inside for Pi
|
||||
but outside as given is refused.
|
||||
- `pi-session.test.mjs`, "pi: a relative path climbs from the real path of
|
||||
a workspace behind a symlink", and the same for a dataRoot. A real Pi
|
||||
0.85.1 session runs through the adapter and the mock API, in the
|
||||
symlinked workspace:
|
||||
- the escape read and write come back as gate errors;
|
||||
- the outside file's text never reaches stdout;
|
||||
- the outside directory holds only the planted secret, and no
|
||||
`planted.txt` appears in the workspace;
|
||||
- the inside read returns its content, and the inside write lands.
|
||||
|
||||
`session()` in that file gains an optional workspace placement for this;
|
||||
the other tests keep `<dir>/ws`.
|
||||
|
||||
### Mutants
|
||||
|
||||
Each was made on `gate.mjs`, restored from a copy and checked with `cmp`.
|
||||
The base passes `gate.test.mjs` and `pi-session.test.mjs`, 19/19.
|
||||
|
||||
| Mutant | Change | Result |
|
||||
|---|---|---|
|
||||
| given | the given path only (round 3) | 16/3: the gate test and both Pi sessions |
|
||||
| real | the real path only | 18/1: the gate test's stricter case |
|
||||
|
||||
The first draft of the gate test let `real` survive. The stricter case was
|
||||
added for it.
|
||||
|
||||
### Darkwing's probes against round 4
|
||||
|
||||
`r3/probe/pi-cwd-dotdot.sh` and `claude-cwd-dotdot.sh`, run unchanged with
|
||||
`WT` set to the round 4 build, both modes each. Outputs are in
|
||||
`out/probe-pi-cwd-dotdot.txt` and `out/probe-claude-cwd-dotdot.txt`.
|
||||
|
||||
| Probe | Mode | Result |
|
||||
|---|---|---|
|
||||
| Pi | read | gate refuses `../../data/ws/secret.txt`; the tool result is the refusal; the secret isn't read |
|
||||
| Pi | write | gate refuses `../../data/ws/planted.txt`; nothing is written outside or in the workspace |
|
||||
| Claude Code | read | the hook refuses `<R>/deep/data/ws/secret.txt` (absolute) |
|
||||
| Claude Code | write | the hook refuses `<R>/deep/data/ws/planted.txt`; nothing is written |
|
||||
|
||||
### Darkwing's other round 3 notes
|
||||
|
||||
Per Sage's ruling, nothing else changed this round. These go to
|
||||
follow-ups:
|
||||
|
||||
- Mw, Mx, My and Mz survive the spelling tests. Each flips some of
|
||||
Darkwing's spell-edge cases from refused to allowed: lowercase `am`,
|
||||
two apostrophes, a path below a respelled self-loop, and the
|
||||
normalised forms (`@`, NBSP before AM, `file://`, `%27`).
|
||||
- Darkwing's fix 2: realpath the workspace in the seat's `workspaceDir`
|
||||
(`packages/seat/src/session.mjs:46`), so a symlinked dataRoot never
|
||||
reaches the harness as a given path.
|
||||
- The Ma leftover (`fake-adapter.mjs`) reproduced, and stays a follow-up.
|
||||
|
||||
## Round 3
|
||||
|
||||
Darkwing's round 2 asked for changes (#1523 comment 27010, rev 259, packet
|
||||
@@ -285,7 +397,7 @@ How the new tests separate the mutants:
|
||||
## Files (`files.txt`, 42)
|
||||
|
||||
`build.patch` is `git diff --cached --binary 915e00e5` over those files,
|
||||
+4873/−63. It applies cleanly to `66b9e082` with `git apply --index`, and
|
||||
+4998/−63. It applies cleanly to `0a4c8f13` with `git apply`, and
|
||||
`sha256sum -c candidate-manifest.sha256` passes in that tree.
|
||||
|
||||
| Area | Files | What |
|
||||
@@ -400,27 +512,31 @@ project extensions) and is untouched.
|
||||
|
||||
## Gate
|
||||
|
||||
Run at `66b9e082` with this round's patch applied, in a detached worktree.
|
||||
Run at `0a4c8f13` with this round's patch applied, in a detached worktree.
|
||||
`sha256sum -c candidate-manifest.sha256` passed there (42 OK). Suites ran
|
||||
sequentially, each output in `out/` (`out/summary.txt`). `TMPDIR` was on
|
||||
the scratch disk and `DOCKER_HOST=unix:///nonexistent.sock`, so nothing
|
||||
reached Docker. Round 1's outputs stay in this directory at `9b670e27`,
|
||||
and round 2's at `779e9780`.
|
||||
round 2's at `779e9780` and round 3's at `0f38236f`. Darkwing's R5 probe
|
||||
outputs from round 4 are `out/probe-pi-cwd-dotdot.txt` and
|
||||
`out/probe-claude-cwd-dotdot.txt`.
|
||||
|
||||
| Suite | Pass | Fail |
|
||||
|---|---|---|
|
||||
| node: bus, business, cli, control-board | 74, 60, 83, 124 | 0 |
|
||||
| node: conversation, discord, harness, ledger | 152, 178, 53, 78 | 0 |
|
||||
| node: mosaic, queue, seat, tasks, webui | 69, 148, 27, 51, 14 | 0 |
|
||||
| node: conversation, discord, harness, ledger | 161, 178, 56, 78 | 0 |
|
||||
| node: mosaic, queue, seat, tasks, webui | 69, 148, 27, 51, 22 | 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 |
|
||||
|
||||
Against round 2, cli gains 1 test (Mr) and harness 3 (R4).
|
||||
Against round 3, harness gains 3 tests (R5). Conversation (161) and webui
|
||||
(22) grew because row 40 landed (08b428ec); none of their files is in this
|
||||
candidate.
|
||||
|
||||
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 at `66b9e082` fails the same two (`out/base-test-task.txt`,
|
||||
unpatched base at `0a4c8f13` fails the same two (`out/base-test-task.txt`,
|
||||
identical PASS/FAIL lines), so they come from the environment, not this
|
||||
candidate. No test process was left running after the gate.
|
||||
|
||||
@@ -446,3 +562,9 @@ names one, and fixtures use 127.0.0.1 and example.test.
|
||||
that test cleans up when it fails, isn't traced.
|
||||
- `packages/conversation` (row 40): a `shim.mjs` from a gate's
|
||||
conversation suite outlived the run, and it ignored SIGTERM.
|
||||
- Darkwing's round 3 survivors Mw, Mx, My and Mz in the gate's spelling
|
||||
check (lowercase `am`, two apostrophes, a path below a respelled
|
||||
self-loop, the normalised forms). Each needs a test; Sage kept them out
|
||||
of round 4.
|
||||
- Realpath the workspace in the seat's `workspaceDir`
|
||||
(`packages/seat/src/session.mjs:46`), Darkwing's round 3 fix 2.
|
||||
|
||||
@@ -2234,10 +2234,10 @@ index 00000000..41432793
|
||||
+});
|
||||
diff --git a/packages/harness/README.md b/packages/harness/README.md
|
||||
new file mode 100644
|
||||
index 00000000..aa705b77
|
||||
index 00000000..7598a573
|
||||
--- /dev/null
|
||||
+++ b/packages/harness/README.md
|
||||
@@ -0,0 +1,193 @@
|
||||
@@ -0,0 +1,206 @@
|
||||
+# harness
|
||||
+
|
||||
+What a managed role session runs from and runs as (slice 1 S6, issue #1523):
|
||||
@@ -2323,6 +2323,19 @@ index 00000000..aa705b77
|
||||
+directories first. Pi calls it from the extension, Claude Code from
|
||||
+`claude-gate.mjs`.
|
||||
+
|
||||
+A relative path is resolved the way Pi resolves it: against its working
|
||||
+directory. Both adapters `cd` into the workspace, and a process's working
|
||||
+directory is the real path, so `..` climbs the real path's parents. With
|
||||
+the workspace or the dataRoot behind a symlink, those differ from the
|
||||
+parents of the path as given, and `../../data/ws/x` can be inside as given
|
||||
+but outside for Pi. The gate requires a relative path to be inside from
|
||||
+both the real path and the path as given. The second check is stricter
|
||||
+than Pi: a path that only climbs out and back in through the real path's
|
||||
+parents is refused. That keeps the gate fail-closed whichever directory it
|
||||
+runs in. Claude Code isn't affected the same way: it makes a path absolute
|
||||
+against its own (real) working directory before the `PreToolUse` hook
|
||||
+sees it, so the gate gets the path Claude Code will open.
|
||||
+
|
||||
+Pi's `read` doesn't always open the name it is given. When that name
|
||||
+doesn't exist, it tries other spellings of the whole resolved path: a
|
||||
+narrow no-break space (U+202F) before ` AM.` or ` PM.`, the NFD form, a
|
||||
@@ -2646,10 +2659,10 @@ index 00000000..ecada305
|
||||
+}
|
||||
diff --git a/packages/harness/src/gate.mjs b/packages/harness/src/gate.mjs
|
||||
new file mode 100644
|
||||
index 00000000..eaddd5dd
|
||||
index 00000000..e68e8f92
|
||||
--- /dev/null
|
||||
+++ b/packages/harness/src/gate.mjs
|
||||
@@ -0,0 +1,158 @@
|
||||
@@ -0,0 +1,164 @@
|
||||
+// The tool gate both harnesses share. decide(policy, tool, input) answers
|
||||
+// one tool call: the policy's built-in tools and its typed tools pass,
|
||||
+// everything else is blocked, and every path argument of a file tool must
|
||||
@@ -2720,9 +2733,15 @@ index 00000000..eaddd5dd
|
||||
+ return target === root || target.startsWith(root + sep);
|
||||
+}
|
||||
+
|
||||
+// A relative path is resolved the way the tool resolves it: against its
|
||||
+// cwd. Both adapters cd into the workspace, and a process's cwd is the real
|
||||
+// path, so `..` climbs the real path's parents, not those of a workspace or
|
||||
+// dataRoot given through a symlink. It must be inside against the path as
|
||||
+// given too, so the check doesn't rest on how the harness was started.
|
||||
+export function insideWorkspace(workspace, p) {
|
||||
+ const s = normalise(p);
|
||||
+ return within(workspace, isAbsolute(s) ? resolve(s) : resolve(workspace, s));
|
||||
+ if (isAbsolute(s)) return within(workspace, resolve(s));
|
||||
+ return within(workspace, resolve(realpathSync(workspace), s)) && within(workspace, resolve(workspace, s));
|
||||
+}
|
||||
+
|
||||
+// Pi's read (dist/core/tools/path-utils.js resolveReadPathAsync) opens
|
||||
@@ -3913,15 +3932,15 @@ index 00000000..4024172d
|
||||
+}
|
||||
diff --git a/packages/harness/tests/gate.test.mjs b/packages/harness/tests/gate.test.mjs
|
||||
new file mode 100644
|
||||
index 00000000..116364a0
|
||||
index 00000000..85862fcc
|
||||
--- /dev/null
|
||||
+++ b/packages/harness/tests/gate.test.mjs
|
||||
@@ -0,0 +1,186 @@
|
||||
@@ -0,0 +1,233 @@
|
||||
+import { test } from "node:test";
|
||||
+import assert from "node:assert/strict";
|
||||
+import { mkdirSync, symlinkSync, writeFileSync } from "node:fs";
|
||||
+import { mkdirSync, realpathSync, symlinkSync, writeFileSync } from "node:fs";
|
||||
+import { homedir } from "node:os";
|
||||
+import { join } from "node:path";
|
||||
+import { join, resolve } from "node:path";
|
||||
+import { pathToFileURL } from "node:url";
|
||||
+import { claudeBuiltins, decide, insideWorkspace } from "../src/gate.mjs";
|
||||
+import { scratch } from "./helpers.mjs";
|
||||
@@ -4077,6 +4096,53 @@ index 00000000..116364a0
|
||||
+ blocked(decide(viaLink, "read", { path: "x.txt" }), /outside the workspace under another spelling/);
|
||||
+});
|
||||
+
|
||||
+// Both adapters cd into the workspace, and the tool's cwd is its real path,
|
||||
+// so `..` climbs the real path's parents. Through a symlinked workspace or
|
||||
+// dataRoot those differ from the given path's.
|
||||
+const CLIMBS = {
|
||||
+ // <dir>/a/ws -> <dir>/deep/store/ws: up two is <dir> as given, <dir>/deep for pi.
|
||||
+ workspace(dir) {
|
||||
+ mkdirSync(join(dir, "deep", "store", "ws"), { recursive: true });
|
||||
+ mkdirSync(join(dir, "a"));
|
||||
+ symlinkSync(join(dir, "deep", "store", "ws"), join(dir, "a", "ws"));
|
||||
+ return { workspace: join(dir, "a", "ws"), outside: join(dir, "deep", "a", "ws"), escape: "../../a/ws", stay: "../ws", strict: "../../store/ws" };
|
||||
+ },
|
||||
+ // dataRoot <dir>/data -> <dir>/deep/store, the workspace under it as the
|
||||
+ // launcher builds it: up four is <dir> as given, <dir>/deep for pi.
|
||||
+ dataRoot(dir) {
|
||||
+ mkdirSync(join(dir, "deep", "store", "workspaces", "b", "i"), { recursive: true });
|
||||
+ symlinkSync(join(dir, "deep", "store"), join(dir, "data"));
|
||||
+ return { workspace: join(dir, "data", "workspaces", "b", "i"), outside: join(dir, "deep", "data", "workspaces", "b", "i"), escape: "../../../../data/workspaces/b/i", stay: "../i", strict: "../../../../store/workspaces/b/i" };
|
||||
+ },
|
||||
+};
|
||||
+
|
||||
+test("a relative path climbs from the workspace's real path, in both harnesses", (t) => {
|
||||
+ for (const [name, build] of Object.entries(CLIMBS)) {
|
||||
+ for (const harness of ["pi", "claude-code"]) {
|
||||
+ const dir = scratch(t);
|
||||
+ const { workspace, outside, escape, stay, strict } = build(dir);
|
||||
+ writeFileSync(join(workspace, "a.txt"), "a");
|
||||
+ mkdirSync(outside, { recursive: true });
|
||||
+ writeFileSync(join(outside, "secret.txt"), "s");
|
||||
+ const policy = { harness, workspace, tools: ["read", "write"], typed: [] };
|
||||
+ const [read, write, field] = harness === "pi" ? ["read", "write", "path"] : ["Read", "Write", "file_path"];
|
||||
+ const at = `${name}, ${harness}`;
|
||||
+ // As given, the escape is inside; from the real path it is outside.
|
||||
+ assert.equal(resolve(workspace, escape), workspace, at);
|
||||
+ assert.equal(resolve(realpathSync(workspace), escape), outside, at);
|
||||
+ blocked(decide(policy, read, { [field]: `${escape}/secret.txt` }), /outside the workspace/);
|
||||
+ blocked(decide(policy, write, { [field]: `${escape}/planted.txt` }), /outside the workspace/);
|
||||
+ // Climbing out and back in by the same name stays inside on both paths.
|
||||
+ allowed(decide(policy, read, { [field]: `${stay}/a.txt` }));
|
||||
+ allowed(decide(policy, write, { [field]: `${stay}/new.txt` }));
|
||||
+ // The other way round, inside for pi but outside as given, is refused
|
||||
+ // too: the gate doesn't rest on the cwd the harness started in.
|
||||
+ assert.equal(resolve(realpathSync(workspace), strict), realpathSync(workspace), at);
|
||||
+ blocked(decide(policy, read, { [field]: `${strict}/a.txt` }), /outside the workspace/);
|
||||
+ }
|
||||
+ }
|
||||
+});
|
||||
+
|
||||
+test("claude path fields per tool", (t) => {
|
||||
+ const { workspace, policy } = setup(t, "claude-code", ["read", "write", "edit", "grep", "find"]);
|
||||
+ allowed(decide(policy, "Read", { file_path: join(workspace, "a.txt") }));
|
||||
@@ -4312,10 +4378,10 @@ index 00000000..72992dde
|
||||
+});
|
||||
diff --git a/packages/harness/tests/pi-session.test.mjs b/packages/harness/tests/pi-session.test.mjs
|
||||
new file mode 100644
|
||||
index 00000000..d2a89aef
|
||||
index 00000000..32db2aa3
|
||||
--- /dev/null
|
||||
+++ b/packages/harness/tests/pi-session.test.mjs
|
||||
@@ -0,0 +1,172 @@
|
||||
@@ -0,0 +1,231 @@
|
||||
+// Real pi (the pinned CLI) through adapters/pi with the extension, against
|
||||
+// the scripted Messages API. No real model, no network beyond 127.0.0.1.
|
||||
+
|
||||
@@ -4330,11 +4396,11 @@ index 00000000..d2a89aef
|
||||
+
|
||||
+const ADAPTER = join(REPO, "adapters", "pi", "adapter.sh");
|
||||
+
|
||||
+async function session(t, script) {
|
||||
+async function session(t, script, place = (dir) => join(dir, "ws")) {
|
||||
+ const dir = scratch(t);
|
||||
+ const workspace = join(dir, "ws");
|
||||
+ const workspace = place(dir);
|
||||
+ const agentDir = join(dir, "pi-agent");
|
||||
+ mkdirSync(workspace);
|
||||
+ mkdirSync(workspace, { recursive: true });
|
||||
+ mkdirSync(agentDir);
|
||||
+ writeFileSync(join(workspace, "notes.txt"), "inside\n");
|
||||
+ writeFileSync(join(dir, "secret.txt"), "outside\n");
|
||||
@@ -4471,6 +4537,65 @@ index 00000000..d2a89aef
|
||||
+ assert.ok(existsSync(s.turnMarker));
|
||||
+});
|
||||
+
|
||||
+// The adapter cds into the workspace, and pi resolves `..` from that real
|
||||
+// path. Each layout: where the workspace is given, and where `escape` lands
|
||||
+// for pi (as given, it is the workspace itself).
|
||||
+const CLIMBS = {
|
||||
+ workspace: {
|
||||
+ place(dir) {
|
||||
+ mkdirSync(join(dir, "deep", "store", "ws"), { recursive: true });
|
||||
+ mkdirSync(join(dir, "a"));
|
||||
+ symlinkSync(join(dir, "deep", "store", "ws"), join(dir, "a", "ws"));
|
||||
+ return join(dir, "a", "ws");
|
||||
+ },
|
||||
+ outside: (dir) => join(dir, "deep", "a", "ws"),
|
||||
+ escape: "../../a/ws",
|
||||
+ stay: "../ws",
|
||||
+ },
|
||||
+ dataRoot: {
|
||||
+ place(dir) {
|
||||
+ mkdirSync(join(dir, "deep", "store", "workspaces", "b", "i"), { recursive: true });
|
||||
+ symlinkSync(join(dir, "deep", "store"), join(dir, "data"));
|
||||
+ return join(dir, "data", "workspaces", "b", "i");
|
||||
+ },
|
||||
+ outside: (dir) => join(dir, "deep", "data", "workspaces", "b", "i"),
|
||||
+ escape: "../../../../data/workspaces/b/i",
|
||||
+ stay: "../i",
|
||||
+ },
|
||||
+};
|
||||
+
|
||||
+for (const [name, { place, outside, escape, stay }] of Object.entries(CLIMBS)) {
|
||||
+ test(`pi: a relative path climbs from the real path of a ${name} behind a symlink`, async (t) => {
|
||||
+ const { dir, workspace, s, env } = await session(
|
||||
+ t,
|
||||
+ [
|
||||
+ { name: "read", input: { path: `${escape}/secret.txt` } },
|
||||
+ { name: "write", input: { path: `${escape}/planted.txt`, content: "planted\n" } },
|
||||
+ { name: "read", input: { path: `${stay}/notes.txt` } },
|
||||
+ { name: "write", input: { path: `${stay}/fine.md`, content: "inside\n" } },
|
||||
+ ],
|
||||
+ place,
|
||||
+ );
|
||||
+ mkdirSync(outside(dir), { recursive: true });
|
||||
+ writeFileSync(join(outside(dir), "secret.txt"), "SECRET-OUTSIDE\n");
|
||||
+ const r = await turn(env, "Message 1 from jason, class REQUEST:\n\nread and write", workspace);
|
||||
+ assert.equal(r.code, 0, r.stderr);
|
||||
+ assert.doesNotMatch(r.stdout, /SECRET/);
|
||||
+ const results = JSON.parse(r.stdout.trim().slice("ANSWER ".length));
|
||||
+ assert.equal(results.length, 4);
|
||||
+ for (const i of [0, 1]) {
|
||||
+ assert.equal(results[i][0], true);
|
||||
+ assert.match(results[i][1], /outside the workspace/);
|
||||
+ }
|
||||
+ assert.deepEqual(readdirSync(outside(dir)), ["secret.txt"]);
|
||||
+ assert.ok(!existsSync(join(workspace, "planted.txt")));
|
||||
+ assert.deepEqual(results[2], [false, "inside\n"]);
|
||||
+ assert.equal(results[3][0], false);
|
||||
+ assert.ok(existsSync(join(workspace, "fine.md")), "the write inside landed");
|
||||
+ assert.ok(existsSync(s.turnMarker));
|
||||
+ });
|
||||
+}
|
||||
+
|
||||
+test("pi: a missing extension refuses before any model call", async (t) => {
|
||||
+ const { dir, api, s, env } = await session(t, []);
|
||||
+ const r = await turn({ ...env, MOSAIC_EXTENSIONS: join(dir, "missing.mjs") }, "x", s.workspace);
|
||||
|
||||
@@ -15,11 +15,11 @@ e9eeec4bef3384b5b15d1e7a2c9d913d2f3e329228c3e3e2cacb8f741aa21379 packages/cli/s
|
||||
0a2a452fdb3a4ea68478e54b3ca6694c51d074fa29a0ad8ea3740eb1b44ab780 packages/cli/tests/host.test.mjs
|
||||
1dbdb8166e8c47290bb4499b330ea32bec9a6f07179eef816edaa35603b1c408 packages/cli/tests/launcher.test.mjs
|
||||
709bd331bb0d76f28b464e518f4e1fc3bb0b303614e79d7e82479dcd679043cc packages/cli/tests/verbs.test.mjs
|
||||
570817222c5cc2f195963569e295db11f09f5dd56f19e3c9bdc220d5ee54435f packages/harness/README.md
|
||||
f996119e0afe972516408c8058c49d93192cabebbc48778cf9da5a9bbfa94a7c packages/harness/README.md
|
||||
34ef8212e27f052223443c66830839517f7a54ecb6ef7d85795b3e89c65b4d4c packages/harness/package.json
|
||||
22efd0bed7991561920ad29f6bf9a1ab2d7384185fab4267684a037e026d8e85 packages/harness/src/bundle.mjs
|
||||
32b5090760c95eea0e1232ff36ec13a1d9b58335b2eade621196dec1ebdbe784 packages/harness/src/claude-gate.mjs
|
||||
be416a0c70cd84c2c53e3259296077c28c5d3006e0a151efc950b2ce73354604 packages/harness/src/gate.mjs
|
||||
38219ee9ea8bc9239e6de375a79e98a431338ec51eee0b71e865c4efea620765 packages/harness/src/gate.mjs
|
||||
71e00113b8e5b55b410c7aae6232f76e972fc77aa68afa893da45c6b78d297e2 packages/harness/src/mcp-server.mjs
|
||||
d19753fa72f0b57e751749359644093713bcb650bfac566f55d2bc05cb817121 packages/harness/src/pi-extension.mjs
|
||||
1047aa092080e92efde2044dddaf82bacf428ed4b143c3846693471481f3612e packages/harness/src/runner.mjs
|
||||
@@ -28,10 +28,10 @@ d19753fa72f0b57e751749359644093713bcb650bfac566f55d2bc05cb817121 packages/harne
|
||||
96524da43b212e84d78108cbbf05eef324c934f3ad9e0d4cb2edcb0f3df256d4 packages/harness/tests/claude-gate.test.mjs
|
||||
197ec0f6c842552bea65fb2ab4c8cb8615fd6e691a2d0a592e1465d18a45d75f packages/harness/tests/claude-session.test.mjs
|
||||
8392172b243356932c974bdca9b4c449a312ae7a042ee7a22e0f22fbb98dbec9 packages/harness/tests/fixtures/fake-adapter.mjs
|
||||
38111dc604d32486941f9422654cc392fa2c7767e7f02d13ebbb0038b71669ce packages/harness/tests/gate.test.mjs
|
||||
c406566d92f79dc74f52588549303309e6d843bc8013885c33fb8e5e12d89195 packages/harness/tests/gate.test.mjs
|
||||
6e7509cfe6ad909440c25b750528360c0ba617c6c68b05d400bcd94b481c1f76 packages/harness/tests/helpers.mjs
|
||||
793eb11f970e4a8eecddec4dc48c24331e4de795bc04bfadf806a2fc3a15a8b4 packages/harness/tests/mcp-server.test.mjs
|
||||
c4798bb5681172e707d41446388e0490ca8a5398e801766988c8daba2646beaf packages/harness/tests/pi-session.test.mjs
|
||||
6d31a44a8e9835406df201e111d88fb3e6307c5993e471cce0d533153237e451 packages/harness/tests/pi-session.test.mjs
|
||||
c8f23144316501c3e163cf5a5566ddee552b3c93dee13594ae8c1eac66aedc64 packages/harness/tests/runner.test.mjs
|
||||
ef9130a3cb1ca3e278a8ed370060afd1145d1ceb211a915e83d75c8346b04931 packages/harness/tests/tools.test.mjs
|
||||
4e34456187387b02fa6d996c270dea4076b5d57952cddaa01f7a04843b351a02 packages/seat/README.md
|
||||
|
||||
@@ -1,77 +1,77 @@
|
||||
✔ launch identity is stamped, payload identity is refused and stale holder cannot send (239.740458ms)
|
||||
✔ decision classes route from policy; gated resolution is human-only, choice and target must match (434.495891ms)
|
||||
✔ claim exclusion, holder release, gated revoke and rerouting to a new holder are atomic (318.977003ms)
|
||||
✔ launch events require a human CLI capability; generic emit cannot forge authority events (182.276491ms)
|
||||
✔ within-role decisions close atomically and invalid options or blocking omissions refuse (193.552882ms)
|
||||
✔ observer capabilities read human inbox but cannot mutate or forge launch identity (170.799289ms)
|
||||
✔ task action subjects and linked decision trail are complete and ordered (203.941682ms)
|
||||
✔ launch binding is durable and reconnecting requires the identical trusted record (95.56779ms)
|
||||
✔ business isolation includes inherited object names and cross-business message references (163.750481ms)
|
||||
✔ authority never transfers between action, run, target, unresolved or replaced role holder (320.028764ms)
|
||||
✔ task projection uses schema current view, skipping earlier and equal-start polls (133.747714ms)
|
||||
✔ revocation permanently bars the old run from reclaiming first, including after broker restart (191.711174ms)
|
||||
✔ empty message references refuse before storage; refusal-evidence failure stays a typed error (134.762457ms)
|
||||
✔ both arbiters require human resolution when their cross-role route is themselves (255.780592ms)
|
||||
✔ S1 adapter takes resolved limits and refs, rejects mismatched instance, never mutates input (2.307073ms)
|
||||
✔ only validated broker references load; returned data and exceptions cannot expose a known token (4.946341ms)
|
||||
✔ bad file modes, symlinks, repository/data paths, malformed tokens and missing dates refuse (6.01105ms)
|
||||
✔ expiry refuses use and env references never become client data (0.903117ms)
|
||||
✔ S1 parsed service refs work, service mismatch refuses, Gitea rotation due is a warning state (1.796059ms)
|
||||
✔ opaque tokens shorter than 16 characters refuse before use (0.398635ms)
|
||||
✔ endLaunch writes session.ended, releases the run claim and kills its capabilities (206.587941ms)
|
||||
✔ endLaunch refuses an unknown run, a second end and a rebind of the ended run (134.400371ms)
|
||||
✔ a restarted broker refuses to rebind an ended run; a refused rebind leaves the run unbound (323.818329ms)
|
||||
✔ endLaunch leaves a claim another run took alone (183.703048ms)
|
||||
✔ refuse records action.refused against the caller with the code only (186.044736ms)
|
||||
✔ launches off refuses role.launch with launch-revoked until launches on (189.472852ms)
|
||||
✔ broker process: launch ops authorize role.launch, record refusals and end runs (274.249507ms)
|
||||
✔ human proof binds CLI entry, process start and nonce; agents and incomplete ancestry refuse (2.232835ms)
|
||||
✔ process reader gets own kernel identity without exposing environment values (1.347663ms)
|
||||
✔ EACCES ancestor environments skip only markers; commands and registered launches still refuse (0.961234ms)
|
||||
✔ real pid 1 remains inspectable when its environment is protected (0.369189ms)
|
||||
✔ within-role sends cite an open gated launch decision without spending it or naming it in grants (253.116133ms)
|
||||
✔ missing and foreign-business citations refuse and roll back message and grant (200.662761ms)
|
||||
✔ cross-role sends still need a matching resolved decision and consume it once (427.569214ms)
|
||||
✔ broker process binds trusted launches, offers reader capabilities, refuses human mutation, closes cleanly (253.423734ms)
|
||||
✔ startup token refusal returns safe code without value or partial listening broker (35.293578ms)
|
||||
✔ loaded fixture token is absent from socket replies and SQLite, including refusal evidence (291.67146ms)
|
||||
✔ killed broker leaves an explicit stale lock; another process cannot silently reclaim it (192.094911ms)
|
||||
✔ trusted host registers later launches; socket clients never have a registration verb (239.798505ms)
|
||||
✔ runtime excludes declared project roots even when host supplies no repoRoots (37.521057ms)
|
||||
✔ a refused launch binding leaves the broker and existing capabilities alive; bad protocol stops it (167.72013ms)
|
||||
✔ v3b prototype refusals, views and append-only mutations (1374.206323ms)
|
||||
✔ gated approval authorizes once, survives store reopen, and fresh approval works (315.775301ms)
|
||||
✔ another run cannot consume an approval; a failed check leaves it usable (391.815636ms)
|
||||
✔ two scheduled callers have exactly one grant and one consumed refusal (203.799579ms)
|
||||
✔ failed commit rolls consumption back; cross-role consumes and within-role stays reusable (362.938983ms)
|
||||
✔ class drift gated to cross-role refuses before consumption (228.029982ms)
|
||||
✔ class drift cross-role to gated refuses before consumption (244.576932ms)
|
||||
✔ class drift gated to within-role refuses before consumption (177.896571ms)
|
||||
✔ class drift cross-role to within-role refuses before consumption (195.452584ms)
|
||||
✔ class drift within-role to gated refuses before consumption (243.556487ms)
|
||||
✔ class drift within-role to cross-role refuses before consumption (179.418758ms)
|
||||
✔ message.send consumes approval and prevents a later send or authorize (199.875778ms)
|
||||
✔ role.revoke consumes approval and prevents a later revoke or authorize (269.090073ms)
|
||||
✔ creates private WAL store and excludes a second writer until explicit close (184.52587ms)
|
||||
✔ rollback is atomic and schema metadata is checked against trusted DDL, not just itself (177.881186ms)
|
||||
✔ existing empty database and symlink runtime directory refuse, never initialize over damage (357.948758ms)
|
||||
✔ crash during a transaction recovers no partial event after explicit fixture-only lock removal (218.819903ms)
|
||||
✔ writer refuses mixed at/read_at forms atomically, even through trusted SQL helpers (101.258819ms)
|
||||
✔ async transactions refuse before invoking their function (96.216937ms)
|
||||
✔ recordTask keeps sync reads and a role write apart (262.686766ms)
|
||||
✔ read_at must be one canonical UTC format, so the projection compares strings safely (116.864183ms)
|
||||
✔ a bad entry refuses the whole record (273.912315ms)
|
||||
✔ taskView reads the projection for one business (139.393069ms)
|
||||
✔ requestTask hands only a holder and a task verb to the handler, and records refusals (298.358617ms)
|
||||
✔ the server sends task verbs to the adapter with its own timeout; other verbs stay synchronous (434.226779ms)
|
||||
✔ without an adapter the server refuses every task verb (214.917219ms)
|
||||
✔ the runtime refuses an invalid adapter and closes a valid one (193.702117ms)
|
||||
✔ the process loads the S3 adapter from plain-data trackers (305.830649ms)
|
||||
✔ socket capability stamps launch identity; shared views use wire, no SQL client (218.013406ms)
|
||||
✔ two wire claims serialize; a lost reply never automatically retries (188.544206ms)
|
||||
✔ malformed, oversized and identity-forging envelopes refuse without echoing input (268.564851ms)
|
||||
✔ client preserves UTF-8 when a response divides a multibyte character (11.877466ms)
|
||||
✔ committed mutation followed by dropped reply reports unknown and is never retried (149.111642ms)
|
||||
✔ launch identity is stamped, payload identity is refused and stale holder cannot send (167.142297ms)
|
||||
✔ decision classes route from policy; gated resolution is human-only, choice and target must match (230.420936ms)
|
||||
✔ claim exclusion, holder release, gated revoke and rerouting to a new holder are atomic (235.647129ms)
|
||||
✔ launch events require a human CLI capability; generic emit cannot forge authority events (151.061215ms)
|
||||
✔ within-role decisions close atomically and invalid options or blocking omissions refuse (153.380732ms)
|
||||
✔ observer capabilities read human inbox but cannot mutate or forge launch identity (141.15041ms)
|
||||
✔ task action subjects and linked decision trail are complete and ordered (152.693428ms)
|
||||
✔ launch binding is durable and reconnecting requires the identical trusted record (86.297407ms)
|
||||
✔ business isolation includes inherited object names and cross-business message references (148.976508ms)
|
||||
✔ authority never transfers between action, run, target, unresolved or replaced role holder (210.77989ms)
|
||||
✔ task projection uses schema current view, skipping earlier and equal-start polls (104.339254ms)
|
||||
✔ revocation permanently bars the old run from reclaiming first, including after broker restart (173.293573ms)
|
||||
✔ empty message references refuse before storage; refusal-evidence failure stays a typed error (102.099882ms)
|
||||
✔ both arbiters require human resolution when their cross-role route is themselves (159.939336ms)
|
||||
✔ S1 adapter takes resolved limits and refs, rejects mismatched instance, never mutates input (2.227341ms)
|
||||
✔ only validated broker references load; returned data and exceptions cannot expose a known token (4.907077ms)
|
||||
✔ bad file modes, symlinks, repository/data paths, malformed tokens and missing dates refuse (8.415907ms)
|
||||
✔ expiry refuses use and env references never become client data (0.601154ms)
|
||||
✔ S1 parsed service refs work, service mismatch refuses, Gitea rotation due is a warning state (1.194888ms)
|
||||
✔ opaque tokens shorter than 16 characters refuse before use (0.261952ms)
|
||||
✔ endLaunch writes session.ended, releases the run claim and kills its capabilities (130.971346ms)
|
||||
✔ endLaunch refuses an unknown run, a second end and a rebind of the ended run (111.098252ms)
|
||||
✔ a restarted broker refuses to rebind an ended run; a refused rebind leaves the run unbound (131.39879ms)
|
||||
✔ endLaunch leaves a claim another run took alone (157.697482ms)
|
||||
✔ refuse records action.refused against the caller with the code only (125.302528ms)
|
||||
✔ launches off refuses role.launch with launch-revoked until launches on (156.743888ms)
|
||||
✔ broker process: launch ops authorize role.launch, record refusals and end runs (231.008594ms)
|
||||
✔ human proof binds CLI entry, process start and nonce; agents and incomplete ancestry refuse (2.64164ms)
|
||||
✔ process reader gets own kernel identity without exposing environment values (1.564657ms)
|
||||
✔ EACCES ancestor environments skip only markers; commands and registered launches still refuse (0.992806ms)
|
||||
✔ real pid 1 remains inspectable when its environment is protected (0.366038ms)
|
||||
✔ within-role sends cite an open gated launch decision without spending it or naming it in grants (192.270951ms)
|
||||
✔ missing and foreign-business citations refuse and roll back message and grant (165.248188ms)
|
||||
✔ cross-role sends still need a matching resolved decision and consume it once (225.177294ms)
|
||||
✔ broker process binds trusted launches, offers reader capabilities, refuses human mutation, closes cleanly (181.027469ms)
|
||||
✔ startup token refusal returns safe code without value or partial listening broker (37.780904ms)
|
||||
✔ loaded fixture token is absent from socket replies and SQLite, including refusal evidence (157.839221ms)
|
||||
✔ killed broker leaves an explicit stale lock; another process cannot silently reclaim it (164.036638ms)
|
||||
✔ trusted host registers later launches; socket clients never have a registration verb (163.83318ms)
|
||||
✔ runtime excludes declared project roots even when host supplies no repoRoots (37.888926ms)
|
||||
✔ a refused launch binding leaves the broker and existing capabilities alive; bad protocol stops it (145.998882ms)
|
||||
✔ v3b prototype refusals, views and append-only mutations (931.868277ms)
|
||||
✔ gated approval authorizes once, survives store reopen, and fresh approval works (234.144952ms)
|
||||
✔ another run cannot consume an approval; a failed check leaves it usable (192.203781ms)
|
||||
✔ two scheduled callers have exactly one grant and one consumed refusal (152.903468ms)
|
||||
✔ failed commit rolls consumption back; cross-role consumes and within-role stays reusable (273.028459ms)
|
||||
✔ class drift gated to cross-role refuses before consumption (176.737816ms)
|
||||
✔ class drift cross-role to gated refuses before consumption (173.106468ms)
|
||||
✔ class drift gated to within-role refuses before consumption (153.556383ms)
|
||||
✔ class drift cross-role to within-role refuses before consumption (172.837053ms)
|
||||
✔ class drift within-role to gated refuses before consumption (150.262194ms)
|
||||
✔ class drift within-role to cross-role refuses before consumption (146.76894ms)
|
||||
✔ message.send consumes approval and prevents a later send or authorize (166.63171ms)
|
||||
✔ role.revoke consumes approval and prevents a later revoke or authorize (189.942568ms)
|
||||
✔ creates private WAL store and excludes a second writer until explicit close (130.323883ms)
|
||||
✔ rollback is atomic and schema metadata is checked against trusted DDL, not just itself (151.473064ms)
|
||||
✔ existing empty database and symlink runtime directory refuse, never initialize over damage (160.113086ms)
|
||||
✔ crash during a transaction recovers no partial event after explicit fixture-only lock removal (159.365395ms)
|
||||
✔ writer refuses mixed at/read_at forms atomically, even through trusted SQL helpers (99.519215ms)
|
||||
✔ async transactions refuse before invoking their function (75.624887ms)
|
||||
✔ recordTask keeps sync reads and a role write apart (176.035958ms)
|
||||
✔ read_at must be one canonical UTC format, so the projection compares strings safely (102.593544ms)
|
||||
✔ a bad entry refuses the whole record (98.782671ms)
|
||||
✔ taskView reads the projection for one business (98.322625ms)
|
||||
✔ requestTask hands only a holder and a task verb to the handler, and records refusals (226.263728ms)
|
||||
✔ the server sends task verbs to the adapter with its own timeout; other verbs stay synchronous (390.207533ms)
|
||||
✔ without an adapter the server refuses every task verb (165.545934ms)
|
||||
✔ the runtime refuses an invalid adapter and closes a valid one (170.701529ms)
|
||||
✔ the process loads the S3 adapter from plain-data trackers (226.542656ms)
|
||||
✔ socket capability stamps launch identity; shared views use wire, no SQL client (145.169982ms)
|
||||
✔ two wire claims serialize; a lost reply never automatically retries (164.806381ms)
|
||||
✔ malformed, oversized and identity-forging envelopes refuse without echoing input (104.686616ms)
|
||||
✔ client preserves UTF-8 when a response divides a multibyte character (11.749373ms)
|
||||
✔ committed mutation followed by dropped reply reports unknown and is never retried (127.787676ms)
|
||||
ℹ tests 74
|
||||
ℹ suites 0
|
||||
ℹ pass 74
|
||||
@@ -79,4 +79,4 @@
|
||||
ℹ cancelled 0
|
||||
ℹ skipped 0
|
||||
ℹ todo 0
|
||||
ℹ duration_ms 3108.809343
|
||||
ℹ duration_ms 2287.87016
|
||||
|
||||
@@ -1,63 +1,63 @@
|
||||
✔ config directory and file path follow MOSAIC_CONFIG (1.94594ms)
|
||||
✔ the fixture business validates and comes back frozen (5.707571ms)
|
||||
✔ two instances may share a definition (1.972691ms)
|
||||
✔ top-level refusals (5.720077ms)
|
||||
✔ arbiters and projects (7.599597ms)
|
||||
✔ role instances (3.911216ms)
|
||||
✔ Vikunja bots (8.160669ms)
|
||||
✔ a role without Vikunja takes no tracker block (3.078813ms)
|
||||
✔ credential references match the definition's services (3.806947ms)
|
||||
✔ launch (10.102968ms)
|
||||
✔ loadBusiness: file checks (2.022471ms)
|
||||
✔ loadBusiness: not a regular file (45.202904ms)
|
||||
✔ loading writes nothing (1.253617ms)
|
||||
✔ names that are Object.prototype properties don't count as declared (2.657347ms)
|
||||
✔ the shipped example refuses as written and validates once filled in (0.542513ms)
|
||||
✔ usage errors exit 4 (285.619026ms)
|
||||
✔ validate: a good business exits 0 and prints instance digests (61.986118ms)
|
||||
✔ validate: project files (315.802361ms)
|
||||
✔ validate: missing files and a broken system config (235.381833ms)
|
||||
✔ validate: credential reference problems exit 2 and name each one (62.229075ms)
|
||||
✔ validate: a token file inside the repository is refused (64.115882ms)
|
||||
✔ validate: role definitions come from MOSAIC_ROLES_DIR (200.324478ms)
|
||||
✔ resolve: prints one instance's record (193.814279ms)
|
||||
✔ resolve: refusals (386.379682ms)
|
||||
✔ parse: exactly one of file or env, plus the service's date (3.254409ms)
|
||||
✔ check: a good file has no problems (1.798437ms)
|
||||
✔ check never opens the file: a write-only token passes (0.467731ms)
|
||||
✔ check: file problems (1.279571ms)
|
||||
✔ check: token files can't live in the repository or dataRoot, even through a linked directory (1.400708ms)
|
||||
✔ check: dates and environment references (0.575693ms)
|
||||
✔ path and load (2.919347ms)
|
||||
✔ refusals (1.710575ms)
|
||||
✔ systemVars flattens the validated config (2.516987ms)
|
||||
✔ precedence: system, business, project, project role, agent (6.390188ms)
|
||||
✔ limits narrow the definition and never widen it (3.070619ms)
|
||||
✔ role.launch stays within-role only for the instance the launch block names (6.400121ms)
|
||||
✔ limits.authority without role.launch leaves the launcher with no launch block (1.722667ms)
|
||||
✔ limits.authority narrows cross-role actions too (1.035999ms)
|
||||
✔ classify (1.279788ms)
|
||||
✔ the record carries what the broker and launcher need (1.144397ms)
|
||||
✔ digest: key order doesn't matter, any value change does (8.9708ms)
|
||||
✔ refusals (3.013121ms)
|
||||
✔ the four shipped version 2 roles load (5.279908ms)
|
||||
✔ shipped role scopes match addendum B section 2 and the SR runbook (1.490848ms)
|
||||
✔ shipped authority follows the note's table (0.764831ms)
|
||||
✔ version 1 files keep loading with no authority (1.274805ms)
|
||||
✔ the conductor policy isn't a role (0.306126ms)
|
||||
✔ a missing role file is exit 4, a symbolic link too (0.509379ms)
|
||||
✔ version 2 refusals (1.439143ms)
|
||||
✔ authority: closed vocabulary, no gated-only action, no overlap (2.33389ms)
|
||||
✔ credentials: Gitea scopes (0.923002ms)
|
||||
✔ credentials: Vikunja scopes are a group-to-verbs map from the grantable list (1.222158ms)
|
||||
✔ credentials: services (0.632125ms)
|
||||
✔ contract: a non-empty regular Markdown file beside the role file (1.111439ms)
|
||||
✔ every key names known layers and a merge rule (1.289197ms)
|
||||
✔ unknown keys and wrong layers refuse (0.896979ms)
|
||||
✔ types (1.783348ms)
|
||||
✔ merge: defaults, then the most specific layer wins (0.30552ms)
|
||||
✔ merge: limits only narrow, and provenance lists each source (0.393139ms)
|
||||
✔ merge doesn't change its inputs (0.177042ms)
|
||||
✔ config directory and file path follow MOSAIC_CONFIG (1.885852ms)
|
||||
✔ the fixture business validates and comes back frozen (5.658424ms)
|
||||
✔ two instances may share a definition (1.339683ms)
|
||||
✔ top-level refusals (3.506042ms)
|
||||
✔ arbiters and projects (5.159999ms)
|
||||
✔ role instances (3.489544ms)
|
||||
✔ Vikunja bots (6.636274ms)
|
||||
✔ a role without Vikunja takes no tracker block (3.122115ms)
|
||||
✔ credential references match the definition's services (3.679316ms)
|
||||
✔ launch (11.088925ms)
|
||||
✔ loadBusiness: file checks (2.76976ms)
|
||||
✔ loadBusiness: not a regular file (39.208263ms)
|
||||
✔ loading writes nothing (1.965275ms)
|
||||
✔ names that are Object.prototype properties don't count as declared (2.547254ms)
|
||||
✔ the shipped example refuses as written and validates once filled in (0.504688ms)
|
||||
✔ usage errors exit 4 (291.725474ms)
|
||||
✔ validate: a good business exits 0 and prints instance digests (71.185766ms)
|
||||
✔ validate: project files (320.543732ms)
|
||||
✔ validate: missing files and a broken system config (230.119986ms)
|
||||
✔ validate: credential reference problems exit 2 and name each one (64.013596ms)
|
||||
✔ validate: a token file inside the repository is refused (64.848806ms)
|
||||
✔ validate: role definitions come from MOSAIC_ROLES_DIR (185.260127ms)
|
||||
✔ resolve: prints one instance's record (194.639072ms)
|
||||
✔ resolve: refusals (379.459948ms)
|
||||
✔ parse: exactly one of file or env, plus the service's date (3.370475ms)
|
||||
✔ check: a good file has no problems (0.962955ms)
|
||||
✔ check never opens the file: a write-only token passes (0.425714ms)
|
||||
✔ check: file problems (1.175521ms)
|
||||
✔ check: token files can't live in the repository or dataRoot, even through a linked directory (1.182776ms)
|
||||
✔ check: dates and environment references (0.537616ms)
|
||||
✔ path and load (3.154659ms)
|
||||
✔ refusals (1.625627ms)
|
||||
✔ systemVars flattens the validated config (2.569268ms)
|
||||
✔ precedence: system, business, project, project role, agent (6.532793ms)
|
||||
✔ limits narrow the definition and never widen it (3.03896ms)
|
||||
✔ role.launch stays within-role only for the instance the launch block names (6.526328ms)
|
||||
✔ limits.authority without role.launch leaves the launcher with no launch block (2.854804ms)
|
||||
✔ limits.authority narrows cross-role actions too (1.628806ms)
|
||||
✔ classify (1.759815ms)
|
||||
✔ the record carries what the broker and launcher need (1.618335ms)
|
||||
✔ digest: key order doesn't matter, any value change does (9.545966ms)
|
||||
✔ refusals (2.287967ms)
|
||||
✔ the four shipped version 2 roles load (3.980318ms)
|
||||
✔ shipped role scopes match addendum B section 2 and the SR runbook (1.340415ms)
|
||||
✔ shipped authority follows the note's table (0.727965ms)
|
||||
✔ version 1 files keep loading with no authority (1.265329ms)
|
||||
✔ the conductor policy isn't a role (0.308318ms)
|
||||
✔ a missing role file is exit 4, a symbolic link too (0.53197ms)
|
||||
✔ version 2 refusals (1.734995ms)
|
||||
✔ authority: closed vocabulary, no gated-only action, no overlap (3.184459ms)
|
||||
✔ credentials: Gitea scopes (1.293834ms)
|
||||
✔ credentials: Vikunja scopes are a group-to-verbs map from the grantable list (1.699624ms)
|
||||
✔ credentials: services (0.850815ms)
|
||||
✔ contract: a non-empty regular Markdown file beside the role file (1.165891ms)
|
||||
✔ every key names known layers and a merge rule (1.208191ms)
|
||||
✔ unknown keys and wrong layers refuse (0.984427ms)
|
||||
✔ types (2.450103ms)
|
||||
✔ merge: defaults, then the most specific layer wins (0.37003ms)
|
||||
✔ merge: limits only narrow, and provenance lists each source (0.490281ms)
|
||||
✔ merge doesn't change its inputs (0.19074ms)
|
||||
ℹ tests 60
|
||||
ℹ suites 0
|
||||
ℹ pass 60
|
||||
@@ -65,4 +65,4 @@
|
||||
ℹ cancelled 0
|
||||
ℹ skipped 0
|
||||
ℹ todo 0
|
||||
ℹ duration_ms 1887.219031
|
||||
ℹ duration_ms 1878.19385
|
||||
|
||||
@@ -1,88 +1,88 @@
|
||||
✔ inbox lists only decisions routed to the human, with what approving authorizes and how to decide (116.16035ms)
|
||||
✔ decide resolves by id prefix with --yes, and the trail shows it in broker order with the task to follow (132.767677ms)
|
||||
✔ decide refuses without a terminal or --yes, on an unknown option and on a short reference (112.711559ms)
|
||||
✔ decide prints a declining choice as declining (109.36668ms)
|
||||
✔ an unknown outcome is reported once and never resent (99.216529ms)
|
||||
✔ a decision closed before the answer arrives exits 2 and points at its trail (99.275753ms)
|
||||
✔ a prefix that matches two open decisions exits 2 and resolves neither (79.160142ms)
|
||||
✔ without --business a command uses the live host's business, and a stale host.json is not a host (81.978588ms)
|
||||
✔ every human command refuses inside an agent run before it touches the bus (106.067287ms)
|
||||
✔ usage errors exit 4; no business and no host is a usage error (102.352812ms)
|
||||
✔ agents and tasks print through the broker (103.812899ms)
|
||||
✔ notify.json: missing, loose, malformed and extra keys refuse; a binding or null passes (1.186116ms)
|
||||
✔ bootConfig builds the broker's boot message for one business, with no trackers key when no project names one (42.538119ms)
|
||||
✔ trackers come from the tracker.* variables of the one project that names a tracker project (38.865355ms)
|
||||
✔ with two projects, the one that sets tracker.project supplies the tracker and the other is no conflict (40.992134ms)
|
||||
✔ two projects that each name a tracker project refuse, since the boot shape holds one (37.564192ms)
|
||||
✔ a business without tracker.baseUrl gets no trackers entry (31.426747ms)
|
||||
✔ an unknown business and a broken system config refuse with exit 3 (54.512934ms)
|
||||
✔ empty views say so (0.965635ms)
|
||||
✔ the trail keeps the broker's order and names a decision's task without its rows (1.169355ms)
|
||||
✔ tasks print the tracker fields the snapshot carries (0.231141ms)
|
||||
✔ the host boots the broker, binds a launch in process, and the notifier DMs a blocking decision exactly once (921.823079ms)
|
||||
✔ a notifier that dies takes the host down with exit 1, so the unit restarts the pair (234.249151ms)
|
||||
✔ a second host for the same data root refuses with exit 3 while the first runs (145.474385ms)
|
||||
✔ a broker reply that misses the wait breaks the channel: the late reply answers nothing, later requests refuse, the host exits 1 (1163.334044ms)
|
||||
✔ a broker reply with another request's id, or none, breaks the channel and the host exits 1 (234.382551ms)
|
||||
✔ a broker reply with no request waiting breaks the channel and the host exits 1 (143.162698ms)
|
||||
✔ a notifier that refuses stops the broker and the host refuses with exit 3 (161.984306ms)
|
||||
✔ a notifier that refuses after the broker died still refuses with exit 3, without a send to the dead broker (156.804707ms)
|
||||
✔ a notifier that dies before it replies, after the broker died, still refuses, without a send to the dead broker (101.744364ms)
|
||||
✔ a close send that fails with EPIPE after the notifier refuses still gives the notifier's refusal, exit 3 (148.28775ms)
|
||||
✔ a close send that fails with EPIPE after the notifier dies unanswered still gives the notifier's error (103.666899ms)
|
||||
✔ close() whose stop and close sends fail with EPIPE still finishes, with exit 1 (163.923058ms)
|
||||
✔ watchChildren reports a child that died before it was called, and one that dies later (22.293656ms)
|
||||
✔ bus stop refuses to signal a live pid that is not a bus host (202.92148ms)
|
||||
✔ bus start refuses with exit 3 and the code when the broker refuses to boot; bus status names the lock (204.29267ms)
|
||||
✔ bus start refuses with exit 3 without a notifier config (88.991059ms)
|
||||
✔ bus start runs until bus stop; status reports it while it runs (656.946975ms)
|
||||
✔ bus-service.sh renders the unit and installs it into a given directory (26.216533ms)
|
||||
✔ bus start --pm: the PM runs under its own PID namespace, registered, with a manifest (285.286099ms)
|
||||
✔ the PM launches a coder through its launch tool; the coder answers; refusals name their code (677.971127ms)
|
||||
✔ a runner that stops at once ends its launch with the runner's reason (266.797745ms)
|
||||
✔ 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 (698.435904ms)
|
||||
✔ 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 (3619.790341ms)
|
||||
✔ a malformed sessions.json refuses the host start with exit 3 and stays as it was (127.464512ms)
|
||||
✔ an over-long launch request is refused at once, not at the 10 s idle timeout (138.102925ms)
|
||||
✔ a launch client that never closes its side doesn't hold the host's close (123.236149ms)
|
||||
✔ a runner that ignores SIGTERM is killed when the host closes (1222.473883ms)
|
||||
✔ zoned uses the IANA zone across DST (15.721138ms)
|
||||
✔ each open blocking decision is DM'd once, across polls and a restart; non-blocking ones are not (121.040523ms)
|
||||
✔ two blocking decisions get two DMs with different nonces (132.938253ms)
|
||||
✔ the digest nonce differs per business and per day and fits Discord's 25 characters (0.184982ms)
|
||||
✔ a failed DM is journaled, backs off, and is retried until it lands (103.264969ms)
|
||||
✔ five definite refusals stop a DM: one gave-up line, one log line, and a restart keeps the count (111.979075ms)
|
||||
✔ 429s, 5xx-style unknowns and refusals without a status never count toward the limit (96.261206ms)
|
||||
✔ a crash between the fifth refusal and its gave-up line: the next poll appends it and sends nothing (109.787479ms)
|
||||
✔ 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 (141.731781ms)
|
||||
✔ a restart after the second refusal does not send before that refusal's 30 min are up (131.77121ms)
|
||||
✔ the digest goes at 08:00 Chicago once a day, with blocking ones marked as DM'd (135.646325ms)
|
||||
✔ a late start with no digest for the day sends one at once; an empty inbox gets one line (121.708992ms)
|
||||
✔ an inbox read failure is logged and the next poll retries (0.643351ms)
|
||||
✔ no Discord id reaches the journal or the log (97.433045ms)
|
||||
✔ the journal: a torn tail is copied out and truncated, so an append after it reopens cleanly (13.455834ms)
|
||||
✔ the journal: a crash between the copy and the truncate leaves a tail the next open repairs (26.085454ms)
|
||||
✔ the journal: a whole file that is one torn line truncates to empty (22.000323ms)
|
||||
✔ the journal: a malformed complete line refuses and leaves the file and any torn tail alone (2.339076ms)
|
||||
✔ the journal: a loose file mode, a loose directory or a symlinked journal refuses (0.593157ms)
|
||||
✔ the journal: a line with a wrong type refuses with exit 3 and names the field (4.170535ms)
|
||||
✔ the journal: a symlinked directory refuses and says it is a link (0.308408ms)
|
||||
✔ 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.463907ms)
|
||||
✔ the journal: an append after the file was swapped for a symlink refuses and writes nothing through it (0.378159ms)
|
||||
✔ the journal: a directory it cannot write or create refuses with exit 3 and names the path (0.576535ms)
|
||||
✔ digest content stays within Discord's 2000 characters (0.254664ms)
|
||||
✔ runLoop never overlaps ticks and stops after the one in flight (110.674806ms)
|
||||
✔ inbox lists only decisions routed to the human, with what approving authorizes and how to decide (109.821098ms)
|
||||
✔ decide resolves by id prefix with --yes, and the trail shows it in broker order with the task to follow (114.050715ms)
|
||||
✔ decide refuses without a terminal or --yes, on an unknown option and on a short reference (93.336022ms)
|
||||
✔ decide prints a declining choice as declining (110.188339ms)
|
||||
✔ an unknown outcome is reported once and never resent (87.255753ms)
|
||||
✔ a decision closed before the answer arrives exits 2 and points at its trail (90.124364ms)
|
||||
✔ a prefix that matches two open decisions exits 2 and resolves neither (77.82934ms)
|
||||
✔ without --business a command uses the live host's business, and a stale host.json is not a host (48.600651ms)
|
||||
✔ every human command refuses inside an agent run before it touches the bus (71.832475ms)
|
||||
✔ usage errors exit 4; no business and no host is a usage error (76.458185ms)
|
||||
✔ agents and tasks print through the broker (94.030107ms)
|
||||
✔ notify.json: missing, loose, malformed and extra keys refuse; a binding or null passes (1.432523ms)
|
||||
✔ bootConfig builds the broker's boot message for one business, with no trackers key when no project names one (38.895541ms)
|
||||
✔ trackers come from the tracker.* variables of the one project that names a tracker project (34.10218ms)
|
||||
✔ with two projects, the one that sets tracker.project supplies the tracker and the other is no conflict (33.11702ms)
|
||||
✔ two projects that each name a tracker project refuse, since the boot shape holds one (31.703833ms)
|
||||
✔ a business without tracker.baseUrl gets no trackers entry (29.926688ms)
|
||||
✔ an unknown business and a broken system config refuse with exit 3 (52.638178ms)
|
||||
✔ empty views say so (0.663714ms)
|
||||
✔ the trail keeps the broker's order and names a decision's task without its rows (0.76844ms)
|
||||
✔ tasks print the tracker fields the snapshot carries (0.141757ms)
|
||||
✔ the host boots the broker, binds a launch in process, and the notifier DMs a blocking decision exactly once (875.484388ms)
|
||||
✔ a notifier that dies takes the host down with exit 1, so the unit restarts the pair (222.741237ms)
|
||||
✔ a second host for the same data root refuses with exit 3 while the first runs (113.902862ms)
|
||||
✔ a broker reply that misses the wait breaks the channel: the late reply answers nothing, later requests refuse, the host exits 1 (1113.784714ms)
|
||||
✔ a broker reply with another request's id, or none, breaks the channel and the host exits 1 (272.214333ms)
|
||||
✔ a broker reply with no request waiting breaks the channel and the host exits 1 (132.624701ms)
|
||||
✔ a notifier that refuses stops the broker and the host refuses with exit 3 (209.872831ms)
|
||||
✔ a notifier that refuses after the broker died still refuses with exit 3, without a send to the dead broker (155.868613ms)
|
||||
✔ a notifier that dies before it replies, after the broker died, still refuses, without a send to the dead broker (101.015421ms)
|
||||
✔ a close send that fails with EPIPE after the notifier refuses still gives the notifier's refusal, exit 3 (147.852004ms)
|
||||
✔ a close send that fails with EPIPE after the notifier dies unanswered still gives the notifier's error (107.019954ms)
|
||||
✔ close() whose stop and close sends fail with EPIPE still finishes, with exit 1 (157.980688ms)
|
||||
✔ watchChildren reports a child that died before it was called, and one that dies later (22.95826ms)
|
||||
✔ bus stop refuses to signal a live pid that is not a bus host (202.708424ms)
|
||||
✔ bus start refuses with exit 3 and the code when the broker refuses to boot; bus status names the lock (217.25076ms)
|
||||
✔ bus start refuses with exit 3 without a notifier config (90.309442ms)
|
||||
✔ bus start runs until bus stop; status reports it while it runs (671.246535ms)
|
||||
✔ bus-service.sh renders the unit and installs it into a given directory (26.06789ms)
|
||||
✔ bus start --pm: the PM runs under its own PID namespace, registered, with a manifest (1259.700815ms)
|
||||
✔ the PM launches a coder through its launch tool; the coder answers; refusals name their code (579.017824ms)
|
||||
✔ a runner that stops at once ends its launch with the runner's reason (190.735678ms)
|
||||
✔ 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 (678.538547ms)
|
||||
✔ 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 (3616.19803ms)
|
||||
✔ a malformed sessions.json refuses the host start with exit 3 and stays as it was (107.76491ms)
|
||||
✔ an over-long launch request is refused at once, not at the 10 s idle timeout (117.120068ms)
|
||||
✔ a launch client that never closes its side doesn't hold the host's close (119.733944ms)
|
||||
✔ a runner that ignores SIGTERM is killed when the host closes (1231.559454ms)
|
||||
✔ zoned uses the IANA zone across DST (19.652324ms)
|
||||
✔ each open blocking decision is DM'd once, across polls and a restart; non-blocking ones are not (104.108826ms)
|
||||
✔ two blocking decisions get two DMs with different nonces (112.469993ms)
|
||||
✔ the digest nonce differs per business and per day and fits Discord's 25 characters (0.172163ms)
|
||||
✔ a failed DM is journaled, backs off, and is retried until it lands (105.067217ms)
|
||||
✔ five definite refusals stop a DM: one gave-up line, one log line, and a restart keeps the count (93.834153ms)
|
||||
✔ 429s, 5xx-style unknowns and refusals without a status never count toward the limit (83.061084ms)
|
||||
✔ a crash between the fifth refusal and its gave-up line: the next poll appends it and sends nothing (82.164605ms)
|
||||
✔ 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 (148.69866ms)
|
||||
✔ a restart after the second refusal does not send before that refusal's 30 min are up (111.61552ms)
|
||||
✔ the digest goes at 08:00 Chicago once a day, with blocking ones marked as DM'd (114.794865ms)
|
||||
✔ a late start with no digest for the day sends one at once; an empty inbox gets one line (77.93107ms)
|
||||
✔ an inbox read failure is logged and the next poll retries (0.608316ms)
|
||||
✔ no Discord id reaches the journal or the log (49.790922ms)
|
||||
✔ the journal: a torn tail is copied out and truncated, so an append after it reopens cleanly (16.051158ms)
|
||||
✔ the journal: a crash between the copy and the truncate leaves a tail the next open repairs (24.73249ms)
|
||||
✔ the journal: a whole file that is one torn line truncates to empty (10.327754ms)
|
||||
✔ the journal: a malformed complete line refuses and leaves the file and any torn tail alone (0.625063ms)
|
||||
✔ the journal: a loose file mode, a loose directory or a symlinked journal refuses (0.601874ms)
|
||||
✔ the journal: a line with a wrong type refuses with exit 3 and names the field (1.880814ms)
|
||||
✔ the journal: a symlinked directory refuses and says it is a link (0.328759ms)
|
||||
✔ 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.473835ms)
|
||||
✔ the journal: an append after the file was swapped for a symlink refuses and writes nothing through it (0.390065ms)
|
||||
✔ the journal: a directory it cannot write or create refuses with exit 3 and names the path (0.411686ms)
|
||||
✔ digest content stays within Discord's 2000 characters (0.265279ms)
|
||||
✔ runLoop never overlaps ticks and stops after the one in flight (111.390327ms)
|
||||
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 (388.552078ms)
|
||||
✔ the transport writes {business, verb, args} to the child and reads its JSON (33.653582ms)
|
||||
✔ a bus code on stderr becomes the exit code; garbage and timeouts are outcome problems (2175.341854ms)
|
||||
✔ busExit and refuseInsideAgent (0.649603ms)
|
||||
✔ talk sends a REQUEST, prints and reads everything that arrives, and stops at the reply (222.190237ms)
|
||||
✔ talk --wait 0 only sends; no reply within --wait exits 1 and says where it will show (1115.846632ms)
|
||||
✔ talk, stop and launches off/on refuse inside an agent run; bad arguments are usage errors (95.891317ms)
|
||||
✔ launches off and on go to the broker and change the business's launch state (103.309056ms)
|
||||
✔ launches list reads sessions.json and marks a stale entry; stop reports it and refuses a non-runner (64.963523ms)
|
||||
✔ bootConfig trackers reach the S3 adapter in the real broker child, which goes ready against a fake Vikunja (360.024844ms)
|
||||
✔ the transport writes {business, verb, args} to the child and reads its JSON (32.113859ms)
|
||||
✔ a bus code on stderr becomes the exit code; garbage and timeouts are outcome problems (2159.761004ms)
|
||||
✔ busExit and refuseInsideAgent (0.402234ms)
|
||||
✔ talk sends a REQUEST, prints and reads everything that arrives, and stops at the reply (190.507141ms)
|
||||
✔ talk --wait 0 only sends; no reply within --wait exits 1 and says where it will show (1127.402634ms)
|
||||
✔ talk, stop and launches off/on refuse inside an agent run; bad arguments are usage errors (69.059366ms)
|
||||
✔ launches off and on go to the broker and change the business's launch state (71.916292ms)
|
||||
✔ launches list reads sessions.json and marks a stale entry; stop reports it and refuses a non-runner (69.964586ms)
|
||||
ℹ tests 83
|
||||
ℹ suites 0
|
||||
ℹ pass 83
|
||||
@@ -90,4 +90,4 @@ task.close on a missing task answered: task-not-found; it made GET /tasks/999 40
|
||||
ℹ cancelled 0
|
||||
ℹ skipped 0
|
||||
ℹ todo 0
|
||||
ℹ duration_ms 7229.556012
|
||||
ℹ duration_ms 7977.647548
|
||||
|
||||
@@ -1,127 +1,127 @@
|
||||
✔ explicit request, Seen, ordinary completion and a new request have distinct attention states (4.096287ms)
|
||||
✔ attention convention ignores reasoning/quoted examples and permits leading blank lines (0.242785ms)
|
||||
✔ completed smoke replies and ordinary questions are idle, not human blockers (0.846915ms)
|
||||
✔ only an explicit first-line input request makes a finished reply waiting (0.171746ms)
|
||||
✔ tool activity, user text, errors and unfinished turns override attention text (0.136452ms)
|
||||
✔ STOP access failure is unknown, not absence, under a non-root identity (38.327363ms)
|
||||
✔ connector Task never inherits Discord routing envelopes; ordinary Task still uses user text (3.000707ms)
|
||||
✔ connector discovery keeps only safe identity; rejects modes, mismatches, links and traversal (1.497489ms)
|
||||
✔ canonical owner identity and STOP are independent; no tmux fallback or forged registration (5.879171ms)
|
||||
✔ connector reply refusal precedes forged live tmux registration; ordinary agent still sends (0.584339ms)
|
||||
✔ server rescans connector discovery and refuses HTTP reply without transport (35.559009ms)
|
||||
✔ connector session links and linked directories are not read (1.077521ms)
|
||||
✔ newer live matching launch marks old activity, preserves history/attention/attribution, then clears on new activity (3.749232ms)
|
||||
✔ CLI print uses the relaunch notice instead of old current preview (61.194863ms)
|
||||
✔ connector owner and fixed task never inherit a native relaunch notice (2.122352ms)
|
||||
✔ equality, stale/unknown/offline, mismatched registration and unknown activity do not assert relaunch (1.149868ms)
|
||||
✔ loadConfig: missing file throws ConfigError (2.276188ms)
|
||||
✔ loadConfig: invalid JSON throws ConfigError (0.410099ms)
|
||||
✔ loadConfig: missing dataRoot throws ConfigError (0.325667ms)
|
||||
✔ loadConfig: relative dataRoot throws ConfigError (0.324818ms)
|
||||
✔ loadConfig: valid config returns dataRoot (0.983333ms)
|
||||
✔ findNewestSession: picks the newest by mtime among two files (0.644738ms)
|
||||
✔ findNewestSession: finds files in nested subdirectories (0.3859ms)
|
||||
✔ findNewestSession: returns null for a missing dir (0.126045ms)
|
||||
✔ readSession: extracts fields, collapses/truncates text, counts a truncated final line (0.665906ms)
|
||||
✔ readSession: model and provider follow the latest model_change entry or assistant turn; null when the log names neither; scanAgent carries them (1.644308ms)
|
||||
✔ readSession: lastError carries the assistant errorMessage only when the last assistant turn errored (0.50558ms)
|
||||
✔ findNewestSession/scan: never read sibling auth or secrets next to a sessions dir (1.156336ms)
|
||||
✔ deriveState: full state table (0.18949ms)
|
||||
✔ rule: newest entry is an assistant message with a tool call, after a question-looking text, is working (0.317703ms)
|
||||
✔ rule: newest entry is a tool result with no assistant text after it is working (0.275116ms)
|
||||
✔ rule: a finished ordinary turn is idle, even if it says your move (0.335356ms)
|
||||
✔ task: the first user message of the session, from text blocks (0.252296ms)
|
||||
✔ task: a plain-string user content is accepted, whitespace collapsed and long text capped (0.247832ms)
|
||||
✔ task: no user message in the log means null (shown as unknown), never a guess (0.302295ms)
|
||||
✔ workspace: the live tmux pane path wins; the session cwd is the fallback; neither means null (0.384643ms)
|
||||
✔ activeProject: basename of the nearest .git directory or .git file above the workspace; none means null (0.569899ms)
|
||||
✔ scan: the written record carries task, workspace and activeProject (0.505496ms)
|
||||
✔ registration: overrides task, project and workspace; every source says registration; registered carries the launch fields; the grouping column is untouched (0.613266ms)
|
||||
✔ registration: empty task and null project/workspace leave the derived values in place; registered is still non-null (0.40108ms)
|
||||
✔ 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.061634ms)
|
||||
✔ registration: no registration leaves the Gate A fields exactly as before, and registered is null (0.447677ms)
|
||||
✔ loadRegistrations: a missing seatsDir gives empty lists (0.156698ms)
|
||||
✔ loadRegistrations: one good record, one malformed JSON, one with an unknown field; a stray file under seatsDir is ignored (0.751044ms)
|
||||
✔ 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.33041ms)
|
||||
✔ scan: writes the registration override to disk; index.json carries registered and registrationErrors (0.730321ms)
|
||||
✔ scan: a relative seatsDir throws ConfigError; an omitted seatsDir behaves as before (0.688744ms)
|
||||
✔ scanAgent: waitingOnYou is true for waiting/error and false otherwise (0.569466ms)
|
||||
✔ scanAgent: ageSeconds is computed from the injected now (0.263488ms)
|
||||
✔ scanAgent: sessionFile null and state idle when sessions dir is empty but alive (0.185838ms)
|
||||
✔ discoverRepoAgents: finds agents with a sessions dir, skips those without, sorted by name (0.323154ms)
|
||||
✔ discoverFleetAgents: finds agents with a sessions dir, sorted by name, fleet tmux fields (0.329147ms)
|
||||
✔ scan: writes per-agent files and index.json, rerun overwrites, no leftover tmp files (0.846915ms)
|
||||
✔ scan: relative boardDir throws ConfigError (0.116027ms)
|
||||
✔ CLI: scan with assume-alive liveness exits 0, prints board summary, writes board files (60.587692ms)
|
||||
✔ CLI: missing config exits 2 with a refused: message (60.171409ms)
|
||||
✔ CLI: unknown command exits 2 (52.257478ms)
|
||||
✔ CLI: unknown --liveness value exits 2 (49.224582ms)
|
||||
✔ panesRunPi: true when any trimmed line equals 'pi' (0.178207ms)
|
||||
✔ panesRunPi: false for bash-only, claude, empty, or node-pi-style lines (0.064143ms)
|
||||
✔ tmuxIsAlive: a pane running pi is alive (0.164539ms)
|
||||
✔ tmuxIsAlive: session exists but pi has exited is not alive (0.078587ms)
|
||||
✔ tmuxIsAlive: no such tmux session is not alive (0.046507ms)
|
||||
✔ tmuxIsAlive: tmux could not be run at all is unknown (null), never assumed alive (0.057703ms)
|
||||
✔ tmuxIsAlive: passes -L <socket> only when a socket is given (0.084943ms)
|
||||
✔ parsePanes: one pane per line, command and optional tab-separated path (0.083186ms)
|
||||
✔ tmuxInspect: reports the path of the pane running pi, not of a shell pane (0.068346ms)
|
||||
✔ tmuxInspect: no pi pane, no session, or no tmux gives no workspace and the matching liveness (0.085678ms)
|
||||
✔ loadSeen: missing file returns {} (0.143595ms)
|
||||
✔ loadSeen: invalid JSON throws ConfigError (0.176736ms)
|
||||
✔ loadSeen: a JSON array throws ConfigError (0.146391ms)
|
||||
✔ loadSeen: a non-string value throws ConfigError (0.1506ms)
|
||||
✔ markSeen: seen true adds the key and writes seen.json mode 0600, no leftover tmp files (0.317689ms)
|
||||
✔ markSeen: seen false deletes the key (0.245251ms)
|
||||
✔ markSeen: missing, empty, or non-string fields throw ConfigError (0.190901ms)
|
||||
✔ markSeen: project containing '/' throws ConfigError (0.114614ms)
|
||||
✔ markSeen: non-boolean seen throws ConfigError (0.097122ms)
|
||||
✔ scanAgent: a seen mark matching the waiting session's lastTimestamp clears waitingOnYou (0.270157ms)
|
||||
✔ scanAgent: a stale mark (agent wrote something newer) is not seen and waitingOnYou is true (0.207344ms)
|
||||
✔ scanAgent: a working session with a matching mark is not seen (marks only apply to waiting/error) (0.221339ms)
|
||||
✔ scanAgent: an error-state session with a matching mark is seen (0.22026ms)
|
||||
✔ scan: index.seen and waitingOnYou reflect seen.json, which scan never rewrites or deletes (0.461494ms)
|
||||
✔ scan: a corrupt seen.json makes scan throw ConfigError (fail closed) (0.168406ms)
|
||||
✔ 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.584854ms)
|
||||
✔ 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.559492ms)
|
||||
✔ 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 (0.887582ms)
|
||||
✔ isLoopbackHost: recognizes loopback hosts (1.257641ms)
|
||||
✔ isLoopbackHost: rejects non-loopback hosts (4.586746ms)
|
||||
✔ startServer: refuses a non-loopback host with ConfigError, never opens a socket (3.074877ms)
|
||||
✔ startServer: serves page, healthz, and a rescanning /api/board (34.39639ms)
|
||||
✔ startServer: a seatsDir registration overrides the row and index.registered reflects it (6.300202ms)
|
||||
✔ startServer: /api/board returns 500 JSON with an error field when scan throws (2.380365ms)
|
||||
✔ CLI: serve refuses a non-loopback host with exit 2 and a refused: message (55.462546ms)
|
||||
✔ CLI: serve rejects a non-numeric --port with exit 2 (54.15464ms)
|
||||
✔ CLI: scan still works after the async cli refactor (48.467479ms)
|
||||
✔ CLI: live serve prints its URL and answers /healthz (60.472536ms)
|
||||
✔ page.html: esc() escapes every HTML-significant character (0.615927ms)
|
||||
✔ POST /api/seen marks a row; GET /api/board still shows it seen; seen:false clears it (8.194124ms)
|
||||
✔ POST /api/seen without a JSON content-type returns 400 and does not write a mark (2.489995ms)
|
||||
✔ POST /api/seen with invalid JSON returns 400 (2.794772ms)
|
||||
✔ POST /api/seen with a body over 4096 bytes returns 400 (or resets the connection) and writes no mark (2.07639ms)
|
||||
✔ POST /api/seen with a missing agent returns 400 (1.345416ms)
|
||||
✔ POST /api/board returns 405; PUT /api/seen returns 405 (1.686153ms)
|
||||
✔ CLI: scan --print marks a seen row with 's' and the summary line ends with 'N seen)' (54.281161ms)
|
||||
✔ page.html: seenControl() escapes rec.project/agent/lastActivity, and the POST uses a JSON content-type (0.320625ms)
|
||||
✔ page.html: has a collapsed Seen section that lists seen rows with the shared row builder (0.264823ms)
|
||||
✔ page.html: each project has a Hide seen checkbox (default on) beside Hide offline, with a hidden-count note (0.169369ms)
|
||||
✔ page.html: a project header reads "N of N" only while a checkbox hides rows (0.128358ms)
|
||||
✔ page.html: every row shows Task and Active project, derived or the word unknown, with the workspace in the detail (0.308994ms)
|
||||
✔ 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.560554ms)
|
||||
✔ 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 (27.75916ms)
|
||||
✔ POST /api/reply: a registration with a tmux socket adds -L <socket> (25.018106ms)
|
||||
✔ POST /api/reply: a non-zero tool exit is a 200 with delivered false, the exit code and the stderr verbatim (26.681206ms)
|
||||
✔ 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 (14.406945ms)
|
||||
✔ POST /api/reply: a missing agent-send.sh is a 500 with the path in the error, not a crash (4.315379ms)
|
||||
✔ replyToRow: DEFAULT_AGENT_SEND is the repository's tools/tmux/agent-send.sh and it is executable (0.152417ms)
|
||||
✔ 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.568856ms)
|
||||
✔ startServer: /api/board carries taskSetBy from a live registration and null for the derived rows (2.500947ms)
|
||||
✔ 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.300847ms)
|
||||
✔ Host/Origin guard: GET /api/board and POST /api/reply refuse a foreign Host, a wrong port and a cross-origin Origin with 403 JSON, before any scan or send, and never send CORS headers (7.930828ms)
|
||||
✔ Host/Origin guard: loopback names on this port are accepted, with or without a same-origin Origin (29.300207ms)
|
||||
✔ conversation routes (F16): a foreign Host, a wrong port and a cross-origin Origin get 403 before the reader runs, with no CORS headers (5.619576ms)
|
||||
✔ every refusal code the reader can raise has an HTTP status (0.918207ms)
|
||||
✔ conversation routes: catalogue, first page, next page and follow over HTTP; refusals map to 4xx with their code; nothing is written (45.444967ms)
|
||||
✔ explicit request, Seen, ordinary completion and a new request have distinct attention states (3.516208ms)
|
||||
✔ attention convention ignores reasoning/quoted examples and permits leading blank lines (0.221007ms)
|
||||
✔ completed smoke replies and ordinary questions are idle, not human blockers (1.021704ms)
|
||||
✔ only an explicit first-line input request makes a finished reply waiting (0.214674ms)
|
||||
✔ tool activity, user text, errors and unfinished turns override attention text (0.201649ms)
|
||||
✔ STOP access failure is unknown, not absence, under a non-root identity (34.432562ms)
|
||||
✔ connector Task never inherits Discord routing envelopes; ordinary Task still uses user text (2.75456ms)
|
||||
✔ connector discovery keeps only safe identity; rejects modes, mismatches, links and traversal (1.225648ms)
|
||||
✔ canonical owner identity and STOP are independent; no tmux fallback or forged registration (4.882625ms)
|
||||
✔ connector reply refusal precedes forged live tmux registration; ordinary agent still sends (0.494122ms)
|
||||
✔ server rescans connector discovery and refuses HTTP reply without transport (32.30068ms)
|
||||
✔ connector session links and linked directories are not read (1.085108ms)
|
||||
✔ newer live matching launch marks old activity, preserves history/attention/attribution, then clears on new activity (2.307688ms)
|
||||
✔ CLI print uses the relaunch notice instead of old current preview (59.647081ms)
|
||||
✔ connector owner and fixed task never inherit a native relaunch notice (1.913401ms)
|
||||
✔ equality, stale/unknown/offline, mismatched registration and unknown activity do not assert relaunch (1.034946ms)
|
||||
✔ loadConfig: missing file throws ConfigError (1.421104ms)
|
||||
✔ loadConfig: invalid JSON throws ConfigError (0.285492ms)
|
||||
✔ loadConfig: missing dataRoot throws ConfigError (0.21408ms)
|
||||
✔ loadConfig: relative dataRoot throws ConfigError (0.2884ms)
|
||||
✔ loadConfig: valid config returns dataRoot (0.759311ms)
|
||||
✔ findNewestSession: picks the newest by mtime among two files (0.45778ms)
|
||||
✔ findNewestSession: finds files in nested subdirectories (0.275495ms)
|
||||
✔ findNewestSession: returns null for a missing dir (0.114267ms)
|
||||
✔ readSession: extracts fields, collapses/truncates text, counts a truncated final line (0.621165ms)
|
||||
✔ readSession: model and provider follow the latest model_change entry or assistant turn; null when the log names neither; scanAgent carries them (1.160846ms)
|
||||
✔ readSession: lastError carries the assistant errorMessage only when the last assistant turn errored (0.445693ms)
|
||||
✔ findNewestSession/scan: never read sibling auth or secrets next to a sessions dir (1.029261ms)
|
||||
✔ deriveState: full state table (0.169314ms)
|
||||
✔ rule: newest entry is an assistant message with a tool call, after a question-looking text, is working (0.299936ms)
|
||||
✔ rule: newest entry is a tool result with no assistant text after it is working (0.258709ms)
|
||||
✔ rule: a finished ordinary turn is idle, even if it says your move (0.248311ms)
|
||||
✔ task: the first user message of the session, from text blocks (0.239443ms)
|
||||
✔ task: a plain-string user content is accepted, whitespace collapsed and long text capped (0.285634ms)
|
||||
✔ task: no user message in the log means null (shown as unknown), never a guess (0.262166ms)
|
||||
✔ workspace: the live tmux pane path wins; the session cwd is the fallback; neither means null (0.355924ms)
|
||||
✔ activeProject: basename of the nearest .git directory or .git file above the workspace; none means null (0.479249ms)
|
||||
✔ scan: the written record carries task, workspace and activeProject (0.440493ms)
|
||||
✔ registration: overrides task, project and workspace; every source says registration; registered carries the launch fields; the grouping column is untouched (0.556582ms)
|
||||
✔ registration: empty task and null project/workspace leave the derived values in place; registered is still non-null (0.372034ms)
|
||||
✔ registration: a record whose pid is gone is stale; derived values win, sources say derived, registered stays with alive false; a pid the probe cannot decide is not stale; pidAlive itself (0.969936ms)
|
||||
✔ registration: no registration leaves the Gate A fields exactly as before, and registered is null (0.434541ms)
|
||||
✔ loadRegistrations: a missing seatsDir gives empty lists (0.14972ms)
|
||||
✔ loadRegistrations: one good record, one malformed JSON, one with an unknown field; a stray file under seatsDir is ignored (0.809575ms)
|
||||
✔ 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.322588ms)
|
||||
✔ scan: writes the registration override to disk; index.json carries registered and registrationErrors (0.689167ms)
|
||||
✔ scan: a relative seatsDir throws ConfigError; an omitted seatsDir behaves as before (0.293359ms)
|
||||
✔ scanAgent: waitingOnYou is true for waiting/error and false otherwise (0.549315ms)
|
||||
✔ scanAgent: ageSeconds is computed from the injected now (0.274554ms)
|
||||
✔ scanAgent: sessionFile null and state idle when sessions dir is empty but alive (0.212229ms)
|
||||
✔ discoverRepoAgents: finds agents with a sessions dir, skips those without, sorted by name (0.317955ms)
|
||||
✔ discoverFleetAgents: finds agents with a sessions dir, sorted by name, fleet tmux fields (0.333089ms)
|
||||
✔ scan: writes per-agent files and index.json, rerun overwrites, no leftover tmp files (0.874775ms)
|
||||
✔ scan: relative boardDir throws ConfigError (0.101351ms)
|
||||
✔ CLI: scan with assume-alive liveness exits 0, prints board summary, writes board files (56.416439ms)
|
||||
✔ CLI: missing config exits 2 with a refused: message (50.322403ms)
|
||||
✔ CLI: unknown command exits 2 (49.401288ms)
|
||||
✔ CLI: unknown --liveness value exits 2 (51.189689ms)
|
||||
✔ panesRunPi: true when any trimmed line equals 'pi' (0.193137ms)
|
||||
✔ panesRunPi: false for bash-only, claude, empty, or node-pi-style lines (0.072732ms)
|
||||
✔ tmuxIsAlive: a pane running pi is alive (0.182205ms)
|
||||
✔ tmuxIsAlive: session exists but pi has exited is not alive (0.071052ms)
|
||||
✔ tmuxIsAlive: no such tmux session is not alive (0.048439ms)
|
||||
✔ tmuxIsAlive: tmux could not be run at all is unknown (null), never assumed alive (0.067025ms)
|
||||
✔ tmuxIsAlive: passes -L <socket> only when a socket is given (0.091318ms)
|
||||
✔ parsePanes: one pane per line, command and optional tab-separated path (0.074592ms)
|
||||
✔ tmuxInspect: reports the path of the pane running pi, not of a shell pane (0.066011ms)
|
||||
✔ tmuxInspect: no pi pane, no session, or no tmux gives no workspace and the matching liveness (0.091921ms)
|
||||
✔ loadSeen: missing file returns {} (0.141722ms)
|
||||
✔ loadSeen: invalid JSON throws ConfigError (0.177117ms)
|
||||
✔ loadSeen: a JSON array throws ConfigError (0.149854ms)
|
||||
✔ loadSeen: a non-string value throws ConfigError (0.161851ms)
|
||||
✔ markSeen: seen true adds the key and writes seen.json mode 0600, no leftover tmp files (0.323351ms)
|
||||
✔ markSeen: seen false deletes the key (0.271685ms)
|
||||
✔ markSeen: missing, empty, or non-string fields throw ConfigError (0.226171ms)
|
||||
✔ markSeen: project containing '/' throws ConfigError (0.125133ms)
|
||||
✔ markSeen: non-boolean seen throws ConfigError (0.113424ms)
|
||||
✔ scanAgent: a seen mark matching the waiting session's lastTimestamp clears waitingOnYou (0.298807ms)
|
||||
✔ scanAgent: a stale mark (agent wrote something newer) is not seen and waitingOnYou is true (0.219178ms)
|
||||
✔ scanAgent: a working session with a matching mark is not seen (marks only apply to waiting/error) (0.216414ms)
|
||||
✔ scanAgent: an error-state session with a matching mark is seen (0.23189ms)
|
||||
✔ scan: index.seen and waitingOnYou reflect seen.json, which scan never rewrites or deletes (0.491272ms)
|
||||
✔ scan: a corrupt seen.json makes scan throw ConfigError (fail closed) (0.188284ms)
|
||||
✔ 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.597317ms)
|
||||
✔ 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.567468ms)
|
||||
✔ 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 (0.907092ms)
|
||||
✔ isLoopbackHost: recognizes loopback hosts (1.301749ms)
|
||||
✔ isLoopbackHost: rejects non-loopback hosts (4.395006ms)
|
||||
✔ startServer: refuses a non-loopback host with ConfigError, never opens a socket (3.037957ms)
|
||||
✔ startServer: serves page, healthz, and a rescanning /api/board (32.7969ms)
|
||||
✔ startServer: a seatsDir registration overrides the row and index.registered reflects it (5.757893ms)
|
||||
✔ startServer: /api/board returns 500 JSON with an error field when scan throws (2.289878ms)
|
||||
✔ CLI: serve refuses a non-loopback host with exit 2 and a refused: message (48.755191ms)
|
||||
✔ CLI: serve rejects a non-numeric --port with exit 2 (48.672064ms)
|
||||
✔ CLI: scan still works after the async cli refactor (53.128023ms)
|
||||
✔ CLI: live serve prints its URL and answers /healthz (58.19203ms)
|
||||
✔ page.html: esc() escapes every HTML-significant character (0.583626ms)
|
||||
✔ POST /api/seen marks a row; GET /api/board still shows it seen; seen:false clears it (7.824105ms)
|
||||
✔ POST /api/seen without a JSON content-type returns 400 and does not write a mark (1.849175ms)
|
||||
✔ POST /api/seen with invalid JSON returns 400 (2.650642ms)
|
||||
✔ POST /api/seen with a body over 4096 bytes returns 400 (or resets the connection) and writes no mark (1.955345ms)
|
||||
✔ POST /api/seen with a missing agent returns 400 (1.288633ms)
|
||||
✔ POST /api/board returns 405; PUT /api/seen returns 405 (1.62688ms)
|
||||
✔ CLI: scan --print marks a seen row with 's' and the summary line ends with 'N seen)' (53.682982ms)
|
||||
✔ page.html: seenControl() escapes rec.project/agent/lastActivity, and the POST uses a JSON content-type (0.306082ms)
|
||||
✔ page.html: has a collapsed Seen section that lists seen rows with the shared row builder (0.272848ms)
|
||||
✔ page.html: each project has a Hide seen checkbox (default on) beside Hide offline, with a hidden-count note (0.163563ms)
|
||||
✔ page.html: a project header reads "N of N" only while a checkbox hides rows (0.128431ms)
|
||||
✔ page.html: every row shows Task and Active project, derived or the word unknown, with the workspace in the detail (0.267941ms)
|
||||
✔ 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.543486ms)
|
||||
✔ 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 (26.420253ms)
|
||||
✔ POST /api/reply: a registration with a tmux socket adds -L <socket> (27.128876ms)
|
||||
✔ POST /api/reply: a non-zero tool exit is a 200 with delivered false, the exit code and the stderr verbatim (24.803785ms)
|
||||
✔ 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 (14.134004ms)
|
||||
✔ POST /api/reply: a missing agent-send.sh is a 500 with the path in the error, not a crash (4.289581ms)
|
||||
✔ replyToRow: DEFAULT_AGENT_SEND is the repository's tools/tmux/agent-send.sh and it is executable (0.16247ms)
|
||||
✔ 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.568982ms)
|
||||
✔ startServer: /api/board carries taskSetBy from a live registration and null for the derived rows (2.348384ms)
|
||||
✔ 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.300945ms)
|
||||
✔ Host/Origin guard: GET /api/board and POST /api/reply refuse a foreign Host, a wrong port and a cross-origin Origin with 403 JSON, before any scan or send, and never send CORS headers (7.60865ms)
|
||||
✔ Host/Origin guard: loopback names on this port are accepted, with or without a same-origin Origin (36.209428ms)
|
||||
✔ conversation routes (F16): a foreign Host, a wrong port and a cross-origin Origin get 403 before the reader runs, with no CORS headers (5.451935ms)
|
||||
✔ every refusal code the reader can raise has an HTTP status (0.909668ms)
|
||||
✔ conversation routes: catalogue, first page, next page and follow over HTTP; refusals map to 4xx with their code; nothing is written (48.522532ms)
|
||||
ℹ tests 124
|
||||
ℹ suites 0
|
||||
ℹ pass 124
|
||||
@@ -129,4 +129,4 @@
|
||||
ℹ cancelled 0
|
||||
ℹ skipped 0
|
||||
ℹ todo 0
|
||||
ℹ duration_ms 621.068041
|
||||
ℹ duration_ms 622.509231
|
||||
|
||||
@@ -1,160 +1,169 @@
|
||||
✔ W1: two processes acquire the same pair at once; exactly one claim (477.086964ms)
|
||||
✔ W1: two writers publish the same revision at once: one wins, the other gets null, the winner's record stays (37.843206ms)
|
||||
✔ W1: a revision name appears only after its bytes are synced; before that, only a temp file exists (19.247751ms)
|
||||
✔ W2: acquire while a claim is reserved or active refuses already-active (368.84834ms)
|
||||
✔ W3: acquire while stopping, uncertain, or stopped without proof refuses unsafe-replacement (410.911345ms)
|
||||
✔ W4: same session with another seat tuple, and the reverse, both refuse; a loser on the seat key closes it no-unit (280.706297ms)
|
||||
✔ W4: a hard link of one session under another seat is the same session: the second controller refuses already-active and launches nothing (217.122101ms)
|
||||
✔ W4: a copy of one session under another seat is the same session: the second controller refuses already-active and launches nothing (199.76267ms)
|
||||
✔ W4: a session header ID that changes after construction refuses target; nothing is claimed or launched (2.223489ms)
|
||||
✔ W5: SIGKILL between every publication barrier of acquire and transition; restart never finds two holders or a lost claim (9978.92205ms)
|
||||
✔ W5: SIGKILL between every publication barrier of release; restart finishes or holds the release (32447.817836ms)
|
||||
✔ W6: controller killed mid-turn while the engine lives; restart is uncertain, no launch, prompts refuse (328.138189ms)
|
||||
✔ W12: a live owner paused with SIGSTOP; a second controller refuses already-active and changes nothing (193.375637ms)
|
||||
✔ W13: crash after the engine spawns, before active; restart finds the live unit: uncertain, no second spawn, force stop only (647.531117ms)
|
||||
✔ W14: crash after reservation, before the spawn marker: stopped with a no-unit observation; the pair is free (307.626437ms)
|
||||
✔ W20: crash after the spawn marker, scope collected; uncertain in both runs, the marker is copied, no launch until a boot proof (442.883523ms)
|
||||
✔ W15: crash between the two keys during release; restart finishes it under the same claim ID (120.786161ms)
|
||||
✔ W7: recorded boot ID differs on the same machine: stopped with a boot proof; open tool calls become uncertain (199.28518ms)
|
||||
✔ W8: resume after a proven stop with the same pins: new claim ID, generation +1, same conversation, branch and leaf (510.669432ms)
|
||||
✔ W9: resume with a changed binary, argv digest, branch or leaf is refused and the claim is unchanged (978.317356ms)
|
||||
✔ W11: the controller writes no session file; only the fake engine's own appends appear (384.697568ms)
|
||||
✔ W16: a highest revision that won't parse holds the pair uncertain; the older stopped revision is not reused (93.004349ms)
|
||||
✔ W17: a claim root copied from another host refuses foreign-host and promotes nothing (93.796846ms)
|
||||
✔ G1: a session path or claim root under .pi/state, ~/.claude, the data root or a registration refuses at construction (3.873228ms)
|
||||
✔ G2: a symlink inside the fixture root to a live session file is refused by the real-path check (1.11013ms)
|
||||
✔ G3: a fixture path swapped for a live path after construction is refused at bind (1.892311ms)
|
||||
✔ K1: force stop kills a tool child that called setsid; stopped with a verified proof (2760.86431ms)
|
||||
✔ K2: K1 on the process-group fallback ends uncertain, never stopped (630.859964ms)
|
||||
✔ K3: SIGTERM acknowledged while a member lives: stopping until the kill phase, never stopped from TERM (2695.393731ms)
|
||||
✔ K4: two engines; force stop one; the other survives by independent observation (4759.634246ms)
|
||||
✔ K5: a stop during a tool call leaves the effect uncertain, and it is shown (2495.83911ms)
|
||||
✔ K12: a member forking in a loop: the freeze stops it, enumeration is complete, populated 0 after cgroup.kill (2572.283036ms)
|
||||
✔ K13: a member writing its pid into another cgroup is refused by the namespace; the kill is complete (2503.7825ms)
|
||||
✔ K15: the shim gone, engine/cgroup.events unreadable, or the engine cgroup missing: evidence unavailable, not empty; uncertain (5381.751999ms)
|
||||
✔ K10: controller killed between the TERM and kill phases: restart checks the invocation ID and re-runs from TERM for the same stop (817.464529ms)
|
||||
✔ K11: controller killed after the confirmation is recorded, before TERM: restart checks the invocation ID and re-runs from TERM for the same stop (622.709407ms)
|
||||
✔ K14: a unit with the recorded name but another invocation ID: evidence unavailable, no signals, uncertain (529.469371ms)
|
||||
✔ K6: recover without proof, without confirmation, or with changed pins is refused (1132.171151ms)
|
||||
✔ K7: recover after proof, then launch: new claim and execution, generation +1, same leaf; the cancelled prompt is not replayed (411.918412ms)
|
||||
✔ K8: an engine that loads another leaf on resume is refused before admission; it stays claimed until a proven stop (746.116766ms)
|
||||
✔ K9: an interrupt that never settles stays uncertain; force stop stays available; takeover is refused while fenced (3445.858035ms)
|
||||
✔ K16: a claim from another machine ID refuses foreign-host; no boot proof is issued (42.281682ms)
|
||||
✔ K17: two launcher calls with one eligibility record: one launch, the other refuses, no second engine (507.081862ms)
|
||||
✔ K18: the leaf changes after eligibility: launch refused; the reservation stays until released with proof (390.990071ms)
|
||||
✔ S1: `/goal x`, with leading spaces or a tab, refuses text-policy at admission; zero engine bytes (474.385289ms)
|
||||
✔ S2: every prefix pinned Pi interprets is refused, from the list the code uses; the rest reach the engine exactly (210.910934ms)
|
||||
✔ S3: `/goal` on the second line is pinned from the source: Pi checks only index 0, so it is admitted and sent exactly (205.02606ms)
|
||||
✔ S4: a `/` left in the composer is cleared when control transfers and returns; the next submit sends only the new text (292.731024ms)
|
||||
✔ S5: an observer terminal gets a paste then Enter, as send-message.sh does: not admitted: controller, nothing sent (215.429587ms)
|
||||
✔ S6: a mediated-shaped registration (no tmux) passed to the board's replyToRow: 409 no tmux session; exec never runs (0.556421ms)
|
||||
✔ S7: ESC, bracketed-paste markers and U+2028/U+2029 travel as one JSON string; the engine receives the exact text in one record (215.469925ms)
|
||||
✔ P3: a Pi confirm, select, input or editor dialog is shown disabled with a reason and never answered (319.283723ms)
|
||||
✔ E1: send, ack, user, toolCall, toolResult, final answer: shown once, no refresh, draft and reading position kept (200.663647ms)
|
||||
✔ E2: U+2028, U+2029 inside JSON strings and CRLF line ends each parse as one record, on the splitter and through the controller (204.301153ms)
|
||||
✔ E3: a multipart final, two blocks, null request correlation and duplicate delivery (221.684003ms)
|
||||
✔ 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 (228.565966ms)
|
||||
✔ E4: a gap or a new epoch also reconciles; nothing is concatenated across a gap (10.969327ms)
|
||||
✔ E5: an unknown native event gives no client event; evidence records its type and bytes; the terminal count goes up (408.146813ms)
|
||||
✔ E6: a tool result delayed across a pause and a reconnect is reconciled without a manual refresh (219.412379ms)
|
||||
✔ E7: the terminal renders the same stream as the library client, as observer and then as controller, and submits only as controller (242.362495ms)
|
||||
✔ terminal: engine control characters are made visible; a lost connection refuses submit (191.926822ms)
|
||||
✔ terminal: outcome unknown is shown as such, with no resend offer, and nothing is resent (0.410826ms)
|
||||
✔ terminal: text after Enter in the same input chunk starts the next message; it never joins the one submitted (0.247826ms)
|
||||
✔ terminal: a paste-start marker split right after its ESC still opens the paste; the Enter inside it never submits (0.29839ms)
|
||||
✔ terminal: invisible and bidi characters are made visible; head, status and notice lines stay one line (0.105483ms)
|
||||
✔ every record these fixtures produced is a valid CHAT-01 record (E5: no record fails the schema) (217.236622ms)
|
||||
✔ H1: two takeovers with the same expected generation: one wins, +1; the other refuses generation (520.226617ms)
|
||||
✔ H2: the old controller's prompt after a takeover commits is refused with zero engine bytes (311.119802ms)
|
||||
✔ H3: a takeover while a prompt holds the dispatch lock: written under the old actor, or refused; never both (866.507043ms)
|
||||
✔ H4: self-takeover is refused (204.501347ms)
|
||||
✔ H9: Interrupt racing a prompt's dispatch: before the write, dispatch-refused and no-turn; after, §3 rules (447.543165ms)
|
||||
✔ H10: Interrupt and force stop together: one stop chain, force stop supersedes (1285.535233ms)
|
||||
✔ H10: an overlap during the pause before the abort: no abort, the stop ends uncertain (218.518959ms)
|
||||
✔ H10: a no-turn Interrupt lifts only its own fence; admission stays closed under force stop, overlap or revocation (810.936365ms)
|
||||
✔ H11: the controller disconnects mid-turn: work continues, the claim is unchanged, control stays put (330.683336ms)
|
||||
✔ H12: an exact retry after reconnecting to the same incarnation returns the same receipt; one dispatch (179.099307ms)
|
||||
✔ H13: a retry with the same request ID and different text is refused (175.414126ms)
|
||||
✔ H14: late stdout from the old engine after a replacement is dropped by incarnation, counted, never rendered (598.452038ms)
|
||||
✔ H15: a revoked connection's command is refused; the revocation fence holds (276.572706ms)
|
||||
✔ H16: a second controller for the same session refuses already-active; the first is untouched (194.198551ms)
|
||||
✔ H10: a second force stop while the first escalation runs refuses fenced; one escalation, and the claim records only the first stop's phases (824.830099ms)
|
||||
✔ H17: a confirmation reused, answered from another connection, or used after the stop changed is refused (899.906199ms)
|
||||
✔ H18: two prompts before any native output: the second refuses busy; one engine write (207.591122ms)
|
||||
✔ H19: the pipe fails mid-line under a large prompt: delivery-unknown transport-unknown, poisoned, no later write (353.488159ms)
|
||||
✔ H19: the link itself never writes again after an unknown outcome, whoever calls it (0.622545ms)
|
||||
✔ H19: the controller dies mid-write of a large line: after restart the outcome is unknown and nothing is resent (651.247301ms)
|
||||
✔ H20: the line is written but the ack is lost when the controller dies: orphan, outcome unknown, nothing resent (547.326231ms)
|
||||
✔ H21: a retry of the exact request with the old token after a crash is stale-incarnation; no second write (725.273442ms)
|
||||
✔ H22: after H21 and a valid recovery, a new request with the new token is admitted (3026.548403ms)
|
||||
✔ H23: requests pending at a restart are not resent; each shows outcome unknown (669.354531ms)
|
||||
✔ a plain conversation: catalogue row, one page, CHAT-01 records (7.522907ms)
|
||||
✔ native entries map to blocks: tools, thinking, bash, notices, ids that do not fit (1.848223ms)
|
||||
✔ F1: a malformed line is an unavailable part at its position, and reading continues (2.044985ms)
|
||||
✔ F1: a missing parent stops the history with a notice that names the unreadable lines (2.975236ms)
|
||||
✔ F1: an unreadable fork is never merged into another branch's history (1.92965ms)
|
||||
✔ F1: a follow stays on its branch when the next entry's parent is unreadable (3.307045ms)
|
||||
✔ F1: a file whose entries are all unreadable shows a notice per line (1.344609ms)
|
||||
✔ F2: a truncated trailing line marks the view incomplete, not an error (2.117891ms)
|
||||
✔ pagination: 100 parts, then the rest; parts concatenate to the whole branch (6.007126ms)
|
||||
✔ F3: a replaced file (new inode) refuses old cursors with reconcile (5.955883ms)
|
||||
✔ F4: a same-inode rewrite of the prefix refuses old cursors with reconcile (5.84029ms)
|
||||
✔ F5: growth between pages keeps the epoch and the page stops at the pinned length (5.009923ms)
|
||||
✔ F6: unknown, foreign and expired cursors refuse and leave the cursor usable (9.34471ms)
|
||||
✔ F7: a symlinked file and a symlinked directory component are refused, never opened (7.62926ms)
|
||||
✔ F8: a file swapped for a symlink after the catalogue is refused (1.867437ms)
|
||||
✔ F9: registrations never add or redirect a root (1.373913ms)
|
||||
✔ F10: a header cwd naming another project is refused (3.03181ms)
|
||||
✔ F11: parentSession renders with a marker and the parent is never opened (0.704383ms)
|
||||
✔ F12: two leaves: the default leaf is shown and the other branch reads alone (4.582929ms)
|
||||
✔ F12: a follow refuses when an appended duplicate id changes the branch's earlier parts (2.130198ms)
|
||||
✔ F12: a second root (Pi's resetLeaf) starts its own branch (1.109862ms)
|
||||
✔ F13: compaction is a marker in place, then the retained content (0.617581ms)
|
||||
✔ F14: long strings split into fragments and parts, reassemble exactly, and pages respect the byte cap (509.870195ms)
|
||||
✔ fragments never cut a surrogate pair and keep an empty string (4.365936ms)
|
||||
✔ F15: a Claude seat is an unsupported-harness placeholder whose directory is never read (1.369863ms)
|
||||
✔ unknown conversations, empty files and non-Pi files refuse (2.036043ms)
|
||||
✔ an unreadable file or root inside the roots is refused per row, not a failed catalogue (0.950981ms)
|
||||
✔ a seat directory without search permission refuses that root, not the catalogue (1.775213ms)
|
||||
✔ every page and cursor is a valid CHAT-01 record (809.072625ms)
|
||||
✔ the engine pin holds for the installed package (2.202385ms)
|
||||
✔ pinned Pi, sealed and without credentials, answers the controller's commands with the shapes the fake models (267.632344ms)
|
||||
✔ pinned Pi appends thinking_level_change at start when the branch lacks one, so the leaf moves (K8 then fails closed) (262.448955ms)
|
||||
✔ N25: ordinary Interrupt reconciles; a non-empty queue_update in the window is O5 (766.717715ms)
|
||||
✔ N1: an extension's follow-up queued after the fence is cleared before any abort; O5, Unknown (446.523598ms)
|
||||
✔ N1: a follow-up queued before the fence is O5 at once; the Interrupt refuses fenced (256.450936ms)
|
||||
✔ N2: with abort first, the fake runs the external item (the ordering guard has teeth) (22.687863ms)
|
||||
✔ N3: the fence lands in preflight, preflight errors, no run: failed, No run, uncertain (386.692522ms)
|
||||
✔ N4: the ack arrives after the first abort and a run starts: clear and abort again; Interrupted (193.528679ms)
|
||||
✔ N5: an input handler takes the prompt: ack, no run, delivery-unknown handled-without-run (301.720364ms)
|
||||
✔ N6: an extension queues between clear_queue and abort: O5 and O6, Unknown (718.122152ms)
|
||||
✔ N7: clear_queue times out: no abort, nativeQueue unknown, force stop still ends it (1878.204406ms)
|
||||
✔ N7: clear_queue answers an error: no abort, nativeQueue unknown, the link not poisoned (183.386995ms)
|
||||
✔ N8: an extension prompt starts a run during Mosaic preflight; the losing settle is O3 (259.106542ms)
|
||||
✔ N9: a run that started before the fence and ends aborted: failed interrupted, Interrupted (187.803775ms)
|
||||
✔ N9: decision 34: a run that ends aborted with no stop in progress: aborted-without-stop, uncertain, outcome unknown (240.606876ms)
|
||||
✔ N9: an aborted that lands after the fence but before any abort is written: aborted-without-stop, Unknown (364.999405ms)
|
||||
✔ N10: fake conformance (33.333721ms)
|
||||
✔ N11: the run fails before any user message_start: delivery-unknown ack-without-start, never failed (422.040983ms)
|
||||
✔ N12: input that starts a run after the final empty clear is O1 and not part of the stop's proof (268.737656ms)
|
||||
✔ N13: agent_start with no slot held is O1; a later prompt refuses with zero engine bytes (279.646287ms)
|
||||
✔ N14: the run completes while clear_queue is in flight: finished, Completed first, uncertain (362.146811ms)
|
||||
✔ N14: the run completes after the abort is written, before Pi applies it: finished, never relabelled (496.549872ms)
|
||||
✔ N15: the fence lands in preflight, then an input handler takes it: handled-without-run, No run (363.792931ms)
|
||||
✔ N16: Interrupt with no slot and no run refuses no-turn: no stop, no bytes, admission open (214.329359ms)
|
||||
✔ N17: the run fails on its own during the exchange: failed, Failed on its own (373.398495ms)
|
||||
✔ N18: no final assistant message_end, or a lost line: working stays working; before working, transport-unknown (800.518546ms)
|
||||
✔ N19: a losing extension prompt settles inside the Mosaic run before its user message: O3, run-overlap (623.374789ms)
|
||||
✔ N20: an extension triggerTurn during Mosaic preflight starts first; while streaming it queues with no signal (588.079734ms)
|
||||
✔ N21: a losing settle after the receipt settled finished is O2; the receipt stays finished (243.209329ms)
|
||||
✔ N22: an agent-level custom message is dropped by the clear with no signal; evidence names the seal (245.190658ms)
|
||||
✔ N23: a nextTurn message survives clear and abort and attaches to the next prompt, with no signal (236.863088ms)
|
||||
✔ 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 (260.673316ms)
|
||||
ℹ tests 152
|
||||
✔ W1: two processes acquire the same pair at once; exactly one claim (359.377469ms)
|
||||
✔ W1: two writers publish the same revision at once: one wins, the other gets null, the winner's record stays (33.328804ms)
|
||||
✔ W1: a revision name appears only after its bytes are synced; before that, only a temp file exists (18.46367ms)
|
||||
✔ W2: acquire while a claim is reserved or active refuses already-active (363.910358ms)
|
||||
✔ W3: acquire while stopping, uncertain, or stopped without proof refuses unsafe-replacement (388.698692ms)
|
||||
✔ W4: same session with another seat tuple, and the reverse, both refuse; a loser on the seat key closes it no-unit (269.874367ms)
|
||||
✔ W4: a hard link of one session under another seat is the same session: the second controller refuses already-active and launches nothing (285.475085ms)
|
||||
✔ W4: a copy of one session under another seat is the same session: the second controller refuses already-active and launches nothing (284.470985ms)
|
||||
✔ W4: a session header ID that changes after construction refuses target; nothing is claimed or launched (2.493769ms)
|
||||
✔ W5: SIGKILL between every publication barrier of acquire and transition; restart never finds two holders or a lost claim (10296.932811ms)
|
||||
✔ W5: SIGKILL between every publication barrier of release; restart finishes or holds the release (28433.437816ms)
|
||||
✔ W6: controller killed mid-turn while the engine lives; restart is uncertain, no launch, prompts refuse (253.554797ms)
|
||||
✔ W12: a live owner paused with SIGSTOP; a second controller refuses already-active and changes nothing (142.395586ms)
|
||||
✔ W13: crash after the engine spawns, before active; restart finds the live unit: uncertain, no second spawn, force stop only (382.889006ms)
|
||||
✔ W14: crash after reservation, before the spawn marker: stopped with a no-unit observation; the pair is free (272.398595ms)
|
||||
✔ W20: crash after the spawn marker, scope collected; uncertain in both runs, the marker is copied, no launch until a boot proof (345.056704ms)
|
||||
✔ W15: crash between the two keys during release; restart finishes it under the same claim ID (104.150164ms)
|
||||
✔ W7: recorded boot ID differs on the same machine: stopped with a boot proof; open tool calls become uncertain (168.293925ms)
|
||||
✔ W8: resume after a proven stop with the same pins: new claim ID, generation +1, same conversation, branch and leaf (274.310046ms)
|
||||
✔ W9: resume with a changed binary, argv digest, branch or leaf is refused and the claim is unchanged (778.563954ms)
|
||||
✔ W11: the controller writes no session file; only the fake engine's own appends appear (185.423529ms)
|
||||
✔ W16: a highest revision that won't parse holds the pair uncertain; the older stopped revision is not reused (92.977508ms)
|
||||
✔ W17: a claim root copied from another host refuses foreign-host and promotes nothing (76.575557ms)
|
||||
✔ G1: a session path or claim root under .pi/state, ~/.claude, the data root or a registration refuses at construction (4.590669ms)
|
||||
✔ G2: a symlink inside the fixture root to a live session file is refused by the real-path check (1.149257ms)
|
||||
✔ G3: a fixture path swapped for a live path after construction is refused at bind (1.74318ms)
|
||||
✔ K1: force stop kills a tool child that called setsid; stopped with a verified proof (3050.091635ms)
|
||||
✔ K2: K1 on the process-group fallback ends uncertain, never stopped (548.184536ms)
|
||||
✔ K3: SIGTERM acknowledged while a member lives: stopping until the kill phase, never stopped from TERM (2802.363959ms)
|
||||
✔ K4: two engines; force stop one; the other survives by independent observation (5084.349885ms)
|
||||
✔ K5: a stop during a tool call leaves the effect uncertain, and it is shown (2496.187397ms)
|
||||
✔ K12: a member forking in a loop: the freeze stops it, enumeration is complete, populated 0 after cgroup.kill (2576.331714ms)
|
||||
✔ K13: a member writing its pid into another cgroup is refused by the namespace; the kill is complete (2344.11219ms)
|
||||
✔ K15: the shim gone, engine/cgroup.events unreadable, or the engine cgroup missing: evidence unavailable, not empty; uncertain (4908.867845ms)
|
||||
✔ K10: controller killed between the TERM and kill phases: restart checks the invocation ID and re-runs from TERM for the same stop (767.49496ms)
|
||||
✔ K11: controller killed after the confirmation is recorded, before TERM: restart checks the invocation ID and re-runs from TERM for the same stop (598.437478ms)
|
||||
✔ K14: a unit with the recorded name but another invocation ID: evidence unavailable, no signals, uncertain (447.999699ms)
|
||||
✔ K6: recover without proof, without confirmation, or with changed pins is refused (766.506912ms)
|
||||
✔ K7: recover after proof, then launch: new claim and execution, generation +1, same leaf; the cancelled prompt is not replayed (422.600411ms)
|
||||
✔ K8: an engine that loads another leaf on resume is refused before admission; it stays claimed until a proven stop (578.979806ms)
|
||||
✔ K9: an interrupt that never settles stays uncertain; force stop stays available; takeover is refused while fenced (3319.848238ms)
|
||||
✔ K16: a claim from another machine ID refuses foreign-host; no boot proof is issued (56.082477ms)
|
||||
✔ K17: two launcher calls with one eligibility record: one launch, the other refuses, no second engine (398.032896ms)
|
||||
✔ K18: the leaf changes after eligibility: launch refused; the reservation stays until released with proof (379.039059ms)
|
||||
✔ K19: a scope launched with only the engine environment still reaches the user manager; the engine sees no other names (61.955645ms)
|
||||
✔ S1: `/goal x`, with leading spaces or a tab, refuses text-policy at admission; zero engine bytes (183.172361ms)
|
||||
✔ S2: every prefix pinned Pi interprets is refused, from the list the code uses; the rest reach the engine exactly (321.103419ms)
|
||||
✔ S3: `/goal` on the second line is pinned from the source: Pi checks only index 0, so it is admitted and sent exactly (202.586883ms)
|
||||
✔ S4: a `/` left in the composer is cleared when control transfers and returns; the next submit sends only the new text (286.026448ms)
|
||||
✔ S5: an observer terminal gets a paste then Enter, as send-message.sh does: not admitted: controller, nothing sent (201.937752ms)
|
||||
✔ S6: a mediated-shaped registration (no tmux) passed to the board's replyToRow: 409 no tmux session; exec never runs (0.440232ms)
|
||||
✔ S7: ESC, bracketed-paste markers and U+2028/U+2029 travel as one JSON string; the engine receives the exact text in one record (191.66305ms)
|
||||
✔ P3: a Pi confirm, select, input or editor dialog is shown disabled with a reason and never answered (310.844897ms)
|
||||
✔ E1: send, ack, user, toolCall, toolResult, final answer: shown once, no refresh, draft and reading position kept (304.129242ms)
|
||||
✔ E2: U+2028, U+2029 inside JSON strings and CRLF line ends each parse as one record, on the splitter and through the controller (273.873123ms)
|
||||
✔ E3: a multipart final, two blocks, null request correlation and duplicate delivery (249.26308ms)
|
||||
✔ 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 (233.10852ms)
|
||||
✔ E4: a gap or a new epoch also reconciles; nothing is concatenated across a gap (10.562087ms)
|
||||
✔ E5: an unknown native event gives no client event; evidence records its type and bytes; the terminal count goes up (228.685521ms)
|
||||
✔ E6: a tool result delayed across a pause and a reconnect is reconciled without a manual refresh (263.866374ms)
|
||||
✔ E7: the terminal renders the same stream as the library client, as observer and then as controller, and submits only as controller (354.599299ms)
|
||||
✔ terminal: engine control characters are made visible; a lost connection refuses submit (232.217026ms)
|
||||
✔ terminal: outcome unknown is shown as such, with no resend offer, and nothing is resent (0.426386ms)
|
||||
✔ terminal: text after Enter in the same input chunk starts the next message; it never joins the one submitted (0.279896ms)
|
||||
✔ terminal: a paste-start marker split right after its ESC still opens the paste; the Enter inside it never submits (0.319455ms)
|
||||
✔ terminal: Ctrl-T then Enter in one chunk is judged after the takeover, as if typed one key at a time (Filbert F2, #1507) (0.490795ms)
|
||||
✔ terminal: input held behind Ctrl-T waits for that takeover while an earlier action is still pending (Darkwing T1 on #1522) (31.062222ms)
|
||||
✔ terminal: an action that throws still releases the input held behind it, in order, then rethrows (5.78363ms)
|
||||
✔ terminal: after an action throws, later input still runs; input() puts the error in the status line (Filbert N1 on #1522) (0.388833ms)
|
||||
✔ terminal: input() reports an error when it happens, so it never overwrites a later status; held input's promise doesn't carry the holder's error (Filbert N4 on #1522) (0.402692ms)
|
||||
✔ terminal: invisible and bidi characters are made visible; head, status and notice lines stay one line (0.113939ms)
|
||||
✔ every record these fixtures produced is a valid CHAT-01 record (E5: no record fails the schema) (224.218939ms)
|
||||
✔ H1: two takeovers with the same expected generation: one wins, +1; the other refuses generation (389.777116ms)
|
||||
✔ H2: the old controller's prompt after a takeover commits is refused with zero engine bytes (298.231563ms)
|
||||
✔ H3: a takeover while a prompt holds the dispatch lock: written under the old actor, or refused; never both (878.173246ms)
|
||||
✔ H4: self-takeover is refused (281.507485ms)
|
||||
✔ H9: Interrupt racing a prompt's dispatch: before the write, dispatch-refused and no-turn; after, §3 rules (541.231998ms)
|
||||
✔ H10: Interrupt and force stop together: one stop chain, force stop supersedes (1273.826772ms)
|
||||
✔ H10: an overlap during the pause before the abort: no abort, the stop ends uncertain (187.264249ms)
|
||||
✔ H10: a no-turn Interrupt lifts only its own fence; admission stays closed under force stop, overlap or revocation (681.954522ms)
|
||||
✔ H11: the controller disconnects mid-turn: work continues, the claim is unchanged, control stays put (360.958917ms)
|
||||
✔ H12: an exact retry after reconnecting to the same incarnation returns the same receipt; one dispatch (179.335026ms)
|
||||
✔ H13: a retry with the same request ID and different text is refused (214.621381ms)
|
||||
✔ H14: late stdout from the old engine after a replacement is dropped by incarnation, counted, never rendered (669.427202ms)
|
||||
✔ H15: a revoked connection's command is refused; the revocation fence holds (324.988382ms)
|
||||
✔ H16: a second controller for the same session refuses already-active; the first is untouched (252.975247ms)
|
||||
✔ H10: a second force stop while the first escalation runs refuses fenced; one escalation, and the claim records only the first stop's phases (946.156333ms)
|
||||
✔ a force stop whose fence throws leaves no escalation flag behind, so the next force stop runs (Darkwing F2, #1507) (384.175884ms)
|
||||
✔ H17: a confirmation reused, answered from another connection, or used after the stop changed is refused (790.562657ms)
|
||||
✔ H18: two prompts before any native output: the second refuses busy; one engine write (213.007221ms)
|
||||
✔ H19: the pipe fails mid-line under a large prompt: delivery-unknown transport-unknown, poisoned, no later write (354.983148ms)
|
||||
✔ H19: the link itself never writes again after an unknown outcome, whoever calls it (1.133225ms)
|
||||
✔ H19: the controller dies mid-write of a large line: after restart the outcome is unknown and nothing is resent (672.737607ms)
|
||||
✔ H20: the line is written but the ack is lost when the controller dies: orphan, outcome unknown, nothing resent (571.854496ms)
|
||||
✔ H21: a retry of the exact request with the old token after a crash is stale-incarnation; no second write (766.474869ms)
|
||||
✔ H22: after H21 and a valid recovery, a new request with the new token is admitted (2921.745089ms)
|
||||
✔ H23: requests pending at a restart are not resent; each shows outcome unknown (784.232685ms)
|
||||
✔ a plain conversation: catalogue row, one page, CHAT-01 records (10.200413ms)
|
||||
✔ native entries map to blocks: tools, thinking, bash, notices, ids that do not fit (2.191269ms)
|
||||
✔ F1: a malformed line is an unavailable part at its position, and reading continues (1.732908ms)
|
||||
✔ F1: a missing parent stops the history with a notice that names the unreadable lines (2.436723ms)
|
||||
✔ F1: an unreadable fork is never merged into another branch's history (1.55108ms)
|
||||
✔ F1: a follow stays on its branch when the next entry's parent is unreadable (1.945379ms)
|
||||
✔ F1: a file whose entries are all unreadable shows a notice per line (0.772242ms)
|
||||
✔ F2: a truncated trailing line marks the view incomplete, not an error (1.20009ms)
|
||||
✔ pagination: 100 parts, then the rest; parts concatenate to the whole branch (3.62995ms)
|
||||
✔ F3: a replaced file (new inode) refuses old cursors with reconcile (4.026251ms)
|
||||
✔ F4: a same-inode rewrite of the prefix refuses old cursors with reconcile (4.677737ms)
|
||||
✔ F5: growth between pages keeps the epoch and the page stops at the pinned length (5.242173ms)
|
||||
✔ F6: unknown, foreign and expired cursors refuse and leave the cursor usable (7.719936ms)
|
||||
✔ F7: a symlinked file and a symlinked directory component are refused, never opened (9.703355ms)
|
||||
✔ F8: a file swapped for a symlink after the catalogue is refused (3.123398ms)
|
||||
✔ F9: registrations never add or redirect a root (1.979044ms)
|
||||
✔ F10: a header cwd naming another project is refused (4.129598ms)
|
||||
✔ F11: parentSession renders with a marker and the parent is never opened (1.188308ms)
|
||||
✔ F12: two leaves: the default leaf is shown and the other branch reads alone (4.379712ms)
|
||||
✔ F12: a follow refuses when an appended duplicate id changes the branch's earlier parts (2.106846ms)
|
||||
✔ F12: a second root (Pi's resetLeaf) starts its own branch (1.443953ms)
|
||||
✔ F13: compaction is a marker in place, then the retained content (0.834612ms)
|
||||
✔ F14: long strings split into fragments and parts, reassemble exactly, and pages respect the byte cap (545.028141ms)
|
||||
✔ fragments never cut a surrogate pair and keep an empty string (4.819554ms)
|
||||
✔ F15: a Claude seat is an unsupported-harness placeholder whose directory is never read (1.532997ms)
|
||||
✔ unknown conversations, empty files and non-Pi files refuse (2.227861ms)
|
||||
✔ an unreadable file or root inside the roots is refused per row, not a failed catalogue (1.045168ms)
|
||||
✔ a seat directory without search permission refuses that root, not the catalogue (1.925268ms)
|
||||
✔ every page and cursor is a valid CHAT-01 record (806.800026ms)
|
||||
✔ the engine pin holds for the installed package (2.653271ms)
|
||||
✔ pinned Pi, sealed and without credentials, answers the controller's commands with the shapes the fake models (296.340457ms)
|
||||
✔ sealed, pinned Pi ignores a trusted project's .pi resources; --approve past the seal would load them, and checkSeal refuses it (Filbert F2 on #1522) (580.767636ms)
|
||||
✔ pinned Pi appends thinking_level_change at start when the branch lacks one, so the leaf moves (K8 then fails closed) (245.397943ms)
|
||||
✔ N25: ordinary Interrupt reconciles; a non-empty queue_update in the window is O5 (596.98809ms)
|
||||
✔ N1: an extension's follow-up queued after the fence is cleared before any abort; O5, Unknown (442.973028ms)
|
||||
✔ N1: a follow-up queued before the fence is O5 at once; the Interrupt refuses fenced (231.583496ms)
|
||||
✔ N2: with abort first, the fake runs the external item (the ordering guard has teeth) (21.201669ms)
|
||||
✔ N3: the fence lands in preflight, preflight errors, no run: failed, No run, uncertain (355.566202ms)
|
||||
✔ N4: the ack arrives after the first abort and a run starts: clear and abort again; Interrupted (290.291879ms)
|
||||
✔ N5: an input handler takes the prompt: ack, no run, delivery-unknown handled-without-run (379.339574ms)
|
||||
✔ N6: an extension queues between clear_queue and abort: O5 and O6, Unknown (532.60621ms)
|
||||
✔ N7: clear_queue times out: no abort, nativeQueue unknown, force stop still ends it (1929.104949ms)
|
||||
✔ N7: clear_queue answers an error: no abort, nativeQueue unknown, the link not poisoned (211.450644ms)
|
||||
✔ N8: an extension prompt starts a run during Mosaic preflight; the losing settle is O3 (243.00463ms)
|
||||
✔ N9: a run that started before the fence and ends aborted: failed interrupted, Interrupted (218.445843ms)
|
||||
✔ N9: decision 34: a run that ends aborted with no stop in progress: aborted-without-stop, uncertain, outcome unknown (259.280416ms)
|
||||
✔ N9: an aborted that lands after the fence but before any abort is written: aborted-without-stop, Unknown (439.538163ms)
|
||||
✔ N10: fake conformance (32.625402ms)
|
||||
✔ N11: the run fails before any user message_start: delivery-unknown ack-without-start, never failed (444.021683ms)
|
||||
✔ N12: input that starts a run after the final empty clear is O1 and not part of the stop's proof (324.9118ms)
|
||||
✔ N13: agent_start with no slot held is O1; a later prompt refuses with zero engine bytes (310.151067ms)
|
||||
✔ N14: the run completes while clear_queue is in flight: finished, Completed first, uncertain (383.659084ms)
|
||||
✔ N14: the run completes after the abort is written, before Pi applies it: finished, never relabelled (395.918821ms)
|
||||
✔ N15: the fence lands in preflight, then an input handler takes it: handled-without-run, No run (357.274182ms)
|
||||
✔ N16: Interrupt with no slot and no run refuses no-turn: no stop, no bytes, admission open (216.272632ms)
|
||||
✔ N17: the run fails on its own during the exchange: failed, Failed on its own (372.258093ms)
|
||||
✔ N18: no final assistant message_end, or a lost line: working stays working; before working, transport-unknown (807.464808ms)
|
||||
✔ N19: a losing extension prompt settles inside the Mosaic run before its user message: O3, run-overlap (470.065465ms)
|
||||
✔ N20: an extension triggerTurn during Mosaic preflight starts first; while streaming it queues with no signal (476.174159ms)
|
||||
✔ N21: a losing settle after the receipt settled finished is O2; the receipt stays finished (212.272242ms)
|
||||
✔ N22: an agent-level custom message is dropped by the clear with no signal; evidence names the seal (201.480679ms)
|
||||
✔ N23: a nextTurn message survives clear and abort and attaches to the next prompt, with no signal (209.403669ms)
|
||||
✔ 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 (234.187048ms)
|
||||
✔ N24b: the seal covers the engine command and environment: config can't name either, the env is built from names, and a mutated command is refused at bind (207.709795ms)
|
||||
ℹ tests 161
|
||||
ℹ suites 0
|
||||
ℹ pass 152
|
||||
ℹ pass 161
|
||||
ℹ fail 0
|
||||
ℹ cancelled 0
|
||||
ℹ skipped 0
|
||||
ℹ todo 0
|
||||
ℹ duration_ms 50646.61675
|
||||
ℹ duration_ms 46015.209142
|
||||
|
||||
@@ -1,181 +1,181 @@
|
||||
✔ approvals: a request is validated before anything is posted; the rendering shows names and never ids (3.061117ms)
|
||||
✔ approvals: the ledger is appended and folded into open requests with bind and approval states (1.767093ms)
|
||||
✔ approvals: a reply approves only when it points at a request, says exactly approve, and comes from a listed approver once (0.615603ms)
|
||||
✔ approvals: a button approves only on its own request message with the matching custom id (0.478036ms)
|
||||
✔ approvals flow: a turn that opened a request posts the message with the button, records it, binds it, and both approvers approve (16.487303ms)
|
||||
✔ approvals flow: a non-approver, a repeat, a wrong custom id and a service refusal each get their fixed line and a drop entry (6.236326ms)
|
||||
✔ approvals flow: an invalid request from the model, a refused post, and no api client are recorded and post nothing (7.83515ms)
|
||||
✔ approvals flow: start retries a bind and an approval left as unknown, under their original keys (2.795146ms)
|
||||
✔ authorize: open channel, listed user (4.438211ms)
|
||||
✔ authorize: wrong guild (0.234954ms)
|
||||
✔ authorize: no guild (DM) (0.297955ms)
|
||||
✔ authorize: unlisted channel (0.180274ms)
|
||||
✔ authorize: unknown channel, no info (0.281968ms)
|
||||
✔ authorize: thread of listed parent (0.188408ms)
|
||||
✔ authorize: thread of unlisted parent (0.277177ms)
|
||||
✔ authorize: text channel that is not a thread and not listed (0.135711ms)
|
||||
✔ authorize: unlisted user (1.317345ms)
|
||||
✔ authorize: no author (0.494804ms)
|
||||
✔ authorize: bot author (listed id, bot flag) (0.229594ms)
|
||||
✔ authorize: system author (0.324529ms)
|
||||
✔ authorize: the bot itself (0.080666ms)
|
||||
✔ authorize: webhook (0.567864ms)
|
||||
✔ authorize: mention channel without mention (0.132158ms)
|
||||
✔ authorize: mention channel with bot mention (0.119555ms)
|
||||
✔ authorize: mention channel with @everyone only (0.264226ms)
|
||||
✔ authorize: mention channel mentioning someone else (0.073763ms)
|
||||
✔ authorize: mention channel, content says @bot but mentions empty (0.07773ms)
|
||||
✔ authorize: private thread under mention channel, mentioned (0.077728ms)
|
||||
✔ authorize: private thread under mention channel, not mentioned (0.063982ms)
|
||||
✔ authorize: thread in another guild per channel info (0.068838ms)
|
||||
✔ authorize: not an object (0.064676ms)
|
||||
✔ authorize: no id (0.050024ms)
|
||||
✔ authorize: oversize content is accepted and flagged (0.060955ms)
|
||||
✔ authorize: exactly the limit is not oversize (0.038377ms)
|
||||
✔ authorize: a user's channel allowlist drops them outside it, threads count as the parent, others are unaffected (0.433661ms)
|
||||
✔ authorize: order puts wrong guild before user, and user before channel (no channel lookup for strangers) (0.211684ms)
|
||||
✔ binding: a complete binding validates and is frozen (2.713898ms)
|
||||
✔ binding: unknown key, missing field, wrong type refuse with exit 2 (3.959445ms)
|
||||
✔ binding: empty allowlists refuse (0.42288ms)
|
||||
✔ binding: a user's channel allowlist must be non-empty, listed and unique; absent means every listed channel (1.510414ms)
|
||||
✔ reloadDiff: reloadable keys are summarised by id; every fixed key refuses with exit 2 (1.56028ms)
|
||||
✔ binding: dmRecipient is optional, a snowflake, one of the listed users, and a fixed key (1.942119ms)
|
||||
✔ binding: file must be 0600, regular, not a symlink (1.904793ms)
|
||||
✔ binding: token file mode, symlink, emptiness and shape are checked; token never appears in errors (1.618572ms)
|
||||
✔ cli: check refuses a non-0600 token file with exit 2 before any network use (95.112607ms)
|
||||
✔ context files: absolute paths, traversal, symlinks and out-of-repo targets refuse; in-repo files resolve (2.882373ms)
|
||||
✔ cli: check refuses a missing context file and a missing binding with exit 2; usage is exit 4 (324.375472ms)
|
||||
✔ cli: reload validates the file first (exit 2), then needs a live owner (exit 1); usage is exit 4 (215.962601ms)
|
||||
✔ cli: run refuses when STOP is present, before any network use (130.511988ms)
|
||||
✔ binding: tools is optional, validated strictly, a fixed key for reload, and its roots are resolved against the data root (1.732355ms)
|
||||
✔ binding: a git key is validated at load and reaches the extension whole, and only on a writable root (1.188652ms)
|
||||
✔ 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 (21.059867ms)
|
||||
✔ delivery: refused and unknown outcomes are journaled; a later chunk is not sent after a failure (16.533069ms)
|
||||
✔ delivery: restart with an unknown entry re-sends the same nonce once and reconciles before accepting traffic (1.394054ms)
|
||||
✔ delivery: an unknown entry older than the dedupe window is marked refused, not re-sent; a still-unknown one refuses start (1.059545ms)
|
||||
✔ delivery: repeated unknown reconciliations never refresh the dedupe window; the original intent time decides (1.278418ms)
|
||||
✔ turn: a failed engine turn posts the fixed line, never model output, and writes a failed record (2.785008ms)
|
||||
✔ turn: a second message during a turn is held by the engine, both get their own reply and record (32.205924ms)
|
||||
✔ turn: a thread under a listed channel is answered in the thread; an unknown thread is looked up once (2.018175ms)
|
||||
✔ drop: an unlisted user gets silence and one drop line; no inbox entry, no REST call, no engine call (1.07793ms)
|
||||
✔ drop: an oversize message is accepted into the inbox, answered with the fixed line and journaled as a drop (0.911887ms)
|
||||
✔ restart: an inbox with three ids and a replay of the same three produces zero turns (42.432734ms)
|
||||
✔ stop: STOP present refuses start; STOP written while running refuses new turns and the current one finishes (32.443071ms)
|
||||
✔ ceiling: the ceiling plus one is refused and journaled; one fixed line per UTC day; a new day accepts again (5.276205ms)
|
||||
✔ ceiling: a burst arriving while turns are still running cannot queue past the ceiling (3.124916ms)
|
||||
✔ ceiling: a turn interrupted by a crash still counts after restart; admissions are durable (2.308167ms)
|
||||
✔ ceiling: the daily notice survives a same-day restart; one delivery attempt in total, even when the first attempt crashed mid-flight (4.251125ms)
|
||||
✔ duplicate: the same event delivered twice while the thread lookup is held yields one prompt, one admission and one reply (1.25404ms)
|
||||
✔ journal: no token-shaped string and no model output on the drop path reaches disk (0.496794ms)
|
||||
✔ receipt: an admitted message gets one eyes reaction on the inbound message; drops and refusals get none; a failed reaction is recorded and does not fail the turn (2.467632ms)
|
||||
✔ receipt: Discord refusing the reaction leaves the turn intact and records ok false (2.027341ms)
|
||||
✔ reload: a new user is silent before and answered after; a removed channel goes silent; a lower ceiling applies at once (4.119314ms)
|
||||
✔ reload: a fixed key refuses with exit 2 and the old binding stays in force (4.968528ms)
|
||||
✔ tools: with a tools binding the turn record lists every read and its outcome; without one the field is null (4.093585ms)
|
||||
✔ context: the Discord block names the server, channels and modes, and states the rules from Q15 and Q16 (2.581971ms)
|
||||
✔ context: with tools the block names the roots, keeps file content as data, and says to state refusals plainly (0.869654ms)
|
||||
✔ context: a writable root adds the write rules and says a write is real only once Jason commits (2.108748ms)
|
||||
✔ context: the envelope is one bracketed line then the text; names cannot break the line (1.341709ms)
|
||||
✔ context: a git root swaps the terminal-commit line for the git verbs, and a vault root adds the id protocol (1.476551ms)
|
||||
✔ context: assembleContext concatenates files in launcher format and appends the block; sha256 is stable (1.314316ms)
|
||||
✔ context: splitReply keeps paragraphs together under the limit and splits long ones at lines, spaces, then hard (0.832442ms)
|
||||
✔ engine: buildPiArgs carries the fixed flags, engine settings, session dir and prompt file (2.866483ms)
|
||||
✔ engine: with tools, buildPiArgs turns pi's own tools off, loads the extension explicitly and allowlists exactly our three (0.41256ms)
|
||||
✔ engine: a run with tool turns settles once, on the answer, with every tool call in the result (56.029123ms)
|
||||
✔ engine: a run that ends on a tool-only turn fails the prompt as empty; a retried run settles on the real end (38.421229ms)
|
||||
✔ engine: one prompt, one turn, text and usage come back (32.150553ms)
|
||||
✔ engine: a prompt while streaming is held until pi settles, then sent as its own run, and answered in order (332.641169ms)
|
||||
✔ engine: a held prompt that times out before pi settles fails on its own and is never sent (235.751447ms)
|
||||
✔ engine: timeout sends abort and fails only that turn; the process stays (105.139637ms)
|
||||
✔ engine: tool events from a run that outlived its timeout never land in the next prompt's record (228.086886ms)
|
||||
✔ engine: a prompt after a turn that timed out before its agent_start waits for pi to settle instead of being refused (135.450867ms)
|
||||
✔ engine: when pi has not started a timed-out turn by the end of the abort grace, the engine stops pi and fails held prompts (213.23065ms)
|
||||
✔ engine: a timed-out turn pi starts only after the grace never answers a later prompt (614.382317ms)
|
||||
✔ engine: late events of a run past its grace, before pi exits, answer nothing and nothing more is sent (early prompt response) (1.267878ms)
|
||||
✔ engine: late events of a run past its grace, before pi exits, answer nothing and nothing more is sent (late prompt response) (0.480882ms)
|
||||
✔ engine: a timed-out run pi did start outlives the grace; the next prompt goes out when it ends (425.419954ms)
|
||||
✔ engine: a malformed JSONL line fails the turn, not the process (23.979553ms)
|
||||
✔ engine: a turn that ends in error rejects with the error code; process exit fails pending turns (47.337302ms)
|
||||
✔ gateway: hello -> identify with intents, ready, heartbeat with jitter, ack (2.488183ms)
|
||||
✔ gateway: missed ack closes the socket and resumes with the last sequence (1.739254ms)
|
||||
✔ gateway: op 7 reconnect resumes; op 9 non-resumable re-identifies (0.751079ms)
|
||||
✔ gateway: op 9 resumable resumes (0.355449ms)
|
||||
✔ gateway: close 4014 is fatal, reports the missing intent, never reconnects (0.814585ms)
|
||||
✔ gateway: 4004 and 4013 are fatal too; 1006 reconnects with identify when no session (0.53415ms)
|
||||
✔ gateway: close() is final and unparseable frames are ignored (0.397126ms)
|
||||
✔ git: config validation is strict, needs write: true, a work tree and a private token file (68.353129ms)
|
||||
✔ git: the child environment drops every host git config, names one helper, and carries the token path only for origin (47.851387ms)
|
||||
✔ git: status reports the branch, ahead/behind and changed paths, and refuses off the named branch or mid-merge (94.180702ms)
|
||||
✔ git: parseStatus reads porcelain v2 including renames and conflicts (0.281882ms)
|
||||
✔ git: a commit stages exactly the named files, carries the seat author and the requester trailer, and pushes at once (87.170171ms)
|
||||
✔ git: commit refusals: message, paths, requester, nothing to commit, and an index that already holds other work (97.112654ms)
|
||||
✔ git: a commit whose push fails is still a commit, says so, and the next commit's push carries both (D6) (94.925433ms)
|
||||
✔ git: pull is fast-forward only; a diverged origin or dirty local files refuse with nothing merged (210.685657ms)
|
||||
✔ git: push pushes the named branch only and reports up to date (75.691059ms)
|
||||
✔ git: no token value or token path ever reaches a git argument list; outputs are masked and capped (87.339624ms)
|
||||
✔ git: the credential helper answers get over https from a private file and nothing else (197.323202ms)
|
||||
✔ git: the vault protocol validates before a commit, honours another owner's lock, reserves ids, and locks around writes (751.124043ms)
|
||||
✔ lock: the claim is exclusive; a second start against a live owner refuses (5.01841ms)
|
||||
✔ lock: a stale lock (dead owner, reused pid, or record without start) refuses run and is never signaled; only unlock clears it (79.953023ms)
|
||||
✔ lock: an incomplete claim (directory without owner record) is busy and refuses run; unlock clears it (0.800902ms)
|
||||
✔ lock: an owner record that exists but cannot be read is invalid: never signaled, never removed, never claimed over (2.668839ms)
|
||||
✔ lock: legacy upgrade; a live connector holding a {pid, start} record is unknown, unlock refuses and nothing changes; after it exits, unlock clears it (39.552971ms)
|
||||
✔ 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 (301.652949ms)
|
||||
✔ lock: identity syntax; only canonical unsigned decimal start ticks and lowercase boot uuids are identities (0.599138ms)
|
||||
✔ lock: a process whose start marker or boot id cannot be read refuses to claim (28.147864ms)
|
||||
✔ lock: a live pid whose identity cannot be read right now is unknown: never signaled, never removed, never claimed over (1.525339ms)
|
||||
✔ lock: four processes racing for the same binding; exactly one claims it and the others refuse (48.503637ms)
|
||||
✔ lock: stale handoff; concurrent starts over a stale lock all refuse, nothing reclaims, one unlock then exactly one live owner (146.638909ms)
|
||||
✔ lock: four-party schedule; claims landing inside an unlock's gap never survive, one unlock leaves no owner and no residue (96.227093ms)
|
||||
✔ notices: a kind is recorded per UTC day and found again (0.413961ms)
|
||||
✔ notify: the DM channel opens once, every send carries the nonce, and only the message id comes back (5.040159ms)
|
||||
✔ notify: refusals and unknowns surface as RestOutcome without ids; a refused send reopens the channel next time (2.517272ms)
|
||||
✔ notify: no dmRecipient, a non-0600 binding or token, or a missing binding refuse before any network use (1.894822ms)
|
||||
✔ recover: nothing to do is clean; a lock whose owner is gone or that has no record is cleared and STOP ends up absent (52.534669ms)
|
||||
✔ recover: an operator STOP refuses with exit 3 and is never removed, whatever the lock says (32.739742ms)
|
||||
✔ recover: a brake written during the unlock wins; STOP stays with both lines and the start is refused (32.258484ms)
|
||||
✔ recover: a held binding refuses with exit 3 and writes no STOP: live owner, alive pid without verifiable identity, unreadable record (74.709691ms)
|
||||
✔ cli: recover exits 0 when ready, 3 behind a brake or a held binding, and run's own STOP refusal is 3 (674.149506ms)
|
||||
✔ rest: createMessage sends nonce, enforce_nonce, empty allowed_mentions and a soft reply reference (8.893063ms)
|
||||
✔ rest: 429 waits retry_after and retries; 4xx is refused; 5xx and socket errors are unknown (3.289935ms)
|
||||
✔ rest: content and nonce limits are enforced locally; typing never throws (0.578211ms)
|
||||
✔ rest: react PUTs the encoded emoji on the inbound message for @me; 2xx is true, anything else is false and never throws (0.766243ms)
|
||||
✔ rest: createDm posts the recipient and resolves the channel id; 4xx refused, 5xx unknown, bad id never sent (1.383503ms)
|
||||
✔ setspark config: a bare https or loopback origin, a private key file, a principal (5.518356ms)
|
||||
✔ setspark config: reaches the tools config and the binding as a fixed key (3.994465ms)
|
||||
✔ setspark config: the binding's key survives resolveToolRoots and the engine's JSON hand-off to the extension (1.523408ms)
|
||||
✔ setspark config: approvers come from the binding's users, never from the binding's setspark key (1.479859ms)
|
||||
✔ setspark verbs: required_approvers go out as discord ids from names and come back as names (18.113285ms)
|
||||
✔ setspark verbs: no Discord user id reaches tool text, whatever shape the service returns it in (8.297505ms)
|
||||
✔ setspark contract: a decision made with names opens a request the connector accepts; names stored by an old record still refuse (6.297547ms)
|
||||
✔ setspark keys: read per call, one printable token per file, rotation without a restart (3.103093ms)
|
||||
✔ setspark idempotency keys: principal, turn id, call index; connector keys name a step (0.455146ms)
|
||||
✔ setspark http core: json in and out, bearer header, idempotency header, fixed user agent, no key anywhere else (2.041441ms)
|
||||
✔ setspark http core: error bodies become fixed refusals with code and the 409 fields; server text is data, cut (1009.053659ms)
|
||||
✔ setspark verbs: a setspark key enables the eight verbs and no counters (0.981369ms)
|
||||
✔ setspark verbs: writes carry the turn's key and the asserted requester, reads carry no key, and the api key never appears in text or details (6.59881ms)
|
||||
✔ setspark verbs: no turn refuses every write before any request; bad arguments refuse before any request; reads still work (1.329166ms)
|
||||
✔ setspark verbs: renderRecord caps long output and hides the accepted snapshot (0.217806ms)
|
||||
✔ setspark api: bind, add_approval (button and reply) and get use integer request ids and the connector's keys (2.626764ms)
|
||||
✔ tools: config refuses a missing, symlinked, dotted, non-directory or duplicate root and bad limits (3.419099ms)
|
||||
✔ tools: every escape is refused with a fixed reason and nothing outside the root is read (3.60831ms)
|
||||
✔ tools: happy paths list, read a window, and search case-insensitively; dotfiles and symlinks never appear (5.026763ms)
|
||||
✔ tools: the tool set renders text for the model, records details for the journal, and enforces the per-run budget (3.707762ms)
|
||||
✔ tools: listing and search caps hold (10.282751ms)
|
||||
✔ tools: credential shapes are caught; ordinary prose and ids are not (0.943132ms)
|
||||
✔ 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.419338ms)
|
||||
✔ tools: an unreadable file under the root is skipped by search and refused by read (0.868225ms)
|
||||
✔ tools: config accepts write: true only as a boolean, and enables the write tools only then (0.995424ms)
|
||||
✔ tools: every write outside the fence is refused before any byte lands, and no temp file remains (4.213116ms)
|
||||
✔ tools: write_file leaves the exact bytes, edit_file replaces one exact match, and the set renders the change as uncommitted (2.597178ms)
|
||||
✔ tools: a target that changed between the check and the rename is refused and the temp file is removed (1.282156ms)
|
||||
✔ web: config takes an https or loopback-http SearXNG base url and a bounded fetch cap (2.530552ms)
|
||||
✔ web: address rules refuse every private, loopback, link-local, mapped and multicast form (2.209727ms)
|
||||
✔ web: web_fetch refuses bad urls, private hosts, rebinding names, non-https redirects, too many hops, error status, non-text bodies, and times out (1022.074646ms)
|
||||
✔ web: web_fetch returns html as text with the title, follows an https redirect, keeps plain text and json, and cuts at the cap (4.468695ms)
|
||||
✔ web: html to text drops scripts, styles and comments, decodes entities and keeps block breaks (0.248987ms)
|
||||
✔ web: web_search asks the instance for json, returns at most ten clean results, and refuses a bad query, a down instance or an unusable answer (2.702411ms)
|
||||
✔ web: the tool set enables the web tools only with a web key, counts them in the budget, and records url, status and hits (2.555878ms)
|
||||
✔ approvals: a request is validated before anything is posted; the rendering shows names and never ids (3.081039ms)
|
||||
✔ approvals: the ledger is appended and folded into open requests with bind and approval states (1.450448ms)
|
||||
✔ approvals: a reply approves only when it points at a request, says exactly approve, and comes from a listed approver once (0.539404ms)
|
||||
✔ approvals: a button approves only on its own request message with the matching custom id (0.486838ms)
|
||||
✔ approvals flow: a turn that opened a request posts the message with the button, records it, binds it, and both approvers approve (19.212333ms)
|
||||
✔ approvals flow: a non-approver, a repeat, a wrong custom id and a service refusal each get their fixed line and a drop entry (7.226047ms)
|
||||
✔ approvals flow: an invalid request from the model, a refused post, and no api client are recorded and post nothing (6.793303ms)
|
||||
✔ approvals flow: start retries a bind and an approval left as unknown, under their original keys (2.348484ms)
|
||||
✔ authorize: open channel, listed user (2.034771ms)
|
||||
✔ authorize: wrong guild (0.197565ms)
|
||||
✔ authorize: no guild (DM) (0.306546ms)
|
||||
✔ authorize: unlisted channel (0.195089ms)
|
||||
✔ authorize: unknown channel, no info (0.278659ms)
|
||||
✔ authorize: thread of listed parent (0.20402ms)
|
||||
✔ authorize: thread of unlisted parent (0.248401ms)
|
||||
✔ authorize: text channel that is not a thread and not listed (0.135501ms)
|
||||
✔ authorize: unlisted user (1.223655ms)
|
||||
✔ authorize: no author (0.567415ms)
|
||||
✔ authorize: bot author (listed id, bot flag) (0.23823ms)
|
||||
✔ authorize: system author (0.329115ms)
|
||||
✔ authorize: the bot itself (0.090371ms)
|
||||
✔ authorize: webhook (0.116421ms)
|
||||
✔ authorize: mention channel without mention (1.030567ms)
|
||||
✔ authorize: mention channel with bot mention (0.138834ms)
|
||||
✔ authorize: mention channel with @everyone only (0.244925ms)
|
||||
✔ authorize: mention channel mentioning someone else (0.125004ms)
|
||||
✔ authorize: mention channel, content says @bot but mentions empty (0.115784ms)
|
||||
✔ authorize: private thread under mention channel, mentioned (0.370489ms)
|
||||
✔ authorize: private thread under mention channel, not mentioned (0.0842ms)
|
||||
✔ authorize: thread in another guild per channel info (0.08482ms)
|
||||
✔ authorize: not an object (0.063472ms)
|
||||
✔ authorize: no id (0.067893ms)
|
||||
✔ authorize: oversize content is accepted and flagged (0.072189ms)
|
||||
✔ authorize: exactly the limit is not oversize (0.062234ms)
|
||||
✔ authorize: a user's channel allowlist drops them outside it, threads count as the parent, others are unaffected (0.444603ms)
|
||||
✔ authorize: order puts wrong guild before user, and user before channel (no channel lookup for strangers) (0.136339ms)
|
||||
✔ binding: a complete binding validates and is frozen (4.514209ms)
|
||||
✔ binding: unknown key, missing field, wrong type refuse with exit 2 (2.66757ms)
|
||||
✔ binding: empty allowlists refuse (0.553791ms)
|
||||
✔ binding: a user's channel allowlist must be non-empty, listed and unique; absent means every listed channel (1.228209ms)
|
||||
✔ reloadDiff: reloadable keys are summarised by id; every fixed key refuses with exit 2 (1.918301ms)
|
||||
✔ binding: dmRecipient is optional, a snowflake, one of the listed users, and a fixed key (1.292749ms)
|
||||
✔ binding: file must be 0600, regular, not a symlink (1.797933ms)
|
||||
✔ binding: token file mode, symlink, emptiness and shape are checked; token never appears in errors (1.463434ms)
|
||||
✔ cli: check refuses a non-0600 token file with exit 2 before any network use (94.273472ms)
|
||||
✔ context files: absolute paths, traversal, symlinks and out-of-repo targets refuse; in-repo files resolve (1.998433ms)
|
||||
✔ cli: check refuses a missing context file and a missing binding with exit 2; usage is exit 4 (302.76564ms)
|
||||
✔ cli: reload validates the file first (exit 2), then needs a live owner (exit 1); usage is exit 4 (199.69244ms)
|
||||
✔ cli: run refuses when STOP is present, before any network use (124.63982ms)
|
||||
✔ binding: tools is optional, validated strictly, a fixed key for reload, and its roots are resolved against the data root (1.614976ms)
|
||||
✔ binding: a git key is validated at load and reaches the extension whole, and only on a writable root (1.05988ms)
|
||||
✔ delivery: an accepted message is in the inbox before the turn, the reply is chunked with one nonce per chunk, and the turn record is write-once (20.951929ms)
|
||||
✔ delivery: refused and unknown outcomes are journaled; a later chunk is not sent after a failure (17.465003ms)
|
||||
✔ delivery: restart with an unknown entry re-sends the same nonce once and reconciles before accepting traffic (2.33533ms)
|
||||
✔ delivery: an unknown entry older than the dedupe window is marked refused, not re-sent; a still-unknown one refuses start (1.130995ms)
|
||||
✔ delivery: repeated unknown reconciliations never refresh the dedupe window; the original intent time decides (1.249969ms)
|
||||
✔ turn: a failed engine turn posts the fixed line, never model output, and writes a failed record (2.347004ms)
|
||||
✔ turn: a second message during a turn is held by the engine, both get their own reply and record (33.966768ms)
|
||||
✔ turn: a thread under a listed channel is answered in the thread; an unknown thread is looked up once (3.277186ms)
|
||||
✔ drop: an unlisted user gets silence and one drop line; no inbox entry, no REST call, no engine call (0.969178ms)
|
||||
✔ drop: an oversize message is accepted into the inbox, answered with the fixed line and journaled as a drop (1.000196ms)
|
||||
✔ restart: an inbox with three ids and a replay of the same three produces zero turns (42.244621ms)
|
||||
✔ stop: STOP present refuses start; STOP written while running refuses new turns and the current one finishes (31.720943ms)
|
||||
✔ ceiling: the ceiling plus one is refused and journaled; one fixed line per UTC day; a new day accepts again (5.445929ms)
|
||||
✔ ceiling: a burst arriving while turns are still running cannot queue past the ceiling (3.647123ms)
|
||||
✔ ceiling: a turn interrupted by a crash still counts after restart; admissions are durable (2.283507ms)
|
||||
✔ ceiling: the daily notice survives a same-day restart; one delivery attempt in total, even when the first attempt crashed mid-flight (4.164505ms)
|
||||
✔ duplicate: the same event delivered twice while the thread lookup is held yields one prompt, one admission and one reply (2.080797ms)
|
||||
✔ journal: no token-shaped string and no model output on the drop path reaches disk (0.388785ms)
|
||||
✔ receipt: an admitted message gets one eyes reaction on the inbound message; drops and refusals get none; a failed reaction is recorded and does not fail the turn (2.259766ms)
|
||||
✔ receipt: Discord refusing the reaction leaves the turn intact and records ok false (1.846438ms)
|
||||
✔ reload: a new user is silent before and answered after; a removed channel goes silent; a lower ceiling applies at once (3.025452ms)
|
||||
✔ reload: a fixed key refuses with exit 2 and the old binding stays in force (0.945613ms)
|
||||
✔ tools: with a tools binding the turn record lists every read and its outcome; without one the field is null (3.908303ms)
|
||||
✔ context: the Discord block names the server, channels and modes, and states the rules from Q15 and Q16 (2.623828ms)
|
||||
✔ context: with tools the block names the roots, keeps file content as data, and says to state refusals plainly (0.757913ms)
|
||||
✔ context: a writable root adds the write rules and says a write is real only once Jason commits (2.061002ms)
|
||||
✔ context: the envelope is one bracketed line then the text; names cannot break the line (1.182448ms)
|
||||
✔ context: a git root swaps the terminal-commit line for the git verbs, and a vault root adds the id protocol (1.48004ms)
|
||||
✔ context: assembleContext concatenates files in launcher format and appends the block; sha256 is stable (1.465377ms)
|
||||
✔ context: splitReply keeps paragraphs together under the limit and splits long ones at lines, spaces, then hard (0.909559ms)
|
||||
✔ engine: buildPiArgs carries the fixed flags, engine settings, session dir and prompt file (2.455854ms)
|
||||
✔ engine: with tools, buildPiArgs turns pi's own tools off, loads the extension explicitly and allowlists exactly our three (1.255634ms)
|
||||
✔ engine: a run with tool turns settles once, on the answer, with every tool call in the result (49.034823ms)
|
||||
✔ engine: a run that ends on a tool-only turn fails the prompt as empty; a retried run settles on the real end (45.677101ms)
|
||||
✔ engine: one prompt, one turn, text and usage come back (32.578224ms)
|
||||
✔ engine: a prompt while streaming is held until pi settles, then sent as its own run, and answered in order (329.393546ms)
|
||||
✔ engine: a held prompt that times out before pi settles fails on its own and is never sent (232.374139ms)
|
||||
✔ engine: timeout sends abort and fails only that turn; the process stays (105.090744ms)
|
||||
✔ engine: tool events from a run that outlived its timeout never land in the next prompt's record (231.736524ms)
|
||||
✔ engine: a prompt after a turn that timed out before its agent_start waits for pi to settle instead of being refused (125.739998ms)
|
||||
✔ engine: when pi has not started a timed-out turn by the end of the abort grace, the engine stops pi and fails held prompts (213.427403ms)
|
||||
✔ engine: a timed-out turn pi starts only after the grace never answers a later prompt (615.124756ms)
|
||||
✔ engine: late events of a run past its grace, before pi exits, answer nothing and nothing more is sent (early prompt response) (1.313447ms)
|
||||
✔ engine: late events of a run past its grace, before pi exits, answer nothing and nothing more is sent (late prompt response) (0.500103ms)
|
||||
✔ engine: a timed-out run pi did start outlives the grace; the next prompt goes out when it ends (428.708785ms)
|
||||
✔ engine: a malformed JSONL line fails the turn, not the process (27.22705ms)
|
||||
✔ engine: a turn that ends in error rejects with the error code; process exit fails pending turns (47.573585ms)
|
||||
✔ gateway: hello -> identify with intents, ready, heartbeat with jitter, ack (2.512369ms)
|
||||
✔ gateway: missed ack closes the socket and resumes with the last sequence (1.513556ms)
|
||||
✔ gateway: op 7 reconnect resumes; op 9 non-resumable re-identifies (0.430292ms)
|
||||
✔ gateway: op 9 resumable resumes (0.362511ms)
|
||||
✔ gateway: close 4014 is fatal, reports the missing intent, never reconnects (0.950744ms)
|
||||
✔ gateway: 4004 and 4013 are fatal too; 1006 reconnects with identify when no session (0.529163ms)
|
||||
✔ gateway: close() is final and unparseable frames are ignored (0.743481ms)
|
||||
✔ git: config validation is strict, needs write: true, a work tree and a private token file (80.124457ms)
|
||||
✔ git: the child environment drops every host git config, names one helper, and carries the token path only for origin (43.024752ms)
|
||||
✔ git: status reports the branch, ahead/behind and changed paths, and refuses off the named branch or mid-merge (63.196562ms)
|
||||
✔ git: parseStatus reads porcelain v2 including renames and conflicts (0.267156ms)
|
||||
✔ git: a commit stages exactly the named files, carries the seat author and the requester trailer, and pushes at once (75.95283ms)
|
||||
✔ git: commit refusals: message, paths, requester, nothing to commit, and an index that already holds other work (85.863999ms)
|
||||
✔ git: a commit whose push fails is still a commit, says so, and the next commit's push carries both (D6) (91.976349ms)
|
||||
✔ git: pull is fast-forward only; a diverged origin or dirty local files refuse with nothing merged (197.172243ms)
|
||||
✔ git: push pushes the named branch only and reports up to date (70.161556ms)
|
||||
✔ git: no token value or token path ever reaches a git argument list; outputs are masked and capped (92.70954ms)
|
||||
✔ git: the credential helper answers get over https from a private file and nothing else (211.880329ms)
|
||||
✔ git: the vault protocol validates before a commit, honours another owner's lock, reserves ids, and locks around writes (724.394004ms)
|
||||
✔ lock: the claim is exclusive; a second start against a live owner refuses (4.810224ms)
|
||||
✔ lock: a stale lock (dead owner, reused pid, or record without start) refuses run and is never signaled; only unlock clears it (83.089733ms)
|
||||
✔ lock: an incomplete claim (directory without owner record) is busy and refuses run; unlock clears it (1.097574ms)
|
||||
✔ lock: an owner record that exists but cannot be read is invalid: never signaled, never removed, never claimed over (3.792137ms)
|
||||
✔ lock: legacy upgrade; a live connector holding a {pid, start} record is unknown, unlock refuses and nothing changes; after it exits, unlock clears it (46.780073ms)
|
||||
✔ 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 (296.037764ms)
|
||||
✔ lock: identity syntax; only canonical unsigned decimal start ticks and lowercase boot uuids are identities (0.490814ms)
|
||||
✔ lock: a process whose start marker or boot id cannot be read refuses to claim (27.03662ms)
|
||||
✔ lock: a live pid whose identity cannot be read right now is unknown: never signaled, never removed, never claimed over (1.406834ms)
|
||||
✔ lock: four processes racing for the same binding; exactly one claims it and the others refuse (47.041024ms)
|
||||
✔ lock: stale handoff; concurrent starts over a stale lock all refuse, nothing reclaims, one unlock then exactly one live owner (144.507832ms)
|
||||
✔ lock: four-party schedule; claims landing inside an unlock's gap never survive, one unlock leaves no owner and no residue (87.869235ms)
|
||||
✔ notices: a kind is recorded per UTC day and found again (0.424756ms)
|
||||
✔ notify: the DM channel opens once, every send carries the nonce, and only the message id comes back (5.061268ms)
|
||||
✔ notify: refusals and unknowns surface as RestOutcome without ids; a refused send reopens the channel next time (2.175207ms)
|
||||
✔ notify: no dmRecipient, a non-0600 binding or token, or a missing binding refuse before any network use (1.537785ms)
|
||||
✔ recover: nothing to do is clean; a lock whose owner is gone or that has no record is cleared and STOP ends up absent (49.091675ms)
|
||||
✔ recover: an operator STOP refuses with exit 3 and is never removed, whatever the lock says (41.402473ms)
|
||||
✔ recover: a brake written during the unlock wins; STOP stays with both lines and the start is refused (28.892318ms)
|
||||
✔ recover: a held binding refuses with exit 3 and writes no STOP: live owner, alive pid without verifiable identity, unreadable record (65.565161ms)
|
||||
✔ cli: recover exits 0 when ready, 3 behind a brake or a held binding, and run's own STOP refusal is 3 (664.943136ms)
|
||||
✔ rest: createMessage sends nonce, enforce_nonce, empty allowed_mentions and a soft reply reference (2.390487ms)
|
||||
✔ rest: 429 waits retry_after and retries; 4xx is refused; 5xx and socket errors are unknown (2.97225ms)
|
||||
✔ rest: content and nonce limits are enforced locally; typing never throws (0.745505ms)
|
||||
✔ rest: react PUTs the encoded emoji on the inbound message for @me; 2xx is true, anything else is false and never throws (0.833698ms)
|
||||
✔ rest: createDm posts the recipient and resolves the channel id; 4xx refused, 5xx unknown, bad id never sent (1.367962ms)
|
||||
✔ setspark config: a bare https or loopback origin, a private key file, a principal (5.533771ms)
|
||||
✔ setspark config: reaches the tools config and the binding as a fixed key (2.879168ms)
|
||||
✔ setspark config: the binding's key survives resolveToolRoots and the engine's JSON hand-off to the extension (1.567929ms)
|
||||
✔ setspark config: approvers come from the binding's users, never from the binding's setspark key (2.519442ms)
|
||||
✔ setspark verbs: required_approvers go out as discord ids from names and come back as names (26.079152ms)
|
||||
✔ setspark verbs: no Discord user id reaches tool text, whatever shape the service returns it in (11.731332ms)
|
||||
✔ setspark contract: a decision made with names opens a request the connector accepts; names stored by an old record still refuse (6.588171ms)
|
||||
✔ setspark keys: read per call, one printable token per file, rotation without a restart (2.787692ms)
|
||||
✔ setspark idempotency keys: principal, turn id, call index; connector keys name a step (0.507426ms)
|
||||
✔ setspark http core: json in and out, bearer header, idempotency header, fixed user agent, no key anywhere else (2.036213ms)
|
||||
✔ setspark http core: error bodies become fixed refusals with code and the 409 fields; server text is data, cut (1011.362787ms)
|
||||
✔ setspark verbs: a setspark key enables the eight verbs and no counters (0.427291ms)
|
||||
✔ setspark verbs: writes carry the turn's key and the asserted requester, reads carry no key, and the api key never appears in text or details (6.326911ms)
|
||||
✔ setspark verbs: no turn refuses every write before any request; bad arguments refuse before any request; reads still work (1.238285ms)
|
||||
✔ setspark verbs: renderRecord caps long output and hides the accepted snapshot (0.210112ms)
|
||||
✔ setspark api: bind, add_approval (button and reply) and get use integer request ids and the connector's keys (2.333518ms)
|
||||
✔ tools: config refuses a missing, symlinked, dotted, non-directory or duplicate root and bad limits (4.301345ms)
|
||||
✔ tools: every escape is refused with a fixed reason and nothing outside the root is read (3.938099ms)
|
||||
✔ tools: happy paths list, read a window, and search case-insensitively; dotfiles and symlinks never appear (5.360337ms)
|
||||
✔ tools: the tool set renders text for the model, records details for the journal, and enforces the per-run budget (4.51199ms)
|
||||
✔ tools: listing and search caps hold (11.549555ms)
|
||||
✔ tools: credential shapes are caught; ordinary prose and ids are not (0.910634ms)
|
||||
✔ tools: the read uses the checked file itself; a symlink, a swapped file, a FIFO, a grown file or a hard link at read time is refused (5.70352ms)
|
||||
✔ tools: an unreadable file under the root is skipped by search and refused by read (1.793211ms)
|
||||
✔ tools: config accepts write: true only as a boolean, and enables the write tools only then (1.496735ms)
|
||||
✔ tools: every write outside the fence is refused before any byte lands, and no temp file remains (6.125267ms)
|
||||
✔ tools: write_file leaves the exact bytes, edit_file replaces one exact match, and the set renders the change as uncommitted (2.60861ms)
|
||||
✔ tools: a target that changed between the check and the rename is refused and the temp file is removed (1.286364ms)
|
||||
✔ web: config takes an https or loopback-http SearXNG base url and a bounded fetch cap (3.900582ms)
|
||||
✔ web: address rules refuse every private, loopback, link-local, mapped and multicast form (2.233099ms)
|
||||
✔ web: web_fetch refuses bad urls, private hosts, rebinding names, non-https redirects, too many hops, error status, non-text bodies, and times out (1028.613642ms)
|
||||
✔ web: web_fetch returns html as text with the title, follows an https redirect, keeps plain text and json, and cuts at the cap (4.24281ms)
|
||||
✔ web: html to text drops scripts, styles and comments, decodes entities and keeps block breaks (0.220327ms)
|
||||
✔ web: web_search asks the instance for json, returns at most ten clean results, and refuses a bad query, a down instance or an unusable answer (2.593587ms)
|
||||
✔ web: the tool set enables the web tools only with a web key, counts them in the budget, and records url, status and hits (2.237781ms)
|
||||
ℹ tests 178
|
||||
ℹ suites 0
|
||||
ℹ pass 178
|
||||
@@ -183,4 +183,4 @@
|
||||
ℹ cancelled 0
|
||||
ℹ skipped 0
|
||||
ℹ todo 0
|
||||
ℹ duration_ms 2645.723969
|
||||
ℹ duration_ms 2606.762318
|
||||
|
||||
@@ -1,61 +1,64 @@
|
||||
✔ sessionModel: agent vars win, then the system's execution settings (9.866803ms)
|
||||
✔ a pi bundle: prompt, policy, tools and manifest, 0600 in a 0700 directory (4.436208ms)
|
||||
✔ a claude-code bundle adds the wrapped gate hook and the MCP config (2.729439ms)
|
||||
✔ a bundle is written once: an existing file refuses (1.77934ms)
|
||||
✔ a path with a single quote can't go into the hook command (1.80468ms)
|
||||
✔ allow exits 0, a deny exits 2 with the reason on stderr (126.770406ms)
|
||||
✔ a missing or wrong policy, or a bad event, exits 2 (76.681201ms)
|
||||
✔ the bundle's wrapped command: a missing gate or node still blocks (1093.817326ms)
|
||||
✔ claude: typed tools through MCP, the hook blocks, builtins outside --tools don't exist (754.278903ms)
|
||||
✔ claude: the hook alone blocks a path outside the workspace (481.812812ms)
|
||||
✔ claude: a second turn resumes the first turn's session (713.762483ms)
|
||||
✔ claude adapter: --restricted is always passed (4.950058ms)
|
||||
✔ claude: CLAUDE.md files and auto-memory don't reach the model; without --restricted they do (717.308696ms)
|
||||
✔ claude: a missing hook or MCP file refuses before claude starts (7.466398ms)
|
||||
✔ pi: policy tools and typed tools pass, anything else is blocked (3.399466ms)
|
||||
✔ claude: builtins map from pi names, typed tools need the mcp prefix (0.784748ms)
|
||||
✔ file tool paths must resolve inside the workspace (0.998403ms)
|
||||
✔ pi's own path normalisation can't be used to step out (0.906289ms)
|
||||
✔ a symlink inside the workspace that points out is outside (0.787366ms)
|
||||
✔ a dangling symlink is refused at any depth, in both harnesses (2.581633ms)
|
||||
✔ read is checked under every spelling pi's read would open, in both harnesses (15.173931ms)
|
||||
✔ other spellings cover directories, dangling links and pi's cwd (1.755587ms)
|
||||
✔ claude path fields per tool (0.614355ms)
|
||||
✔ glob patterns stay inside the workspace (0.669965ms)
|
||||
✔ a path that can't be checked is blocked (0.330426ms)
|
||||
✔ initialize, ping and tools/list (43.946107ms)
|
||||
✔ tools/call goes through the tool socket; a refusal is an isError result (39.418977ms)
|
||||
✔ unknown tools and methods are JSON-RPC errors and never reach the socket (28.163039ms)
|
||||
✔ a missing argument is a usage error (33.807275ms)
|
||||
✔ pi: typed tools reach the socket, the gate blocks, agent_end writes the marker (357.982252ms)
|
||||
✔ pi: a write through a dangling symlink is blocked, and nothing appears outside (329.534992ms)
|
||||
✔ pi: a read is refused when pi would open another spelling outside (330.331356ms)
|
||||
✔ pi: a missing extension refuses before any model call (7.207519ms)
|
||||
✔ pi: an extension without its configuration fails pi's start (262.623748ms)
|
||||
✔ founderCheck: founder variables, then a needed service without a usable token (1.361445ms)
|
||||
✔ turnRequest names the sender, class, reply and decision (0.286463ms)
|
||||
✔ a message becomes a turn, the answer goes back as a RESULT, SIGTERM releases and exits 0 (280.080623ms)
|
||||
✔ a SIGTERM before the claim stops the runner with exit 0 and no claim (131.852678ms)
|
||||
✔ typed tools carry the runner's capability; launch goes to the host's launch socket (555.206373ms)
|
||||
✔ a RESULT gets no automatic reply; failed turns reply with the reason (1350.970037ms)
|
||||
✔ SIGTERM during a turn kills the turn's process group and still exits 0 (163.705924ms)
|
||||
✔ founder credentials stop before the claim (20) (181.182154ms)
|
||||
✔ a refused claim exits 21; an ended run's capability exits 22 (216.20297ms)
|
||||
✔ the launch ending under a running session exits 22 (150.079251ms)
|
||||
✔ a broker that stays unreachable exits 23 after brokerRetries polls (246.177626ms)
|
||||
✔ a broker that is down at the claim exits 23, not 21 (143.792117ms)
|
||||
✔ no capability, or a malformed one, on stdin exits 2 (260.79667ms)
|
||||
✔ a missing or malformed policy exits 2 before the claim (260.249644ms)
|
||||
✔ the PM gets launch, its task verbs and the reads (9.127299ms)
|
||||
✔ a coder gets no launch, no resolve_decision, and no task tools without a tracker (3.041734ms)
|
||||
✔ launch only when the business's launch block names the instance as launcher (1.525074ms)
|
||||
✔ an action outside the instance's authority has no tool (1.464781ms)
|
||||
✔ callTool: one JSON line out, the result back, a refusal rejects (9.215168ms)
|
||||
ℹ tests 53
|
||||
✔ sessionModel: agent vars win, then the system's execution settings (12.460806ms)
|
||||
✔ a pi bundle: prompt, policy, tools and manifest, 0600 in a 0700 directory (4.803458ms)
|
||||
✔ a claude-code bundle adds the wrapped gate hook and the MCP config (3.183429ms)
|
||||
✔ a bundle is written once: an existing file refuses (2.558688ms)
|
||||
✔ a path with a single quote can't go into the hook command (5.575367ms)
|
||||
✔ allow exits 0, a deny exits 2 with the reason on stderr (120.182248ms)
|
||||
✔ a missing or wrong policy, or a bad event, exits 2 (92.907722ms)
|
||||
✔ the bundle's wrapped command: a missing gate or node still blocks (1098.912574ms)
|
||||
✔ claude: typed tools through MCP, the hook blocks, builtins outside --tools don't exist (784.493298ms)
|
||||
✔ claude: the hook alone blocks a path outside the workspace (475.438299ms)
|
||||
✔ claude: a second turn resumes the first turn's session (761.131232ms)
|
||||
✔ claude adapter: --restricted is always passed (5.633714ms)
|
||||
✔ claude: CLAUDE.md files and auto-memory don't reach the model; without --restricted they do (755.874661ms)
|
||||
✔ claude: a missing hook or MCP file refuses before claude starts (7.642982ms)
|
||||
✔ pi: policy tools and typed tools pass, anything else is blocked (4.44314ms)
|
||||
✔ claude: builtins map from pi names, typed tools need the mcp prefix (2.810102ms)
|
||||
✔ file tool paths must resolve inside the workspace (2.416083ms)
|
||||
✔ pi's own path normalisation can't be used to step out (1.033877ms)
|
||||
✔ a symlink inside the workspace that points out is outside (1.215724ms)
|
||||
✔ a dangling symlink is refused at any depth, in both harnesses (3.331492ms)
|
||||
✔ read is checked under every spelling pi's read would open, in both harnesses (16.236489ms)
|
||||
✔ other spellings cover directories, dangling links and pi's cwd (1.531911ms)
|
||||
✔ a relative path climbs from the workspace's real path, in both harnesses (3.899281ms)
|
||||
✔ claude path fields per tool (0.621252ms)
|
||||
✔ glob patterns stay inside the workspace (0.575952ms)
|
||||
✔ a path that can't be checked is blocked (0.348937ms)
|
||||
✔ initialize, ping and tools/list (49.829524ms)
|
||||
✔ tools/call goes through the tool socket; a refusal is an isError result (34.967036ms)
|
||||
✔ unknown tools and methods are JSON-RPC errors and never reach the socket (27.488498ms)
|
||||
✔ a missing argument is a usage error (37.772085ms)
|
||||
✔ pi: typed tools reach the socket, the gate blocks, agent_end writes the marker (366.795327ms)
|
||||
✔ pi: a write through a dangling symlink is blocked, and nothing appears outside (342.967992ms)
|
||||
✔ pi: a read is refused when pi would open another spelling outside (331.351008ms)
|
||||
✔ pi: a relative path climbs from the real path of a workspace behind a symlink (347.772096ms)
|
||||
✔ pi: a relative path climbs from the real path of a dataRoot behind a symlink (326.24484ms)
|
||||
✔ pi: a missing extension refuses before any model call (6.749538ms)
|
||||
✔ pi: an extension without its configuration fails pi's start (278.617538ms)
|
||||
✔ founderCheck: founder variables, then a needed service without a usable token (1.375374ms)
|
||||
✔ turnRequest names the sender, class, reply and decision (0.20362ms)
|
||||
✔ a message becomes a turn, the answer goes back as a RESULT, SIGTERM releases and exits 0 (260.24884ms)
|
||||
✔ a SIGTERM before the claim stops the runner with exit 0 and no claim (109.649271ms)
|
||||
✔ typed tools carry the runner's capability; launch goes to the host's launch socket (398.824797ms)
|
||||
✔ a RESULT gets no automatic reply; failed turns reply with the reason (1319.410101ms)
|
||||
✔ SIGTERM during a turn kills the turn's process group and still exits 0 (163.443065ms)
|
||||
✔ founder credentials stop before the claim (20) (199.484693ms)
|
||||
✔ a refused claim exits 21; an ended run's capability exits 22 (208.332497ms)
|
||||
✔ the launch ending under a running session exits 22 (144.453341ms)
|
||||
✔ a broker that stays unreachable exits 23 after brokerRetries polls (247.951469ms)
|
||||
✔ a broker that is down at the claim exits 23, not 21 (86.773045ms)
|
||||
✔ no capability, or a malformed one, on stdin exits 2 (190.450865ms)
|
||||
✔ a missing or malformed policy exits 2 before the claim (120.443165ms)
|
||||
✔ the PM gets launch, its task verbs and the reads (9.240713ms)
|
||||
✔ a coder gets no launch, no resolve_decision, and no task tools without a tracker (2.476487ms)
|
||||
✔ launch only when the business's launch block names the instance as launcher (1.907133ms)
|
||||
✔ an action outside the instance's authority has no tool (2.007447ms)
|
||||
✔ callTool: one JSON line out, the result back, a refusal rejects (9.197691ms)
|
||||
ℹ tests 56
|
||||
ℹ suites 0
|
||||
ℹ pass 53
|
||||
ℹ pass 56
|
||||
ℹ fail 0
|
||||
ℹ cancelled 0
|
||||
ℹ skipped 0
|
||||
ℹ todo 0
|
||||
ℹ duration_ms 10592.472255
|
||||
ℹ duration_ms 10351.534481
|
||||
|
||||
@@ -1,81 +1,81 @@
|
||||
✔ a raw token file, with or without one trailing newline, reaches curl only through the config stream (114.71017ms)
|
||||
✔ the raw path accepts nothing else, and refuses before curl runs (363.007926ms)
|
||||
✔ the file checks still apply on the raw path: mode, symlink, missing, directory (304.628111ms)
|
||||
✔ the raw path base URL has no override (64.735925ms)
|
||||
✔ the JSON path is unchanged, and JSON never falls through to the raw path (228.133678ms)
|
||||
✔ a file that changes between the two reads refuses before curl runs, with or without a body (715.743095ms)
|
||||
✔ the token reaches no child environment, even with an inherited CFG or SHELLOPTS=allexport (689.540339ms)
|
||||
✔ real helper GET HTTP 200 preserves exit 0 without credentials (14.872591ms)
|
||||
✔ real helper POST HTTP 201 preserves exit 0 without credentials (10.438959ms)
|
||||
✔ real helper GET HTTP 403 preserves exit 1 without credentials (9.544019ms)
|
||||
✔ fixture git subjects only, follow-ups and three session kinds (241.298844ms)
|
||||
✔ text and JSON carry same numbers, open and truncated title (308.52844ms)
|
||||
✔ missing credentials exit 2, no-issues never calls API and shows unknown (280.299865ms)
|
||||
✔ empty range gives no rows and zero totals (213.054503ms)
|
||||
✔ inclusive UTC dates, first-line preamble only, role and seat boundaries (215.603367ms)
|
||||
✔ close-only issue included, even median, missing metadata stays unknown (197.614777ms)
|
||||
✔ unique commits but per-issue links count multiple tags once each (215.527408ms)
|
||||
✔ page cap refuses rather than silently undercounting (201.211581ms)
|
||||
✔ bad API payload not JSON refuses (140.742625ms)
|
||||
✔ bad API payload {} refuses (196.87563ms)
|
||||
✔ bad API payload [{"number":1}] refuses (182.932775ms)
|
||||
✔ partial or malformed session log refuses with location, not content (186.997309ms)
|
||||
✔ a U+2028 or U+2029 inside a session string is one line, not a malformed record (213.14167ms)
|
||||
✔ no sessions is an empty table; symlink source refuses (291.100853ms)
|
||||
✔ reads only refactor even when another branch is checked out (226.626428ms)
|
||||
✔ invalid dates, reverse dates and duplicate options refuse (176.688993ms)
|
||||
✔ T3 agent assignments do not count as human in Table 2 (219.617626ms)
|
||||
✔ preamble parsing and issue number boundaries (0.434196ms)
|
||||
✔ T3 header: agent, or board from control-board; anything short of the full header is human (0.143448ms)
|
||||
✔ no closed issues with human messages means undefined ratio, not invented zero (0.19361ms)
|
||||
✔ T3: seat, archived, unmapped and Researcher threads count; imported, deleted and other-project threads do not (824.563293ms)
|
||||
✔ T3: the default path is read from HOME and prints no path line; --no-t3 says so (1000.905234ms)
|
||||
✔ T3: a HOME with no database exits 1 and names --no-t3 (203.609674ms)
|
||||
✔ T3: a file that is not a database exits 1 and names --no-t3 (194.71429ms)
|
||||
✔ T3: a seat thread renamed to another seat exits 1 naming thread, title and roles (518.142029ms)
|
||||
✔ T3: an unmapped thread addressed as a seat exits 1 (569.946942ms)
|
||||
✔ T3: a header to another thread id is not cross-checked (787.859966ms)
|
||||
✔ T3: no project, or two, for this root exits 1 (1458.185096ms)
|
||||
✔ T3: a removed column exits 1 and names it (374.658545ms)
|
||||
✔ T3: a missing table exits 1 and names it (333.135189ms)
|
||||
✔ T3: a counted row with an unknown role exits 1 without its text (518.1692ms)
|
||||
✔ T3: a counted row with non-text content exits 1 without its text (470.069153ms)
|
||||
✔ T3: a counted row with an unparseable created_at exits 1 without its text (594.355264ms)
|
||||
✔ T3: a missing orchestration_events makes the diagnostic unknown and keeps the counts (1101.049509ms)
|
||||
✔ T3: a human message with no event counts in humanWithoutEvent (559.271689ms)
|
||||
✔ T3: an unparseable event makes the diagnostic unknown and keeps the counts (909.949273ms)
|
||||
✔ T3: an event with no string messageId makes the diagnostic unknown and keeps the counts (971.023265ms)
|
||||
✔ T3: an error that is not from SQLite is rethrown, not reported as a database failure (424.377883ms)
|
||||
✔ T3: a symlink at ~/.t3 exits 1 (178.873006ms)
|
||||
✔ T3: a symlink at ~/.t3/userdata exits 1 (194.873019ms)
|
||||
✔ T3: a symlink at ~/.t3/userdata/state.sqlite exits 1 (138.790741ms)
|
||||
✔ T3: with --t3-db, a symlinked file or directory exits 1 (598.260244ms)
|
||||
✔ T3 WAL: the newest message only in -wal, writer attached, is counted (616.999343ms)
|
||||
✔ T3 WAL: stopped cleanly, counts are correct and the main file is unchanged (747.300765ms)
|
||||
✔ T3 WAL: -wal without -shm in a writable directory is read (669.972464ms)
|
||||
✔ T3 WAL: -wal without -shm in a read-only directory exits 1 (859.988644ms)
|
||||
✔ T3 WAL: stopped cleanly in a read-only directory exits 1 (780.564496ms)
|
||||
✔ T3: a lock held past the 5 s busy timeout exits 1 and names --no-t3 (5802.477411ms)
|
||||
✔ a done row whose closing issue is open is a violation; a row that is not done is not (1.814873ms)
|
||||
✔ an issue several rows close is expected closed only once all of them are done (0.493759ms)
|
||||
✔ closure needs positive evidence: unknown is undecided, and so is a skipped or short issue check (0.366812ms)
|
||||
✔ each owner of an in-progress or in-review row gets one liveness class (7.558511ms)
|
||||
✔ a required row not done after 14 days is a violation; a legacy row uses genesis as its lower bound (0.382616ms)
|
||||
✔ an ISO requiredSince, as `set required` writes it, ages from its UTC day; one that does not parse is a violation (0.239616ms)
|
||||
✔ the text section always ends in a count and a result, and never prints a full pass (0.322972ms)
|
||||
✔ pidAlive: a running pid is present, an exited one is gone, and EPERM still means present (24.300433ms)
|
||||
✔ issue states: open list first, then the metric page, then at most 10 lookups (299.016341ms)
|
||||
✔ a full open list: lookups settle what it leaves out, and only an unsettled issue keeps it undecided (379.027884ms)
|
||||
✔ the open list refuses on a failed call or a bad record, and never echoes the helper (187.181352ms)
|
||||
✔ a helper call past the deadline is killed with its child, and the call reports it (2008.003463ms)
|
||||
✔ readQueue loads queue.json through the queue validator and refuses anything else (242.189312ms)
|
||||
✔ protected changes list every in-range entry that changes a required or parked row (525.618188ms)
|
||||
✔ the CLI prints the queue section above the weekly table and under a queue key in --json (563.216329ms)
|
||||
✔ a queue with nothing wrong prints 0 violations and a reduced pass, never a full pass (284.538284ms)
|
||||
✔ --no-issues makes no call and leaves the issue checks undecided; --no-queue skips the section (343.338245ms)
|
||||
✔ the CLI refuses a bad queue before any call, and a failed open list with exit 2 (308.846971ms)
|
||||
✔ --unsupported-runtime repeats once per seat and takes a seat name (342.898875ms)
|
||||
✔ an unreadable config makes every owner invalid instead of passing them (197.221174ms)
|
||||
✔ a raw token file, with or without one trailing newline, reaches curl only through the config stream (120.327844ms)
|
||||
✔ the raw path accepts nothing else, and refuses before curl runs (369.402648ms)
|
||||
✔ the file checks still apply on the raw path: mode, symlink, missing, directory (279.837232ms)
|
||||
✔ the raw path base URL has no override (54.291872ms)
|
||||
✔ the JSON path is unchanged, and JSON never falls through to the raw path (230.9248ms)
|
||||
✔ a file that changes between the two reads refuses before curl runs, with or without a body (713.684199ms)
|
||||
✔ the token reaches no child environment, even with an inherited CFG or SHELLOPTS=allexport (694.027535ms)
|
||||
✔ real helper GET HTTP 200 preserves exit 0 without credentials (13.070169ms)
|
||||
✔ real helper POST HTTP 201 preserves exit 0 without credentials (13.083497ms)
|
||||
✔ real helper GET HTTP 403 preserves exit 1 without credentials (9.033467ms)
|
||||
✔ fixture git subjects only, follow-ups and three session kinds (162.480984ms)
|
||||
✔ text and JSON carry same numbers, open and truncated title (216.533556ms)
|
||||
✔ missing credentials exit 2, no-issues never calls API and shows unknown (187.42729ms)
|
||||
✔ empty range gives no rows and zero totals (153.553472ms)
|
||||
✔ inclusive UTC dates, first-line preamble only, role and seat boundaries (145.104879ms)
|
||||
✔ close-only issue included, even median, missing metadata stays unknown (138.143192ms)
|
||||
✔ unique commits but per-issue links count multiple tags once each (137.264651ms)
|
||||
✔ page cap refuses rather than silently undercounting (130.219584ms)
|
||||
✔ bad API payload not JSON refuses (124.426361ms)
|
||||
✔ bad API payload {} refuses (130.835828ms)
|
||||
✔ bad API payload [{"number":1}] refuses (131.717421ms)
|
||||
✔ partial or malformed session log refuses with location, not content (141.490599ms)
|
||||
✔ a U+2028 or U+2029 inside a session string is one line, not a malformed record (135.981775ms)
|
||||
✔ no sessions is an empty table; symlink source refuses (208.0173ms)
|
||||
✔ reads only refactor even when another branch is checked out (147.21053ms)
|
||||
✔ invalid dates, reverse dates and duplicate options refuse (103.221367ms)
|
||||
✔ T3 agent assignments do not count as human in Table 2 (139.224294ms)
|
||||
✔ preamble parsing and issue number boundaries (0.429217ms)
|
||||
✔ T3 header: agent, or board from control-board; anything short of the full header is human (0.141263ms)
|
||||
✔ no closed issues with human messages means undefined ratio, not invented zero (0.183749ms)
|
||||
✔ T3: seat, archived, unmapped and Researcher threads count; imported, deleted and other-project threads do not (439.040684ms)
|
||||
✔ T3: the default path is read from HOME and prints no path line; --no-t3 says so (755.195371ms)
|
||||
✔ T3: a HOME with no database exits 1 and names --no-t3 (140.301202ms)
|
||||
✔ T3: a file that is not a database exits 1 and names --no-t3 (143.325169ms)
|
||||
✔ T3: a seat thread renamed to another seat exits 1 naming thread, title and roles (423.944271ms)
|
||||
✔ T3: an unmapped thread addressed as a seat exits 1 (405.235245ms)
|
||||
✔ T3: a header to another thread id is not cross-checked (399.242708ms)
|
||||
✔ T3: no project, or two, for this root exits 1 (983.743767ms)
|
||||
✔ T3: a removed column exits 1 and names it (243.999694ms)
|
||||
✔ T3: a missing table exits 1 and names it (242.680597ms)
|
||||
✔ T3: a counted row with an unknown role exits 1 without its text (381.381698ms)
|
||||
✔ T3: a counted row with non-text content exits 1 without its text (409.893147ms)
|
||||
✔ T3: a counted row with an unparseable created_at exits 1 without its text (436.160156ms)
|
||||
✔ T3: a missing orchestration_events makes the diagnostic unknown and keeps the counts (777.585819ms)
|
||||
✔ T3: a human message with no event counts in humanWithoutEvent (411.360534ms)
|
||||
✔ T3: an unparseable event makes the diagnostic unknown and keeps the counts (723.421105ms)
|
||||
✔ T3: an event with no string messageId makes the diagnostic unknown and keeps the counts (715.375859ms)
|
||||
✔ T3: an error that is not from SQLite is rethrown, not reported as a database failure (397.517748ms)
|
||||
✔ T3: a symlink at ~/.t3 exits 1 (158.225544ms)
|
||||
✔ T3: a symlink at ~/.t3/userdata exits 1 (165.345952ms)
|
||||
✔ T3: a symlink at ~/.t3/userdata/state.sqlite exits 1 (140.074401ms)
|
||||
✔ T3: with --t3-db, a symlinked file or directory exits 1 (487.275139ms)
|
||||
✔ T3 WAL: the newest message only in -wal, writer attached, is counted (399.702885ms)
|
||||
✔ T3 WAL: stopped cleanly, counts are correct and the main file is unchanged (393.441586ms)
|
||||
✔ T3 WAL: -wal without -shm in a writable directory is read (499.066966ms)
|
||||
✔ T3 WAL: -wal without -shm in a read-only directory exits 1 (453.861577ms)
|
||||
✔ T3 WAL: stopped cleanly in a read-only directory exits 1 (383.574487ms)
|
||||
✔ T3: a lock held past the 5 s busy timeout exits 1 and names --no-t3 (5389.251886ms)
|
||||
✔ a done row whose closing issue is open is a violation; a row that is not done is not (1.769997ms)
|
||||
✔ an issue several rows close is expected closed only once all of them are done (0.479396ms)
|
||||
✔ closure needs positive evidence: unknown is undecided, and so is a skipped or short issue check (0.352468ms)
|
||||
✔ each owner of an in-progress or in-review row gets one liveness class (7.450645ms)
|
||||
✔ a required row not done after 14 days is a violation; a legacy row uses genesis as its lower bound (0.375297ms)
|
||||
✔ an ISO requiredSince, as `set required` writes it, ages from its UTC day; one that does not parse is a violation (0.202726ms)
|
||||
✔ the text section always ends in a count and a result, and never prints a full pass (0.31282ms)
|
||||
✔ pidAlive: a running pid is present, an exited one is gone, and EPERM still means present (23.634577ms)
|
||||
✔ issue states: open list first, then the metric page, then at most 10 lookups (278.392933ms)
|
||||
✔ a full open list: lookups settle what it leaves out, and only an unsettled issue keeps it undecided (377.736803ms)
|
||||
✔ the open list refuses on a failed call or a bad record, and never echoes the helper (169.378535ms)
|
||||
✔ a helper call past the deadline is killed with its child, and the call reports it (2007.711363ms)
|
||||
✔ readQueue loads queue.json through the queue validator and refuses anything else (119.664719ms)
|
||||
✔ protected changes list every in-range entry that changes a required or parked row (444.485151ms)
|
||||
✔ the CLI prints the queue section above the weekly table and under a queue key in --json (526.301912ms)
|
||||
✔ a queue with nothing wrong prints 0 violations and a reduced pass, never a full pass (251.548181ms)
|
||||
✔ --no-issues makes no call and leaves the issue checks undecided; --no-queue skips the section (320.738002ms)
|
||||
✔ the CLI refuses a bad queue before any call, and a failed open list with exit 2 (288.417953ms)
|
||||
✔ --unsupported-runtime repeats once per seat and takes a seat name (346.556078ms)
|
||||
✔ an unreadable config makes every owner invalid instead of passing them (179.623698ms)
|
||||
ℹ tests 78
|
||||
ℹ suites 0
|
||||
ℹ pass 78
|
||||
@@ -83,4 +83,4 @@
|
||||
ℹ cancelled 0
|
||||
ℹ skipped 0
|
||||
ℹ todo 0
|
||||
ℹ duration_ms 26163.120468
|
||||
ℹ duration_ms 19490.132391
|
||||
|
||||
@@ -1,72 +1,72 @@
|
||||
✔ pure resolution selects current default or explicit enrolled account (2.054528ms)
|
||||
✔ scope is explicit, bounded and never inferred (1.596542ms)
|
||||
✔ fork pin is preserved against default change, override, missing account and revocation (0.79176ms)
|
||||
✔ unenrolled account/provider, missing harness, model expansion and native model ceiling refuse (1.159723ms)
|
||||
✔ only explicit synthetic credential forms and internal fixture stores admitted (6.518592ms)
|
||||
✔ two concurrent workspaces of the same agent publish distinct complete private generations (68.351516ms)
|
||||
✔ same execution ID is exclusively claimed and cannot overwrite a published generation (33.972978ms)
|
||||
✔ failed generation after-auth preserves prior files, records failure and refuses blind same-ID retry (52.451468ms)
|
||||
✔ failed generation before-publish preserves prior files, records failure and refuses blind same-ID retry (58.496772ms)
|
||||
✔ credential lock contention refuses without duplicate side effects (18.327397ms)
|
||||
✔ symlinked pre-existing final target is refused and never followed (44.660615ms)
|
||||
✔ invalid registry cannot resolve; no fallback to supplied partial entries (0.259178ms)
|
||||
✔ post-publication failure records uncertainty, preserves complete generation and prevents replay (35.197219ms)
|
||||
✔ expired credentials refresh under transaction and subsequent generation reuses rotation (120.496699ms)
|
||||
✔ refresh failure retains prior generation and store state (98.926492ms)
|
||||
✔ refresh timeout retains prior generation and store state (173.954281ms)
|
||||
✔ refresh malformed retains prior generation and store state (94.802167ms)
|
||||
✔ concurrent refresh on same account refuses contention while unrelated account proceeds (216.827049ms)
|
||||
✔ invalid refresh options refuse before burning claim (50.160412ms)
|
||||
✔ fixed fake process rotates both OAuth fields without mutating caller input (31.053588ms)
|
||||
✔ concurrent isolated processes preserve separate provider credentials (27.535508ms)
|
||||
✔ fake failure is refused with fixed diagnostics (22.739666ms)
|
||||
✔ fake malformed is refused with fixed diagnostics (27.208676ms)
|
||||
✔ fake timeout is refused with fixed diagnostics (103.025381ms)
|
||||
✔ fake unchanged is refused with fixed diagnostics (25.003792ms)
|
||||
✔ caller executable/environment injection is rejected before spawning (0.310957ms)
|
||||
✔ valid fixture tree validates and lists without secrets (72.979155ms)
|
||||
✔ unknown-field refuses (0.36807ms)
|
||||
✔ invalid-id refuses uppercase and traversal shapes (0.267451ms)
|
||||
✔ plain-http baseUrl requires allowInsecureTransport (0.341539ms)
|
||||
✔ native provider rejects allowInsecureTransport (0.128697ms)
|
||||
✔ unsupported credential type and kind refuse (0.153047ms)
|
||||
✔ account provider-path mismatch refuses (0.108869ms)
|
||||
✔ profile account refs must be provider/account shaped (0.223655ms)
|
||||
✔ seat selection accepts fork pin field, validates account refs (0.67099ms)
|
||||
✔ harness manifest id must equal executable (gate 1) (0.208191ms)
|
||||
✔ CLI validate: duplicate provider id across files refuses (30.331947ms)
|
||||
✔ CLI validate: missing referenced provider/account refuse (35.752038ms)
|
||||
✔ CLI validate: broken JSON refuses without secret echo (26.482716ms)
|
||||
✔ CLI usage errors exit 2 (52.783092ms)
|
||||
✔ credential.json sibling presence does not break validation and is never read (67.16895ms)
|
||||
✔ D1 missing, empty and structurally empty roots refuse, no list projection (177.535483ms)
|
||||
✔ D1 required directory auth cannot be absent (53.462767ms)
|
||||
✔ D1 required directory auth/providers cannot be absent (58.208941ms)
|
||||
✔ D1 required directory auth/accounts cannot be absent (60.123059ms)
|
||||
✔ D1 required directory auth/settings cannot be absent (64.777835ms)
|
||||
✔ D1 required directory harnesses cannot be absent (63.810214ms)
|
||||
✔ D1 root file and unreadable metadata refuse (117.813395ms)
|
||||
✔ D2 no symlink traversal at auth/providers/openai-codex.json (54.008571ms)
|
||||
✔ D2 no symlink traversal at auth/accounts/openai-codex/homelab-openai (65.397095ms)
|
||||
✔ D2 no symlink traversal at auth/providers (54.760645ms)
|
||||
✔ D2 no symlink traversal at auth (56.803918ms)
|
||||
✔ D2 root and ancestor symlinks and lexical traversal refuse (165.243458ms)
|
||||
✔ private filesystem modes enforced for root (57.733162ms)
|
||||
✔ private filesystem modes enforced for auth (53.800829ms)
|
||||
✔ private filesystem modes enforced for auth/providers/openai-codex.json (64.945974ms)
|
||||
✔ private filesystem modes enforced for auth/accounts/openai-codex/homelab-openai/account.json (63.277721ms)
|
||||
✔ D3 numeric version 1 only across all record kinds (1.246662ms)
|
||||
✔ D4 nested unknown keys and missing per-kind required fields refuse (64.557054ms)
|
||||
✔ D5 unenrolled default refuses even when account exists (65.244555ms)
|
||||
✔ D6 provider/account credential type must match (69.207623ms)
|
||||
✔ D7 every harness endpoint enforces HTTP opt-in and shape (0.51095ms)
|
||||
✔ D8 URLs reject embedded credentials and unsupported protocols without echo (166.65518ms)
|
||||
✔ D9 malformed JSON diagnostics contain no content excerpt (62.800794ms)
|
||||
✔ D10 missing metadata is missing-path, not invalid-json (56.760167ms)
|
||||
✔ D10 library returns no partial entries on any invalid record (74.177067ms)
|
||||
✔ null/scalar/array metadata refuses without stack or echo (229.571906ms)
|
||||
✔ credential sibling is never opened, even when an unreadable symlink (32.269336ms)
|
||||
✔ oversized metadata refuses before parsing (54.703633ms)
|
||||
✔ pure resolution selects current default or explicit enrolled account (2.429019ms)
|
||||
✔ scope is explicit, bounded and never inferred (2.008188ms)
|
||||
✔ fork pin is preserved against default change, override, missing account and revocation (1.028803ms)
|
||||
✔ unenrolled account/provider, missing harness, model expansion and native model ceiling refuse (0.937079ms)
|
||||
✔ only explicit synthetic credential forms and internal fixture stores admitted (6.706158ms)
|
||||
✔ two concurrent workspaces of the same agent publish distinct complete private generations (66.687633ms)
|
||||
✔ same execution ID is exclusively claimed and cannot overwrite a published generation (35.350014ms)
|
||||
✔ failed generation after-auth preserves prior files, records failure and refuses blind same-ID retry (53.622341ms)
|
||||
✔ failed generation before-publish preserves prior files, records failure and refuses blind same-ID retry (62.771761ms)
|
||||
✔ credential lock contention refuses without duplicate side effects (14.189607ms)
|
||||
✔ symlinked pre-existing final target is refused and never followed (27.916602ms)
|
||||
✔ invalid registry cannot resolve; no fallback to supplied partial entries (0.324141ms)
|
||||
✔ post-publication failure records uncertainty, preserves complete generation and prevents replay (27.397863ms)
|
||||
✔ expired credentials refresh under transaction and subsequent generation reuses rotation (95.071147ms)
|
||||
✔ refresh failure retains prior generation and store state (66.210873ms)
|
||||
✔ refresh timeout retains prior generation and store state (145.945902ms)
|
||||
✔ refresh malformed retains prior generation and store state (67.020929ms)
|
||||
✔ concurrent refresh on same account refuses contention while unrelated account proceeds (215.216001ms)
|
||||
✔ invalid refresh options refuse before burning claim (32.635753ms)
|
||||
✔ fixed fake process rotates both OAuth fields without mutating caller input (34.494178ms)
|
||||
✔ concurrent isolated processes preserve separate provider credentials (30.51094ms)
|
||||
✔ fake failure is refused with fixed diagnostics (28.545153ms)
|
||||
✔ fake malformed is refused with fixed diagnostics (23.610575ms)
|
||||
✔ fake timeout is refused with fixed diagnostics (105.288146ms)
|
||||
✔ fake unchanged is refused with fixed diagnostics (33.437171ms)
|
||||
✔ caller executable/environment injection is rejected before spawning (0.361978ms)
|
||||
✔ valid fixture tree validates and lists without secrets (78.943192ms)
|
||||
✔ unknown-field refuses (0.466063ms)
|
||||
✔ invalid-id refuses uppercase and traversal shapes (0.780417ms)
|
||||
✔ plain-http baseUrl requires allowInsecureTransport (0.329672ms)
|
||||
✔ native provider rejects allowInsecureTransport (0.14812ms)
|
||||
✔ unsupported credential type and kind refuse (0.179903ms)
|
||||
✔ account provider-path mismatch refuses (0.137888ms)
|
||||
✔ profile account refs must be provider/account shaped (0.278809ms)
|
||||
✔ seat selection accepts fork pin field, validates account refs (0.228679ms)
|
||||
✔ harness manifest id must equal executable (gate 1) (0.237291ms)
|
||||
✔ CLI validate: duplicate provider id across files refuses (34.642672ms)
|
||||
✔ CLI validate: missing referenced provider/account refuse (37.622294ms)
|
||||
✔ CLI validate: broken JSON refuses without secret echo (38.871585ms)
|
||||
✔ CLI usage errors exit 2 (64.004687ms)
|
||||
✔ credential.json sibling presence does not break validation and is never read (74.131397ms)
|
||||
✔ D1 missing, empty and structurally empty roots refuse, no list projection (198.454483ms)
|
||||
✔ D1 required directory auth cannot be absent (67.557644ms)
|
||||
✔ D1 required directory auth/providers cannot be absent (62.914613ms)
|
||||
✔ D1 required directory auth/accounts cannot be absent (66.596975ms)
|
||||
✔ D1 required directory auth/settings cannot be absent (62.26027ms)
|
||||
✔ D1 required directory harnesses cannot be absent (62.855613ms)
|
||||
✔ D1 root file and unreadable metadata refuse (110.859654ms)
|
||||
✔ D2 no symlink traversal at auth/providers/openai-codex.json (56.377228ms)
|
||||
✔ D2 no symlink traversal at auth/accounts/openai-codex/homelab-openai (61.738757ms)
|
||||
✔ D2 no symlink traversal at auth/providers (58.632499ms)
|
||||
✔ D2 no symlink traversal at auth (61.411345ms)
|
||||
✔ D2 root and ancestor symlinks and lexical traversal refuse (162.750712ms)
|
||||
✔ private filesystem modes enforced for root (51.93814ms)
|
||||
✔ private filesystem modes enforced for auth (53.662983ms)
|
||||
✔ private filesystem modes enforced for auth/providers/openai-codex.json (58.640811ms)
|
||||
✔ private filesystem modes enforced for auth/accounts/openai-codex/homelab-openai/account.json (63.272857ms)
|
||||
✔ D3 numeric version 1 only across all record kinds (1.40059ms)
|
||||
✔ D4 nested unknown keys and missing per-kind required fields refuse (64.66648ms)
|
||||
✔ D5 unenrolled default refuses even when account exists (65.199118ms)
|
||||
✔ D6 provider/account credential type must match (66.85376ms)
|
||||
✔ D7 every harness endpoint enforces HTTP opt-in and shape (0.478063ms)
|
||||
✔ D8 URLs reject embedded credentials and unsupported protocols without echo (174.054132ms)
|
||||
✔ D9 malformed JSON diagnostics contain no content excerpt (60.943055ms)
|
||||
✔ D10 missing metadata is missing-path, not invalid-json (64.69376ms)
|
||||
✔ D10 library returns no partial entries on any invalid record (71.901694ms)
|
||||
✔ null/scalar/array metadata refuses without stack or echo (223.397559ms)
|
||||
✔ credential sibling is never opened, even when an unreadable symlink (31.932356ms)
|
||||
✔ oversized metadata refuses before parsing (55.12523ms)
|
||||
ℹ tests 69
|
||||
ℹ suites 0
|
||||
ℹ pass 69
|
||||
@@ -74,4 +74,4 @@
|
||||
ℹ cancelled 0
|
||||
ℹ skipped 0
|
||||
ℹ todo 0
|
||||
ℹ duration_ms 2162.523966
|
||||
ℹ duration_ms 2194.660704
|
||||
|
||||
@@ -1,153 +1,153 @@
|
||||
✔ F1: an ordinary commit after update-ref is refused until step 8; then it commits and the queue stays at C (1260.532537ms)
|
||||
✔ F1: a plain `commit -e` whose guard ran before update-ref fails at its own HEAD update (1076.47454ms)
|
||||
✔ F1: an ordinary commit after update-ref is refused until step 8; then it commits and the queue stays at C (1279.93069ms)
|
||||
✔ F1: a plain `commit -e` whose guard ran before update-ref fails at its own HEAD update (1061.181758ms)
|
||||
ℹ 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 (1085.134721ms)
|
||||
✔ F1: a `commit -e -- path` whose guard ran before update-ref fails at its own HEAD update (1039.890109ms)
|
||||
ℹ 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 (1061.824905ms)
|
||||
✔ F1: HEAD moving after the step-7 guard check and before update-ref: refused, nothing published (1066.708895ms)
|
||||
✔ F1: H is recorded before the canary, so HEAD moving during the step-1 canary is refused at step 7 (1017.127371ms)
|
||||
✔ F1: a queue commit landing after H is recorded: step 1 says HEAD moved, not the guard (1355.445314ms)
|
||||
✔ F1: a queue commit landing between the HEAD check and the canary: the failed clean run is reported as HEAD moved (1001.736236ms)
|
||||
✔ F1: a shared-index change during the procedure is not committed (1123.875559ms)
|
||||
✔ F1: a queue path staged after update-ref: step 8 stops and touches nothing (1133.958432ms)
|
||||
✔ F1: a missing or a different hook refuses (787.253511ms)
|
||||
✔ F1: same bytes without the exec bit, a symlinked hook, and core.hooksPath in the local or global scope each refuse before update-ref (1268.622115ms)
|
||||
✔ F1: the canary refuses a hook that git would not run (767.910797ms)
|
||||
✔ F1: the guard deactivated after step 1 is refused at the step-7 recheck (1000.642982ms)
|
||||
✔ bootstrap: implementation-only HEAD, the guard, genesis, the --genesis commit, then an extending commit (1216.284271ms)
|
||||
✔ bootstrap: --genesis with a base present, no base without --genesis, an op before the first commit, a changed map, another branch (1072.93868ms)
|
||||
✔ bootstrap: the archived tests and validator run outside any repository (1114.228462ms)
|
||||
✔ general: an unrelated staged file stays staged, and the committed blobs are the snapshot bytes (1133.802007ms)
|
||||
✔ general: a queue write after the snapshot is not committed (1588.451551ms)
|
||||
✔ general: a snapshot whose log does not extend the base refuses (1345.572094ms)
|
||||
✔ general: install-hook privilege, repair of a same-bytes hook, and its refusals (158.499452ms)
|
||||
✔ general: environment overrides, a linked worktree and usage (809.991692ms)
|
||||
✔ general: a queue path staged before the run refuses at step 1 (752.034412ms)
|
||||
✔ general: HEAD's queue tests failing in the archive refuse (974.440619ms)
|
||||
✔ genesis document serializes deterministically and replays (4.243965ms)
|
||||
✔ a hand edit that stays valid JSON fails replay; a formatting-only edit fails re-serialization (2.363785ms)
|
||||
✔ a tampered result, receipt or viewSha fails replay (2.23881ms)
|
||||
✔ op ids: 8 to 72 characters for callers, 80 in the log for .outcome entries (0.180499ms)
|
||||
✔ add: defaults for an ordinary seat, privileged extras, refusals (4.59724ms)
|
||||
✔ matrix: queued→briefed privileged; briefed→in-progress owner with after satisfied (5.711521ms)
|
||||
✔ matrix: release, review round, changes requested and waiting-on-jason (12.703351ms)
|
||||
✔ matrix J5: in-review→done by the gate owner with evidence naming the current round (7.854129ms)
|
||||
✔ review issue, lead decision 23: none refuses, one is used, several need --issue, later rounds keep it (18.361508ms)
|
||||
✔ the row schema refuses a round with a null issue, and the A1 review shape (P2) (2.443285ms)
|
||||
✔ matrix R1: every state × target × actor class matches 8.7, gate owner jason or not, required or not (1321.934974ms)
|
||||
✔ matrix: blocked keeps the claim and returns only to previousState (3.498242ms)
|
||||
✔ matrix J4: parking is Jason's, refused while required; unpark returns to queued (2.258188ms)
|
||||
✔ field edits: who may change what (4.444263ms)
|
||||
✔ set issues keeps a logged narrowing of closes (N10) (2.396823ms)
|
||||
✔ text the table shows refuses \ and <, everywhere it enters (N8) (1.627096ms)
|
||||
✔ every accepted text renders to nine cells on every row (N8) (20.232327ms)
|
||||
✔ genesis: the map refuses an owner among its row's reviewers; replay doesn't (2026-10-04) (0.463181ms)
|
||||
✔ times and dates must be calendar values, not just the shape (2026-10-04) (1.396766ms)
|
||||
✔ replay holds every op id to the caller's rule (N11) (4.32996ms)
|
||||
✔ note: owner, listed reviewer or privileged; empty clears (1.31496ms)
|
||||
✔ assign moves the claim with the owner; done clears it (2.206523ms)
|
||||
✔ render is byte-stable and escapes pipes (0.421495ms)
|
||||
✔ view classification: current, genuine stale, edited stale marker, changed current body, markers (0.547949ms)
|
||||
✔ next: resume, then review, then start, then wait, then nothing; lowest id first (15.614361ms)
|
||||
✔ canonical args make a retry's identity independent of list order (0.261014ms)
|
||||
✔ manifests, headings and blob ids (0.41674ms)
|
||||
✔ the migration map: one queue-map block, exact keys (0.712422ms)
|
||||
✔ every call but `queue` reaches the seat CLI exactly as before A2 (593.551394ms)
|
||||
✔ `queue` reaches the queue CLI with the rest of the arguments (214.598714ms)
|
||||
✔ the pre-A2 fixture is the script A2 changed (0.270252ms)
|
||||
✔ acquire publishes the record by link; release removes only its own lock (13.699806ms)
|
||||
✔ a kill between the temp write and the link leaves no lock (54.469267ms)
|
||||
✔ a short or failed temp write refuses and leaves no lock and no temp (9.326024ms)
|
||||
✔ a link error other than EEXIST refuses (6.479184ms)
|
||||
✔ an error after the link releases the lock: unreadable gate, failing temp stat (17.151141ms)
|
||||
✔ a release that fails on a gate path is reported, never a stack trace (P1) (42.844766ms)
|
||||
✔ a paused holder: another writer waits 10 s, then refuses naming it live (10092.670947ms)
|
||||
✔ two concurrent unlockers: the second refuses on the gate (44.779225ms)
|
||||
✔ a writer publishing during an unlock, lock first: unlock sees it live and refuses (22.456092ms)
|
||||
✔ a writer publishing during an unlock, gate first: the writer releases and refuses (23.746529ms)
|
||||
✔ a gate swapped while held is left in place and reported, on success and on refusal (N1) (26.303537ms)
|
||||
✔ a reused pid within one boot is mismatch; unlock removes the lock and never signals the process (18.32773ms)
|
||||
✔ the same pid and start on a different boot is mismatch (0.773792ms)
|
||||
✔ a foreign host is unknown whatever the local pid says; unlock refuses (82.512363ms)
|
||||
✔ unreadable /proc: classification is unknown and acquire refuses (0.801342ms)
|
||||
✔ invalid records: empty, unparsable, wrong keys, bad start or boot (0.247735ms)
|
||||
✔ a stale gate blocks writers; --check-gate says mismatch for a reused pid (24.859261ms)
|
||||
✔ a delayed release by a dead owner, after unlock and a new owner: the inode check keeps the new lock (28.793256ms)
|
||||
✔ release checks the inode too: a byte-identical lock file with a new inode is left in place (12.386533ms)
|
||||
✔ unlock refuses a live, unknown or invalid lock, and does nothing without one (53.426691ms)
|
||||
✔ the migration map validates and renders the golden genesis table (3.605974ms)
|
||||
✔ the marked QUEUE.md holds every row and parked item between its markers (1.374021ms)
|
||||
✔ map-check reports each kind of drift (3.769497ms)
|
||||
✔ a request posts once as the requester; a retry sends nothing (925.490028ms)
|
||||
✔ each transport answer maps to posted, failed or uncertain (8.9 step 3) (4633.824139ms)
|
||||
✔ the pre-send checks: GET user must name the requester, under the deadline (1649.705611ms)
|
||||
✔ the lead's request refuses a token for login sage (1002.199905ms)
|
||||
✔ the credential file: the seat's own, 0600, no symlink, never the shared default (936.047983ms)
|
||||
✔ an unresolved request blocks a new request, a new round, waiting-on-jason and done (2754.343636ms)
|
||||
✔ a same-op retry after a kill sends nothing, even with a stale view (3581.147531ms)
|
||||
✔ a held lock at the outcome exits 3 and names what the transport said (972.520861ms)
|
||||
✔ late outcomes: after an abandon, and after a resolve with the same or another id (3471.630995ms)
|
||||
✔ resolve checks the comment: issue, markers, round, candidate and author (2199.757148ms)
|
||||
✔ the lead resolves a seat's request: the comment's author is the requester, fetched with the lead's token (963.207915ms)
|
||||
✔ validateRow checks a request round's shape, which every replayed entry must keep (871.059553ms)
|
||||
✔ request, changes, a new candidate, approval: every round pinned; no review files (2129.60435ms)
|
||||
✔ a row with no reviewers opens a round that posts nothing (1824.373429ms)
|
||||
✔ verify-commit: a prospective tree must hold exactly the candidate's paths (1550.689384ms)
|
||||
✔ semantics: v1 entries replay as before; review entries need v2 (619.323764ms)
|
||||
✔ set reviewers refuses the row's owner (2026-09-28) (427.726772ms)
|
||||
✔ the owner records no verdict, even as a listed reviewer (760.230018ms)
|
||||
✔ a request comment over the length limit is not sent (640.357309ms)
|
||||
✔ a late POST on a closed row leaves a conflict nothing can resolve, and resolve asks nothing (775.036707ms)
|
||||
✔ a Jason-gated row reaches waiting-on-jason only on every reviewer's approval (2832.128294ms)
|
||||
✔ genesis: refusals before anything is written (403.047086ms)
|
||||
✔ genesis: the map must be committed, well formed, with committed briefs and seats (623.222448ms)
|
||||
✔ genesis: markers, a stray witness, once only; a retry returns the receipt (535.769096ms)
|
||||
✔ genesis: a file holding genesis alone with no witness is confirmed by sync or a retry (411.50267ms)
|
||||
✔ canonical checks: worktree, second clone, detached HEAD, wrong branch, GIT_DIR, foreign code; a symlink works (654.999389ms)
|
||||
✔ --by that differs from MOSAIC_AGENT_NAME warns on stderr and logs nothing more (N12) (863.898734ms)
|
||||
✔ op ids: missing, too long, reserved; a retry answers; another payload refuses (732.001521ms)
|
||||
✔ a retried add returns the id it first allocated, after reassignment and after done (961.703142ms)
|
||||
✔ Rocko's S4 schedule: a lost result, another writer, then the retry opens no second round (767.524552ms)
|
||||
✔ the review issue and the evidence round through the CLI (lead decision 23, 8.7) (1288.336113ms)
|
||||
✔ claims and add defaults through the CLI; candidates are manifests or reachable commits (851.663689ms)
|
||||
✔ add, set reviewers and assign refuse the row's owner as a reviewer (568.745548ms)
|
||||
✔ the working-brief check: a changed working copy refuses the start and flags next (688.532933ms)
|
||||
✔ next: resume first, then nothing for an idle seat; needs a seat (343.272758ms)
|
||||
✔ view stale: new ops and verify refuse naming the unshown op; retries answer; reads warn; render fixes (736.097429ms)
|
||||
✔ view unknown: a hand edit, an old marker over an edited body, missing or duplicate markers (1284.828802ms)
|
||||
✔ a hand edit to queue.json refuses every verb, reads included (686.215095ms)
|
||||
✔ verify and render --check leave bytes and mtimes unchanged (452.027098ms)
|
||||
✔ render is byte-stable across runs and repositories (323.191895ms)
|
||||
✔ snapshot and verify --snapshot (831.73524ms)
|
||||
✔ usage errors exit 4 (533.103717ms)
|
||||
✔ a short write, ENOSPC or a file fsync failure: nothing visible, temp removed (343.935558ms)
|
||||
✔ a rename failure: nothing visible, temp removed (209.136069ms)
|
||||
✔ a directory fsync failure: uncertain, exit 3, no receipt; the tail refuses new ops; a retry confirms (259.837306ms)
|
||||
✔ a directory fsync failure, then sync names the op (406.195753ms)
|
||||
✔ a witness write failure: uncertain, durable, exit 3; the view is untouched (220.710753ms)
|
||||
✔ the .git fsync after the witness rename fails: uncertain, exit 3, the witness says so (205.853729ms)
|
||||
✔ confirming a tail fsyncs queue.json and docs/plans before the witness; either failure changes nothing (252.052248ms)
|
||||
✔ the docs/plans fsync after the view rename fails: the op stands, the view is written, a warning says so (201.571019ms)
|
||||
✔ a lock swapped while held is left in place and reported, on a receipt and on a refusal (228.011398ms)
|
||||
✔ a lock that cannot be released after an op is reported, on a receipt and on a refusal (234.160899ms)
|
||||
✔ unlock prints a swapped gate's warning on stderr, the result on stdout (145.80488ms)
|
||||
✔ a view write that fails keeps the op and reports a stale view (211.930761ms)
|
||||
✔ SIGKILL before the rename: nothing recorded; the retry removes the leftover temp (673.688149ms)
|
||||
✔ SIGKILL after the rename, before the witness: the tail refuses new ops and sync names the op (686.847329ms)
|
||||
✔ SIGKILL after the witness, before the view: the stale refusal names the op (650.580533ms)
|
||||
✔ SIGKILL after the view, before the receipt: the retry returns the receipt (704.871034ms)
|
||||
✔ git checkout between steps 1 and 7: step 7 refuses and nothing is written (262.774005ms)
|
||||
✔ git stash restoring an older valid pair: history lost; accept-history needs privilege, a reason and --yes (1074.729772ms)
|
||||
✔ a deleted witness: refused after the locked recheck; accept-history records it absent (386.684099ms)
|
||||
✔ a header edit during a write: the op stands, the view write is skipped with a warning (227.042343ms)
|
||||
✔ a reader paused between the witness and the file while a writer finishes: no lost-history report (227.289466ms)
|
||||
✔ file-then-witness order forced by a hook: the locked recheck prevents a false report (230.099313ms)
|
||||
✔ a writer paused before and after the witness rename: readers see a tail, then a match (226.156708ms)
|
||||
✔ a true rollback is reported only after the locked recheck; a held lock names its holder instead (621.321999ms)
|
||||
✔ an accept-history in progress: an unlocked reader waits on the lock and never reports lost history (649.492684ms)
|
||||
✔ the platform check refuses other filesystems (156.699881ms)
|
||||
✔ tmpfs passes only a test layer that allows it (N5) (259.564709ms)
|
||||
✔ unlock keeps a multi-line lock record on stdout (P3) (228.264417ms)
|
||||
✔ F1: step 8 with index.lock held exits 3, and ordinary commits stay refused until the printed command runs (1113.030413ms)
|
||||
✔ F1: HEAD moving after the step-7 guard check and before update-ref: refused, nothing published (1049.393326ms)
|
||||
✔ F1: H is recorded before the canary, so HEAD moving during the step-1 canary is refused at step 7 (1110.939364ms)
|
||||
✔ F1: a queue commit landing after H is recorded: step 1 says HEAD moved, not the guard (1901.512818ms)
|
||||
✔ F1: a queue commit landing between the HEAD check and the canary: the failed clean run is reported as HEAD moved (1153.947616ms)
|
||||
✔ F1: a shared-index change during the procedure is not committed (1320.421343ms)
|
||||
✔ F1: a queue path staged after update-ref: step 8 stops and touches nothing (1268.461107ms)
|
||||
✔ F1: a missing or a different hook refuses (887.119522ms)
|
||||
✔ F1: same bytes without the exec bit, a symlinked hook, and core.hooksPath in the local or global scope each refuse before update-ref (1479.523604ms)
|
||||
✔ F1: the canary refuses a hook that git would not run (753.192489ms)
|
||||
✔ F1: the guard deactivated after step 1 is refused at the step-7 recheck (1057.980151ms)
|
||||
✔ bootstrap: implementation-only HEAD, the guard, genesis, the --genesis commit, then an extending commit (1014.061543ms)
|
||||
✔ bootstrap: --genesis with a base present, no base without --genesis, an op before the first commit, a changed map, another branch (1062.788887ms)
|
||||
✔ bootstrap: the archived tests and validator run outside any repository (999.382289ms)
|
||||
✔ general: an unrelated staged file stays staged, and the committed blobs are the snapshot bytes (1034.48839ms)
|
||||
✔ general: a queue write after the snapshot is not committed (1544.53368ms)
|
||||
✔ general: a snapshot whose log does not extend the base refuses (1059.429735ms)
|
||||
✔ general: install-hook privilege, repair of a same-bytes hook, and its refusals (173.000296ms)
|
||||
✔ general: environment overrides, a linked worktree and usage (677.48337ms)
|
||||
✔ general: a queue path staged before the run refuses at step 1 (712.677427ms)
|
||||
✔ general: HEAD's queue tests failing in the archive refuse (950.893886ms)
|
||||
✔ genesis document serializes deterministically and replays (3.684709ms)
|
||||
✔ a hand edit that stays valid JSON fails replay; a formatting-only edit fails re-serialization (2.123963ms)
|
||||
✔ a tampered result, receipt or viewSha fails replay (2.124902ms)
|
||||
✔ op ids: 8 to 72 characters for callers, 80 in the log for .outcome entries (0.160084ms)
|
||||
✔ add: defaults for an ordinary seat, privileged extras, refusals (3.307489ms)
|
||||
✔ matrix: queued→briefed privileged; briefed→in-progress owner with after satisfied (6.854022ms)
|
||||
✔ matrix: release, review round, changes requested and waiting-on-jason (11.855146ms)
|
||||
✔ matrix J5: in-review→done by the gate owner with evidence naming the current round (9.291068ms)
|
||||
✔ review issue, lead decision 23: none refuses, one is used, several need --issue, later rounds keep it (18.606488ms)
|
||||
✔ the row schema refuses a round with a null issue, and the A1 review shape (P2) (2.376603ms)
|
||||
✔ matrix R1: every state × target × actor class matches 8.7, gate owner jason or not, required or not (1351.108857ms)
|
||||
✔ matrix: blocked keeps the claim and returns only to previousState (3.790996ms)
|
||||
✔ matrix J4: parking is Jason's, refused while required; unpark returns to queued (2.439068ms)
|
||||
✔ field edits: who may change what (5.543327ms)
|
||||
✔ set issues keeps a logged narrowing of closes (N10) (2.904337ms)
|
||||
✔ text the table shows refuses \ and <, everywhere it enters (N8) (2.09567ms)
|
||||
✔ every accepted text renders to nine cells on every row (N8) (25.296747ms)
|
||||
✔ genesis: the map refuses an owner among its row's reviewers; replay doesn't (2026-10-04) (0.59648ms)
|
||||
✔ times and dates must be calendar values, not just the shape (2026-10-04) (1.736801ms)
|
||||
✔ replay holds every op id to the caller's rule (N11) (5.412384ms)
|
||||
✔ note: owner, listed reviewer or privileged; empty clears (1.149404ms)
|
||||
✔ assign moves the claim with the owner; done clears it (2.661687ms)
|
||||
✔ render is byte-stable and escapes pipes (0.574402ms)
|
||||
✔ view classification: current, genuine stale, edited stale marker, changed current body, markers (0.646752ms)
|
||||
✔ next: resume, then review, then start, then wait, then nothing; lowest id first (16.872894ms)
|
||||
✔ canonical args make a retry's identity independent of list order (0.28851ms)
|
||||
✔ manifests, headings and blob ids (0.390992ms)
|
||||
✔ the migration map: one queue-map block, exact keys (0.504008ms)
|
||||
✔ every call but `queue` reaches the seat CLI exactly as before A2 (594.850867ms)
|
||||
✔ `queue` reaches the queue CLI with the rest of the arguments (200.393007ms)
|
||||
✔ the pre-A2 fixture is the script A2 changed (0.278964ms)
|
||||
✔ acquire publishes the record by link; release removes only its own lock (16.628947ms)
|
||||
✔ a kill between the temp write and the link leaves no lock (52.152311ms)
|
||||
✔ a short or failed temp write refuses and leaves no lock and no temp (9.972978ms)
|
||||
✔ a link error other than EEXIST refuses (6.223349ms)
|
||||
✔ an error after the link releases the lock: unreadable gate, failing temp stat (17.749792ms)
|
||||
✔ a release that fails on a gate path is reported, never a stack trace (P1) (38.678105ms)
|
||||
✔ a paused holder: another writer waits 10 s, then refuses naming it live (10092.400082ms)
|
||||
✔ two concurrent unlockers: the second refuses on the gate (48.091667ms)
|
||||
✔ a writer publishing during an unlock, lock first: unlock sees it live and refuses (25.144331ms)
|
||||
✔ a writer publishing during an unlock, gate first: the writer releases and refuses (21.298481ms)
|
||||
✔ a gate swapped while held is left in place and reported, on success and on refusal (N1) (11.879281ms)
|
||||
✔ a reused pid within one boot is mismatch; unlock removes the lock and never signals the process (21.635177ms)
|
||||
✔ the same pid and start on a different boot is mismatch (0.427637ms)
|
||||
✔ a foreign host is unknown whatever the local pid says; unlock refuses (78.81943ms)
|
||||
✔ unreadable /proc: classification is unknown and acquire refuses (0.782644ms)
|
||||
✔ invalid records: empty, unparsable, wrong keys, bad start or boot (0.192916ms)
|
||||
✔ a stale gate blocks writers; --check-gate says mismatch for a reused pid (23.436998ms)
|
||||
✔ a delayed release by a dead owner, after unlock and a new owner: the inode check keeps the new lock (34.209647ms)
|
||||
✔ release checks the inode too: a byte-identical lock file with a new inode is left in place (11.896035ms)
|
||||
✔ unlock refuses a live, unknown or invalid lock, and does nothing without one (45.435129ms)
|
||||
✔ the migration map validates and renders the golden genesis table (3.068396ms)
|
||||
✔ the marked QUEUE.md holds every row and parked item between its markers (1.179375ms)
|
||||
✔ map-check reports each kind of drift (3.696429ms)
|
||||
✔ a request posts once as the requester; a retry sends nothing (793.821048ms)
|
||||
✔ each transport answer maps to posted, failed or uncertain (8.9 step 3) (4509.211371ms)
|
||||
✔ the pre-send checks: GET user must name the requester, under the deadline (1671.143785ms)
|
||||
✔ the lead's request refuses a token for login sage (1164.929541ms)
|
||||
✔ the credential file: the seat's own, 0600, no symlink, never the shared default (925.760743ms)
|
||||
✔ an unresolved request blocks a new request, a new round, waiting-on-jason and done (2618.360505ms)
|
||||
✔ a same-op retry after a kill sends nothing, even with a stale view (3532.557044ms)
|
||||
✔ a held lock at the outcome exits 3 and names what the transport said (774.476475ms)
|
||||
✔ late outcomes: after an abandon, and after a resolve with the same or another id (2464.206174ms)
|
||||
✔ resolve checks the comment: issue, markers, round, candidate and author (1885.971503ms)
|
||||
✔ the lead resolves a seat's request: the comment's author is the requester, fetched with the lead's token (825.961039ms)
|
||||
✔ validateRow checks a request round's shape, which every replayed entry must keep (562.251089ms)
|
||||
✔ request, changes, a new candidate, approval: every round pinned; no review files (1806.225057ms)
|
||||
✔ a row with no reviewers opens a round that posts nothing (1256.137801ms)
|
||||
✔ verify-commit: a prospective tree must hold exactly the candidate's paths (1438.083058ms)
|
||||
✔ semantics: v1 entries replay as before; review entries need v2 (498.822158ms)
|
||||
✔ set reviewers refuses the row's owner (2026-09-28) (357.35333ms)
|
||||
✔ the owner records no verdict, even as a listed reviewer (501.631343ms)
|
||||
✔ a request comment over the length limit is not sent (549.087325ms)
|
||||
✔ a late POST on a closed row leaves a conflict nothing can resolve, and resolve asks nothing (620.99129ms)
|
||||
✔ a Jason-gated row reaches waiting-on-jason only on every reviewer's approval (2450.332075ms)
|
||||
✔ genesis: refusals before anything is written (422.824988ms)
|
||||
✔ genesis: the map must be committed, well formed, with committed briefs and seats (612.601687ms)
|
||||
✔ genesis: markers, a stray witness, once only; a retry returns the receipt (584.757227ms)
|
||||
✔ genesis: a file holding genesis alone with no witness is confirmed by sync or a retry (396.183357ms)
|
||||
✔ canonical checks: worktree, second clone, detached HEAD, wrong branch, GIT_DIR, foreign code; a symlink works (637.054022ms)
|
||||
✔ --by that differs from MOSAIC_AGENT_NAME warns on stderr and logs nothing more (N12) (832.113715ms)
|
||||
✔ op ids: missing, too long, reserved; a retry answers; another payload refuses (742.015941ms)
|
||||
✔ a retried add returns the id it first allocated, after reassignment and after done (925.841231ms)
|
||||
✔ Rocko's S4 schedule: a lost result, another writer, then the retry opens no second round (686.636896ms)
|
||||
✔ the review issue and the evidence round through the CLI (lead decision 23, 8.7) (1482.273824ms)
|
||||
✔ claims and add defaults through the CLI; candidates are manifests or reachable commits (1023.354216ms)
|
||||
✔ add, set reviewers and assign refuse the row's owner as a reviewer (628.965565ms)
|
||||
✔ the working-brief check: a changed working copy refuses the start and flags next (782.135781ms)
|
||||
✔ next: resume first, then nothing for an idle seat; needs a seat (399.491743ms)
|
||||
✔ view stale: new ops and verify refuse naming the unshown op; retries answer; reads warn; render fixes (855.215994ms)
|
||||
✔ view unknown: a hand edit, an old marker over an edited body, missing or duplicate markers (1419.455796ms)
|
||||
✔ a hand edit to queue.json refuses every verb, reads included (725.348502ms)
|
||||
✔ verify and render --check leave bytes and mtimes unchanged (508.55243ms)
|
||||
✔ render is byte-stable across runs and repositories (331.023232ms)
|
||||
✔ snapshot and verify --snapshot (904.593087ms)
|
||||
✔ usage errors exit 4 (614.772576ms)
|
||||
✔ a short write, ENOSPC or a file fsync failure: nothing visible, temp removed (299.095639ms)
|
||||
✔ a rename failure: nothing visible, temp removed (163.482344ms)
|
||||
✔ a directory fsync failure: uncertain, exit 3, no receipt; the tail refuses new ops; a retry confirms (236.68444ms)
|
||||
✔ a directory fsync failure, then sync names the op (405.326115ms)
|
||||
✔ a witness write failure: uncertain, durable, exit 3; the view is untouched (237.912353ms)
|
||||
✔ the .git fsync after the witness rename fails: uncertain, exit 3, the witness says so (200.117919ms)
|
||||
✔ confirming a tail fsyncs queue.json and docs/plans before the witness; either failure changes nothing (203.284657ms)
|
||||
✔ the docs/plans fsync after the view rename fails: the op stands, the view is written, a warning says so (204.114537ms)
|
||||
✔ a lock swapped while held is left in place and reported, on a receipt and on a refusal (231.80633ms)
|
||||
✔ a lock that cannot be released after an op is reported, on a receipt and on a refusal (216.257237ms)
|
||||
✔ unlock prints a swapped gate's warning on stderr, the result on stdout (161.411402ms)
|
||||
✔ a view write that fails keeps the op and reports a stale view (189.979668ms)
|
||||
✔ SIGKILL before the rename: nothing recorded; the retry removes the leftover temp (674.312804ms)
|
||||
✔ SIGKILL after the rename, before the witness: the tail refuses new ops and sync names the op (692.940046ms)
|
||||
✔ SIGKILL after the witness, before the view: the stale refusal names the op (634.848768ms)
|
||||
✔ SIGKILL after the view, before the receipt: the retry returns the receipt (678.631233ms)
|
||||
✔ git checkout between steps 1 and 7: step 7 refuses and nothing is written (258.58497ms)
|
||||
✔ git stash restoring an older valid pair: history lost; accept-history needs privilege, a reason and --yes (1173.652358ms)
|
||||
✔ a deleted witness: refused after the locked recheck; accept-history records it absent (433.525482ms)
|
||||
✔ a header edit during a write: the op stands, the view write is skipped with a warning (277.216377ms)
|
||||
✔ a reader paused between the witness and the file while a writer finishes: no lost-history report (245.896099ms)
|
||||
✔ file-then-witness order forced by a hook: the locked recheck prevents a false report (263.774246ms)
|
||||
✔ a writer paused before and after the witness rename: readers see a tail, then a match (262.407703ms)
|
||||
✔ a true rollback is reported only after the locked recheck; a held lock names its holder instead (646.952244ms)
|
||||
✔ an accept-history in progress: an unlocked reader waits on the lock and never reports lost history (617.828779ms)
|
||||
✔ the platform check refuses other filesystems (187.535157ms)
|
||||
✔ tmpfs passes only a test layer that allows it (N5) (243.359408ms)
|
||||
✔ unlock keeps a multi-line lock record on stdout (P3) (252.39877ms)
|
||||
ℹ tests 148
|
||||
ℹ suites 0
|
||||
ℹ pass 148
|
||||
@@ -155,4 +155,4 @@
|
||||
ℹ cancelled 0
|
||||
ℹ skipped 0
|
||||
ℹ todo 0
|
||||
ℹ duration_ms 35595.254131
|
||||
ℹ duration_ms 31286.166236
|
||||
|
||||
@@ -1,30 +1,30 @@
|
||||
✔ resolveSeat: by name under --repo resolves the repo layout (2.38375ms)
|
||||
✔ resolveSeat: by path resolves the fleet layout (0.307ms)
|
||||
✔ resolveSeat: refusals for missing dir, missing launch.sh, non-executable launch.sh, invalid name, and unknown layout (0.687428ms)
|
||||
✔ tmuxContext: outside tmux, default socket, custom socket, and exec failure (0.671855ms)
|
||||
✔ makeRegistration produces a record that validates; each shape violation throws SeatError (0.816835ms)
|
||||
✔ writeRegistration/readRegistration: round trip, permissions, absence, and malformed records (1.280225ms)
|
||||
✔ updateTask: changes task, taskSetBy and updatedAt only, and refuses appropriately (1.155249ms)
|
||||
✔ CLI launch: registers, execs the fake launch script, and passes args through (27.419109ms)
|
||||
✔ CLI launch: --harness lands in the record (28.863116ms)
|
||||
✔ CLI launch: the launch script's own exit code passes through (27.969508ms)
|
||||
✔ CLI launch: relaunching a seat rewrites the one registration record (56.792485ms)
|
||||
✔ CLI launch: omitting --task records an empty string, not null (28.510595ms)
|
||||
✔ CLI seat task: updates only the task after a launch, and refuses on an unlaunched seat (82.272699ms)
|
||||
✔ CLI refusals: no args, unknown flag, missing config, already-registered env, and exec failure (136.848395ms)
|
||||
✔ samePath: equal paths, symlinked dirs, distinct dirs, and non-strings (0.370135ms)
|
||||
✔ resolveSetBy: explicit --by wins over the environment; absent or empty environment gives unknown; invalid explicit or environment values refuse with exit 4 (0.574248ms)
|
||||
✔ 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.756508ms)
|
||||
✔ 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.859621ms)
|
||||
✔ 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 (265.483659ms)
|
||||
✔ family: exactly one launch.max key in the model name, else null (0.688496ms)
|
||||
✔ sessionEnv passes only the allowlist, the repo's bin on PATH, and the run id (0.645819ms)
|
||||
✔ newRun: short ids, 0700 directories, and a refusal when the socket path won't fit (1.455045ms)
|
||||
✔ session file and launch log: 0600, the session file written once (0.94776ms)
|
||||
✔ endReason maps the runner's exit codes; a signal is killed (0.112724ms)
|
||||
✔ Registry mirrors to sessions.json; readSessions marks live entries; bad files refuse (1.128494ms)
|
||||
✔ stopSession refuses an unknown run, reports a stale one, and won't signal a pid that isn't a runner (0.992954ms)
|
||||
✔ a session runs under unshare as pid 1 of its namespace, claims, answers, and stops on mosaic stop (266.652798ms)
|
||||
✔ resolveSeat: by name under --repo resolves the repo layout (1.330962ms)
|
||||
✔ resolveSeat: by path resolves the fleet layout (0.335526ms)
|
||||
✔ resolveSeat: refusals for missing dir, missing launch.sh, non-executable launch.sh, invalid name, and unknown layout (0.743892ms)
|
||||
✔ tmuxContext: outside tmux, default socket, custom socket, and exec failure (0.649939ms)
|
||||
✔ makeRegistration produces a record that validates; each shape violation throws SeatError (0.799606ms)
|
||||
✔ writeRegistration/readRegistration: round trip, permissions, absence, and malformed records (0.834488ms)
|
||||
✔ updateTask: changes task, taskSetBy and updatedAt only, and refuses appropriately (1.132624ms)
|
||||
✔ CLI launch: registers, execs the fake launch script, and passes args through (27.473766ms)
|
||||
✔ CLI launch: --harness lands in the record (27.131259ms)
|
||||
✔ CLI launch: the launch script's own exit code passes through (29.937184ms)
|
||||
✔ CLI launch: relaunching a seat rewrites the one registration record (58.389233ms)
|
||||
✔ CLI launch: omitting --task records an empty string, not null (31.44052ms)
|
||||
✔ CLI seat task: updates only the task after a launch, and refuses on an unlaunched seat (84.00092ms)
|
||||
✔ CLI refusals: no args, unknown flag, missing config, already-registered env, and exec failure (136.324119ms)
|
||||
✔ samePath: equal paths, symlinked dirs, distinct dirs, and non-strings (0.391021ms)
|
||||
✔ resolveSetBy: explicit --by wins over the environment; absent or empty environment gives unknown; invalid explicit or environment values refuse with exit 4 (0.580046ms)
|
||||
✔ 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.759877ms)
|
||||
✔ 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.888353ms)
|
||||
✔ 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 (264.866081ms)
|
||||
✔ family: exactly one launch.max key in the model name, else null (0.638066ms)
|
||||
✔ sessionEnv passes only the allowlist, the repo's bin on PATH, and the run id (0.662147ms)
|
||||
✔ newRun: short ids, 0700 directories, and a refusal when the socket path won't fit (1.475413ms)
|
||||
✔ session file and launch log: 0600, the session file written once (0.983961ms)
|
||||
✔ endReason maps the runner's exit codes; a signal is killed (0.119377ms)
|
||||
✔ Registry mirrors to sessions.json; readSessions marks live entries; bad files refuse (1.159632ms)
|
||||
✔ stopSession refuses an unknown run, reports a stale one, and won't signal a pid that isn't a runner (1.02137ms)
|
||||
✔ a session runs under unshare as pid 1 of its namespace, claims, answers, and stops on mosaic stop (246.698392ms)
|
||||
ℹ tests 27
|
||||
ℹ suites 0
|
||||
ℹ pass 27
|
||||
@@ -32,4 +32,4 @@
|
||||
ℹ cancelled 0
|
||||
ℹ skipped 0
|
||||
ℹ todo 0
|
||||
ℹ duration_ms 715.303268
|
||||
ℹ duration_ms 720.831355
|
||||
|
||||
@@ -1,54 +1,54 @@
|
||||
✔ the boot config is checked before anything starts (197.479775ms)
|
||||
✔ a business with no tracker entry refuses task verbs (139.742344ms)
|
||||
✔ credential.expiring and .expired are recorded once per instance (219.539472ms)
|
||||
✔ a token file that changes on disk records credential.changed (110.804248ms)
|
||||
✔ autostart polls, reconciles and retries a startup the tracker was down for (137.469261ms)
|
||||
✔ a refusal a restart must clear is not retried by the poll (106.393381ms)
|
||||
✔ a poll that fires while two are queued is dropped (102.177258ms)
|
||||
✔ close waits for a running verb and refuses one that has not started (228.556608ms)
|
||||
✔ the bundled Vikunja is the pinned upstream image the runbook names (0.795107ms)
|
||||
✔ every published port is on 127.0.0.1, and no secret is in the file (0.32377ms)
|
||||
✔ the fake answers each route with the statuses and shapes Vikunja v2.7.0 sent (494.146398ms)
|
||||
✔ the recorded task bodies pass the checks S3 applies to every read (0.538607ms)
|
||||
✔ the client works against the fake over real HTTP with the platform fetch (248.265171ms)
|
||||
✔ a correct install starts, and the first reconcile records tasks that already exist (214.536073ms)
|
||||
✔ verbs refuse while a business is starting and after startup refused it (148.912633ms)
|
||||
✔ startup refuses a token that can do more than its role needs (410.384994ms)
|
||||
✔ startup refuses an unsupported version and flags an untested one (320.020087ms)
|
||||
✔ startup refuses a board that the runbook did not install (381.770113ms)
|
||||
✔ startup refuses a project the sync bot cannot read (97.801388ms)
|
||||
✔ startup refuses a configured label the pm bot cannot see (81.711336ms)
|
||||
✔ startup refuses an expired credential and a missing sync credential (221.987872ms)
|
||||
✔ an unreachable tracker refuses with tracker-unavailable (89.937615ms)
|
||||
✔ an edit in the UI is recorded once, with the fields that changed (330.854999ms)
|
||||
✔ a move between open buckets is seen on the board, though updated does not change (190.310234ms)
|
||||
✔ a person's comment is counted and a bot's is not (288.545396ms)
|
||||
✔ the hourly reconcile catches a comment through comment_count (228.898375ms)
|
||||
✔ a task closed in the UI leaves the open view with its done bucket (429.734775ms)
|
||||
✔ a task that leaves the board is recorded as deleted, moved or out of reach (287.164684ms)
|
||||
✔ a poll that read before a verb wrote does not overwrite the verb (176.826276ms)
|
||||
✔ a tracker fault during a tick is reported and the next tick catches up (155.192537ms)
|
||||
✔ a malformed answer refuses the tick with tracker-shape (125.233379ms)
|
||||
✔ no token value reaches the database, the log or a refusal (283.776287ms)
|
||||
✔ the first look at a task counts only comments inside the window (146.045513ms)
|
||||
✔ task.create needs a recorded human request and a requirement id (319.485088ms)
|
||||
✔ only labels named in the business file can be written (228.589076ms)
|
||||
✔ task.schedule sets and clears a due date and relations (308.501904ms)
|
||||
✔ assign and reassign move the role bots and record task.assigned (329.197503ms)
|
||||
✔ task.update.assigned is for the assignee and records task.state (317.345199ms)
|
||||
✔ a wrong expected digest records task.conflict and writes nothing (235.892284ms)
|
||||
✔ a cross-role verb needs a resolved decision, used once (253.179147ms)
|
||||
✔ task.close needs a verdict; after it every verb refuses with task-done (234.270236ms)
|
||||
✔ a lost answer is settled by a re-read and never retried (232.558998ms)
|
||||
✔ a create whose answer is lost is reported uncertain, and the poll finds the task (194.4704ms)
|
||||
✔ a task the sync bot cannot read refuses and records nothing (132.208983ms)
|
||||
✔ verbs and polls for one business run one at a time (185.004946ms)
|
||||
✔ a due date with milliseconds is written to the second (247.995644ms)
|
||||
✔ every write landed and the final read failed: the verb succeeds and records what it wrote (146.862751ms)
|
||||
✔ some writes landed and the final read failed: write-uncertain, and nothing is recorded (110.616531ms)
|
||||
✔ a create whose final read fails succeeds and records task.created (161.566382ms)
|
||||
✔ an edit between the last write and the final read shows as external on the next poll (155.139858ms)
|
||||
✔ task.created is recorded when a later label write fails (124.486898ms)
|
||||
✔ the boot config is checked before anything starts (92.946154ms)
|
||||
✔ a business with no tracker entry refuses task verbs (129.120807ms)
|
||||
✔ credential.expiring and .expired are recorded once per instance (246.504076ms)
|
||||
✔ a token file that changes on disk records credential.changed (108.226458ms)
|
||||
✔ autostart polls, reconciles and retries a startup the tracker was down for (158.650123ms)
|
||||
✔ a refusal a restart must clear is not retried by the poll (95.659816ms)
|
||||
✔ a poll that fires while two are queued is dropped (81.991724ms)
|
||||
✔ close waits for a running verb and refuses one that has not started (219.401264ms)
|
||||
✔ the bundled Vikunja is the pinned upstream image the runbook names (0.718691ms)
|
||||
✔ every published port is on 127.0.0.1, and no secret is in the file (0.269078ms)
|
||||
✔ the fake answers each route with the statuses and shapes Vikunja v2.7.0 sent (410.185085ms)
|
||||
✔ the recorded task bodies pass the checks S3 applies to every read (0.494185ms)
|
||||
✔ the client works against the fake over real HTTP with the platform fetch (249.40339ms)
|
||||
✔ a correct install starts, and the first reconcile records tasks that already exist (114.021264ms)
|
||||
✔ verbs refuse while a business is starting and after startup refused it (167.462238ms)
|
||||
✔ startup refuses a token that can do more than its role needs (408.839768ms)
|
||||
✔ startup refuses an unsupported version and flags an untested one (304.62854ms)
|
||||
✔ startup refuses a board that the runbook did not install (329.127035ms)
|
||||
✔ startup refuses a project the sync bot cannot read (78.086158ms)
|
||||
✔ startup refuses a configured label the pm bot cannot see (87.537102ms)
|
||||
✔ startup refuses an expired credential and a missing sync credential (182.870691ms)
|
||||
✔ an unreachable tracker refuses with tracker-unavailable (101.008667ms)
|
||||
✔ an edit in the UI is recorded once, with the fields that changed (196.851407ms)
|
||||
✔ a move between open buckets is seen on the board, though updated does not change (217.61436ms)
|
||||
✔ a person's comment is counted and a bot's is not (270.709187ms)
|
||||
✔ the hourly reconcile catches a comment through comment_count (231.498755ms)
|
||||
✔ a task closed in the UI leaves the open view with its done bucket (400.619607ms)
|
||||
✔ a task that leaves the board is recorded as deleted, moved or out of reach (278.223735ms)
|
||||
✔ a poll that read before a verb wrote does not overwrite the verb (189.312329ms)
|
||||
✔ a tracker fault during a tick is reported and the next tick catches up (184.458181ms)
|
||||
✔ a malformed answer refuses the tick with tracker-shape (132.48211ms)
|
||||
✔ no token value reaches the database, the log or a refusal (318.139297ms)
|
||||
✔ the first look at a task counts only comments inside the window (152.842231ms)
|
||||
✔ task.create needs a recorded human request and a requirement id (203.480924ms)
|
||||
✔ only labels named in the business file can be written (263.346377ms)
|
||||
✔ task.schedule sets and clears a due date and relations (322.323284ms)
|
||||
✔ assign and reassign move the role bots and record task.assigned (315.730512ms)
|
||||
✔ task.update.assigned is for the assignee and records task.state (310.311839ms)
|
||||
✔ a wrong expected digest records task.conflict and writes nothing (197.657449ms)
|
||||
✔ a cross-role verb needs a resolved decision, used once (292.277958ms)
|
||||
✔ task.close needs a verdict; after it every verb refuses with task-done (251.206961ms)
|
||||
✔ a lost answer is settled by a re-read and never retried (288.544452ms)
|
||||
✔ a create whose answer is lost is reported uncertain, and the poll finds the task (166.984314ms)
|
||||
✔ a task the sync bot cannot read refuses and records nothing (95.348804ms)
|
||||
✔ verbs and polls for one business run one at a time (163.878304ms)
|
||||
✔ a due date with milliseconds is written to the second (173.537098ms)
|
||||
✔ every write landed and the final read failed: the verb succeeds and records what it wrote (113.49429ms)
|
||||
✔ some writes landed and the final read failed: write-uncertain, and nothing is recorded (93.119112ms)
|
||||
✔ a create whose final read fails succeeds and records task.created (93.729323ms)
|
||||
✔ an edit between the last write and the final read shows as external on the next poll (139.563322ms)
|
||||
✔ task.created is recorded when a later label write fails (112.601508ms)
|
||||
ℹ tests 51
|
||||
ℹ suites 0
|
||||
ℹ pass 51
|
||||
@@ -56,4 +56,4 @@
|
||||
ℹ cancelled 0
|
||||
ℹ skipped 0
|
||||
ℹ todo 0
|
||||
ℹ duration_ms 4013.267787
|
||||
ℹ duration_ms 3678.817998
|
||||
|
||||
@@ -1,23 +1,31 @@
|
||||
✔ browser edge states: loading, empty, malformed, stale, hostile/long values, in-flight reply and appearance fallback (3622.121735ms)
|
||||
✔ browser edge states: loading, empty, malformed, stale, hostile/long values, in-flight reply and appearance fallback (3866.262552ms)
|
||||
Rendered contrast: {"failures":[],"count":330,"lowest":4.504658476260286}
|
||||
✔ served Console browser: real board fixtures, keyboard, drafts, receipts, themes, 320px and failures (3565.038836ms)
|
||||
✔ conversation view: full history, collapsed tools, hidden thinking, inert hostile content, malformed and reconcile markers (3611.776637ms)
|
||||
✔ conversation view: a fork keeps the open branch, says so, and opens the new one on request (1686.895048ms)
|
||||
✔ conversation view: a newer session with no readable history keeps the marker (1116.328238ms)
|
||||
✔ conversation view: seats without history say so and offer no reply (851.317089ms)
|
||||
✔ Discord row through real board/WebUI: independent brake/liveness, no Reply, literal content (3608.726306ms)
|
||||
✔ return flow through the conversation view: send, tool call, delayed result, peer message, exact long answers, relaunch (54301.56092ms)
|
||||
✔ both presentations replace old activity with relaunch notice, label retained history, then resume after new activity (3605.029743ms)
|
||||
✔ reported return flow and relative Age: reply sent from the inspector, then the new answer appears there without manual refresh (21936.08607ms)
|
||||
✔ loopback host and board origin fail closed (6.73058ms)
|
||||
✔ real board fixture passes through WebUI; assets and isolated seen/reply work (84.22649ms)
|
||||
✔ proxy preserves exact request bytes, status and receipt, rejects forms and malformed JSON, never follows redirect (95.864801ms)
|
||||
✔ unreachable board reports URL; CLI rejects unsupported options (1030.704712ms)
|
||||
ℹ tests 14
|
||||
✔ served Console browser: real board fixtures, keyboard, drafts, receipts, themes, 320px and failures (4896.033795ms)
|
||||
✔ S5 pages in a browser: real broker rows, inert text, Copy, routes, failures, layout and no writes (5937.94953ms)
|
||||
✔ bus routes serve the four reads from the reader view, unescaped JSON for the page to escape (260.160625ms)
|
||||
✔ bus routes are GET-only, have no write verb, and keep the same-origin checks (110.02109ms)
|
||||
✔ bus refusals map to statuses and never carry a path or a stack (112.994569ms)
|
||||
✔ humanCall speaks the human transport protocol without blocking the server (2037.405502ms)
|
||||
✔ busReader takes --business, else the live bus host, else refuses (160.353205ms)
|
||||
✔ humanCall kills a transport that runs past its timeout (528.132694ms)
|
||||
✔ serve refuses a --business value that is not a business id (154.831599ms)
|
||||
✔ conversation view: full history, collapsed tools, hidden thinking, inert hostile content, malformed and reconcile markers (3833.080507ms)
|
||||
✔ conversation view: a fork keeps the open branch, says so, and opens the new one on request (2510.048378ms)
|
||||
✔ conversation view: a newer session with no readable history keeps the marker (1044.061451ms)
|
||||
✔ conversation view: seats without history say so and offer no reply (879.903005ms)
|
||||
✔ Discord row through real board/WebUI: independent brake/liveness, no Reply, literal content (3870.094794ms)
|
||||
✔ return flow through the conversation view: send, tool call, delayed result, peer message, exact long answers, relaunch (54107.983822ms)
|
||||
✔ both presentations replace old activity with relaunch notice, label retained history, then resume after new activity (3838.04821ms)
|
||||
✔ reported return flow and relative Age: reply sent from the inspector, then the new answer appears there without manual refresh (22664.61891ms)
|
||||
✔ loopback host and board origin fail closed (7.053813ms)
|
||||
✔ real board fixture passes through WebUI; assets and isolated seen/reply work (100.777387ms)
|
||||
✔ proxy preserves exact request bytes, status and receipt, rejects forms and malformed JSON, never follows redirect (92.335193ms)
|
||||
✔ unreachable board reports URL; CLI rejects unsupported options (817.696846ms)
|
||||
ℹ tests 22
|
||||
ℹ suites 0
|
||||
ℹ pass 14
|
||||
ℹ pass 22
|
||||
ℹ fail 0
|
||||
ℹ cancelled 0
|
||||
ℹ skipped 0
|
||||
ℹ todo 0
|
||||
ℹ duration_ms 54764.629902
|
||||
ℹ duration_ms 54480.956526
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
== MODE=read given workspace <R>/data/ws, real <R>/deep/store/ws
|
||||
exit 0; stdout: mock answer; stderr:
|
||||
POST /v1/messages: 2
|
||||
tool_result sent back: [{"is_error":true,"content":"PreToolUse:Read hook error: [timeout -k 2 10 /usr/bin/node /home/jwoltje/filbert-scratch/s6-build/packages/harness/src/claude-gate.mjs <R>/bundle/policy.json || exit 2]: mosaic gate: Read path is outside the workspace: <R>/deep/data/ws/secret.txt\n"}]
|
||||
outside after the run: secret.txt
|
||||
workspace after the run:
|
||||
== MODE=write given workspace <R>/data/ws, real <R>/deep/store/ws
|
||||
exit 0; stdout: mock answer; stderr:
|
||||
POST /v1/messages: 2
|
||||
tool_result sent back: [{"is_error":true,"content":"PreToolUse:Write hook error: [timeout -k 2 10 /usr/bin/node /home/jwoltje/filbert-scratch/s6-build/packages/harness/src/claude-gate.mjs <R>/bundle/policy.json || exit 2]: mosaic gate: Write path is outside the workspace: <R>/deep/data/ws/planted.txt\n"}]
|
||||
outside after the run: secret.txt
|
||||
workspace after the run:
|
||||
@@ -0,0 +1,12 @@
|
||||
== MODE=read given workspace <R>/data/ws, real <R>/deep/store/ws
|
||||
gate decide: {"allow":false,"reason":"mosaic gate: read path is outside the workspace: ../../data/ws/secret.txt"}
|
||||
exit 0; stderr:
|
||||
tool_result sent back: [{"is_error":true,"content":"mosaic gate: read path is outside the workspace: ../../data/ws/secret.txt"}]
|
||||
outside after the run: secret.txt
|
||||
workspace after the run:
|
||||
== MODE=write given workspace <R>/data/ws, real <R>/deep/store/ws
|
||||
gate decide: {"allow":false,"reason":"mosaic gate: write path is outside the workspace: ../../data/ws/planted.txt"}
|
||||
exit 0; stderr:
|
||||
tool_result sent back: [{"is_error":true,"content":"mosaic gate: write path is outside the workspace: ../../data/ws/planted.txt"}]
|
||||
outside after the run: secret.txt
|
||||
workspace after the run:
|
||||
@@ -2,15 +2,15 @@ node-business exit=0 ℹ pass 60 ℹ fail 0
|
||||
node-bus exit=0 ℹ pass 74 ℹ fail 0
|
||||
node-cli exit=0 ℹ pass 83 ℹ fail 0
|
||||
node-control-board exit=0 ℹ pass 124 ℹ fail 0
|
||||
node-conversation exit=0 ℹ pass 152 ℹ fail 0
|
||||
node-conversation exit=0 ℹ pass 161 ℹ fail 0
|
||||
node-discord exit=0 ℹ pass 178 ℹ fail 0
|
||||
node-harness exit=0 ℹ pass 53 ℹ fail 0
|
||||
node-harness exit=0 ℹ pass 56 ℹ 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
|
||||
node-webui exit=0 ℹ pass 22 ℹ 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
|
||||
|
||||
@@ -1,7 +1,39 @@
|
||||
On branch refactor
|
||||
Your branch is up to date with 'origin/refactor'.
|
||||
Note: switching to '0a4c8f13b09b8882ea55f6061d26949330385ee8'.
|
||||
|
||||
You are in 'detached HEAD' state. You can look around, make experimental
|
||||
changes and commit them, and you can discard any commits you make in this
|
||||
state without impacting any branches by switching back to a branch.
|
||||
|
||||
If you want to create a new branch to retain commits you create, you may
|
||||
do so (now or later) by using -c with the switch command. Example:
|
||||
|
||||
git switch -c <new-branch-name>
|
||||
|
||||
Or undo this operation with:
|
||||
|
||||
git switch -
|
||||
|
||||
Turn off this advice by setting config variable advice.detachedHead to false
|
||||
|
||||
Not currently on any branch.
|
||||
nothing to commit, working tree clean
|
||||
Note: switching to '0a4c8f13b09b8882ea55f6061d26949330385ee8'.
|
||||
|
||||
You are in 'detached HEAD' state. You can look around, make experimental
|
||||
changes and commit them, and you can discard any commits you make in this
|
||||
state without impacting any branches by switching back to a branch.
|
||||
|
||||
If you want to create a new branch to retain commits you create, you may
|
||||
do so (now or later) by using -c with the switch command. Example:
|
||||
|
||||
git switch -c <new-branch-name>
|
||||
|
||||
Or undo this operation with:
|
||||
|
||||
git switch -
|
||||
|
||||
Turn off this advice by setting config variable advice.detachedHead to false
|
||||
|
||||
OK dry-run: allowed change, exit 0, nothing committed (exit 0)
|
||||
OK dry-run committed nothing
|
||||
OK apply: allowed change exits 0 (exit 0)
|
||||
|
||||
@@ -8,7 +8,7 @@ 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: 2015159 Killed "$@" > /dev/null 2>&1
|
||||
scripts/test-extension-package.sh: line 14: 2783136 Killed "$@" > /dev/null 2>&1
|
||||
OK forced interruption kills the replacing process
|
||||
OK next invocation recovers old consistent installation
|
||||
OK interrupted replacement rolled back
|
||||
|
||||
@@ -30,6 +30,6 @@ OK packages/queue declares no dependencies
|
||||
ℹ 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-gate-r3) is not the queue's canonical root (/mnt/storage/src/mosaic-stack)
|
||||
skip queue verify and render --check: this checkout (/home/jwoltje/filbert-scratch/s6-gate-r4) is not the queue's canonical root (/mnt/storage/src/mosaic-stack)
|
||||
|
||||
queue suite: 27 passed, 0 failed
|
||||
|
||||
@@ -1,29 +1,31 @@
|
||||
5dc9e67c9ce42b86c40d83c8b1aa8d5ba93d6e8b51bb1f4d2964f4b270ee52aa base.txt
|
||||
d9ae770c156020b3b536f06f0ecfa6e550423343f85c8b4566fa90fd9a650319 BUILD.md
|
||||
7b63583de7a701cfb835ffabe4118b2624df49cfcaedf7c7691b097695b8907e build.patch
|
||||
9f0593af17902e6ec084987175a1e48affd9c71568cb2eeacc7b4cd1ef165cbe candidate-manifest.sha256
|
||||
45a1a67b9c0c20fa911491345a94c28296b614c5ff9526d7e5442e8e787e2c6f BUILD.md
|
||||
f7e83e5171dd2de01fb55624fdbbb3f0d29c6c8da465298151b89dc6aeee0422 build.patch
|
||||
08a78972e316cb3b9cba2050489bd65b2c0b1ec95eb7de9b91c266e89a0d0d66 candidate-manifest.sha256
|
||||
c6309079b471a4b304ba55679bfa2ce7f1e145137ec784f2e35ee7330df844ea files.txt
|
||||
8df77c49bf82833efa56d42401a4a06f20931b7a76dd5eed3a73fdec60e92e79 out/base-test-task.txt
|
||||
08ff9447c1cdff32245cb957876aff62406b5d0343bd50bf0924666523a92de0 out/node-business.txt
|
||||
454d05e1e09f87988d90d302deb0a38790c195973009e14073149e0df3ca26ee out/node-bus.txt
|
||||
155a45072ac3e2068891db10ca9dc7de4da0d58a4e05945f89e61e354444069a out/node-cli.txt
|
||||
1da902b53efce461be93f6841ea257ec08ce345150e3d9a1d76e98f1c65a3ff4 out/node-control-board.txt
|
||||
60ad9ef705fe72365e7bb87470045a10ffb604322dcf7f376b6b11946f453657 out/node-conversation.txt
|
||||
54bc9eda871683552daffc452d6255789210261dee93dd7250da0d10ce8408c5 out/node-discord.txt
|
||||
0518f1253d7ae01547e580a7072d67f93846f438840582e577cd65b746d5c59f out/node-harness.txt
|
||||
5d7953f6c6ec388351712adccb5ce97b74921ec53d0dcb122bc218e3625ca9eb out/node-ledger.txt
|
||||
a39c3ebe4da7a99cf89d71b98cb26f5c9cb90dd7c44df15485debd2b9b79d781 out/node-mosaic.txt
|
||||
cff341d0a8e89e5e316deb91eea7ea13813b85385178fc5e3059c26866494a61 out/node-queue.txt
|
||||
c2290fe1e3d10953c590af952dc22591d7402cdabc64f718cec0fb49f95ecb5b out/node-seat.txt
|
||||
24ce87d27bd52f5554a795da5dbfe409351ec0e265faa68b455fd7863e6b743c out/node-tasks.txt
|
||||
fa73b82e2d48e88d23191f8c7d7a7b5aba283e6675b1d0db63425d764fd43227 out/node-webui.txt
|
||||
195c9716b01d1b25b2de36c99dd1cf970e579fdf9c108b40fc83d910db121769 out/summary.txt
|
||||
5823cfb20738fa2cf2a148d6920ac4180e0c195a31d835f6d8c2015bc786d37b out/node-business.txt
|
||||
b113a27fc69eb6c5e33b67f6aad4704ae9828ead116366cd1067c77f4b8adf10 out/node-bus.txt
|
||||
af74a06ba2122ede59f0adf1d37a1b4c108569fd2fbbe00533250549e39640d6 out/node-cli.txt
|
||||
a00045831c7d8c0a1f75aff7997f41b7a579863bbd66130d7d761a721340428d out/node-control-board.txt
|
||||
67ade5f3961b254f2da6392a0bfdb479fb011299ffc03a0f986743b5bd2f5f03 out/node-conversation.txt
|
||||
090c2e9e183f51e01cb45b4bc2e8f95e715dd8bf0a976f0a669d18f79849b463 out/node-discord.txt
|
||||
e8e4ffa11d1823e99752e2614f29ed06d344ec2a98bdb2769ba601053bf7cbd7 out/node-harness.txt
|
||||
14f6d61cf2bddb62b0dfb39cd2e2963aa98c0e42f3a45b27e902e5667f388cb6 out/node-ledger.txt
|
||||
ab36b5e758eca554649cd8c775ceef64e09f6a81ec588248771600322a39cae3 out/node-mosaic.txt
|
||||
d012f3e7814969e12e0b8dcf85d9a31beee113aa1575727b61d915312421fe3b out/node-queue.txt
|
||||
94cbfd0823506974e067248fe0516b55577a5b0f61ff43ffba2f1a8d8cfc38f6 out/node-seat.txt
|
||||
a502a780bebf4715f4630ca3a6d33666ea77f04ee838bd8de091a7979f31b08c out/node-tasks.txt
|
||||
cfc7ea0834415187e031e1c74a24e40e1295c5e3f54843a6625aa3d8ed8c3c53 out/node-webui.txt
|
||||
36f628d1a329efad68bca0c0188217fa3c64d9eccced483e4ae8e29a2d1d2258 out/probe-claude-cwd-dotdot.txt
|
||||
f6cdb3ed25870314c0208798d6aedd409a8935858457b86f917cbc316e5e8dd0 out/probe-pi-cwd-dotdot.txt
|
||||
a9488308d3b1297b238f4dd0024b300b3c857934bcbcf1d448cab134b20b7d86 out/summary.txt
|
||||
f91fc95a173339137f981e24fbf630acfde957e32c3cd71e28304532083c6a77 out/test-auth.txt
|
||||
e1206366f07f3d9029530dcdcb55c0282d3f052adc60fc7fd51642d5d4ec3600 out/test-conductor.txt
|
||||
375f995f256ddde6fa10fd0c53e6bf74329e3984fc417af45102ffe4c6580646 out/test-conductor.txt
|
||||
52d211444489ea729dafc3c9745d4474d9dfd50f41055cbad792424197f04073 out/test-config.txt
|
||||
7d5016ded1994048642f8b9ae845e52e8af4a069ba05c056c8f580a4589bacbe out/test-discord.txt
|
||||
4726725a020747117f43feaffffd04935633fd809e33306b97146e688233ec25 out/test-extension-package.txt
|
||||
52438293b140f37c304d8ca1be30d9542d2388767d00cf56c8aadb07ef487999 out/test-extension-package.txt
|
||||
83dfaa21c4e4ad941088efab3f69e87ebd2f6bd3cc72ede938e21dc06da8a34f out/test-foundation.txt
|
||||
5bdf623f67f231a226bdc707bfaec9796c3c227c9ffedd95953693714e6182dd out/test-queue.txt
|
||||
440adee8e4bba7105aeaa43aff142e8e0b5842052a537ee4fb516ca854d89a14 out/test-queue.txt
|
||||
6183e6b9b05edb497e92ffd09a19bba49c6f656c750ecb2acc283021113d1a98 out/test-release.txt
|
||||
8df77c49bf82833efa56d42401a4a06f20931b7a76dd5eed3a73fdec60e92e79 out/test-task.txt
|
||||
|
||||
Reference in New Issue
Block a user