diff --git a/agents/filbert/work/s4-follow-up-review/gate.sh b/agents/filbert/work/s4-follow-up-review/gate.sh new file mode 100755 index 00000000..2d619913 --- /dev/null +++ b/agents/filbert/work/s4-follow-up-review/gate.sh @@ -0,0 +1,13 @@ +#!/usr/bin/env bash +# Sequential gate; $1 = tree, $2 = out prefix. Each output teed to a file. +T="$1"; P="$2"; O=~/filbert-scratch/r45/out; cd "$T" +for d in packages/*/tests; do + p=$(basename "$(dirname "$d")") + env -u NODE_TEST_CONTEXT DOCKER_HOST=unix:///nonexistent-filbert-r45.sock timeout 900 node --test "packages/$p/tests/*.test.mjs" 2>&1 | tee "$O/$P-node-$p.txt" > /dev/null + echo "$P node-$p exit ${PIPESTATUS[0]} $(grep -E '^# (pass|fail|cancelled)' "$O/$P-node-$p.txt" | tr '\n' ' ')" +done +for s in scripts/test-*.sh; do + n=$(basename "$s" .sh); n=${n#test-} + env -u NODE_TEST_CONTEXT DOCKER_HOST=unix:///nonexistent-filbert-r45.sock timeout 900 bash "$s" 2>&1 | tee "$O/$P-suite-$n.txt" > /dev/null + echo "$P suite-$n exit ${PIPESTATUS[0]} load $(cut -d" " -f1 /proc/loadavg)" +done diff --git a/agents/filbert/work/s4-follow-up-review/mutants.sh b/agents/filbert/work/s4-follow-up-review/mutants.sh new file mode 100755 index 00000000..c8f3b703 --- /dev/null +++ b/agents/filbert/work/s4-follow-up-review/mutants.sh @@ -0,0 +1,57 @@ +#!/usr/bin/env bash +# Row 45 round 1 mutants (Filbert): Rocko's 18 (agents/rocko/work/s4-follow-up/mutants.sh) +# and X1-X14. One perl substitution each, restored after its run. A run counts +# as killed when any test fails or is cancelled, or the outer timeout fires. +# Usage: mutants.sh +set -u +T="$1"; O="$2"; cd "$T" +run() { + local id="$1" file="$2" expr="$3" + cp "$file" "$file.orig" + perl -0pi -e "$expr" "$file" + if cmp -s "$file" "$file.orig"; then echo "$id NOT-APPLIED $file"; mv "$file.orig" "$file"; return; fi + env -u NODE_TEST_CONTEXT DOCKER_HOST=unix:///nonexistent-filbert-r45.sock timeout 900 node --test --test-timeout=90000 'packages/cli/tests/*.test.mjs' 'packages/discord/tests/*.test.mjs' > "$O/mut-$id.txt" 2>&1 + local rc=$? f c + f=$(grep -E '^ℹ fail ' "$O/mut-$id.txt" | awk '{print $3}') + c=$(grep -E '^ℹ cancelled ' "$O/mut-$id.txt" | awk '{print $3}') + if [ "$rc" = 124 ]; then echo "$id killed (hang, outer timeout)" + elif [ "${f:-?}" = 0 ] && [ "${c:-?}" = 0 ]; then echo "$id SURVIVED" + else echo "$id killed (fail ${f:-?}, cancelled ${c:-?})"; fi + mv "$file.orig" "$file" +} +NT=packages/cli/src/notifier.mjs +HS=packages/cli/src/host.mjs +# Rocko's set, verbatim. +run N2 $NT 's/lstatSync, mkdirSync/lstatSync, statSync, mkdirSync/; s/const ds = lstatSync\(dir\);/const ds = statSync(dir);/' +run N4 $NT 's/O_WRONLY \| O_APPEND \| O_NOFOLLOW\)/O_WRONLY | O_APPEND)/' +run N5 $HS 's/if \(child\.exitCode !== null \|\| child\.signalCode !== null\) onDeath/if (child.exitCode !== null) onDeath/' +run M28 $HS 's/ if \(prior\?\.live\) throw new CliError\([^\n]*\n//' +run G150 $HS 's/(if \(ok\?\.ok !== true\) \{\n\s*await ended\(notify, 5000\);\n\s*)if \(broker\.connected\) /$1/' +run G144 $HS 's/(notify\.kill\("SIGTERM"\);\n\s*await ended\(notify, 5000\);\n\s*)if \(broker\.connected\) /$1/' +run F2a $NT 's/export const REFUSAL_LIMIT = 5;/export const REFUSAL_LIMIT = 6;/' +run F2b $NT 's/ && r\.status !== 429;/;/' +run F2c $NT 's/ \|\| journal\.gaveUp\.has\(d\.id\)\) continue;/) continue;/' +run F2d $NT 's/if \(\(journal\.refusals\.get\(d\.id\) \?\? 0\) >= REFUSAL_LIMIT\) \{/if (false) {/' +run F2e $NT 's/ : dmGaveUp\(d\.id\) \? "\[blocking, DM refused, not retried\] "//' +run F2f $NT 's/ if \(record\.kind === "dm" && \(journal\.refusals\.get\(record\.decision\) \?\? 0\) >= REFUSAL_LIMIT\) \{/ if (false) {/' +run J4a $NT 's/ \|\| new Date\(r\.at\)\.toISOString\(\) !== r\.at\) return "at";/) return "at";/' +run J4b $NT 's/typeof r\.decision !== "string" \|\| r\.decision === ""/false/' +run J4c $NT 's/if \(r\.status !== undefined && !Number\.isInteger\(r\.status\)\) return "status";//' +run J4d $NT 's/r\.outcome === "confirmed" \? typeof r\.messageId !== "string" : //' +run E1 $NT 's/accessSync\(dir, constants\.W_OK \| constants\.X_OK\);//' +run E2 $NT 's/ if \(ds\.isSymbolicLink\(\)\) throw[^\n]*\n//' +# Filbert's set. +run X1 $NT 's/r\.outcome === "refused" && Number\.isInteger\(r\.status\) && r\.status >= 400 && r\.status < 500 && r\.status !== 429;/r.outcome === "refused";/' +run X2 $NT 's/r\.status >= 400 && r\.status < 500 &&/r.status >= 400 \&\& r.status < 600 \&\&/' +run X3 $NT 's/ if \(bad\) throw new CliError\(`notify journal line[^\n]*\n count\(r\);/$&\n if (definite(r)) refusals.delete(r.decision);/' +run X4 $NT 's/r\.kind === "digest" \? typeof r\.day !== "string" \|\| !DAY\.test\(r\.day\) :/r.kind === "digest" ? false :/' +run X5 $NT 's/ \|\| \(r\.outcome === "gave-up" && r\.kind !== "dm"\)\) return "outcome";/) return "outcome";/' +run X6 $NT 's/ if \(UNWRITABLE\.includes\(e\.code\)\) throw new CliError\(`notify journal directory cannot be created[^\n]*\n//' +run X7 $NT 's/ : r\.decision !== null && r\.decision !== undefined\) return "decision";/ : false) return "decision";/' +run X8 $NT 's/ if \(e\.code === "ELOOP"\) throw new CliError\(`notify journal must not be a symlink[^\n]*\n//' +run X9 $NT 's/ if \(UNWRITABLE\.includes\(e\.code\)\) throw new CliError\(`notify journal is not writable[^\n]*\n//' +run X10 $NT 's/: r\.messageId !== null && typeof r\.messageId !== "string"\) return "messageId";/: false) return "messageId";/' +run X11 $NT 's/if \(!\["dm", "digest"\]\.includes\(r\.kind\)\) return "kind";//' +run X12 $NT 's/if \(r\.kind === "dm" && r\.outcome === "gave-up"\) gaveUp\.add\(r\.decision\);/if (false) gaveUp.add(r.decision);/' +run X13 $NT 's/(function giveUp\(decision, t\) \{\n\s*)journal\.append\(/$1if (0) journal.append(/' +run X14 scripts/bus-service.sh 's/^[^\n]*mkdir -m 0700 -p[^\n]*\n//m' diff --git a/agents/filbert/work/s4-follow-up-review/probe/probe.test.mjs b/agents/filbert/work/s4-follow-up-review/probe/probe.test.mjs new file mode 100644 index 00000000..092fd8ca --- /dev/null +++ b/agents/filbert/work/s4-follow-up-review/probe/probe.test.mjs @@ -0,0 +1,109 @@ +// Row 45 probes (Filbert). Run: node --test probe.test.mjs, with TREE set. +import { test } from "node:test"; +import assert from "node:assert/strict"; +import { appendFileSync, readFileSync } from "node:fs"; +const T = process.env.TREE; +const { createNotifier, journalPath, openJournal, POLL_MS } = await import(`${T}/packages/cli/src/notifier.mjs`); +const { RestOutcome } = await import(`${T}/packages/discord/src/rest.mjs`); +const { broker, tmp } = await import(`${T}/packages/cli/tests/helpers.mjs`); + +const log = (...a) => console.log("PROBE", ...a); + +function rig(t, iso, answer) { + const bus = broker(t); + const dataRoot = tmp(t); + const clock = { t: new Date(iso) }; + const sends = []; + const logs = []; + const direct = { async send(m) { sends.push({ at: clock.t.toISOString(), nonce: m.nonce }); return answer(clock.t); } }; + const make = () => createNotifier({ business: "demo", dataRoot, inbox: async () => bus.read("inbox"), direct, now: () => clock.t, log: (l) => logs.push(l) }); + const journal = () => readFileSync(journalPath(dataRoot, "demo"), "utf8").trim().split("\n").filter(Boolean).map((l) => JSON.parse(l)); + return { ...bus, dataRoot, clock, sends, logs, make, journal }; +} +const refuse403 = () => { throw new RestOutcome("refused", "dm: refused", { status: 403 }); }; + +// F2 timing: a binding typo (every DM 403s), polled every POLL_MS. +test("F2-T1 time from first refusal to gave-up, polling every POLL_MS", async (t) => { + const s = rig(t, "2026-10-08T05:00:00Z", refuse403); + s.raise("git.push.protected", { target: "refactor", blocking: true, task_ref: "vikunja:1/7" }); + const n = s.make(); + const start = s.clock.t.getTime(); + for (let i = 0; i < 2000 && !s.journal().some((r) => r.outcome === "gave-up"); i++) { + await n.tick(); + s.clock.t = new Date(s.clock.t.getTime() + POLL_MS); + } + const gave = s.journal().find((r) => r.outcome === "gave-up"); + log("T1 sends at", s.sends.map((x) => `+${(Date.parse(x.at) - start) / 1000}s`).join(" ")); + log("T1 gave-up at", `+${(Date.parse(gave.at) - start) / 1000}s`, "=", ((Date.parse(gave.at) - start) / 60000).toFixed(1), "min"); +}); + +// The typo is fixed an hour after the first refusal: does the DM land? +test("F2-T2 binding fixed after 60 min: the DM never lands", async (t) => { + const t0 = Date.parse("2026-10-08T05:00:00Z"); + const s = rig(t, "2026-10-08T05:00:00Z", (now) => (now.getTime() - t0 < 60 * 60_000 ? refuse403() : { messageId: "123456789012345678" })); + s.raise("git.push.protected", { target: "refactor", blocking: true, task_ref: "vikunja:1/7" }); + const n = s.make(); + for (let i = 0; i < 4 * 120; i++) { + await n.tick(); + s.clock.t = new Date(s.clock.t.getTime() + POLL_MS); + } + log("T2 after 4 h: outcomes", s.journal().filter((r) => r.kind === "dm").map((r) => r.outcome).join(",")); + log("T2 sends", s.sends.filter((x) => x.nonce.startsWith("dm")).length); +}); + +// Restart after two refusals: the in-memory backoff is lost. +test("F2-T3 restart drops the backoff: next attempt is immediate", async (t) => { + const s = rig(t, "2026-10-08T05:00:00Z", refuse403); + s.raise("git.push.protected", { target: "refactor", blocking: true, task_ref: "vikunja:1/7" }); + let n = s.make(); + await n.tick(); + s.clock.t = new Date(s.clock.t.getTime() + 31_000); + await n.tick(); + const before = s.sends.length; + n = s.make(); + await n.tick(); + log("T3 sends before restart", before, "after one tick post-restart", s.sends.length, "(same clock second)"); +}); + +// Unknown outcomes and 429 never give up. +test("F2-T4 500s for 6 h: no gave-up", async (t) => { + const s = rig(t, "2026-10-08T05:00:00Z", () => { throw new RestOutcome("unknown", "dm: unknown", { status: 500 }); }); + s.raise("git.push.protected", { target: "refactor", blocking: true, task_ref: "vikunja:1/7" }); + const n = s.make(); + for (let i = 0; i < 6 * 120; i++) { await n.tick(); s.clock.t = new Date(s.clock.t.getTime() + POLL_MS); } + log("T4 dm sends", s.sends.filter((x) => x.nonce.startsWith("dm")).length, "gave-up", s.journal().some((r) => r.outcome === "gave-up")); +}); + +// A digest 403 forever: the digest has no limit (only DMs give up). +test("F2-T5 digest refusals: retried, never gave-up", async (t) => { + const s = rig(t, "2026-10-08T13:00:00Z", refuse403); + const n = s.make(); + for (let i = 0; i < 120; i++) { await n.tick(); s.clock.t = new Date(s.clock.t.getTime() + POLL_MS); } + log("T5 digest sends in 1 h", s.sends.length, "gave-up", s.journal().some((r) => r.outcome === "gave-up")); +}); + +// J4 on append: a non-string messageId is written, and the next open refuses. +test("J4-A1 append does not type-check: a numeric messageId bricks the next open", async (t) => { + const s = rig(t, "2026-10-08T05:00:00Z", () => ({ messageId: 123 })); + s.raise("git.push.protected", { target: "refactor", blocking: true, task_ref: "vikunja:1/7" }); + const r = await s.make().tick(); + log("A1 tick", JSON.stringify(r), "line", JSON.stringify(s.journal()[0])); + try { s.make(); log("A1 reopen ok"); } catch (e) { log("A1 reopen", e.exitCode, e.message.replace(s.dataRoot, "")); } +}); + +// J4: a journal written by the base (row 39) code still opens. +test("J4-A2 a line the row 39 writer produced still opens", (t) => { + const s = rig(t, "2026-10-08T05:00:00Z", refuse403); + const file = journalPath(s.dataRoot, "demo"); + openJournal(file); + const at = new Date("2026-10-08T05:00:00Z").toISOString(); + appendFileSync(file, [ + { at, kind: "dm", decision: "d1", outcome: "confirmed", messageId: "1" }, + { at, kind: "dm", decision: "d2", outcome: "refused", messageId: null, status: 403 }, + { at, kind: "dm", decision: "d3", outcome: "unknown", messageId: null }, + { at, kind: "digest", decision: null, day: "2026-10-08", outcome: "confirmed", messageId: "2" }, + { at, kind: "digest", decision: null, day: "2026-10-09", outcome: "refused", messageId: null, status: 429 }, + ].map((r) => `${JSON.stringify(r)}\n`).join("")); + const j = openJournal(file); + log("A2 ok sent", [...j.sent].join(","), "days", [...j.days].join(","), "refusals", JSON.stringify([...j.refusals])); +}); diff --git a/agents/filbert/work/s4-follow-up-review/probe/sendwindow.mjs b/agents/filbert/work/s4-follow-up-review/probe/sendwindow.mjs new file mode 100644 index 00000000..9fa79b57 --- /dev/null +++ b/agents/filbert/work/s4-follow-up-review/probe/sendwindow.mjs @@ -0,0 +1,22 @@ +// Filbert, row 45: the host.mjs:144/150 window. The broker is SIGKILLed and +// dead, but this process has not yet run the disconnect, so connected is +// still true. Does send() with no callback raise an unhandled 'error'? +// argv[2]: "nocb" | "cb" +import { fork, spawnSync } from "node:child_process"; +import { once } from "node:events"; +import { fileURLToPath } from "node:url"; +if (process.argv[2] === "child") { process.send("up"); setInterval(() => {}, 1000); } +else { + const mode = process.argv[2]; + const c = fork(fileURLToPath(import.meta.url), ["child"], { stdio: ["ignore", "inherit", "inherit", "ipc"] }); + await once(c, "message"); + process.on("exit", (code) => console.log(mode, "process exit code", code)); + c.kill("SIGKILL"); + spawnSync("sleep", ["0.3"]); // block the loop: the child is dead, the disconnect not yet seen + console.log(mode, "connected before send", c.connected); + if (c.connected) { + if (mode === "cb") c.send({ op: "close" }, (e) => console.log(mode, "callback got", e?.code ?? "no error")); + else c.send({ op: "close" }); + } + process.exitCode = 3; +} diff --git a/agents/filbert/work/s4-follow-up-review/r1-base-node-bus.txt b/agents/filbert/work/s4-follow-up-review/r1-base-node-bus.txt new file mode 100644 index 00000000..1e8f7042 --- /dev/null +++ b/agents/filbert/work/s4-follow-up-review/r1-base-node-bus.txt @@ -0,0 +1,75 @@ +✔ launch identity is stamped, payload identity is refused and stale holder cannot send (24.241496ms) +✔ decision classes route from policy; gated resolution is human-only, choice and target must match (27.401376ms) +✔ claim exclusion, holder release, gated revoke and rerouting to a new holder are atomic (27.377079ms) +✔ launch events require a human CLI capability; generic emit cannot forge authority events (18.540838ms) +✔ within-role decisions close atomically and invalid options or blocking omissions refuse (15.718237ms) +✔ observer capabilities read human inbox but cannot mutate or forge launch identity (17.673868ms) +✔ task action subjects and linked decision trail are complete and ordered (26.350248ms) +✔ launch binding is durable and reconnecting requires the identical trusted record (12.074309ms) +✔ business isolation includes inherited object names and cross-business message references (17.714459ms) +✔ authority never transfers between action, run, target, unresolved or replaced role holder (23.654992ms) +✔ task projection uses schema current view, skipping earlier and equal-start polls (12.601793ms) +✔ revocation permanently bars the old run from reclaiming first, including after broker restart (21.489231ms) +✔ empty message references refuse before storage; refusal-evidence failure stays a typed error (12.121438ms) +✔ both arbiters require human resolution when their cross-role route is themselves (18.100019ms) +✔ S1 adapter takes resolved limits and refs, rejects mismatched instance, never mutates input (2.663501ms) +✔ only validated broker references load; returned data and exceptions cannot expose a known token (7.494723ms) +✔ bad file modes, symlinks, repository/data paths, malformed tokens and missing dates refuse (2.614846ms) +✔ expiry refuses use and env references never become client data (0.953331ms) +✔ S1 parsed service refs work, service mismatch refuses, Gitea rotation due is a warning state (1.997646ms) +✔ opaque tokens shorter than 16 characters refuse before use (0.426004ms) +✔ human proof binds CLI entry, process start and nonce; agents and incomplete ancestry refuse (2.639842ms) +✔ process reader gets own kernel identity without exposing environment values (0.796532ms) +✔ EACCES ancestor environments skip only markers; commands and registered launches still refuse (1.31124ms) +✔ real pid 1 remains inspectable when its environment is protected (0.492702ms) +✔ within-role sends cite an open gated launch decision without spending it or naming it in grants (30.750778ms) +✔ missing and foreign-business citations refuse and roll back message and grant (19.639241ms) +✔ cross-role sends still need a matching resolved decision and consume it once (25.365852ms) +✔ broker process binds trusted launches, offers reader capabilities, refuses human mutation, closes cleanly (90.577896ms) +✔ startup token refusal returns safe code without value or partial listening broker (57.911085ms) +✔ loaded fixture token is absent from socket replies and SQLite, including refusal evidence (81.062244ms) +✔ killed broker leaves an explicit stale lock; another process cannot silently reclaim it (93.951236ms) +✔ trusted host registers later launches; socket clients never have a registration verb (54.687669ms) +✔ runtime excludes declared project roots even when host supplies no repoRoots (37.187253ms) +✔ a refused launch binding leaves the broker and existing capabilities alive; bad protocol stops it (57.471264ms) +✔ v3b prototype refusals, views and append-only mutations (85.698753ms) +✔ gated approval authorizes once, survives store reopen, and fresh approval works (32.966804ms) +✔ another run cannot consume an approval; a failed check leaves it usable (23.112882ms) +✔ two scheduled callers have exactly one grant and one consumed refusal (18.143623ms) +✔ failed commit rolls consumption back; cross-role consumes and within-role stays reusable (30.935331ms) +✔ class drift gated to cross-role refuses before consumption (22.429967ms) +✔ class drift cross-role to gated refuses before consumption (23.526473ms) +✔ class drift gated to within-role refuses before consumption (18.94584ms) +✔ class drift cross-role to within-role refuses before consumption (20.00439ms) +✔ class drift within-role to gated refuses before consumption (20.09949ms) +✔ class drift within-role to cross-role refuses before consumption (20.004313ms) +✔ message.send consumes approval and prevents a later send or authorize (20.507454ms) +✔ role.revoke consumes approval and prevents a later revoke or authorize (24.840891ms) +✔ creates private WAL store and excludes a second writer until explicit close (17.620181ms) +✔ rollback is atomic and schema metadata is checked against trusted DDL, not just itself (16.372243ms) +✔ existing empty database and symlink runtime directory refuse, never initialize over damage (17.06366ms) +✔ crash during a transaction recovers no partial event after explicit fixture-only lock removal (61.104182ms) +✔ writer refuses mixed at/read_at forms atomically, even through trusted SQL helpers (11.117121ms) +✔ async transactions refuse before invoking their function (8.582549ms) +✔ recordTask keeps sync reads and a role write apart (26.654451ms) +✔ read_at must be one canonical UTC format, so the projection compares strings safely (10.565667ms) +✔ a bad entry refuses the whole record (10.346049ms) +✔ taskView reads the projection for one business (14.743855ms) +✔ requestTask hands only a holder and a task verb to the handler, and records refusals (23.223528ms) +✔ the server sends task verbs to the adapter with its own timeout; other verbs stay synchronous (278.8713ms) +✔ without an adapter the server refuses every task verb (14.399987ms) +✔ the runtime refuses an invalid adapter and closes a valid one (16.003453ms) +✔ the process loads the S3 adapter from plain-data trackers (104.50676ms) +✔ socket capability stamps launch identity; shared views use wire, no SQL client (30.89905ms) +✔ two wire claims serialize; a lost reply never automatically retries (25.599399ms) +✔ malformed, oversized and identity-forging envelopes refuse without echoing input (13.8714ms) +✔ client preserves UTF-8 when a response divides a multibyte character (12.484692ms) +✔ committed mutation followed by dropped reply reports unknown and is never retried (16.628757ms) +ℹ tests 67 +ℹ suites 0 +ℹ pass 67 +ℹ fail 0 +ℹ cancelled 0 +ℹ skipped 0 +ℹ todo 0 +ℹ duration_ms 620.262652 diff --git a/agents/filbert/work/s4-follow-up-review/r1-base-node-business.txt b/agents/filbert/work/s4-follow-up-review/r1-base-node-business.txt new file mode 100644 index 00000000..52be1747 --- /dev/null +++ b/agents/filbert/work/s4-follow-up-review/r1-base-node-business.txt @@ -0,0 +1,68 @@ +✔ config directory and file path follow MOSAIC_CONFIG (1.867049ms) +✔ the fixture business validates and comes back frozen (6.613216ms) +✔ two instances may share a definition (1.553281ms) +✔ top-level refusals (4.394638ms) +✔ arbiters and projects (6.806464ms) +✔ role instances (4.007157ms) +✔ Vikunja bots (10.644466ms) +✔ a role without Vikunja takes no tracker block (3.567703ms) +✔ credential references match the definition's services (4.264474ms) +✔ launch (12.958131ms) +✔ loadBusiness: file checks (2.079948ms) +✔ loadBusiness: not a regular file (45.475837ms) +✔ loading writes nothing (1.248191ms) +✔ names that are Object.prototype properties don't count as declared (2.636431ms) +✔ the shipped example refuses as written and validates once filled in (0.938997ms) +✔ usage errors exit 4 (306.920279ms) +✔ validate: a good business exits 0 and prints instance digests (75.941648ms) +✔ validate: project files (372.761359ms) +✔ validate: missing files and a broken system config (286.961712ms) +✔ validate: credential reference problems exit 2 and name each one (92.078302ms) +✔ validate: a token file inside the repository is refused (69.525005ms) +✔ validate: role definitions come from MOSAIC_ROLES_DIR (212.064917ms) +✔ resolve: prints one instance's record (225.62473ms) +✔ resolve: refusals (438.939536ms) +✔ parse: exactly one of file or env, plus the service's date (2.990356ms) +✔ check: a good file has no problems (0.88057ms) +✔ check never opens the file: a write-only token passes (0.344506ms) +✔ check: file problems (1.133557ms) +✔ check: token files can't live in the repository or dataRoot, even through a linked directory (1.180256ms) +✔ check: dates and environment references (0.53199ms) +✔ path and load (2.600044ms) +✔ refusals (1.590197ms) +✔ systemVars flattens the validated config (2.334227ms) +✔ precedence: system, business, project, project role, agent (5.382138ms) +✔ limits narrow the definition and never widen it (2.738644ms) +✔ role.launch stays within-role only for the instance the launch block names (5.289806ms) +✔ limits.authority without role.launch leaves the launcher with no launch block (2.998882ms) +✔ limits.authority narrows cross-role actions too (1.66732ms) +✔ classify (1.855641ms) +✔ the record carries what the broker and launcher need (1.587725ms) +✔ digest: key order doesn't matter, any value change does (10.439567ms) +✔ refusals (3.815951ms) +✔ the four shipped version 2 roles load (3.112119ms) +✔ shipped role scopes match addendum B section 2 and the SR runbook (1.576915ms) +✔ shipped authority follows the note's table (0.5797ms) +✔ version 1 files keep loading with no authority (1.255039ms) +✔ the conductor policy isn't a role (0.287754ms) +✔ a missing role file is exit 4, a symbolic link too (0.465952ms) +✔ version 2 refusals (1.508511ms) +✔ authority: closed vocabulary, no gated-only action, no overlap (2.698625ms) +✔ credentials: Gitea scopes (0.935475ms) +✔ credentials: Vikunja scopes are a group-to-verbs map from the grantable list (1.273969ms) +✔ credentials: services (0.668012ms) +✔ contract: a non-empty regular Markdown file beside the role file (0.725598ms) +✔ every key names known layers and a merge rule (0.99936ms) +✔ unknown keys and wrong layers refuse (0.880079ms) +✔ types (2.195618ms) +✔ merge: defaults, then the most specific layer wins (0.322584ms) +✔ merge: limits only narrow, and provenance lists each source (0.431596ms) +✔ merge doesn't change its inputs (0.188001ms) +ℹ tests 60 +ℹ suites 0 +ℹ pass 60 +ℹ fail 0 +ℹ cancelled 0 +ℹ skipped 0 +ℹ todo 0 +ℹ duration_ms 2158.961409 diff --git a/agents/filbert/work/s4-follow-up-review/r1-base-node-cli.txt b/agents/filbert/work/s4-follow-up-review/r1-base-node-cli.txt new file mode 100644 index 00000000..1d7e422f --- /dev/null +++ b/agents/filbert/work/s4-follow-up-review/r1-base-node-cli.txt @@ -0,0 +1,57 @@ +✔ inbox lists only decisions routed to the human, with what approving authorizes and how to decide (18.683142ms) +✔ decide resolves by id prefix with --yes, and the trail shows it in broker order with the task to follow (23.175532ms) +✔ decide refuses without a terminal or --yes, on an unknown option and on a short reference (12.54802ms) +✔ decide prints a declining choice as declining (12.654365ms) +✔ an unknown outcome is reported once and never resent (12.795995ms) +✔ a decision closed before the answer arrives exits 2 and points at its trail (10.791338ms) +✔ a prefix that matches two open decisions exits 2 and resolves neither (12.657588ms) +✔ without --business a command uses the live host's business, and a stale host.json is not a host (10.224266ms) +✔ every human command refuses inside an agent run before it touches the bus (11.932399ms) +✔ usage errors exit 4; no business and no host is a usage error (10.850137ms) +✔ agents and tasks print through the broker (9.820295ms) +✔ notify.json: missing, loose, malformed and extra keys refuse; a binding or null passes (1.459087ms) +✔ bootConfig builds the broker's boot message for one business, with no trackers key when no project names one (47.077328ms) +✔ trackers come from the tracker.* variables of the one project that names a tracker project (41.209336ms) +✔ with two projects, the one that sets tracker.project supplies the tracker and the other is no conflict (36.360521ms) +✔ two projects that each name a tracker project refuse, since the boot shape holds one (37.183043ms) +✔ a business without tracker.baseUrl gets no trackers entry (34.767644ms) +✔ an unknown business and a broken system config refuse with exit 3 (65.444872ms) +✔ empty views say so (0.866ms) +✔ the trail keeps the broker's order and names a decision's task without its rows (1.026474ms) +✔ tasks print the tracker fields the snapshot carries (0.169169ms) +✔ the host boots the broker, binds a launch in process, and the notifier DMs a blocking decision exactly once (868.369289ms) +✔ a notifier that dies takes the host down with exit 1, so the unit restarts the pair (172.365188ms) +✔ a notifier that refuses stops the broker and the host refuses with exit 3 (147.095184ms) +✔ watchChildren reports a child that died before it was called, and one that dies later (31.864675ms) +✔ bus stop refuses to signal a live pid that is not a bus host (203.54711ms) +✔ bus start refuses with exit 3 and the code when the broker refuses to boot; bus status names the lock (257.814577ms) +✔ bus start refuses with exit 3 without a notifier config (111.480249ms) +✔ bus start runs until bus stop; status reports it while it runs (729.54278ms) +✔ bus-service.sh renders the unit and installs it into a given directory (33.560404ms) +✔ zoned uses the IANA zone across DST (25.853917ms) +✔ each open blocking decision is DM'd once, across polls and a restart; non-blocking ones are not (18.485699ms) +✔ two blocking decisions get two DMs with different nonces (12.527143ms) +✔ the digest nonce differs per business and per day and fits Discord's 25 characters (0.235022ms) +✔ a failed DM is journaled, backs off, and is retried until it lands (13.460085ms) +✔ the digest goes at 08:00 Chicago once a day, with blocking ones marked as DM'd (16.722078ms) +✔ a late start with no digest for the day sends one at once; an empty inbox gets one line (12.203833ms) +✔ an inbox read failure is logged and the next poll retries (0.835125ms) +✔ no Discord id reaches the journal or the log (13.241518ms) +✔ the journal: a torn tail is copied out and truncated, so an append after it reopens cleanly (2.243073ms) +✔ the journal: a crash between the copy and the truncate leaves a tail the next open repairs (3.159201ms) +✔ the journal: a whole file that is one torn line truncates to empty (1.998131ms) +✔ the journal: a malformed complete line refuses and leaves the file and any torn tail alone (0.793662ms) +✔ the journal: a loose file mode, a loose directory or a symlinked journal refuses (0.743756ms) +✔ digest content stays within Discord's 2000 characters (0.392904ms) +✔ runLoop never overlaps ticks and stops after the one in flight (110.519055ms) +✔ the transport writes {business, verb, args} to the child and reads its JSON (34.393972ms) +✔ a bus code on stderr becomes the exit code; garbage and timeouts are outcome problems (2176.092888ms) +✔ busExit and refuseInsideAgent (0.551635ms) +ℹ tests 49 +ℹ suites 0 +ℹ pass 49 +ℹ fail 0 +ℹ cancelled 0 +ℹ skipped 0 +ℹ todo 0 +ℹ duration_ms 2660.213546 diff --git a/agents/filbert/work/s4-follow-up-review/r1-base-node-control-board.txt b/agents/filbert/work/s4-follow-up-review/r1-base-node-control-board.txt new file mode 100644 index 00000000..7cfc2b8e --- /dev/null +++ b/agents/filbert/work/s4-follow-up-review/r1-base-node-control-board.txt @@ -0,0 +1,132 @@ +✔ explicit request, Seen, ordinary completion and a new request have distinct attention states (6.010669ms) +✔ attention convention ignores reasoning/quoted examples and permits leading blank lines (0.427305ms) +✔ completed smoke replies and ordinary questions are idle, not human blockers (1.380473ms) +✔ only an explicit first-line input request makes a finished reply waiting (0.350763ms) +✔ tool activity, user text, errors and unfinished turns override attention text (0.244757ms) +✔ STOP access failure is unknown, not absence, under a non-root identity (46.560522ms) +✔ connector Task never inherits Discord routing envelopes; ordinary Task still uses user text (4.628469ms) +✔ connector discovery keeps only safe identity; rejects modes, mismatches, links and traversal (2.347115ms) +✔ canonical owner identity and STOP are independent; no tmux fallback or forged registration (8.011484ms) +✔ connector reply refusal precedes forged live tmux registration; ordinary agent still sends (1.083299ms) +✔ server rescans connector discovery and refuses HTTP reply without transport (44.367227ms) +✔ connector session links and linked directories are not read (1.464268ms) +✔ newer live matching launch marks old activity, preserves history/attention/attribution, then clears on new activity (3.99754ms) +✔ CLI print uses the relaunch notice instead of old current preview (95.092586ms) +✔ connector owner and fixed task never inherit a native relaunch notice (3.122909ms) +✔ equality, stale/unknown/offline, mismatched registration and unknown activity do not assert relaunch (1.855835ms) +✔ loadConfig: missing file throws ConfigError (2.170209ms) +✔ loadConfig: invalid JSON throws ConfigError (0.404033ms) +✔ loadConfig: missing dataRoot throws ConfigError (0.309222ms) +✔ loadConfig: relative dataRoot throws ConfigError (0.423873ms) +✔ loadConfig: valid config returns dataRoot (0.828693ms) +✔ findNewestSession: picks the newest by mtime among two files (0.711595ms) +✔ findNewestSession: finds files in nested subdirectories (0.463025ms) +✔ findNewestSession: returns null for a missing dir (0.166929ms) +✔ readSession: extracts fields, collapses/truncates text, counts a truncated final line (0.91581ms) +✔ readSession: model and provider follow the latest model_change entry or assistant turn; null when the log names neither; scanAgent carries them (1.74382ms) +✔ readSession: lastError carries the assistant errorMessage only when the last assistant turn errored (0.68941ms) +✔ findNewestSession/scan: never read sibling auth or secrets next to a sessions dir (1.513219ms) +✔ deriveState: full state table (0.217441ms) +✔ rule: newest entry is an assistant message with a tool call, after a question-looking text, is working (0.584263ms) +✔ rule: newest entry is a tool result with no assistant text after it is working (0.4952ms) +✔ rule: a finished ordinary turn is idle, even if it says your move (0.461533ms) +✔ task: the first user message of the session, from text blocks (0.358351ms) +✔ task: a plain-string user content is accepted, whitespace collapsed and long text capped (0.40596ms) +✔ task: no user message in the log means null (shown as unknown), never a guess (0.36415ms) +✔ workspace: the live tmux pane path wins; the session cwd is the fallback; neither means null (0.529594ms) +✔ activeProject: basename of the nearest .git directory or .git file above the workspace; none means null (0.638536ms) +✔ scan: the written record carries task, workspace and activeProject (0.93127ms) +✔ registration: overrides task, project and workspace; every source says registration; registered carries the launch fields; the grouping column is untouched (0.9256ms) +✔ registration: empty task and null project/workspace leave the derived values in place; registered is still non-null (0.674264ms) +✔ 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.455794ms) +✔ registration: no registration leaves the Gate A fields exactly as before, and registered is null (0.591052ms) +✔ loadRegistrations: a missing seatsDir gives empty lists (0.216108ms) +✔ loadRegistrations: one good record, one malformed JSON, one with an unknown field; a stray file under seatsDir is ignored (1.142905ms) +✔ 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.43043ms) +✔ scan: writes the registration override to disk; index.json carries registered and registrationErrors (1.014589ms) +✔ scan: a relative seatsDir throws ConfigError; an omitted seatsDir behaves as before (0.775468ms) +✔ scanAgent: waitingOnYou is true for waiting/error and false otherwise (0.638485ms) +✔ scanAgent: ageSeconds is computed from the injected now (0.325922ms) +✔ scanAgent: sessionFile null and state idle when sessions dir is empty but alive (0.245852ms) +✔ discoverRepoAgents: finds agents with a sessions dir, skips those without, sorted by name (0.434228ms) +✔ discoverFleetAgents: finds agents with a sessions dir, sorted by name, fleet tmux fields (0.552141ms) +✔ scan: writes per-agent files and index.json, rerun overwrites, no leftover tmp files (1.187081ms) +✔ scan: relative boardDir throws ConfigError (0.145247ms) +✔ CLI: scan with assume-alive liveness exits 0, prints board summary, writes board files (86.948336ms) +✔ CLI: missing config exits 2 with a refused: message (60.852136ms) +✔ CLI: unknown command exits 2 (70.857596ms) +✔ CLI: unknown --liveness value exits 2 (77.483179ms) +✔ panesRunPi: true when any trimmed line equals 'pi' (0.263144ms) +✔ panesRunPi: false for bash-only, claude, empty, or node-pi-style lines (0.09522ms) +✔ tmuxIsAlive: a pane running pi is alive (0.206673ms) +✔ tmuxIsAlive: session exists but pi has exited is not alive (0.10368ms) +✔ tmuxIsAlive: no such tmux session is not alive (0.058347ms) +✔ tmuxIsAlive: tmux could not be run at all is unknown (null), never assumed alive (0.067732ms) +✔ tmuxIsAlive: passes -L only when a socket is given (0.105159ms) +✔ parsePanes: one pane per line, command and optional tab-separated path (0.08465ms) +✔ tmuxInspect: reports the path of the pane running pi, not of a shell pane (0.085903ms) +✔ tmuxInspect: no pi pane, no session, or no tmux gives no workspace and the matching liveness (0.102332ms) +✔ loadSeen: missing file returns {} (0.17577ms) +✔ loadSeen: invalid JSON throws ConfigError (0.222621ms) +✔ loadSeen: a JSON array throws ConfigError (0.215859ms) +✔ loadSeen: a non-string value throws ConfigError (0.197642ms) +✔ markSeen: seen true adds the key and writes seen.json mode 0600, no leftover tmp files (0.474763ms) +✔ markSeen: seen false deletes the key (0.295837ms) +✔ markSeen: missing, empty, or non-string fields throw ConfigError (0.214289ms) +✔ markSeen: project containing '/' throws ConfigError (0.663767ms) +✔ markSeen: non-boolean seen throws ConfigError (0.163287ms) +✔ scanAgent: a seen mark matching the waiting session's lastTimestamp clears waitingOnYou (0.366334ms) +✔ scanAgent: a stale mark (agent wrote something newer) is not seen and waitingOnYou is true (0.291198ms) +✔ scanAgent: a working session with a matching mark is not seen (marks only apply to waiting/error) (0.267725ms) +✔ scanAgent: an error-state session with a matching mark is seen (0.310516ms) +✔ scan: index.seen and waitingOnYou reflect seen.json, which scan never rewrites or deletes (0.613896ms) +✔ scan: a corrupt seen.json makes scan throw ConfigError (fail closed) (0.22487ms) +✔ 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.830851ms) +✔ 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.914619ms) +✔ taskSetBy: scan() reads the field from disk through the seat package (bounded there), writes it to the per-agent record and index, and an invalid on-disk value is a registrationError, never a row value (1.340662ms) +✔ isLoopbackHost: recognizes loopback hosts (1.861483ms) +✔ isLoopbackHost: rejects non-loopback hosts (6.326495ms) +✔ startServer: refuses a non-loopback host with ConfigError, never opens a socket (4.294971ms) +✔ startServer: serves page, healthz, and a rescanning /api/board (52.193955ms) +✔ startServer: a seatsDir registration overrides the row and index.registered reflects it (10.198557ms) +✔ startServer: /api/board returns 500 JSON with an error field when scan throws (3.653617ms) +✔ CLI: serve refuses a non-loopback host with exit 2 and a refused: message (67.059083ms) +✔ CLI: serve rejects a non-numeric --port with exit 2 (72.917415ms) +✔ CLI: scan still works after the async cli refactor (71.064375ms) +✔ CLI: live serve prints its URL and answers /healthz (74.988166ms) +✔ page.html: esc() escapes every HTML-significant character (0.657462ms) +✔ POST /api/seen marks a row; GET /api/board still shows it seen; seen:false clears it (8.813455ms) +✔ POST /api/seen without a JSON content-type returns 400 and does not write a mark (2.279532ms) +✔ POST /api/seen with invalid JSON returns 400 (2.984055ms) +✔ POST /api/seen with a body over 4096 bytes returns 400 (or resets the connection) and writes no mark (2.586391ms) +✔ POST /api/seen with a missing agent returns 400 (1.774095ms) +✔ POST /api/board returns 405; PUT /api/seen returns 405 (1.963635ms) +✔ CLI: scan --print marks a seen row with 's' and the summary line ends with 'N seen)' (62.667316ms) +✔ page.html: seenControl() escapes rec.project/agent/lastActivity, and the POST uses a JSON content-type (0.392194ms) +✔ page.html: has a collapsed Seen section that lists seen rows with the shared row builder (0.385316ms) +✔ page.html: each project has a Hide seen checkbox (default on) beside Hide offline, with a hidden-count note (0.239229ms) +✔ page.html: a project header reads "N of N" only while a checkbox hides rows (0.159999ms) +✔ page.html: every row shows Task and Active project, derived or the word unknown, with the workspace in the detail (0.377441ms) +✔ 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.779315ms) +✔ POST /api/reply: runs agent-send.sh with -s from the registration, -S :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 (39.730547ms) +✔ POST /api/reply: a registration with a tmux socket adds -L (36.773894ms) +✔ POST /api/reply: a non-zero tool exit is a 200 with delivered false, the exit code and the stderr verbatim (34.067307ms) +✔ 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 (29.012725ms) +✔ POST /api/reply: a missing agent-send.sh is a 500 with the path in the error, not a crash (6.244474ms) +✔ replyToRow: DEFAULT_AGENT_SEND is the repository's tools/tmux/agent-send.sh and it is executable (0.212342ms) +✔ 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.686283ms) +✔ startServer: /api/board carries taskSetBy from a live registration and null for the derived rows (2.987338ms) +✔ 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.390807ms) +✔ 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 (9.577638ms) +✔ Host/Origin guard: loopback names on this port are accepted, with or without a same-origin Origin (36.075218ms) +✔ conversation routes (F16): a foreign Host, a wrong port and a cross-origin Origin get 403 before the reader runs, with no CORS headers (6.60021ms) +✔ every refusal code the reader can raise has an HTTP status (3.982216ms) +✔ conversation routes: catalogue, first page, next page and follow over HTTP; refusals map to 4xx with their code; nothing is written (49.201664ms) +ℹ tests 124 +ℹ suites 0 +ℹ pass 124 +ℹ fail 0 +ℹ cancelled 0 +ℹ skipped 0 +ℹ todo 0 +ℹ duration_ms 827.412997 diff --git a/agents/filbert/work/s4-follow-up-review/r1-base-node-conversation.txt b/agents/filbert/work/s4-follow-up-review/r1-base-node-conversation.txt new file mode 100644 index 00000000..2119b098 --- /dev/null +++ b/agents/filbert/work/s4-follow-up-review/r1-base-node-conversation.txt @@ -0,0 +1,205 @@ +✔ W1: two processes acquire the same pair at once; exactly one claim (208.33252ms) +✔ W1: two writers publish the same revision at once: one wins, the other gets null, the winner's record stays (9.811798ms) +✔ W1: a revision name appears only after its bytes are synced; before that, only a temp file exists (5.537445ms) +✔ W2: acquire while a claim is reserved or active refuses already-active (228.104072ms) +✔ W3: acquire while stopping, uncertain, or stopped without proof refuses unsafe-replacement (324.120499ms) +✔ W4: same session with another seat tuple, and the reverse, both refuse; a loser on the seat key closes it no-unit (209.598509ms) +✔ W4: a hard link of one session under another seat is the same session: the second controller refuses already-active and launches nothing (27.238547ms) +✔ W4: a copy of one session under another seat is the same session: the second controller refuses already-active and launches nothing (22.122492ms) +✔ W4: a session header ID that changes after construction refuses target; nothing is claimed or launched (3.087845ms) +✔ W5: SIGKILL between every publication barrier of acquire and transition; restart never finds two holders or a lost claim (5701.445297ms) +✔ W5: SIGKILL between every publication barrier of release; restart finishes or holds the release (22244.010272ms) +✔ W6: controller killed mid-turn while the engine lives; restart is uncertain, no launch, prompts refuse (149.81459ms) +✔ W12: a live owner paused with SIGSTOP; a second controller refuses already-active and changes nothing (88.345296ms) +✔ W13: crash after the engine spawns, before active; restart finds the live unit: uncertain, no second spawn, force stop only (223.831116ms) +✔ W14: crash after reservation, before the spawn marker: stopped with a no-unit observation; the pair is free (188.323071ms) +✔ W20: crash after the spawn marker, scope collected; uncertain in both runs, the marker is copied, no launch until a boot proof (217.683612ms) +✔ W15: crash between the two keys during release; restart finishes it under the same claim ID (32.560634ms) +✔ W7: recorded boot ID differs on the same machine: stopped with a boot proof; open tool calls become uncertain (96.660024ms) +✔ W8: resume after a proven stop with the same pins: new claim ID, generation +1, same conversation, branch and leaf (32.019907ms) +✔ W9: resume with a changed binary, argv digest, branch or leaf is refused and the claim is unchanged (91.969696ms) +✔ W11: the controller writes no session file; only the fake engine's own appends appear (22.329944ms) +✔ W16: a highest revision that won't parse holds the pair uncertain; the older stopped revision is not reused (54.201621ms) +✔ W17: a claim root copied from another host refuses foreign-host and promotes nothing (60.552307ms) +✔ G1: a session path or claim root under .pi/state, ~/.claude, the data root or a registration refuses at construction (3.596267ms) +✔ G2: a symlink inside the fixture root to a live session file is refused by the real-path check (1.126052ms) +✔ G3: a fixture path swapped for a live path after construction is refused at bind (1.770404ms) +✖ K1: force stop kills a tool child that called setsid; stopped with a verified proof (2653.677511ms) +✔ K2: K1 on the process-group fallback ends uncertain, never stopped (129.308388ms) +✖ K3: SIGTERM acknowledged while a member lives: stopping until the kill phase, never stopped from TERM (2165.26786ms) +✔ K4: two engines; force stop one; the other survives by independent observation (4287.963097ms) +✔ K5: a stop during a tool call leaves the effect uncertain, and it is shown (2182.565146ms) +✔ K12: a member forking in a loop: the freeze stops it, enumeration is complete, populated 0 after cgroup.kill (2258.603392ms) +✔ K13: a member writing its pid into another cgroup is refused by the namespace; the kill is complete (2153.528491ms) +✔ K15: the shim gone, engine/cgroup.events unreadable, or the engine cgroup missing: evidence unavailable, not empty; uncertain (4403.942608ms) +✖ K10: controller killed between the TERM and kill phases: restart checks the invocation ID and re-runs from TERM for the same stop (180.585304ms) +✔ K11: controller killed after the confirmation is recorded, before TERM: restart checks the invocation ID and re-runs from TERM for the same stop (302.630477ms) +✔ K14: a unit with the recorded name but another invocation ID: evidence unavailable, no signals, uncertain (265.388551ms) +✔ K6: recover without proof, without confirmation, or with changed pins is refused (69.424274ms) +✔ K7: recover after proof, then launch: new claim and execution, generation +1, same leaf; the cancelled prompt is not replayed (38.2806ms) +✔ K8: an engine that loads another leaf on resume is refused before admission; it stays claimed until a proven stop (48.757019ms) +✔ K9: an interrupt that never settles stays uncertain; force stop stays available; takeover is refused while fenced (3026.567752ms) +✔ K16: a claim from another machine ID refuses foreign-host; no boot proof is issued (5.577601ms) +✔ K17: two launcher calls with one eligibility record: one launch, the other refuses, no second engine (35.813713ms) +✔ K18: the leaf changes after eligibility: launch refused; the reservation stays until released with proof (28.909283ms) +✔ S1: `/goal x`, with leading spaces or a tab, refuses text-policy at admission; zero engine bytes (49.842944ms) +✔ S2: every prefix pinned Pi interprets is refused, from the list the code uses; the rest reach the engine exactly (37.949131ms) +✔ S3: `/goal` on the second line is pinned from the source: Pi checks only index 0, so it is admitted and sent exactly (34.393597ms) +✔ S4: a `/` left in the composer is cleared when control transfers and returns; the next submit sends only the new text (55.353224ms) +✔ S5: an observer terminal gets a paste then Enter, as send-message.sh does: not admitted: controller, nothing sent (24.11355ms) +✔ S6: a mediated-shaped registration (no tmux) passed to the board's replyToRow: 409 no tmux session; exec never runs (0.684512ms) +✔ S7: ESC, bracketed-paste markers and U+2028/U+2029 travel as one JSON string; the engine receives the exact text in one record (26.735815ms) +✔ P3: a Pi confirm, select, input or editor dialog is shown disabled with a reason and never answered (127.693023ms) +✔ E1: send, ack, user, toolCall, toolResult, final answer: shown once, no refresh, draft and reading position kept (43.227988ms) +✔ E2: U+2028, U+2029 inside JSON strings and CRLF line ends each parse as one record, on the splitter and through the controller (26.124057ms) +✔ E3: a multipart final, two blocks, null request correlation and duplicate delivery (38.762027ms) +✔ 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 (38.724253ms) +✔ E4: a gap or a new epoch also reconciles; nothing is concatenated across a gap (10.71162ms) +✔ E5: an unknown native event gives no client event; evidence records its type and bytes; the terminal count goes up (30.187625ms) +✔ E6: a tool result delayed across a pause and a reconnect is reconciled without a manual refresh (48.049709ms) +✔ E7: the terminal renders the same stream as the library client, as observer and then as controller, and submits only as controller (43.974693ms) +✔ terminal: engine control characters are made visible; a lost connection refuses submit (30.554731ms) +✔ terminal: outcome unknown is shown as such, with no resend offer, and nothing is resent (0.71206ms) +✔ terminal: text after Enter in the same input chunk starts the next message; it never joins the one submitted (0.501472ms) +✔ terminal: a paste-start marker split right after its ESC still opens the paste; the Enter inside it never submits (0.71016ms) +✔ terminal: invisible and bidi characters are made visible; head, status and notice lines stay one line (0.205325ms) +✔ every record these fixtures produced is a valid CHAT-01 record (E5: no record fails the schema) (440.560715ms) +✔ H1: two takeovers with the same expected generation: one wins, +1; the other refuses generation (70.048519ms) +✔ H2: the old controller's prompt after a takeover commits is refused with zero engine bytes (89.612495ms) +✔ H3: a takeover while a prompt holds the dispatch lock: written under the old actor, or refused; never both (151.740861ms) +✔ H4: self-takeover is refused (19.567355ms) +✔ H9: Interrupt racing a prompt's dispatch: before the write, dispatch-refused and no-turn; after, §3 rules (126.266639ms) +✔ H10: Interrupt and force stop together: one stop chain, force stop supersedes (127.987954ms) +✔ H10: an overlap during the pause before the abort: no abort, the stop ends uncertain (30.194643ms) +✔ H10: a no-turn Interrupt lifts only its own fence; admission stays closed under force stop, overlap or revocation (90.744705ms) +✔ H11: the controller disconnects mid-turn: work continues, the claim is unchanged, control stays put (137.197933ms) +✔ H12: an exact retry after reconnecting to the same incarnation returns the same receipt; one dispatch (15.871752ms) +✔ H13: a retry with the same request ID and different text is refused (16.248157ms) +✔ H14: late stdout from the old engine after a replacement is dropped by incarnation, counted, never rendered (137.840526ms) +✔ H15: a revoked connection's command is refused; the revocation fence holds (67.750797ms) +✔ H16: a second controller for the same session refuses already-active; the first is untouched (18.408706ms) +✔ H10: a second force stop while the first escalation runs refuses fenced; one escalation, and the claim records only the first stop's phases (58.879625ms) +✔ H17: a confirmation reused, answered from another connection, or used after the stop changed is refused (54.75082ms) +✔ H18: two prompts before any native output: the second refuses busy; one engine write (15.210695ms) +✔ H19: the pipe fails mid-line under a large prompt: delivery-unknown transport-unknown, poisoned, no later write (119.912673ms) +✔ H19: the link itself never writes again after an unknown outcome, whoever calls it (0.74475ms) +✔ H19: the controller dies mid-write of a large line: after restart the outcome is unknown and nothing is resent (526.777789ms) +✔ H20: the line is written but the ack is lost when the controller dies: orphan, outcome unknown, nothing resent (406.518664ms) +✔ H21: a retry of the exact request with the old token after a crash is stale-incarnation; no second write (372.815504ms) +✔ H22: after H21 and a valid recovery, a new request with the new token is admitted (2470.446235ms) +✔ H23: requests pending at a restart are not resent; each shows outcome unknown (457.248222ms) +✔ a plain conversation: catalogue row, one page, CHAT-01 records (11.82056ms) +✔ native entries map to blocks: tools, thinking, bash, notices, ids that do not fit (2.271047ms) +✔ F1: a malformed line is an unavailable part at its position, and reading continues (2.730979ms) +✔ F1: a missing parent stops the history with a notice that names the unreadable lines (3.52433ms) +✔ F1: an unreadable fork is never merged into another branch's history (1.850162ms) +✔ F1: a follow stays on its branch when the next entry's parent is unreadable (4.636359ms) +✔ F1: a file whose entries are all unreadable shows a notice per line (1.641695ms) +✔ F2: a truncated trailing line marks the view incomplete, not an error (2.301832ms) +✔ pagination: 100 parts, then the rest; parts concatenate to the whole branch (7.597286ms) +✔ F3: a replaced file (new inode) refuses old cursors with reconcile (5.634047ms) +✔ F4: a same-inode rewrite of the prefix refuses old cursors with reconcile (7.869533ms) +✔ F5: growth between pages keeps the epoch and the page stops at the pinned length (11.185671ms) +✔ F6: unknown, foreign and expired cursors refuse and leave the cursor usable (14.562294ms) +✔ F7: a symlinked file and a symlinked directory component are refused, never opened (15.568579ms) +✔ F8: a file swapped for a symlink after the catalogue is refused (3.87859ms) +✔ F9: registrations never add or redirect a root (2.922432ms) +✔ F10: a header cwd naming another project is refused (6.505993ms) +✔ F11: parentSession renders with a marker and the parent is never opened (1.280591ms) +✔ F12: two leaves: the default leaf is shown and the other branch reads alone (8.701968ms) +✔ F12: a follow refuses when an appended duplicate id changes the branch's earlier parts (3.047678ms) +✔ F12: a second root (Pi's resetLeaf) starts its own branch (2.529818ms) +✔ F13: compaction is a marker in place, then the retained content (1.200056ms) +✔ F14: long strings split into fragments and parts, reassemble exactly, and pages respect the byte cap (927.08769ms) +✔ fragments never cut a surrogate pair and keep an empty string (6.246556ms) +✔ F15: a Claude seat is an unsupported-harness placeholder whose directory is never read (1.891982ms) +✔ unknown conversations, empty files and non-Pi files refuse (3.354143ms) +✔ an unreadable file or root inside the roots is refused per row, not a failed catalogue (1.403634ms) +✔ a seat directory without search permission refuses that root, not the catalogue (2.386158ms) +✔ every page and cursor is a valid CHAT-01 record (963.005061ms) +✔ the engine pin holds for the installed package (4.64981ms) +✔ pinned Pi, sealed and without credentials, answers the controller's commands with the shapes the fake models (492.509878ms) +✔ pinned Pi appends thinking_level_change at start when the branch lacks one, so the leaf moves (K8 then fails closed) (449.300806ms) +✔ N25: ordinary Interrupt reconciles; a non-empty queue_update in the window is O5 (99.699963ms) +✔ N1: an extension's follow-up queued after the fence is cleared before any abort; O5, Unknown (56.005591ms) +✔ N1: a follow-up queued before the fence is O5 at once; the Interrupt refuses fenced (36.010734ms) +✔ N2: with abort first, the fake runs the external item (the ordering guard has teeth) (23.120368ms) +✔ N3: the fence lands in preflight, preflight errors, no run: failed, No run, uncertain (40.605984ms) +✔ N4: the ack arrives after the first abort and a run starts: clear and abort again; Interrupted (32.198961ms) +✔ N5: an input handler takes the prompt: ack, no run, delivery-unknown handled-without-run (129.781085ms) +✔ N6: an extension queues between clear_queue and abort: O5 and O6, Unknown (60.975823ms) +✔ N7: clear_queue times out: no abort, nativeQueue unknown, force stop still ends it (1540.123798ms) +✔ N7: clear_queue answers an error: no abort, nativeQueue unknown, the link not poisoned (17.607252ms) +✔ N8: an extension prompt starts a run during Mosaic preflight; the losing settle is O3 (71.219891ms) +✔ N9: a run that started before the fence and ends aborted: failed interrupted, Interrupted (14.260687ms) +✔ N9: decision 34: a run that ends aborted with no stop in progress: aborted-without-stop, uncertain, outcome unknown (19.355928ms) +✔ N9: an aborted that lands after the fence but before any abort is written: aborted-without-stop, Unknown (40.425771ms) +✔ N10: fake conformance (34.674337ms) +✔ N11: the run fails before any user message_start: delivery-unknown ack-without-start, never failed (45.385869ms) +✔ N12: input that starts a run after the final empty clear is O1 and not part of the stop's proof (26.329147ms) +✔ N13: agent_start with no slot held is O1; a later prompt refuses with zero engine bytes (70.140045ms) +✔ N14: the run completes while clear_queue is in flight: finished, Completed first, uncertain (30.960887ms) +✔ N14: the run completes after the abort is written, before Pi applies it: finished, never relabelled (33.997394ms) +✔ N15: the fence lands in preflight, then an input handler takes it: handled-without-run, No run (24.416409ms) +✔ N16: Interrupt with no slot and no run refuses no-turn: no stop, no bytes, admission open (13.466921ms) +✔ N17: the run fails on its own during the exchange: failed, Failed on its own (26.194657ms) +✔ N18: no final assistant message_end, or a lost line: working stays working; before working, transport-unknown (105.895233ms) +✔ N19: a losing extension prompt settles inside the Mosaic run before its user message: O3, run-overlap (137.573966ms) +✔ N20: an extension triggerTurn during Mosaic preflight starts first; while streaming it queues with no signal (85.246925ms) +✔ N21: a losing settle after the receipt settled finished is O2; the receipt stays finished (16.090188ms) +✔ N22: an agent-level custom message is dropped by the clear with no signal; evidence names the seal (14.423581ms) +✔ N23: a nextTurn message survives clear and abort and attaches to the next prompt, with no signal (14.347618ms) +✔ 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 (44.072894ms) +ℹ tests 152 +ℹ suites 0 +ℹ pass 149 +ℹ fail 3 +ℹ cancelled 0 +ℹ skipped 0 +ℹ todo 0 +ℹ duration_ms 32310.893453 + +✖ failing tests: + +test at packages/conversation/tests/cohort.test.mjs:139:1 +✖ K1: force stop kills a tool child that called setsid; stopped with a verified proof (2653.677511ms) + AssertionError [ERR_ASSERTION]: the escaped child is a listed member + at TestContext. (file:///home/jwoltje/filbert-scratch/r45/base/packages/conversation/tests/cohort.test.mjs:151:12) + at async Test.run (node:internal/test_runner/test:1409:7) + at async startSubtestAfterBootstrap (node:internal/test_runner/harness:387:3) { + generatedMessage: false, + code: 'ERR_ASSERTION', + actual: false, + expected: true, + operator: '==', + diff: 'simple' + } + +test at packages/conversation/tests/cohort.test.mjs:176:1 +✖ K3: SIGTERM acknowledged while a member lives: stopping until the kill phase, never stopped from TERM (2165.26786ms) + AssertionError [ERR_ASSERTION]: the member ignored TERM + at TestContext. (file:///home/jwoltje/filbert-scratch/r45/base/packages/conversation/tests/cohort.test.mjs:190:12) + at async Test.run (node:internal/test_runner/test:1409:7) + at async Test.processPendingSubtests (node:internal/test_runner/test:974:7) { + generatedMessage: false, + code: 'ERR_ASSERTION', + actual: false, + expected: true, + operator: '==', + diff: 'simple' + } + +test at packages/conversation/tests/cohort.test.mjs:426:3 +✖ K10: controller killed between the TERM and kill phases: restart checks the invocation ID and re-runs from TERM for the same stop (180.585304ms) + AssertionError [ERR_ASSERTION]: the member is alive across the crash + at TestContext. (file:///home/jwoltje/filbert-scratch/r45/base/packages/conversation/tests/cohort.test.mjs:431:12) + at process.processTicksAndRejections (node:internal/process/task_queues:104:5) + at async Test.run (node:internal/test_runner/test:1409:7) + at async Test.processPendingSubtests (node:internal/test_runner/test:974:7) { + generatedMessage: false, + code: 'ERR_ASSERTION', + actual: false, + expected: true, + operator: '==', + diff: 'simple' + } diff --git a/agents/filbert/work/s4-follow-up-review/r1-base-node-discord.txt b/agents/filbert/work/s4-follow-up-review/r1-base-node-discord.txt new file mode 100644 index 00000000..fced429c --- /dev/null +++ b/agents/filbert/work/s4-follow-up-review/r1-base-node-discord.txt @@ -0,0 +1,186 @@ +✔ approvals: a request is validated before anything is posted; the rendering shows names and never ids (3.201101ms) +✔ approvals: the ledger is appended and folded into open requests with bind and approval states (1.660034ms) +✔ approvals: a reply approves only when it points at a request, says exactly approve, and comes from a listed approver once (0.580701ms) +✔ approvals: a button approves only on its own request message with the matching custom id (0.486235ms) +✔ approvals flow: a turn that opened a request posts the message with the button, records it, binds it, and both approvers approve (21.333687ms) +✔ 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.393055ms) +✔ approvals flow: an invalid request from the model, a refused post, and no api client are recorded and post nothing (9.050164ms) +✔ approvals flow: start retries a bind and an approval left as unknown, under their original keys (2.631665ms) +✔ authorize: open channel, listed user (1.952524ms) +✔ authorize: wrong guild (0.194831ms) +✔ authorize: no guild (DM) (0.147706ms) +✔ authorize: unlisted channel (0.285655ms) +✔ authorize: unknown channel, no info (0.196753ms) +✔ authorize: thread of listed parent (0.435598ms) +✔ authorize: thread of unlisted parent (0.164288ms) +✔ authorize: text channel that is not a thread and not listed (0.135153ms) +✔ authorize: unlisted user (0.160797ms) +✔ authorize: no author (0.24555ms) +✔ authorize: bot author (listed id, bot flag) (1.405614ms) +✔ authorize: system author (0.126607ms) +✔ authorize: the bot itself (0.082559ms) +✔ authorize: webhook (0.078451ms) +✔ authorize: mention channel without mention (0.129292ms) +✔ authorize: mention channel with bot mention (0.130115ms) +✔ authorize: mention channel with @everyone only (0.138676ms) +✔ authorize: mention channel mentioning someone else (0.08371ms) +✔ authorize: mention channel, content says @bot but mentions empty (0.097637ms) +✔ authorize: private thread under mention channel, mentioned (0.089673ms) +✔ authorize: private thread under mention channel, not mentioned (0.077673ms) +✔ authorize: thread in another guild per channel info (0.128817ms) +✔ authorize: not an object (0.062853ms) +✔ authorize: no id (0.161612ms) +✔ authorize: oversize content is accepted and flagged (0.070759ms) +✔ authorize: exactly the limit is not oversize (0.058304ms) +✔ authorize: a user's channel allowlist drops them outside it, threads count as the parent, others are unaffected (0.476843ms) +✔ authorize: order puts wrong guild before user, and user before channel (no channel lookup for strangers) (0.126935ms) +✔ binding: a complete binding validates and is frozen (4.063052ms) +✔ binding: unknown key, missing field, wrong type refuse with exit 2 (1.558439ms) +✔ binding: empty allowlists refuse (0.781921ms) +✔ binding: a user's channel allowlist must be non-empty, listed and unique; absent means every listed channel (1.743709ms) +✔ reloadDiff: reloadable keys are summarised by id; every fixed key refuses with exit 2 (2.132428ms) +✔ binding: dmRecipient is optional, a snowflake, one of the listed users, and a fixed key (0.614055ms) +✔ binding: file must be 0600, regular, not a symlink (2.040162ms) +✔ binding: token file mode, symlink, emptiness and shape are checked; token never appears in errors (1.347954ms) +✔ cli: check refuses a non-0600 token file with exit 2 before any network use (84.997781ms) +✔ context files: absolute paths, traversal, symlinks and out-of-repo targets refuse; in-repo files resolve (1.854618ms) +✔ cli: check refuses a missing context file and a missing binding with exit 2; usage is exit 4 (312.162463ms) +✔ cli: reload validates the file first (exit 2), then needs a live owner (exit 1); usage is exit 4 (200.198135ms) +✔ cli: run refuses when STOP is present, before any network use (116.785106ms) +✔ binding: tools is optional, validated strictly, a fixed key for reload, and its roots are resolved against the data root (1.681435ms) +✔ binding: a git key is validated at load and reaches the extension whole, and only on a writable root (1.164195ms) +✔ 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 (25.061237ms) +✔ delivery: refused and unknown outcomes are journaled; a later chunk is not sent after a failure (23.476294ms) +✔ delivery: restart with an unknown entry re-sends the same nonce once and reconciles before accepting traffic (1.371221ms) +✔ delivery: an unknown entry older than the dedupe window is marked refused, not re-sent; a still-unknown one refuses start (1.19478ms) +✔ delivery: repeated unknown reconciliations never refresh the dedupe window; the original intent time decides (1.298255ms) +✔ turn: a failed engine turn posts the fixed line, never model output, and writes a failed record (2.87817ms) +✔ turn: a second message during a turn is held by the engine, both get their own reply and record (32.869668ms) +✔ turn: a thread under a listed channel is answered in the thread; an unknown thread is looked up once (2.761047ms) +✔ drop: an unlisted user gets silence and one drop line; no inbox entry, no REST call, no engine call (0.75218ms) +✔ drop: an oversize message is accepted into the inbox, answered with the fixed line and journaled as a drop (0.78081ms) +✔ restart: an inbox with three ids and a replay of the same three produces zero turns (40.645485ms) +✔ stop: STOP present refuses start; STOP written while running refuses new turns and the current one finishes (31.614341ms) +✔ ceiling: the ceiling plus one is refused and journaled; one fixed line per UTC day; a new day accepts again (4.192219ms) +✔ ceiling: a burst arriving while turns are still running cannot queue past the ceiling (3.866001ms) +✔ ceiling: a turn interrupted by a crash still counts after restart; admissions are durable (2.168526ms) +✔ ceiling: the daily notice survives a same-day restart; one delivery attempt in total, even when the first attempt crashed mid-flight (4.138629ms) +✔ duplicate: the same event delivered twice while the thread lookup is held yields one prompt, one admission and one reply (1.999622ms) +✔ journal: no token-shaped string and no model output on the drop path reaches disk (0.428104ms) +✔ 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.076407ms) +✔ receipt: Discord refusing the reaction leaves the turn intact and records ok false (1.717986ms) +✔ reload: a new user is silent before and answered after; a removed channel goes silent; a lower ceiling applies at once (3.409683ms) +✔ reload: a fixed key refuses with exit 2 and the old binding stays in force (2.044094ms) +✔ tools: with a tools binding the turn record lists every read and its outcome; without one the field is null (3.595509ms) +✔ context: the Discord block names the server, channels and modes, and states the rules from Q15 and Q16 (2.590484ms) +✔ context: with tools the block names the roots, keeps file content as data, and says to state refusals plainly (0.907284ms) +✔ context: a writable root adds the write rules and says a write is real only once Jason commits (2.336321ms) +✔ context: the envelope is one bracketed line then the text; names cannot break the line (1.24491ms) +✔ context: a git root swaps the terminal-commit line for the git verbs, and a vault root adds the id protocol (1.4446ms) +✔ context: assembleContext concatenates files in launcher format and appends the block; sha256 is stable (1.222964ms) +✔ context: splitReply keeps paragraphs together under the limit and splits long ones at lines, spaces, then hard (0.652689ms) +✔ engine: buildPiArgs carries the fixed flags, engine settings, session dir and prompt file (2.596485ms) +✔ engine: with tools, buildPiArgs turns pi's own tools off, loads the extension explicitly and allowlists exactly our three (2.135157ms) +✔ engine: a run with tool turns settles once, on the answer, with every tool call in the result (60.544838ms) +✔ engine: a run that ends on a tool-only turn fails the prompt as empty; a retried run settles on the real end (35.320946ms) +✔ engine: one prompt, one turn, text and usage come back (29.839246ms) +✔ engine: a prompt while streaming is held until pi settles, then sent as its own run, and answered in order (333.239021ms) +✔ engine: a held prompt that times out before pi settles fails on its own and is never sent (229.917302ms) +✔ engine: timeout sends abort and fails only that turn; the process stays (104.025224ms) +✔ engine: tool events from a run that outlived its timeout never land in the next prompt's record (228.179554ms) +✔ engine: a prompt after a turn that timed out before its agent_start waits for pi to settle instead of being refused (132.927031ms) +✔ 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.653029ms) +✔ engine: a timed-out turn pi starts only after the grace never answers a later prompt (614.160788ms) +✔ engine: late events of a run past its grace, before pi exits, answer nothing and nothing more is sent (early prompt response) (1.627204ms) +✔ engine: late events of a run past its grace, before pi exits, answer nothing and nothing more is sent (late prompt response) (0.520418ms) +✔ engine: a timed-out run pi did start outlives the grace; the next prompt goes out when it ends (429.886242ms) +✔ engine: a malformed JSONL line fails the turn, not the process (27.00359ms) +✔ engine: a turn that ends in error rejects with the error code; process exit fails pending turns (46.705536ms) +✔ gateway: hello -> identify with intents, ready, heartbeat with jitter, ack (2.638535ms) +✔ gateway: missed ack closes the socket and resumes with the last sequence (1.923849ms) +✔ gateway: op 7 reconnect resumes; op 9 non-resumable re-identifies (0.632194ms) +✔ gateway: op 9 resumable resumes (0.429861ms) +✔ gateway: close 4014 is fatal, reports the missing intent, never reconnects (0.788995ms) +✔ gateway: 4004 and 4013 are fatal too; 1006 reconnects with identify when no session (1.041148ms) +✔ gateway: close() is final and unparseable frames are ignored (0.397092ms) +✔ git: config validation is strict, needs write: true, a work tree and a private token file (71.308392ms) +✔ git: the child environment drops every host git config, names one helper, and carries the token path only for origin (37.288053ms) +✔ git: status reports the branch, ahead/behind and changed paths, and refuses off the named branch or mid-merge (65.469921ms) +✔ git: parseStatus reads porcelain v2 including renames and conflicts (0.257703ms) +✔ git: a commit stages exactly the named files, carries the seat author and the requester trailer, and pushes at once (79.837685ms) +✔ git: commit refusals: message, paths, requester, nothing to commit, and an index that already holds other work (85.969098ms) +✔ git: a commit whose push fails is still a commit, says so, and the next commit's push carries both (D6) (96.306132ms) +✔ git: pull is fast-forward only; a diverged origin or dirty local files refuse with nothing merged (199.121223ms) +✔ git: push pushes the named branch only and reports up to date (61.956937ms) +✔ git: no token value or token path ever reaches a git argument list; outputs are masked and capped (86.647333ms) +✔ git: the credential helper answers get over https from a private file and nothing else (210.101951ms) +✔ git: the vault protocol validates before a commit, honours another owner's lock, reserves ids, and locks around writes (749.416922ms) +✔ lock: the claim is exclusive; a second start against a live owner refuses (5.298493ms) +✔ lock: a stale lock (dead owner, reused pid, or record without start) refuses run and is never signaled; only unlock clears it (5.429571ms) +✔ lock: an incomplete claim (directory without owner record) is busy and refuses run; unlock clears it (1.017847ms) +✔ lock: an owner record that exists but cannot be read is invalid: never signaled, never removed, never claimed over (2.535219ms) +✔ lock: legacy upgrade; a live connector holding a {pid, start} record is unknown, unlock refuses and nothing changes; after it exits, unlock clears it (53.217434ms) +✔ 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 (292.904169ms) +✔ lock: identity syntax; only canonical unsigned decimal start ticks and lowercase boot uuids are identities (0.386939ms) +✔ lock: a process whose start marker or boot id cannot be read refuses to claim (0.427908ms) +✔ lock: a live pid whose identity cannot be read right now is unknown: never signaled, never removed, never claimed over (0.851219ms) +✔ lock: four processes racing for the same binding; exactly one claims it and the others refuse (39.514684ms) +✔ lock: stale handoff; concurrent starts over a stale lock all refuse, nothing reclaims, one unlock then exactly one live owner (113.625484ms) +✔ lock: four-party schedule; claims landing inside an unlock's gap never survive, one unlock leaves no owner and no residue (65.633357ms) +✔ notices: a kind is recorded per UTC day and found again (0.464313ms) +✔ notify: the DM channel opens once, every send carries the nonce, and only the message id comes back (5.44218ms) +✔ notify: refusals and unknowns surface as RestOutcome without ids; a refused send reopens the channel next time (2.120122ms) +✔ notify: no dmRecipient, a non-0600 binding or token, or a missing binding refuse before any network use (2.201331ms) +✔ recover: nothing to do is clean; a lock whose owner is gone or that has no record is cleared and STOP ends up absent (65.287043ms) +✔ recover: an operator STOP refuses with exit 3 and is never removed, whatever the lock says (43.180652ms) +✔ recover: a brake written during the unlock wins; STOP stays with both lines and the start is refused (26.948772ms) +✔ recover: a held binding refuses with exit 3 and writes no STOP: live owner, alive pid without verifiable identity, unreadable record (64.561167ms) +✔ cli: recover exits 0 when ready, 3 behind a brake or a held binding, and run's own STOP refusal is 3 (658.809302ms) +✔ rest: createMessage sends nonce, enforce_nonce, empty allowed_mentions and a soft reply reference (2.434973ms) +✔ rest: 429 waits retry_after and retries; 4xx is refused; 5xx and socket errors are unknown (3.239627ms) +✔ rest: content and nonce limits are enforced locally; typing never throws (0.599442ms) +✔ rest: react PUTs the encoded emoji on the inbound message for @me; 2xx is true, anything else is false and never throws (0.695726ms) +✔ rest: createDm posts the recipient and resolves the channel id; 4xx refused, 5xx unknown, bad id never sent (1.531794ms) +✔ setspark config: a bare https or loopback origin, a private key file, a principal (5.320663ms) +✔ setspark config: reaches the tools config and the binding as a fixed key (2.450232ms) +✔ setspark config: the binding's key survives resolveToolRoots and the engine's JSON hand-off to the extension (1.459636ms) +✔ setspark config: approvers come from the binding's users, never from the binding's setspark key (1.677375ms) +✔ setspark verbs: required_approvers go out as discord ids from names and come back as names (22.100773ms) +✔ setspark verbs: no Discord user id reaches tool text, whatever shape the service returns it in (8.043384ms) +✔ setspark contract: a decision made with names opens a request the connector accepts; names stored by an old record still refuse (6.883186ms) +✔ setspark keys: read per call, one printable token per file, rotation without a restart (4.004438ms) +✔ setspark idempotency keys: principal, turn id, call index; connector keys name a step (0.603316ms) +✔ setspark http core: json in and out, bearer header, idempotency header, fixed user agent, no key anywhere else (2.745332ms) +✔ setspark http core: error bodies become fixed refusals with code and the 409 fields; server text is data, cut (1012.566197ms) +✔ setspark verbs: a setspark key enables the eight verbs and no counters (0.527999ms) +✔ 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.51347ms) +✔ setspark verbs: no turn refuses every write before any request; bad arguments refuse before any request; reads still work (1.238868ms) +✔ setspark verbs: renderRecord caps long output and hides the accepted snapshot (0.210266ms) +✔ setspark api: bind, add_approval (button and reply) and get use integer request ids and the connector's keys (2.395749ms) +✔ tools: config refuses a missing, symlinked, dotted, non-directory or duplicate root and bad limits (4.409164ms) +✔ tools: every escape is refused with a fixed reason and nothing outside the root is read (5.544056ms) +✔ tools: happy paths list, read a window, and search case-insensitively; dotfiles and symlinks never appear (5.663439ms) +✔ tools: the tool set renders text for the model, records details for the journal, and enforces the per-run budget (4.490327ms) +✔ tools: listing and search caps hold (9.712986ms) +✔ tools: credential shapes are caught; ordinary prose and ids are not (0.91641ms) +✔ 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.396493ms) +✔ tools: an unreadable file under the root is skipped by search and refused by read (1.118591ms) +✔ tools: config accepts write: true only as a boolean, and enables the write tools only then (1.239234ms) +✔ tools: every write outside the fence is refused before any byte lands, and no temp file remains (5.472032ms) +✔ tools: write_file leaves the exact bytes, edit_file replaces one exact match, and the set renders the change as uncommitted (2.128317ms) +✔ tools: a target that changed between the check and the rename is refused and the temp file is removed (1.204303ms) +✔ web: config takes an https or loopback-http SearXNG base url and a bounded fetch cap (4.119455ms) +✔ web: address rules refuse every private, loopback, link-local, mapped and multicast form (3.260389ms) +✔ web: web_fetch refuses bad urls, private hosts, rebinding names, non-https redirects, too many hops, error status, non-text bodies, and times out (1026.63004ms) +✔ 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.242048ms) +✔ web: html to text drops scripts, styles and comments, decodes entities and keeps block breaks (0.258013ms) +✔ 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.624952ms) +✔ 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.303252ms) +ℹ tests 178 +ℹ suites 0 +ℹ pass 178 +ℹ fail 0 +ℹ cancelled 0 +ℹ skipped 0 +ℹ todo 0 +ℹ duration_ms 2634.058891 diff --git a/agents/filbert/work/s4-follow-up-review/r1-base-node-ledger.txt b/agents/filbert/work/s4-follow-up-review/r1-base-node-ledger.txt new file mode 100644 index 00000000..f901b387 --- /dev/null +++ b/agents/filbert/work/s4-follow-up-review/r1-base-node-ledger.txt @@ -0,0 +1,86 @@ +✔ a raw token file, with or without one trailing newline, reaches curl only through the config stream (138.671771ms) +✔ the raw path accepts nothing else, and refuses before curl runs (423.976474ms) +✔ the file checks still apply on the raw path: mode, symlink, missing, directory (306.653279ms) +✔ the raw path base URL has no override (68.146647ms) +✔ the JSON path is unchanged, and JSON never falls through to the raw path (268.204784ms) +✔ a file that changes between the two reads refuses before curl runs, with or without a body (796.716083ms) +✔ the token reaches no child environment, even with an inherited CFG or SHELLOPTS=allexport (747.472393ms) +✔ real helper GET HTTP 200 preserves exit 0 without credentials (18.039884ms) +✔ real helper POST HTTP 201 preserves exit 0 without credentials (12.248504ms) +✔ real helper GET HTTP 403 preserves exit 1 without credentials (11.836651ms) +✔ fixture git subjects only, follow-ups and three session kinds (119.607682ms) +✔ text and JSON carry same numbers, open and truncated title (186.370317ms) +✔ missing credentials exit 2, no-issues never calls API and shows unknown (169.209561ms) +✔ empty range gives no rows and zero totals (109.226221ms) +✔ inclusive UTC dates, first-line preamble only, role and seat boundaries (113.45241ms) +✔ close-only issue included, even median, missing metadata stays unknown (113.56368ms) +✔ unique commits but per-issue links count multiple tags once each (112.873951ms) +✔ page cap refuses rather than silently undercounting (101.322696ms) +✔ bad API payload not JSON refuses (96.714416ms) +✔ bad API payload {} refuses (94.745159ms) +✔ bad API payload [{"number":1}] refuses (89.245405ms) +✔ partial or malformed session log refuses with location, not content (101.876415ms) +✔ a U+2028 or U+2029 inside a session string is one line, not a malformed record (132.282475ms) +✔ no sessions is an empty table; symlink source refuses (192.239786ms) +✔ reads only refactor even when another branch is checked out (105.435584ms) +✔ invalid dates, reverse dates and duplicate options refuse (70.488615ms) +✔ T3 agent assignments do not count as human in Table 2 (98.640321ms) +✔ preamble parsing and issue number boundaries (0.473611ms) +✔ T3 header: agent, or board from control-board; anything short of the full header is human (0.160889ms) +✔ no closed issues with human messages means undefined ratio, not invented zero (0.230338ms) +✔ T3: seat, archived, unmapped and Researcher threads count; imported, deleted and other-project threads do not (196.682254ms) +✔ T3: the default path is read from HOME and prints no path line; --no-t3 says so (472.742418ms) +✔ T3: a HOME with no database exits 1 and names --no-t3 (88.149974ms) +✔ T3: a file that is not a database exits 1 and names --no-t3 (94.805533ms) +✔ T3: a seat thread renamed to another seat exits 1 naming thread, title and roles (125.975358ms) +✔ T3: an unmapped thread addressed as a seat exits 1 (121.305514ms) +✔ T3: a header to another thread id is not cross-checked (123.101002ms) +✔ T3: no project, or two, for this root exits 1 (307.420146ms) +✔ T3: a removed column exits 1 and names it (103.087633ms) +✔ T3: a missing table exits 1 and names it (112.352744ms) +✔ T3: a counted row with an unknown role exits 1 without its text (134.640199ms) +✔ T3: a counted row with non-text content exits 1 without its text (134.30338ms) +✔ T3: a counted row with an unparseable created_at exits 1 without its text (124.285473ms) +✔ T3: a missing orchestration_events makes the diagnostic unknown and keeps the counts (228.991249ms) +✔ T3: a human message with no event counts in humanWithoutEvent (127.281084ms) +✔ T3: an unparseable event makes the diagnostic unknown and keeps the counts (224.491946ms) +✔ T3: an event with no string messageId makes the diagnostic unknown and keeps the counts (240.564015ms) +✔ T3: an error that is not from SQLite is rethrown, not reported as a database failure (58.530531ms) +✔ T3: a symlink at ~/.t3 exits 1 (90.708515ms) +✔ T3: a symlink at ~/.t3/userdata exits 1 (85.405863ms) +✔ T3: a symlink at ~/.t3/userdata/state.sqlite exits 1 (87.869959ms) +✔ T3: with --t3-db, a symlinked file or directory exits 1 (172.781915ms) +✔ T3 WAL: the newest message only in -wal, writer attached, is counted (134.769444ms) +✔ T3 WAL: stopped cleanly, counts are correct and the main file is unchanged (123.840167ms) +✔ T3 WAL: -wal without -shm in a writable directory is read (141.073277ms) +✔ T3 WAL: -wal without -shm in a read-only directory exits 1 (134.623712ms) +✔ T3 WAL: stopped cleanly in a read-only directory exits 1 (116.480721ms) +✔ T3: a lock held past the 5 s busy timeout exits 1 and names --no-t3 (5123.515152ms) +✔ a done row whose closing issue is open is a violation; a row that is not done is not (2.262245ms) +✔ an issue several rows close is expected closed only once all of them are done (0.636824ms) +✔ closure needs positive evidence: unknown is undecided, and so is a skipped or short issue check (0.465932ms) +✔ each owner of an in-progress or in-review row gets one liveness class (8.945669ms) +✔ a required row not done after 14 days is a violation; a legacy row uses genesis as its lower bound (0.42539ms) +✔ an ISO requiredSince, as `set required` writes it, ages from its UTC day; one that does not parse is a violation (0.251321ms) +✔ the text section always ends in a count and a result, and never prints a full pass (0.389998ms) +✔ pidAlive: a running pid is present, an exited one is gone, and EPERM still means present (30.271711ms) +✔ issue states: open list first, then the metric page, then at most 10 lookups (318.077146ms) +✔ a full open list: lookups settle what it leaves out, and only an unsettled issue keeps it undecided (419.682997ms) +✔ the open list refuses on a failed call or a bad record, and never echoes the helper (186.450359ms) +✔ a helper call past the deadline is killed with its child, and the call reports it (2007.63492ms) +✔ readQueue loads queue.json through the queue validator and refuses anything else (83.063941ms) +✔ protected changes list every in-range entry that changes a required or parked row (260.831405ms) +✔ the CLI prints the queue section above the weekly table and under a queue key in --json (522.539179ms) +✔ a queue with nothing wrong prints 0 violations and a reduced pass, never a full pass (222.158594ms) +✔ --no-issues makes no call and leaves the issue checks undecided; --no-queue skips the section (282.600961ms) +✔ the CLI refuses a bad queue before any call, and a failed open list with exit 2 (253.421151ms) +✔ --unsupported-runtime repeats once per seat and takes a seat name (327.815403ms) +✔ an unreadable config makes every owner invalid instead of passing them (132.587734ms) +ℹ tests 78 +ℹ suites 0 +ℹ pass 78 +ℹ fail 0 +ℹ cancelled 0 +ℹ skipped 0 +ℹ todo 0 +ℹ duration_ms 11297.728644 diff --git a/agents/filbert/work/s4-follow-up-review/r1-base-node-mosaic.txt b/agents/filbert/work/s4-follow-up-review/r1-base-node-mosaic.txt new file mode 100644 index 00000000..24955489 --- /dev/null +++ b/agents/filbert/work/s4-follow-up-review/r1-base-node-mosaic.txt @@ -0,0 +1,77 @@ +✔ pure resolution selects current default or explicit enrolled account (2.079609ms) +✔ scope is explicit, bounded and never inferred (2.230143ms) +✔ fork pin is preserved against default change, override, missing account and revocation (0.849241ms) +✔ unenrolled account/provider, missing harness, model expansion and native model ceiling refuse (0.911311ms) +✔ only explicit synthetic credential forms and internal fixture stores admitted (6.641375ms) +✔ two concurrent workspaces of the same agent publish distinct complete private generations (61.909614ms) +✔ same execution ID is exclusively claimed and cannot overwrite a published generation (34.701654ms) +✔ failed generation after-auth preserves prior files, records failure and refuses blind same-ID retry (47.614211ms) +✔ failed generation before-publish preserves prior files, records failure and refuses blind same-ID retry (61.009733ms) +✔ credential lock contention refuses without duplicate side effects (12.128114ms) +✔ symlinked pre-existing final target is refused and never followed (27.71374ms) +✔ invalid registry cannot resolve; no fallback to supplied partial entries (0.296627ms) +✔ post-publication failure records uncertainty, preserves complete generation and prevents replay (32.444858ms) +✔ expired credentials refresh under transaction and subsequent generation reuses rotation (88.493926ms) +✔ refresh failure retains prior generation and store state (70.906183ms) +✔ refresh timeout retains prior generation and store state (150.598599ms) +✔ refresh malformed retains prior generation and store state (67.369629ms) +✔ concurrent refresh on same account refuses contention while unrelated account proceeds (217.751194ms) +✔ invalid refresh options refuse before burning claim (36.896252ms) +✔ fixed fake process rotates both OAuth fields without mutating caller input (35.708505ms) +✔ concurrent isolated processes preserve separate provider credentials (28.15465ms) +✔ fake failure is refused with fixed diagnostics (25.606812ms) +✔ fake malformed is refused with fixed diagnostics (24.03472ms) +✔ fake timeout is refused with fixed diagnostics (104.074695ms) +✔ fake unchanged is refused with fixed diagnostics (30.105534ms) +✔ caller executable/environment injection is rejected before spawning (0.380258ms) +✔ valid fixture tree validates and lists without secrets (78.529984ms) +✔ unknown-field refuses (0.383423ms) +✔ invalid-id refuses uppercase and traversal shapes (0.285878ms) +✔ plain-http baseUrl requires allowInsecureTransport (0.304645ms) +✔ native provider rejects allowInsecureTransport (0.134402ms) +✔ unsupported credential type and kind refuse (0.155521ms) +✔ account provider-path mismatch refuses (0.1092ms) +✔ profile account refs must be provider/account shaped (0.247616ms) +✔ seat selection accepts fork pin field, validates account refs (0.252071ms) +✔ harness manifest id must equal executable (gate 1) (0.217346ms) +✔ CLI validate: duplicate provider id across files refuses (30.167855ms) +✔ CLI validate: missing referenced provider/account refuse (34.544812ms) +✔ CLI validate: broken JSON refuses without secret echo (29.453698ms) +✔ CLI usage errors exit 2 (52.764154ms) +✔ credential.json sibling presence does not break validation and is never read (74.009646ms) +✔ D1 missing, empty and structurally empty roots refuse, no list projection (195.778149ms) +✔ D1 required directory auth cannot be absent (64.656927ms) +✔ D1 required directory auth/providers cannot be absent (67.184602ms) +✔ D1 required directory auth/accounts cannot be absent (65.462742ms) +✔ D1 required directory auth/settings cannot be absent (61.524711ms) +✔ D1 required directory harnesses cannot be absent (64.933926ms) +✔ D1 root file and unreadable metadata refuse (126.575629ms) +✔ D2 no symlink traversal at auth/providers/openai-codex.json (61.71521ms) +✔ D2 no symlink traversal at auth/accounts/openai-codex/homelab-openai (67.898272ms) +✔ D2 no symlink traversal at auth/providers (52.901333ms) +✔ D2 no symlink traversal at auth (57.489447ms) +✔ D2 root and ancestor symlinks and lexical traversal refuse (186.814266ms) +✔ private filesystem modes enforced for root (62.871372ms) +✔ private filesystem modes enforced for auth (55.863789ms) +✔ private filesystem modes enforced for auth/providers/openai-codex.json (56.804104ms) +✔ private filesystem modes enforced for auth/accounts/openai-codex/homelab-openai/account.json (63.768965ms) +✔ D3 numeric version 1 only across all record kinds (1.309762ms) +✔ D4 nested unknown keys and missing per-kind required fields refuse (65.515115ms) +✔ D5 unenrolled default refuses even when account exists (64.050411ms) +✔ D6 provider/account credential type must match (64.344785ms) +✔ D7 every harness endpoint enforces HTTP opt-in and shape (0.459451ms) +✔ D8 URLs reject embedded credentials and unsupported protocols without echo (165.076039ms) +✔ D9 malformed JSON diagnostics contain no content excerpt (64.218719ms) +✔ D10 missing metadata is missing-path, not invalid-json (62.667729ms) +✔ D10 library returns no partial entries on any invalid record (72.329981ms) +✔ null/scalar/array metadata refuses without stack or echo (254.035605ms) +✔ credential sibling is never opened, even when an unreadable symlink (35.816416ms) +✔ oversized metadata refuses before parsing (59.942545ms) +ℹ tests 69 +ℹ suites 0 +ℹ pass 69 +ℹ fail 0 +ℹ cancelled 0 +ℹ skipped 0 +ℹ todo 0 +ℹ duration_ms 2279.776148 diff --git a/agents/filbert/work/s4-follow-up-review/r1-base-node-queue.txt b/agents/filbert/work/s4-follow-up-review/r1-base-node-queue.txt new file mode 100644 index 00000000..20fef221 --- /dev/null +++ b/agents/filbert/work/s4-follow-up-review/r1-base-node-queue.txt @@ -0,0 +1,158 @@ +✔ F1: an ordinary commit after update-ref is refused until step 8; then it commits and the queue stays at C (1293.447642ms) +✔ F1: a plain `commit -e` whose guard ran before update-ref fails at its own HEAD update (1101.083362ms) +ℹ 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 (1074.456052ms) +ℹ 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 (1122.393406ms) +✔ F1: HEAD moving after the step-7 guard check and before update-ref: refused, nothing published (1503.485391ms) +✔ F1: H is recorded before the canary, so HEAD moving during the step-1 canary is refused at step 7 (1079.455381ms) +✔ F1: a queue commit landing after H is recorded: step 1 says HEAD moved, not the guard (1569.18684ms) +✔ F1: a queue commit landing between the HEAD check and the canary: the failed clean run is reported as HEAD moved (1158.207634ms) +✔ F1: a shared-index change during the procedure is not committed (1264.74922ms) +✔ F1: a queue path staged after update-ref: step 8 stops and touches nothing (1315.032514ms) +✔ F1: a missing or a different hook refuses (834.923829ms) +✔ F1: same bytes without the exec bit, a symlinked hook, and core.hooksPath in the local or global scope each refuse before update-ref (1402.70655ms) +✔ F1: the canary refuses a hook that git would not run (785.295278ms) +✔ F1: the guard deactivated after step 1 is refused at the step-7 recheck (1293.939835ms) +✔ bootstrap: implementation-only HEAD, the guard, genesis, the --genesis commit, then an extending commit (1068.985862ms) +✔ bootstrap: --genesis with a base present, no base without --genesis, an op before the first commit, a changed map, another branch (1328.258272ms) +✔ bootstrap: the archived tests and validator run outside any repository (1203.511963ms) +✔ general: an unrelated staged file stays staged, and the committed blobs are the snapshot bytes (1164.216663ms) +✔ general: a queue write after the snapshot is not committed (1266.8914ms) +✔ general: a snapshot whose log does not extend the base refuses (975.109162ms) +✔ general: install-hook privilege, repair of a same-bytes hook, and its refusals (207.935884ms) +✔ general: environment overrides, a linked worktree and usage (612.446006ms) +✔ general: a queue path staged before the run refuses at step 1 (711.225406ms) +✔ general: HEAD's queue tests failing in the archive refuse (959.330303ms) +✔ genesis document serializes deterministically and replays (4.317024ms) +✔ a hand edit that stays valid JSON fails replay; a formatting-only edit fails re-serialization (2.796549ms) +✔ a tampered result, receipt or viewSha fails replay (2.483399ms) +✔ op ids: 8 to 72 characters for callers, 80 in the log for .outcome entries (0.194516ms) +✔ add: defaults for an ordinary seat, privileged extras, refusals (4.962542ms) +✔ matrix: queued→briefed privileged; briefed→in-progress owner with after satisfied (6.010632ms) +✔ matrix: release, review round, changes requested and waiting-on-jason (12.885071ms) +✔ matrix J5: in-review→done by the gate owner with evidence naming the current round (8.050185ms) +✔ review issue, lead decision 23: none refuses, one is used, several need --issue, later rounds keep it (19.180382ms) +✔ the row schema refuses a round with a null issue, and the A1 review shape (P2) (2.426903ms) +✔ matrix R1: every state × target × actor class matches 8.7, gate owner jason or not, required or not (1537.958744ms) +✔ matrix: blocked keeps the claim and returns only to previousState (4.423046ms) +✔ matrix J4: parking is Jason's, refused while required; unpark returns to queued (2.809439ms) +✔ field edits: who may change what (5.358298ms) +✔ set issues keeps a logged narrowing of closes (N10) (2.937073ms) +✔ text the table shows refuses \ and <, everywhere it enters (N8) (2.297068ms) +✔ every accepted text renders to nine cells on every row (N8) (25.303514ms) +✔ genesis: the map refuses an owner among its row's reviewers; replay doesn't (2026-10-04) (0.775011ms) +✔ times and dates must be calendar values, not just the shape (2026-10-04) (1.766389ms) +✔ replay holds every op id to the caller's rule (N11) (5.241836ms) +✔ note: owner, listed reviewer or privileged; empty clears (1.262486ms) +✔ assign moves the claim with the owner; done clears it (2.793087ms) +✔ render is byte-stable and escapes pipes (0.549068ms) +✔ view classification: current, genuine stale, edited stale marker, changed current body, markers (0.642409ms) +✔ next: resume, then review, then start, then wait, then nothing; lowest id first (27.744196ms) +✔ canonical args make a retry's identity independent of list order (0.414978ms) +✔ manifests, headings and blob ids (0.632552ms) +✔ the migration map: one queue-map block, exact keys (0.804261ms) +✔ every call but `queue` reaches the seat CLI exactly as before A2 (731.541362ms) +✔ `queue` reaches the queue CLI with the rest of the arguments (238.216191ms) +✔ the pre-A2 fixture is the script A2 changed (0.287818ms) +✔ acquire publishes the record by link; release removes only its own lock (5.720988ms) +✔ a kill between the temp write and the link leaves no lock (45.276098ms) +✔ a short or failed temp write refuses and leaves no lock and no temp (2.721826ms) +✔ a link error other than EEXIST refuses (1.484864ms) +✔ an error after the link releases the lock: unreadable gate, failing temp stat (3.390013ms) +✔ a release that fails on a gate path is reported, never a stack trace (P1) (4.685306ms) +✔ a paused holder: another writer waits 10 s, then refuses naming it live (10092.593192ms) +✔ two concurrent unlockers: the second refuses on the gate (31.012372ms) +✔ a writer publishing during an unlock, lock first: unlock sees it live and refuses (2.190358ms) +✔ a writer publishing during an unlock, gate first: the writer releases and refuses (2.065014ms) +✔ a gate swapped while held is left in place and reported, on success and on refusal (N1) (2.789643ms) +✔ a reused pid within one boot is mismatch; unlock removes the lock and never signals the process (5.874995ms) +✔ the same pid and start on a different boot is mismatch (1.057776ms) +✔ a foreign host is unknown whatever the local pid says; unlock refuses (92.308672ms) +✔ unreadable /proc: classification is unknown and acquire refuses (1.295867ms) +✔ invalid records: empty, unparsable, wrong keys, bad start or boot (0.354373ms) +✔ a stale gate blocks writers; --check-gate says mismatch for a reused pid (6.94049ms) +✔ a delayed release by a dead owner, after unlock and a new owner: the inode check keeps the new lock (3.76974ms) +✔ release checks the inode too: a byte-identical lock file with a new inode is left in place (1.280141ms) +✔ unlock refuses a live, unknown or invalid lock, and does nothing without one (4.753956ms) +✔ the migration map validates and renders the golden genesis table (3.904784ms) +✔ the marked QUEUE.md holds every row and parked item between its markers (1.388114ms) +✔ map-check reports each kind of drift (5.03177ms) +✔ a request posts once as the requester; a retry sends nothing (644.508495ms) +✔ each transport answer maps to posted, failed or uncertain (8.9 step 3) (3439.609121ms) +✔ the pre-send checks: GET user must name the requester, under the deadline (1220.867052ms) +✔ the lead's request refuses a token for login sage (723.708575ms) +✔ the credential file: the seat's own, 0600, no symlink, never the shared default (493.172025ms) +✔ an unresolved request blocks a new request, a new round, waiting-on-jason and done (1765.486675ms) +✔ a same-op retry after a kill sends nothing, even with a stale view (2829.629069ms) +✔ a held lock at the outcome exits 3 and names what the transport said (628.916615ms) +✔ late outcomes: after an abandon, and after a resolve with the same or another id (1916.002633ms) +✔ resolve checks the comment: issue, markers, round, candidate and author (1924.486952ms) +✔ the lead resolves a seat's request: the comment's author is the requester, fetched with the lead's token (727.313694ms) +✔ validateRow checks a request round's shape, which every replayed entry must keep (507.664198ms) +✔ request, changes, a new candidate, approval: every round pinned; no review files (1729.129969ms) +✔ a row with no reviewers opens a round that posts nothing (1395.516036ms) +✔ verify-commit: a prospective tree must hold exactly the candidate's paths (1543.262043ms) +✔ semantics: v1 entries replay as before; review entries need v2 (363.36264ms) +✔ set reviewers refuses the row's owner (2026-09-28) (269.605903ms) +✔ the owner records no verdict, even as a listed reviewer (369.885082ms) +✔ a request comment over the length limit is not sent (375.006737ms) +✔ a late POST on a closed row leaves a conflict nothing can resolve, and resolve asks nothing (407.509135ms) +✔ a Jason-gated row reaches waiting-on-jason only on every reviewer's approval (1969.588199ms) +✔ genesis: refusals before anything is written (506.562641ms) +✔ genesis: the map must be committed, well formed, with committed briefs and seats (654.406808ms) +✔ genesis: markers, a stray witness, once only; a retry returns the receipt (550.263442ms) +✔ genesis: a file holding genesis alone with no witness is confirmed by sync or a retry (326.533418ms) +✔ canonical checks: worktree, second clone, detached HEAD, wrong branch, GIT_DIR, foreign code; a symlink works (716.581482ms) +✔ --by that differs from MOSAIC_AGENT_NAME warns on stderr and logs nothing more (N12) (596.068448ms) +✔ op ids: missing, too long, reserved; a retry answers; another payload refuses (655.337554ms) +✔ a retried add returns the id it first allocated, after reassignment and after done (725.218464ms) +✔ Rocko's S4 schedule: a lost result, another writer, then the retry opens no second round (693.687288ms) +✔ the review issue and the evidence round through the CLI (lead decision 23, 8.7) (1056.228626ms) +✔ claims and add defaults through the CLI; candidates are manifests or reachable commits (642.166551ms) +✔ add, set reviewers and assign refuse the row's owner as a reviewer (486.174853ms) +✔ the working-brief check: a changed working copy refuses the start and flags next (644.915802ms) +✔ next: resume first, then nothing for an idle seat; needs a seat (346.860331ms) +✔ view stale: new ops and verify refuse naming the unshown op; retries answer; reads warn; render fixes (705.897481ms) +✔ view unknown: a hand edit, an old marker over an edited body, missing or duplicate markers (1322.822023ms) +✔ a hand edit to queue.json refuses every verb, reads included (655.13831ms) +✔ verify and render --check leave bytes and mtimes unchanged (503.99518ms) +✔ render is byte-stable across runs and repositories (364.651361ms) +✔ snapshot and verify --snapshot (843.406358ms) +✔ usage errors exit 4 (649.156839ms) +✔ a short write, ENOSPC or a file fsync failure: nothing visible, temp removed (178.455612ms) +✔ a rename failure: nothing visible, temp removed (149.66643ms) +✔ a directory fsync failure: uncertain, exit 3, no receipt; the tail refuses new ops; a retry confirms (191.396309ms) +✔ a directory fsync failure, then sync names the op (303.146379ms) +✔ a witness write failure: uncertain, durable, exit 3; the view is untouched (163.931607ms) +✔ the .git fsync after the witness rename fails: uncertain, exit 3, the witness says so (130.884674ms) +✔ confirming a tail fsyncs queue.json and docs/plans before the witness; either failure changes nothing (162.482988ms) +✔ the docs/plans fsync after the view rename fails: the op stands, the view is written, a warning says so (133.645043ms) +✔ a lock swapped while held is left in place and reported, on a receipt and on a refusal (140.043364ms) +✔ a lock that cannot be released after an op is reported, on a receipt and on a refusal (154.777597ms) +✔ unlock prints a swapped gate's warning on stderr, the result on stdout (124.738055ms) +✔ a view write that fails keeps the op and reports a stale view (125.171976ms) +✔ SIGKILL before the rename: nothing recorded; the retry removes the leftover temp (586.757792ms) +✔ SIGKILL after the rename, before the witness: the tail refuses new ops and sync names the op (651.543058ms) +✔ SIGKILL after the witness, before the view: the stale refusal names the op (597.367324ms) +✔ SIGKILL after the view, before the receipt: the retry returns the receipt (585.535612ms) +✔ git checkout between steps 1 and 7: step 7 refuses and nothing is written (173.622174ms) +✔ git stash restoring an older valid pair: history lost; accept-history needs privilege, a reason and --yes (1210.247576ms) +✔ a deleted witness: refused after the locked recheck; accept-history records it absent (221.84185ms) +✔ a header edit during a write: the op stands, the view write is skipped with a warning (153.28568ms) +✔ a reader paused between the witness and the file while a writer finishes: no lost-history report (132.389949ms) +✔ file-then-witness order forced by a hook: the locked recheck prevents a false report (130.677054ms) +✔ a writer paused before and after the witness rename: readers see a tail, then a match (147.365771ms) +✔ a true rollback is reported only after the locked recheck; a held lock names its holder instead (498.356751ms) +✔ an accept-history in progress: an unlocked reader waits on the lock and never reports lost history (469.221844ms) +✔ the platform check refuses other filesystems (130.864218ms) +✔ tmpfs passes only a test layer that allows it (N5) (158.150412ms) +✔ unlock keeps a multi-line lock record on stdout (P3) (209.279676ms) +ℹ tests 148 +ℹ suites 0 +ℹ pass 148 +ℹ fail 0 +ℹ cancelled 0 +ℹ skipped 0 +ℹ todo 0 +ℹ duration_ms 26358.293639 diff --git a/agents/filbert/work/s4-follow-up-review/r1-base-node-seat.txt b/agents/filbert/work/s4-follow-up-review/r1-base-node-seat.txt new file mode 100644 index 00000000..2070a7a5 --- /dev/null +++ b/agents/filbert/work/s4-follow-up-review/r1-base-node-seat.txt @@ -0,0 +1,27 @@ +✔ resolveSeat: by name under --repo resolves the repo layout (1.481206ms) +✔ resolveSeat: by path resolves the fleet layout (0.362472ms) +✔ resolveSeat: refusals for missing dir, missing launch.sh, non-executable launch.sh, invalid name, and unknown layout (0.856131ms) +✔ tmuxContext: outside tmux, default socket, custom socket, and exec failure (0.828242ms) +✔ makeRegistration produces a record that validates; each shape violation throws SeatError (0.904445ms) +✔ writeRegistration/readRegistration: round trip, permissions, absence, and malformed records (1.554862ms) +✔ updateTask: changes task, taskSetBy and updatedAt only, and refuses appropriately (1.289573ms) +✔ CLI launch: registers, execs the fake launch script, and passes args through (32.787236ms) +✔ CLI launch: --harness lands in the record (32.828675ms) +✔ CLI launch: the launch script's own exit code passes through (28.31954ms) +✔ CLI launch: relaunching a seat rewrites the one registration record (64.395537ms) +✔ CLI launch: omitting --task records an empty string, not null (29.227222ms) +✔ CLI seat task: updates only the task after a launch, and refuses on an unlaunched seat (81.319556ms) +✔ CLI refusals: no args, unknown flag, missing config, already-registered env, and exec failure (127.788375ms) +✔ samePath: equal paths, symlinked dirs, distinct dirs, and non-strings (0.395671ms) +✔ resolveSetBy: explicit --by wins over the environment; absent or empty environment gives unknown; invalid explicit or environment values refuse with exit 4 (0.553605ms) +✔ 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.726541ms) +✔ 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 (10.854969ms) +✔ 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 (379.051082ms) +ℹ tests 19 +ℹ suites 0 +ℹ pass 19 +ℹ fail 0 +ℹ cancelled 0 +ℹ skipped 0 +ℹ todo 0 +ℹ duration_ms 867.37898 diff --git a/agents/filbert/work/s4-follow-up-review/r1-base-node-tasks.txt b/agents/filbert/work/s4-follow-up-review/r1-base-node-tasks.txt new file mode 100644 index 00000000..ed5da869 --- /dev/null +++ b/agents/filbert/work/s4-follow-up-review/r1-base-node-tasks.txt @@ -0,0 +1,59 @@ +✔ the boot config is checked before anything starts (25.484517ms) +✔ a business with no tracker entry refuses task verbs (18.675299ms) +✔ credential.expiring and .expired are recorded once per instance (44.712914ms) +✔ a token file that changes on disk records credential.changed (21.046939ms) +✔ autostart polls, reconciles and retries a startup the tracker was down for (41.67541ms) +✔ a refusal a restart must clear is not retried by the poll (12.489203ms) +✔ a poll that fires while two are queued is dropped (17.657755ms) +✔ close waits for a running verb and refuses one that has not started (45.913148ms) +✔ the bundled Vikunja is the pinned upstream image the runbook names (1.509112ms) +✔ every published port is on 127.0.0.1, and no secret is in the file (0.505503ms) +✔ the fake answers each route with the statuses and shapes Vikunja v2.7.0 sent (136.056128ms) +✔ the recorded task bodies pass the checks S3 applies to every read (0.687076ms) +✔ the client works against the fake over real HTTP with the platform fetch (111.070926ms) +✔ a correct install starts, and the first reconcile records tasks that already exist (39.319619ms) +✔ verbs refuse while a business is starting and after startup refused it (22.51381ms) +✔ startup refuses a token that can do more than its role needs (52.113844ms) +✔ startup refuses an unsupported version and flags an untested one (29.381676ms) +✔ startup refuses a board that the runbook did not install (42.307367ms) +✔ startup refuses a project the sync bot cannot read (7.346948ms) +✔ startup refuses a configured label the pm bot cannot see (9.464093ms) +✔ startup refuses an expired credential and a missing sync credential (18.02678ms) +✔ an unreachable tracker refuses with tracker-unavailable (8.588022ms) +✔ an edit in the UI is recorded once, with the fields that changed (98.406805ms) +✔ a move between open buckets is seen on the board, though updated does not change (45.2568ms) +✔ a person's comment is counted and a bot's is not (78.116956ms) +✔ the hourly reconcile catches a comment through comment_count (81.889781ms) +✔ a task closed in the UI leaves the open view with its done bucket (129.610061ms) +✔ a task that leaves the board is recorded as deleted, moved or out of reach (82.047323ms) +✔ a poll that read before a verb wrote does not overwrite the verb (41.528925ms) +✔ a tracker fault during a tick is reported and the next tick catches up (37.749804ms) +✔ a malformed answer refuses the tick with tracker-shape (15.198025ms) +✔ no token value reaches the database, the log or a refusal (47.096688ms) +✔ the first look at a task counts only comments inside the window (36.09698ms) +✔ task.create needs a recorded human request and a requirement id (60.738266ms) +✔ only labels named in the business file can be written (36.345449ms) +✔ task.schedule sets and clears a due date and relations (43.647102ms) +✔ assign and reassign move the role bots and record task.assigned (62.506333ms) +✔ task.update.assigned is for the assignee and records task.state (54.68988ms) +✔ a wrong expected digest records task.conflict and writes nothing (20.478626ms) +✔ a cross-role verb needs a resolved decision, used once (32.256943ms) +✔ task.close needs a verdict; after it every verb refuses with task-done (33.220223ms) +✔ a lost answer is settled by a re-read and never retried (38.272731ms) +✔ a create whose answer is lost is reported uncertain, and the poll finds the task (44.567161ms) +✔ a task the sync bot cannot read refuses and records nothing (27.215388ms) +✔ verbs and polls for one business run one at a time (99.301469ms) +✔ a due date with milliseconds is written to the second (68.906673ms) +✔ every write landed and the final read failed: the verb succeeds and records what it wrote (38.970276ms) +✔ some writes landed and the final read failed: write-uncertain, and nothing is recorded (14.496191ms) +✔ a create whose final read fails succeeds and records task.created (34.358337ms) +✔ an edit between the last write and the final read shows as external on the next poll (39.988391ms) +✔ task.created is recorded when a later label write fails (16.768111ms) +ℹ tests 51 +ℹ suites 0 +ℹ pass 51 +ℹ fail 0 +ℹ cancelled 0 +ℹ skipped 0 +ℹ todo 0 +ℹ duration_ms 943.604698 diff --git a/agents/filbert/work/s4-follow-up-review/r1-base-node-webui.txt b/agents/filbert/work/s4-follow-up-review/r1-base-node-webui.txt new file mode 100644 index 00000000..f1a45344 --- /dev/null +++ b/agents/filbert/work/s4-follow-up-review/r1-base-node-webui.txt @@ -0,0 +1,23 @@ +✔ browser edge states: loading, empty, malformed, stale, hostile/long values, in-flight reply and appearance fallback (3375.0441ms) +Rendered contrast: {"failures":[],"count":330,"lowest":4.504658476260286} +✔ served Console browser: real board fixtures, keyboard, drafts, receipts, themes, 320px and failures (3792.760217ms) +✔ conversation view: full history, collapsed tools, hidden thinking, inert hostile content, malformed and reconcile markers (3283.267961ms) +✔ conversation view: a fork keeps the open branch, says so, and opens the new one on request (1639.235309ms) +✔ conversation view: a newer session with no readable history keeps the marker (919.340475ms) +✔ conversation view: seats without history say so and offer no reply (542.697818ms) +✔ Discord row through real board/WebUI: independent brake/liveness, no Reply, literal content (2826.548118ms) +✔ return flow through the conversation view: send, tool call, delayed result, peer message, exact long answers, relaunch (54743.791523ms) +✔ both presentations replace old activity with relaunch notice, label retained history, then resume after new activity (2827.586217ms) +✔ reported return flow and relative Age: reply sent from the inspector, then the new answer appears there without manual refresh (22597.160796ms) +✔ loopback host and board origin fail closed (7.216403ms) +✔ real board fixture passes through WebUI; assets and isolated seen/reply work (94.784523ms) +✔ proxy preserves exact request bytes, status and receipt, rejects forms and malformed JSON, never follows redirect (66.672652ms) +✔ unreachable board reports URL; CLI rejects unsupported options (597.716602ms) +ℹ tests 14 +ℹ suites 0 +ℹ pass 14 +ℹ fail 0 +ℹ cancelled 0 +ℹ skipped 0 +ℹ todo 0 +ℹ duration_ms 55083.850563 diff --git a/agents/filbert/work/s4-follow-up-review/r1-base-suite-auth.txt b/agents/filbert/work/s4-follow-up-review/r1-base-suite-auth.txt new file mode 100644 index 00000000..ac228760 --- /dev/null +++ b/agents/filbert/work/s4-follow-up-review/r1-base-suite-auth.txt @@ -0,0 +1,17 @@ +OK status with missing harness credential exits 3 and still lists accounts +OK status reports harness credential (read-only) + mosaic accounts +OK api key material never reaches output +OK oauth token material never reaches output +OK unparseable credential file exits 2 +OK symlinked credential file exits 4 +OK env-side credential names reported +OK env var values never reach output +OK accounts without an accounts dir reports none and creates nothing +OK accounts lists files and marks the active one +OK loose account perms flagged in listing +OK agent --auth with missing account file refuses (exit 4) +OK agent --auth with non-0600 account file refuses +OK agent --auth with invalid account name refuses +OK auth.sh without valid config refuses + +selftest: 15 passed, 0 failed diff --git a/agents/filbert/work/s4-follow-up-review/r1-base-suite-conductor.txt b/agents/filbert/work/s4-follow-up-review/r1-base-suite-conductor.txt new file mode 100644 index 00000000..a2128b27 --- /dev/null +++ b/agents/filbert/work/s4-follow-up-review/r1-base-suite-conductor.txt @@ -0,0 +1,55 @@ +Note: switching to '521597bbe0cf51a0a1aa3b7a16f13fc7b59c98c6'. + +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 + +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 '521597bbe0cf51a0a1aa3b7a16f13fc7b59c98c6'. + +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 + +Or undo this operation with: + + git switch - + +Turn off this advice by setting config variable advice.detachedHead to false + +OK dry-run: allowed change, exit 0, nothing committed (exit 0) +OK dry-run committed nothing +OK apply: allowed change exits 0 (exit 0) +OK apply: attribution in commit subject +OK apply: target tree clean after commit +OK disallowed path refused (exit 1) +OK disallowed path: target untouched +OK syntax gate refused broken .mjs (exit 1) +OK syntax gate: target untouched +OK suite failure refused (exit 1) +OK suite failure: target reverted to clean +OK disabled policy refused (exit 2) +OK disabled policy: target untouched +OK failed run refused (exit 1) +OK failed run: target untouched +OK missing run exits 4 (exit 4) +OK invalid policy exits 2 (exit 2) + +selftest: 17 passed, 0 failed diff --git a/agents/filbert/work/s4-follow-up-review/r1-base-suite-config.txt b/agents/filbert/work/s4-follow-up-review/r1-base-suite-config.txt new file mode 100644 index 00000000..76c05ef7 --- /dev/null +++ b/agents/filbert/work/s4-follow-up-review/r1-base-suite-config.txt @@ -0,0 +1,26 @@ +OK absent adapter defaults to pi +OK adapter mock validates (exit 0) +OK unsupported adapter exits 2 (exit 2) +OK env exports adapter +OK bootstrap creates default when absent (exit 0) +OK bootstrap wrote config file +OK bootstrap is idempotent on existing config (exit 0) +OK bootstrap did not rewrite existing config +OK validate missing config exits 3 (exit 3) +OK malformed JSON exits 2 (exit 2) +OK unsupported configVersion exits 2 (exit 2) +OK unknown top-level key exits 2 (exit 2) +OK unknown execution key exits 2 (exit 2) +OK unsupported backend exits 2 (exit 2) +OK unsupported environment exits 2 (exit 2) +OK relative dataRoot exits 2 (exit 2) +OK non-canonical dataRoot exits 2 (exit 2) +OK filesystem root dataRoot exits 2 (exit 2) +OK home directory dataRoot exits 2 (exit 2) +OK dataRoot containing config dir exits 2 (exit 2) +OK control character in provider exits 2 (exit 2) +OK symlinked config file exits 2 (exit 2) +OK env exports resolve correctly +OK failed validation modified nothing + +selftest: 24 passed, 0 failed diff --git a/agents/filbert/work/s4-follow-up-review/r1-base-suite-discord.txt b/agents/filbert/work/s4-follow-up-review/r1-base-suite-discord.txt new file mode 100644 index 00000000..b013f9ff --- /dev/null +++ b/agents/filbert/work/s4-follow-up-review/r1-base-suite-discord.txt @@ -0,0 +1,70 @@ +toolchain: node v26.8.1 + +OK syntax: packages/discord/src/approvals.mjs +OK syntax: packages/discord/src/authorize.mjs +OK syntax: packages/discord/src/binding.mjs +OK syntax: packages/discord/src/cli.mjs +OK syntax: packages/discord/src/connector.mjs +OK syntax: packages/discord/src/context.mjs +OK syntax: packages/discord/src/engine-pi.mjs +OK syntax: packages/discord/src/errors.mjs +OK syntax: packages/discord/src/gateway.mjs +OK syntax: packages/discord/src/git.mjs +OK syntax: packages/discord/src/journal.mjs +OK syntax: packages/discord/src/notify.mjs +OK syntax: packages/discord/src/rest.mjs +OK syntax: packages/discord/src/setspark.mjs +OK syntax: packages/discord/src/tools.mjs +OK syntax: packages/discord/src/web.mjs +OK syntax: packages/discord/bin/git-credential.mjs +OK syntax: packages/discord/extension/tools.mjs +OK syntax: packages/discord/tests/approvals.test.mjs +OK syntax: packages/discord/tests/authorize.test.mjs +OK syntax: packages/discord/tests/binding.test.mjs +OK syntax: packages/discord/tests/connector.test.mjs +OK syntax: packages/discord/tests/context.test.mjs +OK syntax: packages/discord/tests/engine.test.mjs +OK syntax: packages/discord/tests/fake-pi.mjs +OK syntax: packages/discord/tests/gateway.test.mjs +OK syntax: packages/discord/tests/git.test.mjs +OK syntax: packages/discord/tests/helpers.mjs +OK syntax: packages/discord/tests/journal.test.mjs +OK syntax: packages/discord/tests/notify.test.mjs +OK syntax: packages/discord/tests/recover.test.mjs +OK syntax: packages/discord/tests/rest.test.mjs +OK syntax: packages/discord/tests/setspark.test.mjs +OK syntax: packages/discord/tests/tools.test.mjs +OK syntax: packages/discord/tests/web.test.mjs +OK syntax: packages/discord/fixtures/claim-worker.mjs +OK syntax: packages/discord/fixtures/legacy-owner-worker.mjs +OK syntax: scripts/discord.sh +OK syntax: scripts/discord-service.sh +OK packages/discord declares no dependencies +OK no bot-token-shaped string in packages/discord +OK fixture binding uses placeholder ids only +OK fixture binding validates +OK real pi with the extension exposes exactly list_dir, read_file, search and no built-in tool +OK real pi with a writable root exposes exactly the three reads plus write_file and edit_file, and writes nothing at start +OK real pi with a web key exposes the three reads plus web_fetch and web_search, and no write tool without a writable root +OK real pi with a git root exposes the reads, writes and the four git verbs, commits nothing at start, and never shows the token +OK real pi with protocol vault adds reserve_id to the git verbs +OK real pi with a setspark key exposes the reads and the eight record verbs, no counters, and never shows the key +OK real pi refuses a git key on a read-only root (fail closed) +OK real pi with the pilot flags (--no-tools) exposes no tool at all +OK real pi exits non-zero without MOSAIC_DISCORD_TOOLS: no session, no tools (fail closed) +OK a failing nested test fails the run under a parent runner's NODE_TEST_CONTEXT +OK node --test packages/discord/tests/ (ℹ pass 178) +OK scripts/discord.sh --help exits 0 +OK scripts/discord.sh check without a binding exits 4 +OK scripts/discord.sh recover without a binding exits 4 +OK scripts/discord.sh reload without a binding exits 4 +OK scripts/discord-service.sh without a command exits 4 +OK service unit renders with the repository path, a supervised run as the main process, exit 3 never retried, and reload as SIGHUP +OK service install writes the rendered unit (0644) and leaves no temp file +OK service install a second time reports unchanged +OK systemd-analyze verify accepts the rendered unit +OK service uninstall removes the unit file +OK service install with an unknown flag exits 4 +OK service install with USER unset finishes and names the account for lingering + +discord suite: 66 passed, 0 failed diff --git a/agents/filbert/work/s4-follow-up-review/r1-base-suite-extension-package.txt b/agents/filbert/work/s4-follow-up-review/r1-base-suite-extension-package.txt new file mode 100644 index 00000000..2273a453 --- /dev/null +++ b/agents/filbert/work/s4-follow-up-review/r1-base-suite-extension-package.txt @@ -0,0 +1,21 @@ +OK initial ordinary-file install +OK installed tree matches canonical source +OK installed tree has no symlinks +OK check detects installation drift +OK sync refuses to overwrite installation drift +OK check detects an extra destination file +OK check detects an extra destination directory +OK check rejects a destination symlink +OK sync accepts a canonical source update +OK updated installation matches canonical source +scripts/test-extension-package.sh: line 14: 38232 Killed "$@" > /dev/null 2>&1 +OK forced interruption kills the replacing process +OK next invocation recovers old consistent installation +OK interrupted replacement rolled back +OK sync succeeds after interruption recovery +OK unlocked stale lock file does not block +OK active lock refuses a concurrent sync +OK source symlink fails closed +OK nested second entrypoint fails closed + +extension package selftest: 18 passed, 0 failed diff --git a/agents/filbert/work/s4-follow-up-review/r1-base-suite-foundation.txt b/agents/filbert/work/s4-follow-up-review/r1-base-suite-foundation.txt new file mode 100644 index 00000000..6338e1e6 --- /dev/null +++ b/agents/filbert/work/s4-follow-up-review/r1-base-suite-foundation.txt @@ -0,0 +1,53 @@ +toolchain: node v26.8.1, python 3.12.8, jsonschema 4.26.0 + +OK syntax: scripts/foundation-inspect.mjs +OK syntax: scripts/foundation/strict-json.mjs +OK syntax: scripts/foundation/canonical.mjs +OK syntax: scripts/foundation/resolve.mjs +OK syntax: scripts/foundation/validate-record.mjs +OK syntax: scripts/foundation/fixtures/build-fixtures.mjs +OK syntax: scripts/foundation/canonical.test.mjs +OK syntax: scripts/foundation/cli.test.mjs +OK syntax: scripts/foundation/fixtures.test.mjs +OK syntax: scripts/foundation/resolve.test.mjs +OK syntax: scripts/foundation/strict-json.test.mjs +OK syntax: scripts/foundation/verify-schema.py (ast only; no bytecode written) +OK fixture generator runs +OK checked-in fixtures/bundles equal a fresh generation +OK checked-in fixtures/raw equal a fresh generation +OK checked-in fixtures/index.json equal a fresh generation +OK checked-in demo bundles equal a fresh generation +OK a failing nested test fails the run under a parent runner's NODE_TEST_CONTEXT +OK node --test scripts/foundation/ (ℹ pass 80) +OK differential schema oracle: PASS: differential schema oracle (finite corpus; compatibility evidence, not equivalence proof) + platform witness: strftime('%Y') for year 999 -> '999' (pinned checker refuses years 0001..0999) + node v26.8.1; corpus 1568 records (38 pinned fixtures, 478 unique bundle records, 1052 typeCase/mutation/lexical cases) + schema column: agree-valid 540, agree-invalid 991, DISAGREEMENTS 0; strict-only (parser-bound) cases: 27; unsupported-kind records not schema-assessed by the inspector: 10 + profile column (schema-valid records only): profile-valid 510, profile-invalid 30 + profile refusals asserted: 30 schema-agreed-valid records refused only by the strict typed-string profile (rule profile-pattern-mismatch), 12 declared by name; 73 named probes verified against declared schema/profile columns +OK oracle: zero schema-column disagreements with the pinned checker +OK oracle: strict-only profile refusals are counted and asserted +OK demo: permitted read preview exits 0 (exit 0) +OK demo: permitted file.change preview exits 0 (exit 0) +OK demo: assignment.change proposal is unresolved (exit 3) (exit 3) +OK demo: revoked registration is refused (exit 3) (exit 3) +OK demo: message is not authority (exit 3) (exit 3) +OK usage: no arguments exits 2 (exit 2) +OK io: missing file exits 4 (exit 4) +OK io: directory exits 4 (exit 4) +OK io: symlink exits 4 (O_NOFOLLOW) (exit 4) +OK bound: oversize fixture exits 2 (exit 2) +OK profile: one final LF in a typed selection id is refused before admission (exit 2) (exit 2) +OK profile: two final LFs fail the schema pattern itself (exit 2) (exit 2) +OK profile: escaped newlines in free-form text stay allowed (exit 0) (exit 0) +OK profile refusal is invalid-request/profile-pattern-mismatch with selection and operation withheld, value not echoed +OK text output starts with the disclaimer +OK json output is valid JSON with result allowed and exactly the charter §7 fields +OK json golden matches byte-for-byte +OK sandboxed bundle run (env -i, PATH=/nonexistent) produced the unresolved proposal +OK sandbox inventory (path/type/size/mode/uid/gid/inode/mtime/sha256) unchanged by runs +OK canary never printed (bundle run and credential-file run) +OK a non-bundle JSON file is refused at the shape gate, not read into output +OK no field of the non-bundle file is echoed + +selftest: 44 passed, 0 failed diff --git a/agents/filbert/work/s4-follow-up-review/r1-base-suite-queue.txt b/agents/filbert/work/s4-follow-up-review/r1-base-suite-queue.txt new file mode 100644 index 00000000..fa62e0cc --- /dev/null +++ b/agents/filbert/work/s4-follow-up-review/r1-base-suite-queue.txt @@ -0,0 +1,35 @@ +toolchain: node v26.8.1, git version 2.55.0 + +OK syntax: packages/queue/src/cli.mjs +OK syntax: packages/queue/src/errors.mjs +OK syntax: packages/queue/src/io.mjs +OK syntax: packages/queue/src/lock.mjs +OK syntax: packages/queue/src/queue.mjs +OK syntax: packages/queue/src/review.mjs +OK syntax: packages/queue/src/store.mjs +OK syntax: packages/queue/tests/commit.test.mjs +OK syntax: packages/queue/tests/data.test.mjs +OK syntax: packages/queue/tests/dispatch.test.mjs +OK syntax: packages/queue/tests/helpers.mjs +OK syntax: packages/queue/tests/lock.test.mjs +OK syntax: packages/queue/tests/migration.test.mjs +OK syntax: packages/queue/tests/review.test.mjs +OK syntax: packages/queue/tests/store.test.mjs +OK syntax: packages/queue/tests/write.test.mjs +OK syntax: packages/queue/tests/fixtures/fake-gitea.mjs +OK syntax: packages/queue/tests/fixtures/kill-at.mjs +OK syntax: packages/queue/tests/fixtures/lock-child.mjs +OK syntax: packages/queue/tests/fixtures/mosaic-pre-a2.sh +OK syntax: scripts/queue-commit.sh +OK syntax: scripts/git-hooks/pre-commit +OK syntax: scripts/mosaic +OK queue-commit.sh, the guard and scripts/mosaic are executable +OK packages/queue declares no dependencies +ℹ tests 148 +ℹ pass 148 +ℹ fail 0 +OK node --test packages/queue/tests/ +OK scripts/mosaic queue help +skip queue verify and render --check: this checkout (/home/jwoltje/filbert-scratch/r45/base) is not the queue's canonical root (/mnt/storage/src/mosaic-stack) + +queue suite: 27 passed, 0 failed diff --git a/agents/filbert/work/s4-follow-up-review/r1-base-suite-release.txt b/agents/filbert/work/s4-follow-up-review/r1-base-suite-release.txt new file mode 100644 index 00000000..1d6ed050 --- /dev/null +++ b/agents/filbert/work/s4-follow-up-review/r1-base-suite-release.txt @@ -0,0 +1,7 @@ +OK valid RELEASE resolves (exit 0) +OK invalid RELEASE exits 1 (exit 1) +OK missing RELEASE exits 1 (exit 1) +OK valid RELEASE leaves image tag consistent with version +skip state-machine cases (docker daemon unavailable) + +selftest: 4 passed, 0 failed diff --git a/agents/filbert/work/s4-follow-up-review/r1-base-suite-task.txt b/agents/filbert/work/s4-follow-up-review/r1-base-suite-task.txt new file mode 100644 index 00000000..85aa46f0 --- /dev/null +++ b/agents/filbert/work/s4-follow-up-review/r1-base-suite-task.txt @@ -0,0 +1,33 @@ +OK valid task validates (exit 0) +OK unknown task key exits 2 (exit 2) +OK unsupported taskVersion exits 2 (exit 2) +OK invalid task id exits 2 (exit 2) +OK empty prompt exits 2 (exit 2) +OK NUL in expectExact exits 2 (exit 2) +OK out-of-range timeout exits 2 (exit 2) +OK missing mission file exits 4 (exit 4) +OK task with valid mission validates (exit 0) +OK invalid mission exits 2 (exit 2) +OK validate missing task exits 4 (exit 4) +OK validation does not modify the task file +OK prune dry-run exits 0 (exit 0) +OK dry-run deleted nothing +OK prune --keep=2 --yes removes oldest (exit 0) +OK kept exactly 2 newest runs +OK newest run kept, oldest pruned +OK append-only receipt written (3 entries) +OK sessions/workspaces untouched by prune +OK prune with invalid keep exits 4 (exit 4) +skip adapter seam cases (docker daemon unavailable) +skip workspace/capability cases (docker daemon unavailable) +skip live task cases (docker unavailable) +OK onboard without name exits 4 (non-interactive) (exit 4) +OK onboard --name renders profile (exit 0) +OK profile written +OK canon structure: required filled, optional placeholdered +OK canon sections present +FAIL user recall run succeeds (exit 1) +FAIL recalled user name (response: ) +OK no agent identity on headless run + +selftest: 26 passed, 2 failed diff --git a/agents/filbert/work/s4-follow-up-review/r1-cand-node-bus.txt b/agents/filbert/work/s4-follow-up-review/r1-cand-node-bus.txt new file mode 100644 index 00000000..0c611bbc --- /dev/null +++ b/agents/filbert/work/s4-follow-up-review/r1-cand-node-bus.txt @@ -0,0 +1,75 @@ +✔ launch identity is stamped, payload identity is refused and stale holder cannot send (22.380445ms) +✔ decision classes route from policy; gated resolution is human-only, choice and target must match (26.652343ms) +✔ claim exclusion, holder release, gated revoke and rerouting to a new holder are atomic (26.817512ms) +✔ launch events require a human CLI capability; generic emit cannot forge authority events (16.912593ms) +✔ within-role decisions close atomically and invalid options or blocking omissions refuse (17.484361ms) +✔ observer capabilities read human inbox but cannot mutate or forge launch identity (14.390004ms) +✔ task action subjects and linked decision trail are complete and ordered (22.844639ms) +✔ launch binding is durable and reconnecting requires the identical trusted record (8.901475ms) +✔ business isolation includes inherited object names and cross-business message references (14.697023ms) +✔ authority never transfers between action, run, target, unresolved or replaced role holder (18.867203ms) +✔ task projection uses schema current view, skipping earlier and equal-start polls (16.611621ms) +✔ revocation permanently bars the old run from reclaiming first, including after broker restart (17.131669ms) +✔ empty message references refuse before storage; refusal-evidence failure stays a typed error (15.160447ms) +✔ both arbiters require human resolution when their cross-role route is themselves (21.001418ms) +✔ S1 adapter takes resolved limits and refs, rejects mismatched instance, never mutates input (2.412021ms) +✔ only validated broker references load; returned data and exceptions cannot expose a known token (4.936616ms) +✔ bad file modes, symlinks, repository/data paths, malformed tokens and missing dates refuse (2.671535ms) +✔ expiry refuses use and env references never become client data (0.864599ms) +✔ S1 parsed service refs work, service mismatch refuses, Gitea rotation due is a warning state (1.892674ms) +✔ opaque tokens shorter than 16 characters refuse before use (0.544106ms) +✔ human proof binds CLI entry, process start and nonce; agents and incomplete ancestry refuse (2.134541ms) +✔ process reader gets own kernel identity without exposing environment values (1.32011ms) +✔ EACCES ancestor environments skip only markers; commands and registered launches still refuse (1.011074ms) +✔ real pid 1 remains inspectable when its environment is protected (0.390425ms) +✔ within-role sends cite an open gated launch decision without spending it or naming it in grants (28.53858ms) +✔ missing and foreign-business citations refuse and roll back message and grant (18.196485ms) +✔ cross-role sends still need a matching resolved decision and consume it once (25.135814ms) +✔ broker process binds trusted launches, offers reader capabilities, refuses human mutation, closes cleanly (79.108955ms) +✔ startup token refusal returns safe code without value or partial listening broker (48.829337ms) +✔ loaded fixture token is absent from socket replies and SQLite, including refusal evidence (60.908712ms) +✔ killed broker leaves an explicit stale lock; another process cannot silently reclaim it (107.873418ms) +✔ trusted host registers later launches; socket clients never have a registration verb (56.981582ms) +✔ runtime excludes declared project roots even when host supplies no repoRoots (42.470637ms) +✔ a refused launch binding leaves the broker and existing capabilities alive; bad protocol stops it (60.65868ms) +✔ v3b prototype refusals, views and append-only mutations (80.57291ms) +✔ gated approval authorizes once, survives store reopen, and fresh approval works (28.953415ms) +✔ another run cannot consume an approval; a failed check leaves it usable (21.466123ms) +✔ two scheduled callers have exactly one grant and one consumed refusal (17.097423ms) +✔ failed commit rolls consumption back; cross-role consumes and within-role stays reusable (31.814084ms) +✔ class drift gated to cross-role refuses before consumption (20.59826ms) +✔ class drift cross-role to gated refuses before consumption (18.052394ms) +✔ class drift gated to within-role refuses before consumption (15.486208ms) +✔ class drift cross-role to within-role refuses before consumption (18.124738ms) +✔ class drift within-role to gated refuses before consumption (13.419772ms) +✔ class drift within-role to cross-role refuses before consumption (20.491545ms) +✔ message.send consumes approval and prevents a later send or authorize (18.817052ms) +✔ role.revoke consumes approval and prevents a later revoke or authorize (25.541538ms) +✔ creates private WAL store and excludes a second writer until explicit close (18.268778ms) +✔ rollback is atomic and schema metadata is checked against trusted DDL, not just itself (17.236638ms) +✔ existing empty database and symlink runtime directory refuse, never initialize over damage (15.924833ms) +✔ crash during a transaction recovers no partial event after explicit fixture-only lock removal (58.344101ms) +✔ writer refuses mixed at/read_at forms atomically, even through trusted SQL helpers (9.566566ms) +✔ async transactions refuse before invoking their function (7.56334ms) +✔ recordTask keeps sync reads and a role write apart (23.135531ms) +✔ read_at must be one canonical UTC format, so the projection compares strings safely (10.143766ms) +✔ a bad entry refuses the whole record (10.400234ms) +✔ taskView reads the projection for one business (14.293611ms) +✔ requestTask hands only a holder and a task verb to the handler, and records refusals (24.359955ms) +✔ the server sends task verbs to the adapter with its own timeout; other verbs stay synchronous (275.793239ms) +✔ without an adapter the server refuses every task verb (16.577329ms) +✔ the runtime refuses an invalid adapter and closes a valid one (17.478752ms) +✔ the process loads the S3 adapter from plain-data trackers (115.152403ms) +✔ socket capability stamps launch identity; shared views use wire, no SQL client (28.115562ms) +✔ two wire claims serialize; a lost reply never automatically retries (19.883232ms) +✔ malformed, oversized and identity-forging envelopes refuse without echoing input (12.093017ms) +✔ client preserves UTF-8 when a response divides a multibyte character (12.273179ms) +✔ committed mutation followed by dropped reply reports unknown and is never retried (18.390612ms) +ℹ tests 67 +ℹ suites 0 +ℹ pass 67 +ℹ fail 0 +ℹ cancelled 0 +ℹ skipped 0 +ℹ todo 0 +ℹ duration_ms 596.109045 diff --git a/agents/filbert/work/s4-follow-up-review/r1-cand-node-business.txt b/agents/filbert/work/s4-follow-up-review/r1-cand-node-business.txt new file mode 100644 index 00000000..850475a4 --- /dev/null +++ b/agents/filbert/work/s4-follow-up-review/r1-cand-node-business.txt @@ -0,0 +1,68 @@ +✔ config directory and file path follow MOSAIC_CONFIG (2.091087ms) +✔ the fixture business validates and comes back frozen (5.321521ms) +✔ two instances may share a definition (2.037789ms) +✔ top-level refusals (6.351726ms) +✔ arbiters and projects (8.005976ms) +✔ role instances (3.893541ms) +✔ Vikunja bots (8.360818ms) +✔ a role without Vikunja takes no tracker block (3.148697ms) +✔ credential references match the definition's services (3.774244ms) +✔ launch (9.004305ms) +✔ loadBusiness: file checks (1.763435ms) +✔ loadBusiness: not a regular file (39.742587ms) +✔ loading writes nothing (1.395759ms) +✔ names that are Object.prototype properties don't count as declared (2.537876ms) +✔ the shipped example refuses as written and validates once filled in (0.542455ms) +✔ usage errors exit 4 (305.032324ms) +✔ validate: a good business exits 0 and prints instance digests (67.820644ms) +✔ validate: project files (326.103036ms) +✔ validate: missing files and a broken system config (241.613221ms) +✔ validate: credential reference problems exit 2 and name each one (65.283695ms) +✔ validate: a token file inside the repository is refused (65.265828ms) +✔ validate: role definitions come from MOSAIC_ROLES_DIR (200.706934ms) +✔ resolve: prints one instance's record (200.999245ms) +✔ resolve: refusals (402.197715ms) +✔ parse: exactly one of file or env, plus the service's date (3.793344ms) +✔ check: a good file has no problems (1.913368ms) +✔ check never opens the file: a write-only token passes (0.714324ms) +✔ check: file problems (1.363191ms) +✔ check: token files can't live in the repository or dataRoot, even through a linked directory (1.906015ms) +✔ check: dates and environment references (0.655015ms) +✔ path and load (3.164846ms) +✔ refusals (1.988671ms) +✔ systemVars flattens the validated config (2.92044ms) +✔ precedence: system, business, project, project role, agent (7.214308ms) +✔ limits narrow the definition and never widen it (3.430914ms) +✔ role.launch stays within-role only for the instance the launch block names (6.183548ms) +✔ limits.authority without role.launch leaves the launcher with no launch block (1.860412ms) +✔ limits.authority narrows cross-role actions too (1.009894ms) +✔ classify (1.115549ms) +✔ the record carries what the broker and launcher need (0.857858ms) +✔ digest: key order doesn't matter, any value change does (6.307397ms) +✔ refusals (2.303527ms) +✔ the four shipped version 2 roles load (3.823656ms) +✔ shipped role scopes match addendum B section 2 and the SR runbook (2.018995ms) +✔ shipped authority follows the note's table (1.408237ms) +✔ version 1 files keep loading with no authority (1.257812ms) +✔ the conductor policy isn't a role (0.384142ms) +✔ a missing role file is exit 4, a symbolic link too (0.568308ms) +✔ version 2 refusals (1.861931ms) +✔ authority: closed vocabulary, no gated-only action, no overlap (3.158576ms) +✔ credentials: Gitea scopes (1.219846ms) +✔ credentials: Vikunja scopes are a group-to-verbs map from the grantable list (1.561772ms) +✔ credentials: services (0.559046ms) +✔ contract: a non-empty regular Markdown file beside the role file (0.63259ms) +✔ every key names known layers and a merge rule (1.216219ms) +✔ unknown keys and wrong layers refuse (1.149061ms) +✔ types (2.600103ms) +✔ merge: defaults, then the most specific layer wins (0.416272ms) +✔ merge: limits only narrow, and provenance lists each source (0.478389ms) +✔ merge doesn't change its inputs (0.197768ms) +ℹ tests 60 +ℹ suites 0 +ℹ pass 60 +ℹ fail 0 +ℹ cancelled 0 +ℹ skipped 0 +ℹ todo 0 +ℹ duration_ms 1954.930287 diff --git a/agents/filbert/work/s4-follow-up-review/r1-cand-node-cli.txt b/agents/filbert/work/s4-follow-up-review/r1-cand-node-cli.txt new file mode 100644 index 00000000..53212829 --- /dev/null +++ b/agents/filbert/work/s4-follow-up-review/r1-cand-node-cli.txt @@ -0,0 +1,70 @@ +✔ inbox lists only decisions routed to the human, with what approving authorizes and how to decide (20.134056ms) +✔ decide resolves by id prefix with --yes, and the trail shows it in broker order with the task to follow (26.241572ms) +✔ decide refuses without a terminal or --yes, on an unknown option and on a short reference (15.711639ms) +✔ decide prints a declining choice as declining (13.380758ms) +✔ an unknown outcome is reported once and never resent (9.236045ms) +✔ a decision closed before the answer arrives exits 2 and points at its trail (9.554273ms) +✔ a prefix that matches two open decisions exits 2 and resolves neither (10.621801ms) +✔ without --business a command uses the live host's business, and a stale host.json is not a host (11.366682ms) +✔ every human command refuses inside an agent run before it touches the bus (12.082916ms) +✔ usage errors exit 4; no business and no host is a usage error (11.128705ms) +✔ agents and tasks print through the broker (10.449874ms) +✔ notify.json: missing, loose, malformed and extra keys refuse; a binding or null passes (1.909287ms) +✔ bootConfig builds the broker's boot message for one business, with no trackers key when no project names one (48.708686ms) +✔ trackers come from the tracker.* variables of the one project that names a tracker project (39.925021ms) +✔ with two projects, the one that sets tracker.project supplies the tracker and the other is no conflict (35.254166ms) +✔ two projects that each name a tracker project refuse, since the boot shape holds one (33.529767ms) +✔ a business without tracker.baseUrl gets no trackers entry (41.164155ms) +✔ an unknown business and a broken system config refuse with exit 3 (68.437879ms) +✔ empty views say so (0.935675ms) +✔ the trail keeps the broker's order and names a decision's task without its rows (0.990128ms) +✔ tasks print the tracker fields the snapshot carries (0.159053ms) +✔ the host boots the broker, binds a launch in process, and the notifier DMs a blocking decision exactly once (881.972396ms) +✔ a notifier that dies takes the host down with exit 1, so the unit restarts the pair (135.708239ms) +✔ a second host for the same data root refuses with exit 3 while the first runs (73.349518ms) +✔ a notifier that refuses stops the broker and the host refuses with exit 3 (124.893626ms) +✔ a notifier that refuses after the broker died still refuses with exit 3, without a send to the dead broker (134.813304ms) +✔ a notifier that dies before it replies, after the broker died, still refuses, without a send to the dead broker (87.76665ms) +✔ watchChildren reports a child that died before it was called, and one that dies later (36.929762ms) +✔ bus stop refuses to signal a live pid that is not a bus host (203.219855ms) +✔ bus start refuses with exit 3 and the code when the broker refuses to boot; bus status names the lock (276.768265ms) +✔ bus start refuses with exit 3 without a notifier config (96.336866ms) +✔ bus start runs until bus stop; status reports it while it runs (651.277703ms) +✔ bus-service.sh renders the unit and installs it into a given directory (41.913796ms) +✔ zoned uses the IANA zone across DST (25.237436ms) +✔ each open blocking decision is DM'd once, across polls and a restart; non-blocking ones are not (19.12349ms) +✔ two blocking decisions get two DMs with different nonces (15.04801ms) +✔ the digest nonce differs per business and per day and fits Discord's 25 characters (0.284039ms) +✔ a failed DM is journaled, backs off, and is retried until it lands (15.246058ms) +✔ five definite refusals stop a DM: one gave-up line, one log line, and a restart keeps the count (18.300879ms) +✔ 429s, 5xx-style unknowns and refusals without a status never count toward the limit (19.283766ms) +✔ a crash between the fifth refusal and its gave-up line: the next poll appends it and sends nothing (12.623787ms) +✔ the digest goes at 08:00 Chicago once a day, with blocking ones marked as DM'd (16.108573ms) +✔ a late start with no digest for the day sends one at once; an empty inbox gets one line (10.506593ms) +✔ an inbox read failure is logged and the next poll retries (0.812213ms) +✔ no Discord id reaches the journal or the log (12.42784ms) +✔ the journal: a torn tail is copied out and truncated, so an append after it reopens cleanly (2.848665ms) +✔ the journal: a crash between the copy and the truncate leaves a tail the next open repairs (2.496016ms) +✔ the journal: a whole file that is one torn line truncates to empty (1.549448ms) +✔ the journal: a malformed complete line refuses and leaves the file and any torn tail alone (0.82019ms) +✔ the journal: a loose file mode, a loose directory or a symlinked journal refuses (0.901995ms) +✔ the journal: a line with a wrong type refuses with exit 3 and names the field (3.155995ms) +✔ the journal: a symlinked directory refuses and says it is a link (0.639875ms) +✔ the journal: an append after the file was swapped for a symlink refuses and writes nothing through it (0.65835ms) +✔ the journal: a directory it cannot write or create refuses with exit 3 and names the path (0.631409ms) +✔ digest content stays within Discord's 2000 characters (0.442711ms) +✔ runLoop never overlaps ticks and stops after the one in flight (110.965551ms) +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 (275.770061ms) +✔ the transport writes {business, verb, args} to the child and reads its JSON (42.708073ms) +✔ a bus code on stderr becomes the exit code; garbage and timeouts are outcome problems (2171.791227ms) +✔ busExit and refuseInsideAgent (0.512192ms) +ℹ tests 60 +ℹ suites 0 +ℹ pass 60 +ℹ fail 0 +ℹ cancelled 0 +ℹ skipped 0 +ℹ todo 0 +ℹ duration_ms 2858.176494 diff --git a/agents/filbert/work/s4-follow-up-review/r1-cand-node-control-board.txt b/agents/filbert/work/s4-follow-up-review/r1-cand-node-control-board.txt new file mode 100644 index 00000000..e49788ed --- /dev/null +++ b/agents/filbert/work/s4-follow-up-review/r1-cand-node-control-board.txt @@ -0,0 +1,132 @@ +✔ explicit request, Seen, ordinary completion and a new request have distinct attention states (6.804459ms) +✔ attention convention ignores reasoning/quoted examples and permits leading blank lines (0.419003ms) +✔ completed smoke replies and ordinary questions are idle, not human blockers (1.05788ms) +✔ only an explicit first-line input request makes a finished reply waiting (0.306714ms) +✔ tool activity, user text, errors and unfinished turns override attention text (0.191863ms) +✔ STOP access failure is unknown, not absence, under a non-root identity (37.705417ms) +✔ connector Task never inherits Discord routing envelopes; ordinary Task still uses user text (3.159431ms) +✔ connector discovery keeps only safe identity; rejects modes, mismatches, links and traversal (1.494166ms) +✔ canonical owner identity and STOP are independent; no tmux fallback or forged registration (6.396242ms) +✔ connector reply refusal precedes forged live tmux registration; ordinary agent still sends (0.5883ms) +✔ server rescans connector discovery and refuses HTTP reply without transport (37.825985ms) +✔ connector session links and linked directories are not read (1.123235ms) +✔ newer live matching launch marks old activity, preserves history/attention/attribution, then clears on new activity (4.059555ms) +✔ CLI print uses the relaunch notice instead of old current preview (69.0968ms) +✔ connector owner and fixed task never inherit a native relaunch notice (2.309453ms) +✔ equality, stale/unknown/offline, mismatched registration and unknown activity do not assert relaunch (1.447186ms) +✔ loadConfig: missing file throws ConfigError (1.74368ms) +✔ loadConfig: invalid JSON throws ConfigError (0.381839ms) +✔ loadConfig: missing dataRoot throws ConfigError (0.285709ms) +✔ loadConfig: relative dataRoot throws ConfigError (0.314598ms) +✔ loadConfig: valid config returns dataRoot (1.0148ms) +✔ findNewestSession: picks the newest by mtime among two files (0.633765ms) +✔ findNewestSession: finds files in nested subdirectories (0.368338ms) +✔ findNewestSession: returns null for a missing dir (0.150221ms) +✔ readSession: extracts fields, collapses/truncates text, counts a truncated final line (0.912704ms) +✔ readSession: model and provider follow the latest model_change entry or assistant turn; null when the log names neither; scanAgent carries them (2.004588ms) +✔ readSession: lastError carries the assistant errorMessage only when the last assistant turn errored (0.831458ms) +✔ findNewestSession/scan: never read sibling auth or secrets next to a sessions dir (1.688843ms) +✔ deriveState: full state table (0.267492ms) +✔ rule: newest entry is an assistant message with a tool call, after a question-looking text, is working (0.485035ms) +✔ rule: newest entry is a tool result with no assistant text after it is working (0.417277ms) +✔ rule: a finished ordinary turn is idle, even if it says your move (0.419062ms) +✔ task: the first user message of the session, from text blocks (0.387843ms) +✔ task: a plain-string user content is accepted, whitespace collapsed and long text capped (0.42006ms) +✔ task: no user message in the log means null (shown as unknown), never a guess (0.451738ms) +✔ workspace: the live tmux pane path wins; the session cwd is the fallback; neither means null (0.73409ms) +✔ activeProject: basename of the nearest .git directory or .git file above the workspace; none means null (1.270857ms) +✔ scan: the written record carries task, workspace and activeProject (0.772335ms) +✔ registration: overrides task, project and workspace; every source says registration; registered carries the launch fields; the grouping column is untouched (0.921072ms) +✔ registration: empty task and null project/workspace leave the derived values in place; registered is still non-null (0.610015ms) +✔ 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.50323ms) +✔ registration: no registration leaves the Gate A fields exactly as before, and registered is null (0.680322ms) +✔ loadRegistrations: a missing seatsDir gives empty lists (0.248283ms) +✔ loadRegistrations: one good record, one malformed JSON, one with an unknown field; a stray file under seatsDir is ignored (1.064486ms) +✔ 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.379473ms) +✔ scan: writes the registration override to disk; index.json carries registered and registrationErrors (0.881035ms) +✔ scan: a relative seatsDir throws ConfigError; an omitted seatsDir behaves as before (0.775753ms) +✔ scanAgent: waitingOnYou is true for waiting/error and false otherwise (0.529713ms) +✔ scanAgent: ageSeconds is computed from the injected now (0.268697ms) +✔ scanAgent: sessionFile null and state idle when sessions dir is empty but alive (0.153852ms) +✔ discoverRepoAgents: finds agents with a sessions dir, skips those without, sorted by name (0.351278ms) +✔ discoverFleetAgents: finds agents with a sessions dir, sorted by name, fleet tmux fields (0.495575ms) +✔ scan: writes per-agent files and index.json, rerun overwrites, no leftover tmp files (1.087513ms) +✔ scan: relative boardDir throws ConfigError (0.101032ms) +✔ CLI: scan with assume-alive liveness exits 0, prints board summary, writes board files (74.511371ms) +✔ CLI: missing config exits 2 with a refused: message (61.342575ms) +✔ CLI: unknown command exits 2 (77.494963ms) +✔ CLI: unknown --liveness value exits 2 (67.918675ms) +✔ panesRunPi: true when any trimmed line equals 'pi' (0.256634ms) +✔ panesRunPi: false for bash-only, claude, empty, or node-pi-style lines (0.082198ms) +✔ tmuxIsAlive: a pane running pi is alive (0.226101ms) +✔ tmuxIsAlive: session exists but pi has exited is not alive (0.089543ms) +✔ tmuxIsAlive: no such tmux session is not alive (0.077396ms) +✔ tmuxIsAlive: tmux could not be run at all is unknown (null), never assumed alive (0.087494ms) +✔ tmuxIsAlive: passes -L only when a socket is given (0.107476ms) +✔ parsePanes: one pane per line, command and optional tab-separated path (0.091544ms) +✔ tmuxInspect: reports the path of the pane running pi, not of a shell pane (0.079394ms) +✔ tmuxInspect: no pi pane, no session, or no tmux gives no workspace and the matching liveness (0.112923ms) +✔ loadSeen: missing file returns {} (0.176853ms) +✔ loadSeen: invalid JSON throws ConfigError (0.215421ms) +✔ loadSeen: a JSON array throws ConfigError (0.270344ms) +✔ loadSeen: a non-string value throws ConfigError (0.199671ms) +✔ markSeen: seen true adds the key and writes seen.json mode 0600, no leftover tmp files (0.401563ms) +✔ markSeen: seen false deletes the key (0.297031ms) +✔ markSeen: missing, empty, or non-string fields throw ConfigError (0.248251ms) +✔ markSeen: project containing '/' throws ConfigError (0.152326ms) +✔ markSeen: non-boolean seen throws ConfigError (0.128344ms) +✔ scanAgent: a seen mark matching the waiting session's lastTimestamp clears waitingOnYou (0.341063ms) +✔ scanAgent: a stale mark (agent wrote something newer) is not seen and waitingOnYou is true (0.251943ms) +✔ scanAgent: a working session with a matching mark is not seen (marks only apply to waiting/error) (0.252277ms) +✔ scanAgent: an error-state session with a matching mark is seen (0.294401ms) +✔ scan: index.seen and waitingOnYou reflect seen.json, which scan never rewrites or deletes (0.565515ms) +✔ scan: a corrupt seen.json makes scan throw ConfigError (fail closed) (0.231652ms) +✔ 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.765658ms) +✔ 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.684286ms) +✔ taskSetBy: scan() reads the field from disk through the seat package (bounded there), writes it to the per-agent record and index, and an invalid on-disk value is a registrationError, never a row value (1.186219ms) +✔ isLoopbackHost: recognizes loopback hosts (1.494333ms) +✔ isLoopbackHost: rejects non-loopback hosts (5.060193ms) +✔ startServer: refuses a non-loopback host with ConfigError, never opens a socket (3.605504ms) +✔ startServer: serves page, healthz, and a rescanning /api/board (38.661997ms) +✔ startServer: a seatsDir registration overrides the row and index.registered reflects it (7.651859ms) +✔ startServer: /api/board returns 500 JSON with an error field when scan throws (2.727ms) +✔ CLI: serve refuses a non-loopback host with exit 2 and a refused: message (63.239491ms) +✔ CLI: serve rejects a non-numeric --port with exit 2 (69.154042ms) +✔ CLI: scan still works after the async cli refactor (64.98425ms) +✔ CLI: live serve prints its URL and answers /healthz (69.66211ms) +✔ page.html: esc() escapes every HTML-significant character (0.655725ms) +✔ POST /api/seen marks a row; GET /api/board still shows it seen; seen:false clears it (8.302311ms) +✔ POST /api/seen without a JSON content-type returns 400 and does not write a mark (1.985436ms) +✔ POST /api/seen with invalid JSON returns 400 (2.886182ms) +✔ POST /api/seen with a body over 4096 bytes returns 400 (or resets the connection) and writes no mark (2.189468ms) +✔ POST /api/seen with a missing agent returns 400 (1.39406ms) +✔ POST /api/board returns 405; PUT /api/seen returns 405 (1.75052ms) +✔ CLI: scan --print marks a seen row with 's' and the summary line ends with 'N seen)' (57.024677ms) +✔ page.html: seenControl() escapes rec.project/agent/lastActivity, and the POST uses a JSON content-type (0.326067ms) +✔ page.html: has a collapsed Seen section that lists seen rows with the shared row builder (0.262888ms) +✔ page.html: each project has a Hide seen checkbox (default on) beside Hide offline, with a hidden-count note (0.163235ms) +✔ page.html: a project header reads "N of N" only while a checkbox hides rows (0.124708ms) +✔ page.html: every row shows Task and Active project, derived or the word unknown, with the workspace in the detail (0.322965ms) +✔ 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.570669ms) +✔ POST /api/reply: runs agent-send.sh with -s from the registration, -S :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 (30.544502ms) +✔ POST /api/reply: a registration with a tmux socket adds -L (30.97077ms) +✔ POST /api/reply: a non-zero tool exit is a 200 with delivered false, the exit code and the stderr verbatim (25.810815ms) +✔ 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.085827ms) +✔ POST /api/reply: a missing agent-send.sh is a 500 with the path in the error, not a crash (5.444484ms) +✔ replyToRow: DEFAULT_AGENT_SEND is the repository's tools/tmux/agent-send.sh and it is executable (0.187269ms) +✔ 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.588031ms) +✔ startServer: /api/board carries taskSetBy from a live registration and null for the derived rows (2.864235ms) +✔ 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.32637ms) +✔ Host/Origin guard: GET /api/board and POST /api/reply refuse a foreign Host, a wrong port and a cross-origin Origin with 403 JSON, before any scan or send, and never send CORS headers (8.426521ms) +✔ Host/Origin guard: loopback names on this port are accepted, with or without a same-origin Origin (31.234601ms) +✔ conversation routes (F16): a foreign Host, a wrong port and a cross-origin Origin get 403 before the reader runs, with no CORS headers (6.307879ms) +✔ every refusal code the reader can raise has an HTTP status (1.362529ms) +✔ conversation routes: catalogue, first page, next page and follow over HTTP; refusals map to 4xx with their code; nothing is written (58.278992ms) +ℹ tests 124 +ℹ suites 0 +ℹ pass 124 +ℹ fail 0 +ℹ cancelled 0 +ℹ skipped 0 +ℹ todo 0 +ℹ duration_ms 739.046363 diff --git a/agents/filbert/work/s4-follow-up-review/r1-cand-node-conversation.txt b/agents/filbert/work/s4-follow-up-review/r1-cand-node-conversation.txt new file mode 100644 index 00000000..ceeaba47 --- /dev/null +++ b/agents/filbert/work/s4-follow-up-review/r1-cand-node-conversation.txt @@ -0,0 +1,205 @@ +✔ W1: two processes acquire the same pair at once; exactly one claim (133.852204ms) +✔ W1: two writers publish the same revision at once: one wins, the other gets null, the winner's record stays (9.903435ms) +✔ W1: a revision name appears only after its bytes are synced; before that, only a temp file exists (4.896975ms) +✔ W2: acquire while a claim is reserved or active refuses already-active (135.640382ms) +✔ W3: acquire while stopping, uncertain, or stopped without proof refuses unsafe-replacement (206.391742ms) +✔ W4: same session with another seat tuple, and the reverse, both refuse; a loser on the seat key closes it no-unit (221.599112ms) +✔ W4: a hard link of one session under another seat is the same session: the second controller refuses already-active and launches nothing (23.384355ms) +✔ W4: a copy of one session under another seat is the same session: the second controller refuses already-active and launches nothing (24.450943ms) +✔ W4: a session header ID that changes after construction refuses target; nothing is claimed or launched (5.102513ms) +✔ W5: SIGKILL between every publication barrier of acquire and transition; restart never finds two holders or a lost claim (5568.837794ms) +✔ W5: SIGKILL between every publication barrier of release; restart finishes or holds the release (21997.530269ms) +✔ W6: controller killed mid-turn while the engine lives; restart is uncertain, no launch, prompts refuse (155.448481ms) +✔ W12: a live owner paused with SIGSTOP; a second controller refuses already-active and changes nothing (92.901015ms) +✔ W13: crash after the engine spawns, before active; restart finds the live unit: uncertain, no second spawn, force stop only (227.675785ms) +✔ W14: crash after reservation, before the spawn marker: stopped with a no-unit observation; the pair is free (210.868261ms) +✔ W20: crash after the spawn marker, scope collected; uncertain in both runs, the marker is copied, no launch until a boot proof (234.593289ms) +✔ W15: crash between the two keys during release; restart finishes it under the same claim ID (36.707665ms) +✔ W7: recorded boot ID differs on the same machine: stopped with a boot proof; open tool calls become uncertain (112.154849ms) +✔ W8: resume after a proven stop with the same pins: new claim ID, generation +1, same conversation, branch and leaf (36.795317ms) +✔ W9: resume with a changed binary, argv digest, branch or leaf is refused and the claim is unchanged (89.467083ms) +✔ W11: the controller writes no session file; only the fake engine's own appends appear (21.736151ms) +✔ W16: a highest revision that won't parse holds the pair uncertain; the older stopped revision is not reused (54.569487ms) +✔ W17: a claim root copied from another host refuses foreign-host and promotes nothing (59.350817ms) +✔ G1: a session path or claim root under .pi/state, ~/.claude, the data root or a registration refuses at construction (4.251176ms) +✔ G2: a symlink inside the fixture root to a live session file is refused by the real-path check (1.178681ms) +✔ G3: a fixture path swapped for a live path after construction is refused at bind (1.751493ms) +✖ K1: force stop kills a tool child that called setsid; stopped with a verified proof (2202.925091ms) +✔ K2: K1 on the process-group fallback ends uncertain, never stopped (131.632977ms) +✖ K3: SIGTERM acknowledged while a member lives: stopping until the kill phase, never stopped from TERM (2166.353377ms) +✔ K4: two engines; force stop one; the other survives by independent observation (4254.701406ms) +✔ K5: a stop during a tool call leaves the effect uncertain, and it is shown (2151.481499ms) +✔ K12: a member forking in a loop: the freeze stops it, enumeration is complete, populated 0 after cgroup.kill (2249.888496ms) +✔ K13: a member writing its pid into another cgroup is refused by the namespace; the kill is complete (2151.391984ms) +✔ K15: the shim gone, engine/cgroup.events unreadable, or the engine cgroup missing: evidence unavailable, not empty; uncertain (4379.099031ms) +✖ K10: controller killed between the TERM and kill phases: restart checks the invocation ID and re-runs from TERM for the same stop (209.431279ms) +✔ K11: controller killed after the confirmation is recorded, before TERM: restart checks the invocation ID and re-runs from TERM for the same stop (378.264182ms) +✔ K14: a unit with the recorded name but another invocation ID: evidence unavailable, no signals, uncertain (303.168036ms) +✔ K6: recover without proof, without confirmation, or with changed pins is refused (63.5942ms) +✔ K7: recover after proof, then launch: new claim and execution, generation +1, same leaf; the cancelled prompt is not replayed (31.907608ms) +✔ K8: an engine that loads another leaf on resume is refused before admission; it stays claimed until a proven stop (39.622245ms) +✔ K9: an interrupt that never settles stays uncertain; force stop stays available; takeover is refused while fenced (3026.332595ms) +✔ K16: a claim from another machine ID refuses foreign-host; no boot proof is issued (5.924081ms) +✔ K17: two launcher calls with one eligibility record: one launch, the other refuses, no second engine (31.602414ms) +✔ K18: the leaf changes after eligibility: launch refused; the reservation stays until released with proof (27.632187ms) +✔ S1: `/goal x`, with leading spaces or a tab, refuses text-policy at admission; zero engine bytes (41.162844ms) +✔ S2: every prefix pinned Pi interprets is refused, from the list the code uses; the rest reach the engine exactly (30.407461ms) +✔ S3: `/goal` on the second line is pinned from the source: Pi checks only index 0, so it is admitted and sent exactly (21.591465ms) +✔ S4: a `/` left in the composer is cleared when control transfers and returns; the next submit sends only the new text (38.967149ms) +✔ S5: an observer terminal gets a paste then Enter, as send-message.sh does: not admitted: controller, nothing sent (24.046296ms) +✔ S6: a mediated-shaped registration (no tmux) passed to the board's replyToRow: 409 no tmux session; exec never runs (0.517583ms) +✔ S7: ESC, bracketed-paste markers and U+2028/U+2029 travel as one JSON string; the engine receives the exact text in one record (20.4388ms) +✔ P3: a Pi confirm, select, input or editor dialog is shown disabled with a reason and never answered (126.035815ms) +✔ E1: send, ack, user, toolCall, toolResult, final answer: shown once, no refresh, draft and reading position kept (36.777384ms) +✔ E2: U+2028, U+2029 inside JSON strings and CRLF line ends each parse as one record, on the splitter and through the controller (18.087403ms) +✔ E3: a multipart final, two blocks, null request correlation and duplicate delivery (31.009894ms) +✔ 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 (39.751146ms) +✔ E4: a gap or a new epoch also reconciles; nothing is concatenated across a gap (11.347106ms) +✔ E5: an unknown native event gives no client event; evidence records its type and bytes; the terminal count goes up (30.498751ms) +✔ E6: a tool result delayed across a pause and a reconnect is reconciled without a manual refresh (50.879279ms) +✔ E7: the terminal renders the same stream as the library client, as observer and then as controller, and submits only as controller (43.41427ms) +✔ terminal: engine control characters are made visible; a lost connection refuses submit (29.777275ms) +✔ terminal: outcome unknown is shown as such, with no resend offer, and nothing is resent (1.192976ms) +✔ terminal: text after Enter in the same input chunk starts the next message; it never joins the one submitted (0.508887ms) +✔ terminal: a paste-start marker split right after its ESC still opens the paste; the Enter inside it never submits (0.635774ms) +✔ terminal: invisible and bidi characters are made visible; head, status and notice lines stay one line (0.203619ms) +✔ every record these fixtures produced is a valid CHAT-01 record (E5: no record fails the schema) (426.881843ms) +✔ H1: two takeovers with the same expected generation: one wins, +1; the other refuses generation (57.446613ms) +✔ H2: the old controller's prompt after a takeover commits is refused with zero engine bytes (82.431468ms) +✔ H3: a takeover while a prompt holds the dispatch lock: written under the old actor, or refused; never both (133.261666ms) +✔ H4: self-takeover is refused (16.211065ms) +✔ H9: Interrupt racing a prompt's dispatch: before the write, dispatch-refused and no-turn; after, §3 rules (97.939907ms) +✔ H10: Interrupt and force stop together: one stop chain, force stop supersedes (90.004566ms) +✔ H10: an overlap during the pause before the abort: no abort, the stop ends uncertain (22.175376ms) +✔ H10: a no-turn Interrupt lifts only its own fence; admission stays closed under force stop, overlap or revocation (101.29259ms) +✔ H11: the controller disconnects mid-turn: work continues, the claim is unchanged, control stays put (162.330347ms) +✔ H12: an exact retry after reconnecting to the same incarnation returns the same receipt; one dispatch (23.073584ms) +✔ H13: a retry with the same request ID and different text is refused (20.503171ms) +✔ H14: late stdout from the old engine after a replacement is dropped by incarnation, counted, never rendered (160.147974ms) +✔ H15: a revoked connection's command is refused; the revocation fence holds (70.472627ms) +✔ H16: a second controller for the same session refuses already-active; the first is untouched (20.528122ms) +✔ H10: a second force stop while the first escalation runs refuses fenced; one escalation, and the claim records only the first stop's phases (57.87053ms) +✔ H17: a confirmation reused, answered from another connection, or used after the stop changed is refused (60.931438ms) +✔ H18: two prompts before any native output: the second refuses busy; one engine write (13.031369ms) +✔ H19: the pipe fails mid-line under a large prompt: delivery-unknown transport-unknown, poisoned, no later write (120.535221ms) +✔ H19: the link itself never writes again after an unknown outcome, whoever calls it (0.769166ms) +✔ H19: the controller dies mid-write of a large line: after restart the outcome is unknown and nothing is resent (474.647371ms) +✔ H20: the line is written but the ack is lost when the controller dies: orphan, outcome unknown, nothing resent (402.296798ms) +✔ H21: a retry of the exact request with the old token after a crash is stale-incarnation; no second write (335.17129ms) +✔ H22: after H21 and a valid recovery, a new request with the new token is admitted (2389.207984ms) +✔ H23: requests pending at a restart are not resent; each shows outcome unknown (459.593295ms) +✔ a plain conversation: catalogue row, one page, CHAT-01 records (11.130499ms) +✔ native entries map to blocks: tools, thinking, bash, notices, ids that do not fit (2.570142ms) +✔ F1: a malformed line is an unavailable part at its position, and reading continues (2.07203ms) +✔ F1: a missing parent stops the history with a notice that names the unreadable lines (2.811213ms) +✔ F1: an unreadable fork is never merged into another branch's history (1.359937ms) +✔ F1: a follow stays on its branch when the next entry's parent is unreadable (2.4616ms) +✔ F1: a file whose entries are all unreadable shows a notice per line (1.05576ms) +✔ F2: a truncated trailing line marks the view incomplete, not an error (1.678293ms) +✔ pagination: 100 parts, then the rest; parts concatenate to the whole branch (5.10643ms) +✔ F3: a replaced file (new inode) refuses old cursors with reconcile (5.602312ms) +✔ F4: a same-inode rewrite of the prefix refuses old cursors with reconcile (7.735978ms) +✔ F5: growth between pages keeps the epoch and the page stops at the pinned length (6.262886ms) +✔ F6: unknown, foreign and expired cursors refuse and leave the cursor usable (11.55803ms) +✔ F7: a symlinked file and a symlinked directory component are refused, never opened (15.622698ms) +✔ F8: a file swapped for a symlink after the catalogue is refused (2.769422ms) +✔ F9: registrations never add or redirect a root (2.073453ms) +✔ F10: a header cwd naming another project is refused (5.419193ms) +✔ F11: parentSession renders with a marker and the parent is never opened (0.926192ms) +✔ F12: two leaves: the default leaf is shown and the other branch reads alone (7.514641ms) +✔ F12: a follow refuses when an appended duplicate id changes the branch's earlier parts (4.071265ms) +✔ F12: a second root (Pi's resetLeaf) starts its own branch (1.694144ms) +✔ F13: compaction is a marker in place, then the retained content (0.920202ms) +✔ F14: long strings split into fragments and parts, reassemble exactly, and pages respect the byte cap (754.032088ms) +✔ fragments never cut a surrogate pair and keep an empty string (6.844356ms) +✔ F15: a Claude seat is an unsupported-harness placeholder whose directory is never read (2.724415ms) +✔ unknown conversations, empty files and non-Pi files refuse (6.117025ms) +✔ an unreadable file or root inside the roots is refused per row, not a failed catalogue (3.380886ms) +✔ a seat directory without search permission refuses that root, not the catalogue (3.162776ms) +✔ every page and cursor is a valid CHAT-01 record (906.130106ms) +✔ the engine pin holds for the installed package (4.095079ms) +✔ pinned Pi, sealed and without credentials, answers the controller's commands with the shapes the fake models (329.078059ms) +✔ pinned Pi appends thinking_level_change at start when the branch lacks one, so the leaf moves (K8 then fails closed) (367.946117ms) +✔ N25: ordinary Interrupt reconciles; a non-empty queue_update in the window is O5 (86.798399ms) +✔ N1: an extension's follow-up queued after the fence is cleared before any abort; O5, Unknown (48.062807ms) +✔ N1: a follow-up queued before the fence is O5 at once; the Interrupt refuses fenced (26.288732ms) +✔ N2: with abort first, the fake runs the external item (the ordering guard has teeth) (22.777314ms) +✔ N3: the fence lands in preflight, preflight errors, no run: failed, No run, uncertain (32.219782ms) +✔ N4: the ack arrives after the first abort and a run starts: clear and abort again; Interrupted (23.411923ms) +✔ N5: an input handler takes the prompt: ack, no run, delivery-unknown handled-without-run (116.896584ms) +✔ N6: an extension queues between clear_queue and abort: O5 and O6, Unknown (43.955144ms) +✔ N7: clear_queue times out: no abort, nativeQueue unknown, force stop still ends it (1542.958269ms) +✔ N7: clear_queue answers an error: no abort, nativeQueue unknown, the link not poisoned (19.768859ms) +✔ N8: an extension prompt starts a run during Mosaic preflight; the losing settle is O3 (81.992353ms) +✔ N9: a run that started before the fence and ends aborted: failed interrupted, Interrupted (15.892432ms) +✔ N9: decision 34: a run that ends aborted with no stop in progress: aborted-without-stop, uncertain, outcome unknown (17.178428ms) +✔ N9: an aborted that lands after the fence but before any abort is written: aborted-without-stop, Unknown (33.514465ms) +✔ N10: fake conformance (32.242568ms) +✔ N11: the run fails before any user message_start: delivery-unknown ack-without-start, never failed (31.604688ms) +✔ N12: input that starts a run after the final empty clear is O1 and not part of the stop's proof (19.419789ms) +✔ N13: agent_start with no slot held is O1; a later prompt refuses with zero engine bytes (64.723847ms) +✔ N14: the run completes while clear_queue is in flight: finished, Completed first, uncertain (30.077104ms) +✔ N14: the run completes after the abort is written, before Pi applies it: finished, never relabelled (32.547228ms) +✔ N15: the fence lands in preflight, then an input handler takes it: handled-without-run, No run (23.737116ms) +✔ N16: Interrupt with no slot and no run refuses no-turn: no stop, no bytes, admission open (15.431539ms) +✔ N17: the run fails on its own during the exchange: failed, Failed on its own (30.152089ms) +✔ N18: no final assistant message_end, or a lost line: working stays working; before working, transport-unknown (113.257341ms) +✔ N19: a losing extension prompt settles inside the Mosaic run before its user message: O3, run-overlap (134.703827ms) +✔ N20: an extension triggerTurn during Mosaic preflight starts first; while streaming it queues with no signal (84.206586ms) +✔ N21: a losing settle after the receipt settled finished is O2; the receipt stays finished (17.434773ms) +✔ N22: an agent-level custom message is dropped by the clear with no signal; evidence names the seal (15.350181ms) +✔ N23: a nextTurn message survives clear and abort and attaches to the next prompt, with no signal (13.781605ms) +✔ N24: the seal is an allow-list: --extension, a missing --no-* flag, a second --mode or --session, a session or output flag, or a stray word refuses unsealed-engine; no engine starts (42.472646ms) +ℹ tests 152 +ℹ suites 0 +ℹ pass 149 +ℹ fail 3 +ℹ cancelled 0 +ℹ skipped 0 +ℹ todo 0 +ℹ duration_ms 31680.952099 + +✖ failing tests: + +test at packages/conversation/tests/cohort.test.mjs:139:1 +✖ K1: force stop kills a tool child that called setsid; stopped with a verified proof (2202.925091ms) + AssertionError [ERR_ASSERTION]: the escaped child is a listed member + at TestContext. (file:///home/jwoltje/filbert-scratch/r45/cand/packages/conversation/tests/cohort.test.mjs:151:12) + at async Test.run (node:internal/test_runner/test:1409:7) + at async startSubtestAfterBootstrap (node:internal/test_runner/harness:387:3) { + generatedMessage: false, + code: 'ERR_ASSERTION', + actual: false, + expected: true, + operator: '==', + diff: 'simple' + } + +test at packages/conversation/tests/cohort.test.mjs:176:1 +✖ K3: SIGTERM acknowledged while a member lives: stopping until the kill phase, never stopped from TERM (2166.353377ms) + AssertionError [ERR_ASSERTION]: the member ignored TERM + at TestContext. (file:///home/jwoltje/filbert-scratch/r45/cand/packages/conversation/tests/cohort.test.mjs:190:12) + at async Test.run (node:internal/test_runner/test:1409:7) + at async Test.processPendingSubtests (node:internal/test_runner/test:974:7) { + generatedMessage: false, + code: 'ERR_ASSERTION', + actual: false, + expected: true, + operator: '==', + diff: 'simple' + } + +test at packages/conversation/tests/cohort.test.mjs:426:3 +✖ K10: controller killed between the TERM and kill phases: restart checks the invocation ID and re-runs from TERM for the same stop (209.431279ms) + AssertionError [ERR_ASSERTION]: the member is alive across the crash + at TestContext. (file:///home/jwoltje/filbert-scratch/r45/cand/packages/conversation/tests/cohort.test.mjs:431:12) + at process.processTicksAndRejections (node:internal/process/task_queues:104:5) + at async Test.run (node:internal/test_runner/test:1409:7) + at async Test.processPendingSubtests (node:internal/test_runner/test:974:7) { + generatedMessage: false, + code: 'ERR_ASSERTION', + actual: false, + expected: true, + operator: '==', + diff: 'simple' + } diff --git a/agents/filbert/work/s4-follow-up-review/r1-cand-node-discord.txt b/agents/filbert/work/s4-follow-up-review/r1-cand-node-discord.txt new file mode 100644 index 00000000..f9272989 --- /dev/null +++ b/agents/filbert/work/s4-follow-up-review/r1-cand-node-discord.txt @@ -0,0 +1,186 @@ +✔ approvals: a request is validated before anything is posted; the rendering shows names and never ids (3.652717ms) +✔ approvals: the ledger is appended and folded into open requests with bind and approval states (2.095993ms) +✔ approvals: a reply approves only when it points at a request, says exactly approve, and comes from a listed approver once (0.54469ms) +✔ approvals: a button approves only on its own request message with the matching custom id (0.452789ms) +✔ approvals flow: a turn that opened a request posts the message with the button, records it, binds it, and both approvers approve (23.493731ms) +✔ 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.837264ms) +✔ approvals flow: an invalid request from the model, a refused post, and no api client are recorded and post nothing (7.33591ms) +✔ approvals flow: start retries a bind and an approval left as unknown, under their original keys (2.359428ms) +✔ authorize: open channel, listed user (2.063606ms) +✔ authorize: wrong guild (0.232728ms) +✔ authorize: no guild (DM) (0.315948ms) +✔ authorize: unlisted channel (0.197313ms) +✔ authorize: unknown channel, no info (1.556667ms) +✔ authorize: thread of listed parent (0.220056ms) +✔ authorize: thread of unlisted parent (0.194441ms) +✔ authorize: text channel that is not a thread and not listed (0.389674ms) +✔ authorize: unlisted user (1.236333ms) +✔ authorize: no author (0.268897ms) +✔ authorize: bot author (listed id, bot flag) (0.144111ms) +✔ authorize: system author (0.957145ms) +✔ authorize: the bot itself (0.097035ms) +✔ authorize: webhook (0.788496ms) +✔ authorize: mention channel without mention (0.216629ms) +✔ authorize: mention channel with bot mention (0.126082ms) +✔ authorize: mention channel with @everyone only (0.200495ms) +✔ authorize: mention channel mentioning someone else (0.080933ms) +✔ authorize: mention channel, content says @bot but mentions empty (0.118102ms) +✔ authorize: private thread under mention channel, mentioned (0.088496ms) +✔ authorize: private thread under mention channel, not mentioned (0.075425ms) +✔ authorize: thread in another guild per channel info (0.074497ms) +✔ authorize: not an object (0.071747ms) +✔ authorize: no id (0.080135ms) +✔ authorize: oversize content is accepted and flagged (0.080836ms) +✔ authorize: exactly the limit is not oversize (0.068839ms) +✔ authorize: a user's channel allowlist drops them outside it, threads count as the parent, others are unaffected (0.468829ms) +✔ authorize: order puts wrong guild before user, and user before channel (no channel lookup for strangers) (0.321456ms) +✔ binding: a complete binding validates and is frozen (2.628137ms) +✔ binding: unknown key, missing field, wrong type refuse with exit 2 (1.454888ms) +✔ binding: empty allowlists refuse (0.431805ms) +✔ binding: a user's channel allowlist must be non-empty, listed and unique; absent means every listed channel (1.282583ms) +✔ reloadDiff: reloadable keys are summarised by id; every fixed key refuses with exit 2 (1.965346ms) +✔ binding: dmRecipient is optional, a snowflake, one of the listed users, and a fixed key (1.284463ms) +✔ binding: file must be 0600, regular, not a symlink (2.055307ms) +✔ binding: token file mode, symlink, emptiness and shape are checked; token never appears in errors (1.46213ms) +✔ cli: check refuses a non-0600 token file with exit 2 before any network use (96.721294ms) +✔ context files: absolute paths, traversal, symlinks and out-of-repo targets refuse; in-repo files resolve (1.578284ms) +✔ cli: check refuses a missing context file and a missing binding with exit 2; usage is exit 4 (355.155821ms) +✔ cli: reload validates the file first (exit 2), then needs a live owner (exit 1); usage is exit 4 (234.212192ms) +✔ cli: run refuses when STOP is present, before any network use (147.786226ms) +✔ binding: tools is optional, validated strictly, a fixed key for reload, and its roots are resolved against the data root (1.908457ms) +✔ binding: a git key is validated at load and reaches the extension whole, and only on a writable root (1.315136ms) +✔ 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 (23.053597ms) +✔ delivery: refused and unknown outcomes are journaled; a later chunk is not sent after a failure (17.105767ms) +✔ delivery: restart with an unknown entry re-sends the same nonce once and reconciles before accepting traffic (1.598351ms) +✔ delivery: an unknown entry older than the dedupe window is marked refused, not re-sent; a still-unknown one refuses start (1.109963ms) +✔ delivery: repeated unknown reconciliations never refresh the dedupe window; the original intent time decides (1.312252ms) +✔ turn: a failed engine turn posts the fixed line, never model output, and writes a failed record (2.646579ms) +✔ turn: a second message during a turn is held by the engine, both get their own reply and record (33.105429ms) +✔ turn: a thread under a listed channel is answered in the thread; an unknown thread is looked up once (3.300546ms) +✔ drop: an unlisted user gets silence and one drop line; no inbox entry, no REST call, no engine call (1.045885ms) +✔ drop: an oversize message is accepted into the inbox, answered with the fixed line and journaled as a drop (1.001041ms) +✔ restart: an inbox with three ids and a replay of the same three produces zero turns (43.015168ms) +✔ stop: STOP present refuses start; STOP written while running refuses new turns and the current one finishes (31.318708ms) +✔ ceiling: the ceiling plus one is refused and journaled; one fixed line per UTC day; a new day accepts again (5.996738ms) +✔ ceiling: a burst arriving while turns are still running cannot queue past the ceiling (3.01912ms) +✔ ceiling: a turn interrupted by a crash still counts after restart; admissions are durable (2.559289ms) +✔ ceiling: the daily notice survives a same-day restart; one delivery attempt in total, even when the first attempt crashed mid-flight (4.581434ms) +✔ duplicate: the same event delivered twice while the thread lookup is held yields one prompt, one admission and one reply (1.157529ms) +✔ journal: no token-shaped string and no model output on the drop path reaches disk (0.426272ms) +✔ 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.700785ms) +✔ receipt: Discord refusing the reaction leaves the turn intact and records ok false (2.075005ms) +✔ reload: a new user is silent before and answered after; a removed channel goes silent; a lower ceiling applies at once (3.891254ms) +✔ reload: a fixed key refuses with exit 2 and the old binding stays in force (2.128902ms) +✔ tools: with a tools binding the turn record lists every read and its outcome; without one the field is null (3.824301ms) +✔ context: the Discord block names the server, channels and modes, and states the rules from Q15 and Q16 (3.098697ms) +✔ context: with tools the block names the roots, keeps file content as data, and says to state refusals plainly (0.835755ms) +✔ context: a writable root adds the write rules and says a write is real only once Jason commits (2.21152ms) +✔ context: the envelope is one bracketed line then the text; names cannot break the line (2.958133ms) +✔ context: a git root swaps the terminal-commit line for the git verbs, and a vault root adds the id protocol (1.624172ms) +✔ context: assembleContext concatenates files in launcher format and appends the block; sha256 is stable (1.738849ms) +✔ context: splitReply keeps paragraphs together under the limit and splits long ones at lines, spaces, then hard (0.688051ms) +✔ engine: buildPiArgs carries the fixed flags, engine settings, session dir and prompt file (4.261033ms) +✔ engine: with tools, buildPiArgs turns pi's own tools off, loads the extension explicitly and allowlists exactly our three (0.451512ms) +✔ engine: a run with tool turns settles once, on the answer, with every tool call in the result (57.228583ms) +✔ engine: a run that ends on a tool-only turn fails the prompt as empty; a retried run settles on the real end (37.77451ms) +✔ engine: one prompt, one turn, text and usage come back (34.179736ms) +✔ engine: a prompt while streaming is held until pi settles, then sent as its own run, and answered in order (344.382562ms) +✔ engine: a held prompt that times out before pi settles fails on its own and is never sent (236.013539ms) +✔ engine: timeout sends abort and fails only that turn; the process stays (104.867427ms) +✔ engine: tool events from a run that outlived its timeout never land in the next prompt's record (232.82586ms) +✔ engine: a prompt after a turn that timed out before its agent_start waits for pi to settle instead of being refused (126.632884ms) +✔ 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 (214.392388ms) +✔ engine: a timed-out turn pi starts only after the grace never answers a later prompt (614.922216ms) +✔ engine: late events of a run past its grace, before pi exits, answer nothing and nothing more is sent (early prompt response) (1.345609ms) +✔ engine: late events of a run past its grace, before pi exits, answer nothing and nothing more is sent (late prompt response) (0.426624ms) +✔ engine: a timed-out run pi did start outlives the grace; the next prompt goes out when it ends (428.485032ms) +✔ engine: a malformed JSONL line fails the turn, not the process (24.737054ms) +✔ engine: a turn that ends in error rejects with the error code; process exit fails pending turns (48.54618ms) +✔ gateway: hello -> identify with intents, ready, heartbeat with jitter, ack (3.303979ms) +✔ gateway: missed ack closes the socket and resumes with the last sequence (1.748646ms) +✔ gateway: op 7 reconnect resumes; op 9 non-resumable re-identifies (0.64364ms) +✔ gateway: op 9 resumable resumes (0.378703ms) +✔ gateway: close 4014 is fatal, reports the missing intent, never reconnects (0.846124ms) +✔ gateway: 4004 and 4013 are fatal too; 1006 reconnects with identify when no session (0.530317ms) +✔ gateway: close() is final and unparseable frames are ignored (0.4019ms) +✔ git: config validation is strict, needs write: true, a work tree and a private token file (67.17032ms) +✔ git: the child environment drops every host git config, names one helper, and carries the token path only for origin (43.499521ms) +✔ git: status reports the branch, ahead/behind and changed paths, and refuses off the named branch or mid-merge (78.79627ms) +✔ git: parseStatus reads porcelain v2 including renames and conflicts (0.291115ms) +✔ git: a commit stages exactly the named files, carries the seat author and the requester trailer, and pushes at once (82.141136ms) +✔ git: commit refusals: message, paths, requester, nothing to commit, and an index that already holds other work (122.090612ms) +✔ git: a commit whose push fails is still a commit, says so, and the next commit's push carries both (D6) (89.938936ms) +✔ git: pull is fast-forward only; a diverged origin or dirty local files refuse with nothing merged (263.800177ms) +✔ git: push pushes the named branch only and reports up to date (79.986947ms) +✔ git: no token value or token path ever reaches a git argument list; outputs are masked and capped (94.991393ms) +✔ git: the credential helper answers get over https from a private file and nothing else (198.30173ms) +✔ git: the vault protocol validates before a commit, honours another owner's lock, reserves ids, and locks around writes (798.143353ms) +✔ lock: the claim is exclusive; a second start against a live owner refuses (4.396422ms) +✔ lock: a stale lock (dead owner, reused pid, or record without start) refuses run and is never signaled; only unlock clears it (76.864189ms) +✔ lock: an incomplete claim (directory without owner record) is busy and refuses run; unlock clears it (0.799364ms) +✔ lock: an owner record that exists but cannot be read is invalid: never signaled, never removed, never claimed over (2.415724ms) +✔ lock: legacy upgrade; a live connector holding a {pid, start} record is unknown, unlock refuses and nothing changes; after it exits, unlock clears it (41.220051ms) +✔ 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 (343.762684ms) +✔ lock: identity syntax; only canonical unsigned decimal start ticks and lowercase boot uuids are identities (0.542473ms) +✔ lock: a process whose start marker or boot id cannot be read refuses to claim (25.632805ms) +✔ lock: a live pid whose identity cannot be read right now is unknown: never signaled, never removed, never claimed over (1.029429ms) +✔ lock: four processes racing for the same binding; exactly one claims it and the others refuse (43.572685ms) +✔ lock: stale handoff; concurrent starts over a stale lock all refuse, nothing reclaims, one unlock then exactly one live owner (183.79061ms) +✔ lock: four-party schedule; claims landing inside an unlock's gap never survive, one unlock leaves no owner and no residue (93.704603ms) +✔ notices: a kind is recorded per UTC day and found again (0.54971ms) +✔ notify: the DM channel opens once, every send carries the nonce, and only the message id comes back (5.184289ms) +✔ notify: refusals and unknowns surface as RestOutcome without ids; a refused send reopens the channel next time (2.440721ms) +✔ notify: no dmRecipient, a non-0600 binding or token, or a missing binding refuse before any network use (3.141812ms) +✔ recover: nothing to do is clean; a lock whose owner is gone or that has no record is cleared and STOP ends up absent (54.032714ms) +✔ recover: an operator STOP refuses with exit 3 and is never removed, whatever the lock says (45.592566ms) +✔ recover: a brake written during the unlock wins; STOP stays with both lines and the start is refused (28.321045ms) +✔ recover: a held binding refuses with exit 3 and writes no STOP: live owner, alive pid without verifiable identity, unreadable record (72.192985ms) +✔ cli: recover exits 0 when ready, 3 behind a brake or a held binding, and run's own STOP refusal is 3 (761.15839ms) +✔ rest: createMessage sends nonce, enforce_nonce, empty allowed_mentions and a soft reply reference (2.576273ms) +✔ rest: 429 waits retry_after and retries; 4xx is refused; 5xx and socket errors are unknown (3.288617ms) +✔ rest: content and nonce limits are enforced locally; typing never throws (0.766124ms) +✔ rest: react PUTs the encoded emoji on the inbound message for @me; 2xx is true, anything else is false and never throws (0.931996ms) +✔ rest: createDm posts the recipient and resolves the channel id; 4xx refused, 5xx unknown, bad id never sent (0.950264ms) +✔ setspark config: a bare https or loopback origin, a private key file, a principal (5.167307ms) +✔ setspark config: reaches the tools config and the binding as a fixed key (2.841201ms) +✔ setspark config: the binding's key survives resolveToolRoots and the engine's JSON hand-off to the extension (1.323203ms) +✔ setspark config: approvers come from the binding's users, never from the binding's setspark key (1.946563ms) +✔ setspark verbs: required_approvers go out as discord ids from names and come back as names (20.420568ms) +✔ setspark verbs: no Discord user id reaches tool text, whatever shape the service returns it in (8.293349ms) +✔ setspark contract: a decision made with names opens a request the connector accepts; names stored by an old record still refuse (5.469769ms) +✔ setspark keys: read per call, one printable token per file, rotation without a restart (6.703627ms) +✔ setspark idempotency keys: principal, turn id, call index; connector keys name a step (0.632777ms) +✔ setspark http core: json in and out, bearer header, idempotency header, fixed user agent, no key anywhere else (3.143337ms) +✔ setspark http core: error bodies become fixed refusals with code and the 409 fields; server text is data, cut (1012.996595ms) +✔ setspark verbs: a setspark key enables the eight verbs and no counters (0.440876ms) +✔ 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.550508ms) +✔ setspark verbs: no turn refuses every write before any request; bad arguments refuse before any request; reads still work (1.366425ms) +✔ setspark verbs: renderRecord caps long output and hides the accepted snapshot (0.222175ms) +✔ setspark api: bind, add_approval (button and reply) and get use integer request ids and the connector's keys (2.498032ms) +✔ tools: config refuses a missing, symlinked, dotted, non-directory or duplicate root and bad limits (3.692007ms) +✔ tools: every escape is refused with a fixed reason and nothing outside the root is read (3.770283ms) +✔ tools: happy paths list, read a window, and search case-insensitively; dotfiles and symlinks never appear (4.802707ms) +✔ tools: the tool set renders text for the model, records details for the journal, and enforces the per-run budget (3.318209ms) +✔ tools: listing and search caps hold (9.386252ms) +✔ tools: credential shapes are caught; ordinary prose and ids are not (0.806032ms) +✔ 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.720349ms) +✔ tools: an unreadable file under the root is skipped by search and refused by read (1.213734ms) +✔ tools: config accepts write: true only as a boolean, and enables the write tools only then (1.268516ms) +✔ tools: every write outside the fence is refused before any byte lands, and no temp file remains (4.495725ms) +✔ tools: write_file leaves the exact bytes, edit_file replaces one exact match, and the set renders the change as uncommitted (2.560525ms) +✔ tools: a target that changed between the check and the rename is refused and the temp file is removed (1.735625ms) +✔ web: config takes an https or loopback-http SearXNG base url and a bounded fetch cap (3.136088ms) +✔ web: address rules refuse every private, loopback, link-local, mapped and multicast form (1.907344ms) +✔ web: web_fetch refuses bad urls, private hosts, rebinding names, non-https redirects, too many hops, error status, non-text bodies, and times out (1024.755556ms) +✔ 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.292763ms) +✔ web: html to text drops scripts, styles and comments, decodes entities and keeps block breaks (0.240366ms) +✔ 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.695733ms) +✔ 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.408608ms) +ℹ tests 178 +ℹ suites 0 +ℹ pass 178 +ℹ fail 0 +ℹ cancelled 0 +ℹ skipped 0 +ℹ todo 0 +ℹ duration_ms 2643.218847 diff --git a/agents/filbert/work/s4-follow-up-review/r1-cand-node-ledger.txt b/agents/filbert/work/s4-follow-up-review/r1-cand-node-ledger.txt new file mode 100644 index 00000000..b36340ee --- /dev/null +++ b/agents/filbert/work/s4-follow-up-review/r1-cand-node-ledger.txt @@ -0,0 +1,86 @@ +✔ a raw token file, with or without one trailing newline, reaches curl only through the config stream (126.128293ms) +✔ the raw path accepts nothing else, and refuses before curl runs (400.542688ms) +✔ the file checks still apply on the raw path: mode, symlink, missing, directory (320.044861ms) +✔ the raw path base URL has no override (68.875532ms) +✔ the JSON path is unchanged, and JSON never falls through to the raw path (317.322975ms) +✔ a file that changes between the two reads refuses before curl runs, with or without a body (768.036835ms) +✔ the token reaches no child environment, even with an inherited CFG or SHELLOPTS=allexport (846.164709ms) +✔ real helper GET HTTP 200 preserves exit 0 without credentials (13.945172ms) +✔ real helper POST HTTP 201 preserves exit 0 without credentials (10.844397ms) +✔ real helper GET HTTP 403 preserves exit 1 without credentials (8.950885ms) +✔ fixture git subjects only, follow-ups and three session kinds (114.725705ms) +✔ text and JSON carry same numbers, open and truncated title (184.761853ms) +✔ missing credentials exit 2, no-issues never calls API and shows unknown (152.862609ms) +✔ empty range gives no rows and zero totals (104.702569ms) +✔ inclusive UTC dates, first-line preamble only, role and seat boundaries (113.97158ms) +✔ close-only issue included, even median, missing metadata stays unknown (118.811086ms) +✔ unique commits but per-issue links count multiple tags once each (129.964094ms) +✔ page cap refuses rather than silently undercounting (114.108135ms) +✔ bad API payload not JSON refuses (113.455091ms) +✔ bad API payload {} refuses (110.844375ms) +✔ bad API payload [{"number":1}] refuses (112.540073ms) +✔ partial or malformed session log refuses with location, not content (121.714506ms) +✔ a U+2028 or U+2029 inside a session string is one line, not a malformed record (107.395892ms) +✔ no sessions is an empty table; symlink source refuses (160.016344ms) +✔ reads only refactor even when another branch is checked out (105.155384ms) +✔ invalid dates, reverse dates and duplicate options refuse (60.13167ms) +✔ T3 agent assignments do not count as human in Table 2 (106.620619ms) +✔ preamble parsing and issue number boundaries (0.513199ms) +✔ T3 header: agent, or board from control-board; anything short of the full header is human (0.149218ms) +✔ no closed issues with human messages means undefined ratio, not invented zero (0.207988ms) +✔ T3: seat, archived, unmapped and Researcher threads count; imported, deleted and other-project threads do not (234.383673ms) +✔ T3: the default path is read from HOME and prints no path line; --no-t3 says so (521.013093ms) +✔ T3: a HOME with no database exits 1 and names --no-t3 (95.790127ms) +✔ T3: a file that is not a database exits 1 and names --no-t3 (101.911798ms) +✔ T3: a seat thread renamed to another seat exits 1 naming thread, title and roles (149.00837ms) +✔ T3: an unmapped thread addressed as a seat exits 1 (117.853131ms) +✔ T3: a header to another thread id is not cross-checked (123.003429ms) +✔ T3: no project, or two, for this root exits 1 (325.128967ms) +✔ T3: a removed column exits 1 and names it (130.524629ms) +✔ T3: a missing table exits 1 and names it (111.192816ms) +✔ T3: a counted row with an unknown role exits 1 without its text (121.609112ms) +✔ T3: a counted row with non-text content exits 1 without its text (119.865902ms) +✔ T3: a counted row with an unparseable created_at exits 1 without its text (117.132498ms) +✔ T3: a missing orchestration_events makes the diagnostic unknown and keeps the counts (219.876231ms) +✔ T3: a human message with no event counts in humanWithoutEvent (123.337704ms) +✔ T3: an unparseable event makes the diagnostic unknown and keeps the counts (217.196841ms) +✔ T3: an event with no string messageId makes the diagnostic unknown and keeps the counts (216.275372ms) +✔ T3: an error that is not from SQLite is rethrown, not reported as a database failure (60.752619ms) +✔ T3: a symlink at ~/.t3 exits 1 (87.471658ms) +✔ T3: a symlink at ~/.t3/userdata exits 1 (94.051514ms) +✔ T3: a symlink at ~/.t3/userdata/state.sqlite exits 1 (89.587247ms) +✔ T3: with --t3-db, a symlinked file or directory exits 1 (180.000997ms) +✔ T3 WAL: the newest message only in -wal, writer attached, is counted (117.32872ms) +✔ T3 WAL: stopped cleanly, counts are correct and the main file is unchanged (131.14448ms) +✔ T3 WAL: -wal without -shm in a writable directory is read (151.206765ms) +✔ T3 WAL: -wal without -shm in a read-only directory exits 1 (138.257245ms) +✔ T3 WAL: stopped cleanly in a read-only directory exits 1 (126.693454ms) +✔ T3: a lock held past the 5 s busy timeout exits 1 and names --no-t3 (5132.393612ms) +✔ a done row whose closing issue is open is a violation; a row that is not done is not (1.841977ms) +✔ an issue several rows close is expected closed only once all of them are done (0.491939ms) +✔ closure needs positive evidence: unknown is undecided, and so is a skipped or short issue check (0.38455ms) +✔ each owner of an in-progress or in-review row gets one liveness class (7.577627ms) +✔ a required row not done after 14 days is a violation; a legacy row uses genesis as its lower bound (0.408923ms) +✔ an ISO requiredSince, as `set required` writes it, ages from its UTC day; one that does not parse is a violation (0.219338ms) +✔ the text section always ends in a count and a result, and never prints a full pass (0.328283ms) +✔ pidAlive: a running pid is present, an exited one is gone, and EPERM still means present (23.340081ms) +✔ issue states: open list first, then the metric page, then at most 10 lookups (308.946454ms) +✔ a full open list: lookups settle what it leaves out, and only an unsettled issue keeps it undecided (395.879857ms) +✔ the open list refuses on a failed call or a bad record, and never echoes the helper (212.507314ms) +✔ a helper call past the deadline is killed with its child, and the call reports it (2009.325987ms) +✔ readQueue loads queue.json through the queue validator and refuses anything else (116.571991ms) +✔ protected changes list every in-range entry that changes a required or parked row (254.072185ms) +✔ the CLI prints the queue section above the weekly table and under a queue key in --json (549.337582ms) +✔ a queue with nothing wrong prints 0 violations and a reduced pass, never a full pass (213.387035ms) +✔ --no-issues makes no call and leaves the issue checks undecided; --no-queue skips the section (277.27238ms) +✔ the CLI refuses a bad queue before any call, and a failed open list with exit 2 (243.798875ms) +✔ --unsupported-runtime repeats once per seat and takes a seat name (309.141792ms) +✔ an unreadable config makes every owner invalid instead of passing them (129.721951ms) +ℹ tests 78 +ℹ suites 0 +ℹ pass 78 +ℹ fail 0 +ℹ cancelled 0 +ℹ skipped 0 +ℹ todo 0 +ℹ duration_ms 11441.871862 diff --git a/agents/filbert/work/s4-follow-up-review/r1-cand-node-mosaic.txt b/agents/filbert/work/s4-follow-up-review/r1-cand-node-mosaic.txt new file mode 100644 index 00000000..b8119edb --- /dev/null +++ b/agents/filbert/work/s4-follow-up-review/r1-cand-node-mosaic.txt @@ -0,0 +1,77 @@ +✔ pure resolution selects current default or explicit enrolled account (1.872151ms) +✔ scope is explicit, bounded and never inferred (1.57115ms) +✔ fork pin is preserved against default change, override, missing account and revocation (0.704128ms) +✔ unenrolled account/provider, missing harness, model expansion and native model ceiling refuse (0.798909ms) +✔ only explicit synthetic credential forms and internal fixture stores admitted (5.36031ms) +✔ two concurrent workspaces of the same agent publish distinct complete private generations (161.818945ms) +✔ same execution ID is exclusively claimed and cannot overwrite a published generation (42.892165ms) +✔ failed generation after-auth preserves prior files, records failure and refuses blind same-ID retry (65.595802ms) +✔ failed generation before-publish preserves prior files, records failure and refuses blind same-ID retry (77.784406ms) +✔ credential lock contention refuses without duplicate side effects (14.592571ms) +✔ symlinked pre-existing final target is refused and never followed (114.334933ms) +✔ invalid registry cannot resolve; no fallback to supplied partial entries (0.27403ms) +✔ post-publication failure records uncertainty, preserves complete generation and prevents replay (35.053473ms) +✔ expired credentials refresh under transaction and subsequent generation reuses rotation (172.252181ms) +✔ refresh failure retains prior generation and store state (115.992743ms) +✔ refresh timeout retains prior generation and store state (175.78184ms) +✔ refresh malformed retains prior generation and store state (82.415683ms) +✔ concurrent refresh on same account refuses contention while unrelated account proceeds (217.852814ms) +✔ invalid refresh options refuse before burning claim (42.224182ms) +✔ fixed fake process rotates both OAuth fields without mutating caller input (30.575047ms) +✔ concurrent isolated processes preserve separate provider credentials (70.011095ms) +✔ fake failure is refused with fixed diagnostics (23.924708ms) +✔ fake malformed is refused with fixed diagnostics (46.619583ms) +✔ fake timeout is refused with fixed diagnostics (103.993257ms) +✔ fake unchanged is refused with fixed diagnostics (24.438804ms) +✔ caller executable/environment injection is rejected before spawning (0.319162ms) +✔ valid fixture tree validates and lists without secrets (69.435664ms) +✔ unknown-field refuses (0.368527ms) +✔ invalid-id refuses uppercase and traversal shapes (0.267159ms) +✔ plain-http baseUrl requires allowInsecureTransport (0.299317ms) +✔ native provider rejects allowInsecureTransport (0.241272ms) +✔ unsupported credential type and kind refuse (0.159833ms) +✔ account provider-path mismatch refuses (0.109991ms) +✔ profile account refs must be provider/account shaped (0.219682ms) +✔ seat selection accepts fork pin field, validates account refs (0.208864ms) +✔ harness manifest id must equal executable (gate 1) (0.20672ms) +✔ CLI validate: duplicate provider id across files refuses (32.824146ms) +✔ CLI validate: missing referenced provider/account refuse (33.993274ms) +✔ CLI validate: broken JSON refuses without secret echo (28.59761ms) +✔ CLI usage errors exit 2 (59.606687ms) +✔ credential.json sibling presence does not break validation and is never read (68.475171ms) +✔ D1 missing, empty and structurally empty roots refuse, no list projection (194.464649ms) +✔ D1 required directory auth cannot be absent (64.16133ms) +✔ D1 required directory auth/providers cannot be absent (63.723043ms) +✔ D1 required directory auth/accounts cannot be absent (63.719991ms) +✔ D1 required directory auth/settings cannot be absent (63.575918ms) +✔ D1 required directory harnesses cannot be absent (63.409486ms) +✔ D1 root file and unreadable metadata refuse (109.21742ms) +✔ D2 no symlink traversal at auth/providers/openai-codex.json (56.303987ms) +✔ D2 no symlink traversal at auth/accounts/openai-codex/homelab-openai (65.600399ms) +✔ D2 no symlink traversal at auth/providers (54.789867ms) +✔ D2 no symlink traversal at auth (59.71393ms) +✔ D2 root and ancestor symlinks and lexical traversal refuse (171.864478ms) +✔ private filesystem modes enforced for root (67.447829ms) +✔ private filesystem modes enforced for auth (68.390099ms) +✔ private filesystem modes enforced for auth/providers/openai-codex.json (64.223364ms) +✔ private filesystem modes enforced for auth/accounts/openai-codex/homelab-openai/account.json (82.608422ms) +✔ D3 numeric version 1 only across all record kinds (1.62563ms) +✔ D4 nested unknown keys and missing per-kind required fields refuse (75.984001ms) +✔ D5 unenrolled default refuses even when account exists (78.872984ms) +✔ D6 provider/account credential type must match (68.848194ms) +✔ D7 every harness endpoint enforces HTTP opt-in and shape (0.482101ms) +✔ D8 URLs reject embedded credentials and unsupported protocols without echo (178.618889ms) +✔ D9 malformed JSON diagnostics contain no content excerpt (73.23815ms) +✔ D10 missing metadata is missing-path, not invalid-json (67.801324ms) +✔ D10 library returns no partial entries on any invalid record (73.320294ms) +✔ null/scalar/array metadata refuses without stack or echo (264.773454ms) +✔ credential sibling is never opened, even when an unreadable symlink (36.871642ms) +✔ oversized metadata refuses before parsing (66.670211ms) +ℹ tests 69 +ℹ suites 0 +ℹ pass 69 +ℹ fail 0 +ℹ cancelled 0 +ℹ skipped 0 +ℹ todo 0 +ℹ duration_ms 2358.596664 diff --git a/agents/filbert/work/s4-follow-up-review/r1-cand-node-queue.txt b/agents/filbert/work/s4-follow-up-review/r1-cand-node-queue.txt new file mode 100644 index 00000000..44ad3196 --- /dev/null +++ b/agents/filbert/work/s4-follow-up-review/r1-cand-node-queue.txt @@ -0,0 +1,158 @@ +✔ F1: an ordinary commit after update-ref is refused until step 8; then it commits and the queue stays at C (1368.524395ms) +✔ F1: a plain `commit -e` whose guard ran before update-ref fails at its own HEAD update (1107.815614ms) +ℹ 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 (1053.859957ms) +ℹ 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 (1315.196413ms) +✔ F1: HEAD moving after the step-7 guard check and before update-ref: refused, nothing published (1160.527505ms) +✔ F1: H is recorded before the canary, so HEAD moving during the step-1 canary is refused at step 7 (1107.586369ms) +✔ F1: a queue commit landing after H is recorded: step 1 says HEAD moved, not the guard (1534.627974ms) +✔ F1: a queue commit landing between the HEAD check and the canary: the failed clean run is reported as HEAD moved (934.919964ms) +✔ F1: a shared-index change during the procedure is not committed (1106.384292ms) +✔ F1: a queue path staged after update-ref: step 8 stops and touches nothing (1034.784534ms) +✔ F1: a missing or a different hook refuses (881.067414ms) +✔ F1: same bytes without the exec bit, a symlinked hook, and core.hooksPath in the local or global scope each refuse before update-ref (1206.112179ms) +✔ F1: the canary refuses a hook that git would not run (700.887362ms) +✔ F1: the guard deactivated after step 1 is refused at the step-7 recheck (1025.963075ms) +✔ bootstrap: implementation-only HEAD, the guard, genesis, the --genesis commit, then an extending commit (974.769984ms) +✔ bootstrap: --genesis with a base present, no base without --genesis, an op before the first commit, a changed map, another branch (1049.631216ms) +✔ bootstrap: the archived tests and validator run outside any repository (965.953223ms) +✔ general: an unrelated staged file stays staged, and the committed blobs are the snapshot bytes (917.850172ms) +✔ general: a queue write after the snapshot is not committed (1337.162804ms) +✔ general: a snapshot whose log does not extend the base refuses (1279.192635ms) +✔ general: install-hook privilege, repair of a same-bytes hook, and its refusals (237.3212ms) +✔ general: environment overrides, a linked worktree and usage (596.349331ms) +✔ general: a queue path staged before the run refuses at step 1 (681.715215ms) +✔ general: HEAD's queue tests failing in the archive refuse (910.425138ms) +✔ genesis document serializes deterministically and replays (7.054109ms) +✔ a hand edit that stays valid JSON fails replay; a formatting-only edit fails re-serialization (3.014575ms) +✔ a tampered result, receipt or viewSha fails replay (4.082269ms) +✔ op ids: 8 to 72 characters for callers, 80 in the log for .outcome entries (0.269546ms) +✔ add: defaults for an ordinary seat, privileged extras, refusals (5.909142ms) +✔ matrix: queued→briefed privileged; briefed→in-progress owner with after satisfied (7.861542ms) +✔ matrix: release, review round, changes requested and waiting-on-jason (20.322188ms) +✔ matrix J5: in-review→done by the gate owner with evidence naming the current round (17.233235ms) +✔ review issue, lead decision 23: none refuses, one is used, several need --issue, later rounds keep it (36.694668ms) +✔ the row schema refuses a round with a null issue, and the A1 review shape (P2) (4.077834ms) +✔ matrix R1: every state × target × actor class matches 8.7, gate owner jason or not, required or not (1594.6255ms) +✔ matrix: blocked keeps the claim and returns only to previousState (4.122793ms) +✔ matrix J4: parking is Jason's, refused while required; unpark returns to queued (2.723419ms) +✔ field edits: who may change what (5.395692ms) +✔ set issues keeps a logged narrowing of closes (N10) (3.281972ms) +✔ text the table shows refuses \ and <, everywhere it enters (N8) (1.962337ms) +✔ every accepted text renders to nine cells on every row (N8) (24.65681ms) +✔ genesis: the map refuses an owner among its row's reviewers; replay doesn't (2026-10-04) (0.566795ms) +✔ times and dates must be calendar values, not just the shape (2026-10-04) (2.504801ms) +✔ replay holds every op id to the caller's rule (N11) (9.205636ms) +✔ note: owner, listed reviewer or privileged; empty clears (1.274341ms) +✔ assign moves the claim with the owner; done clears it (2.904442ms) +✔ render is byte-stable and escapes pipes (0.588507ms) +✔ view classification: current, genuine stale, edited stale marker, changed current body, markers (0.744893ms) +✔ next: resume, then review, then start, then wait, then nothing; lowest id first (25.079881ms) +✔ canonical args make a retry's identity independent of list order (0.330732ms) +✔ manifests, headings and blob ids (0.483773ms) +✔ the migration map: one queue-map block, exact keys (0.588646ms) +✔ every call but `queue` reaches the seat CLI exactly as before A2 (749.22435ms) +✔ `queue` reaches the queue CLI with the rest of the arguments (210.45014ms) +✔ the pre-A2 fixture is the script A2 changed (0.378694ms) +✔ acquire publishes the record by link; release removes only its own lock (8.169093ms) +✔ a kill between the temp write and the link leaves no lock (71.284163ms) +✔ a short or failed temp write refuses and leaves no lock and no temp (4.246816ms) +✔ a link error other than EEXIST refuses (2.629069ms) +✔ an error after the link releases the lock: unreadable gate, failing temp stat (5.709139ms) +✔ a release that fails on a gate path is reported, never a stack trace (P1) (7.955663ms) +✔ a paused holder: another writer waits 10 s, then refuses naming it live (10073.647915ms) +✔ two concurrent unlockers: the second refuses on the gate (27.810421ms) +✔ a writer publishing during an unlock, lock first: unlock sees it live and refuses (1.976767ms) +✔ a writer publishing during an unlock, gate first: the writer releases and refuses (1.948203ms) +✔ a gate swapped while held is left in place and reported, on success and on refusal (N1) (2.299562ms) +✔ a reused pid within one boot is mismatch; unlock removes the lock and never signals the process (3.735687ms) +✔ the same pid and start on a different boot is mismatch (0.45282ms) +✔ a foreign host is unknown whatever the local pid says; unlock refuses (58.060779ms) +✔ unreadable /proc: classification is unknown and acquire refuses (1.143527ms) +✔ invalid records: empty, unparsable, wrong keys, bad start or boot (0.280584ms) +✔ a stale gate blocks writers; --check-gate says mismatch for a reused pid (4.074518ms) +✔ a delayed release by a dead owner, after unlock and a new owner: the inode check keeps the new lock (2.189275ms) +✔ release checks the inode too: a byte-identical lock file with a new inode is left in place (1.390838ms) +✔ unlock refuses a live, unknown or invalid lock, and does nothing without one (3.773733ms) +✔ the migration map validates and renders the golden genesis table (5.562141ms) +✔ the marked QUEUE.md holds every row and parked item between its markers (1.878447ms) +✔ map-check reports each kind of drift (7.292576ms) +✔ a request posts once as the requester; a retry sends nothing (688.350161ms) +✔ each transport answer maps to posted, failed or uncertain (8.9 step 3) (3473.819954ms) +✔ the pre-send checks: GET user must name the requester, under the deadline (1226.019299ms) +✔ the lead's request refuses a token for login sage (572.249013ms) +✔ the credential file: the seat's own, 0600, no symlink, never the shared default (503.984503ms) +✔ an unresolved request blocks a new request, a new round, waiting-on-jason and done (1722.410185ms) +✔ a same-op retry after a kill sends nothing, even with a stale view (2421.424105ms) +✔ a held lock at the outcome exits 3 and names what the transport said (570.240504ms) +✔ late outcomes: after an abandon, and after a resolve with the same or another id (1743.338011ms) +✔ resolve checks the comment: issue, markers, round, candidate and author (1601.710919ms) +✔ the lead resolves a seat's request: the comment's author is the requester, fetched with the lead's token (576.148221ms) +✔ validateRow checks a request round's shape, which every replayed entry must keep (391.275684ms) +✔ request, changes, a new candidate, approval: every round pinned; no review files (1594.71266ms) +✔ a row with no reviewers opens a round that posts nothing (1017.17584ms) +✔ verify-commit: a prospective tree must hold exactly the candidate's paths (1232.03807ms) +✔ semantics: v1 entries replay as before; review entries need v2 (404.516761ms) +✔ set reviewers refuses the row's owner (2026-09-28) (294.923201ms) +✔ the owner records no verdict, even as a listed reviewer (377.194736ms) +✔ a request comment over the length limit is not sent (419.581132ms) +✔ a late POST on a closed row leaves a conflict nothing can resolve, and resolve asks nothing (508.094636ms) +✔ a Jason-gated row reaches waiting-on-jason only on every reviewer's approval (2142.907828ms) +✔ genesis: refusals before anything is written (512.662775ms) +✔ genesis: the map must be committed, well formed, with committed briefs and seats (661.798245ms) +✔ genesis: markers, a stray witness, once only; a retry returns the receipt (570.512249ms) +✔ genesis: a file holding genesis alone with no witness is confirmed by sync or a retry (326.662108ms) +✔ canonical checks: worktree, second clone, detached HEAD, wrong branch, GIT_DIR, foreign code; a symlink works (715.634667ms) +✔ --by that differs from MOSAIC_AGENT_NAME warns on stderr and logs nothing more (N12) (534.717688ms) +✔ op ids: missing, too long, reserved; a retry answers; another payload refuses (710.319686ms) +✔ a retried add returns the id it first allocated, after reassignment and after done (810.716943ms) +✔ Rocko's S4 schedule: a lost result, another writer, then the retry opens no second round (529.80669ms) +✔ the review issue and the evidence round through the CLI (lead decision 23, 8.7) (931.381579ms) +✔ claims and add defaults through the CLI; candidates are manifests or reachable commits (656.148184ms) +✔ add, set reviewers and assign refuse the row's owner as a reviewer (445.647197ms) +✔ the working-brief check: a changed working copy refuses the start and flags next (651.123258ms) +✔ next: resume first, then nothing for an idle seat; needs a seat (323.006358ms) +✔ view stale: new ops and verify refuse naming the unshown op; retries answer; reads warn; render fixes (630.609898ms) +✔ view unknown: a hand edit, an old marker over an edited body, missing or duplicate markers (1084.349585ms) +✔ a hand edit to queue.json refuses every verb, reads included (594.164641ms) +✔ verify and render --check leave bytes and mtimes unchanged (389.003599ms) +✔ render is byte-stable across runs and repositories (264.819899ms) +✔ snapshot and verify --snapshot (770.221731ms) +✔ usage errors exit 4 (681.438982ms) +✔ a short write, ENOSPC or a file fsync failure: nothing visible, temp removed (232.825599ms) +✔ a rename failure: nothing visible, temp removed (145.840545ms) +✔ a directory fsync failure: uncertain, exit 3, no receipt; the tail refuses new ops; a retry confirms (175.377624ms) +✔ a directory fsync failure, then sync names the op (334.200058ms) +✔ a witness write failure: uncertain, durable, exit 3; the view is untouched (146.849109ms) +✔ the .git fsync after the witness rename fails: uncertain, exit 3, the witness says so (154.073374ms) +✔ confirming a tail fsyncs queue.json and docs/plans before the witness; either failure changes nothing (159.117564ms) +✔ the docs/plans fsync after the view rename fails: the op stands, the view is written, a warning says so (152.332799ms) +✔ a lock swapped while held is left in place and reported, on a receipt and on a refusal (148.389128ms) +✔ a lock that cannot be released after an op is reported, on a receipt and on a refusal (151.546104ms) +✔ unlock prints a swapped gate's warning on stderr, the result on stdout (115.474484ms) +✔ a view write that fails keeps the op and reports a stale view (114.610379ms) +✔ SIGKILL before the rename: nothing recorded; the retry removes the leftover temp (615.523099ms) +✔ SIGKILL after the rename, before the witness: the tail refuses new ops and sync names the op (653.62181ms) +✔ SIGKILL after the witness, before the view: the stale refusal names the op (620.210512ms) +✔ SIGKILL after the view, before the receipt: the retry returns the receipt (665.935427ms) +✔ git checkout between steps 1 and 7: step 7 refuses and nothing is written (263.734749ms) +✔ git stash restoring an older valid pair: history lost; accept-history needs privilege, a reason and --yes (920.323427ms) +✔ a deleted witness: refused after the locked recheck; accept-history records it absent (208.428956ms) +✔ a header edit during a write: the op stands, the view write is skipped with a warning (133.27623ms) +✔ a reader paused between the witness and the file while a writer finishes: no lost-history report (142.279617ms) +✔ file-then-witness order forced by a hook: the locked recheck prevents a false report (138.591803ms) +✔ a writer paused before and after the witness rename: readers see a tail, then a match (139.175204ms) +✔ a true rollback is reported only after the locked recheck; a held lock names its holder instead (518.066437ms) +✔ an accept-history in progress: an unlocked reader waits on the lock and never reports lost history (465.309105ms) +✔ the platform check refuses other filesystems (130.878385ms) +✔ tmpfs passes only a test layer that allows it (N5) (156.961291ms) +✔ unlock keeps a multi-line lock record on stdout (P3) (222.786106ms) +ℹ tests 148 +ℹ suites 0 +ℹ pass 148 +ℹ fail 0 +ℹ cancelled 0 +ℹ skipped 0 +ℹ todo 0 +ℹ duration_ms 24575.018457 diff --git a/agents/filbert/work/s4-follow-up-review/r1-cand-node-seat.txt b/agents/filbert/work/s4-follow-up-review/r1-cand-node-seat.txt new file mode 100644 index 00000000..c0c5bee1 --- /dev/null +++ b/agents/filbert/work/s4-follow-up-review/r1-cand-node-seat.txt @@ -0,0 +1,27 @@ +✔ resolveSeat: by name under --repo resolves the repo layout (1.251101ms) +✔ resolveSeat: by path resolves the fleet layout (0.314635ms) +✔ resolveSeat: refusals for missing dir, missing launch.sh, non-executable launch.sh, invalid name, and unknown layout (0.710458ms) +✔ tmuxContext: outside tmux, default socket, custom socket, and exec failure (0.647878ms) +✔ makeRegistration produces a record that validates; each shape violation throws SeatError (0.790986ms) +✔ writeRegistration/readRegistration: round trip, permissions, absence, and malformed records (1.269425ms) +✔ updateTask: changes task, taskSetBy and updatedAt only, and refuses appropriately (1.156407ms) +✔ CLI launch: registers, execs the fake launch script, and passes args through (30.222001ms) +✔ CLI launch: --harness lands in the record (32.913646ms) +✔ CLI launch: the launch script's own exit code passes through (29.484011ms) +✔ CLI launch: relaunching a seat rewrites the one registration record (61.925976ms) +✔ CLI launch: omitting --task records an empty string, not null (27.792171ms) +✔ CLI seat task: updates only the task after a launch, and refuses on an unlaunched seat (82.182296ms) +✔ CLI refusals: no args, unknown flag, missing config, already-registered env, and exec failure (148.150447ms) +✔ samePath: equal paths, symlinked dirs, distinct dirs, and non-strings (0.786055ms) +✔ resolveSetBy: explicit --by wins over the environment; absent or empty environment gives unknown; invalid explicit or environment values refuse with exit 4 (0.674817ms) +✔ 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.868062ms) +✔ 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 (11.536829ms) +✔ 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 (311.700226ms) +ℹ tests 19 +ℹ suites 0 +ℹ pass 19 +ℹ fail 0 +ℹ cancelled 0 +ℹ skipped 0 +ℹ todo 0 +ℹ duration_ms 811.678252 diff --git a/agents/filbert/work/s4-follow-up-review/r1-cand-node-tasks.txt b/agents/filbert/work/s4-follow-up-review/r1-cand-node-tasks.txt new file mode 100644 index 00000000..78de976e --- /dev/null +++ b/agents/filbert/work/s4-follow-up-review/r1-cand-node-tasks.txt @@ -0,0 +1,59 @@ +✔ the boot config is checked before anything starts (31.406315ms) +✔ a business with no tracker entry refuses task verbs (10.50476ms) +✔ credential.expiring and .expired are recorded once per instance (31.40099ms) +✔ a token file that changes on disk records credential.changed (12.76132ms) +✔ autostart polls, reconciles and retries a startup the tracker was down for (36.972972ms) +✔ a refusal a restart must clear is not retried by the poll (11.379556ms) +✔ a poll that fires while two are queued is dropped (15.039808ms) +✔ close waits for a running verb and refuses one that has not started (43.104982ms) +✔ the bundled Vikunja is the pinned upstream image the runbook names (0.83167ms) +✔ every published port is on 127.0.0.1, and no secret is in the file (0.311917ms) +✔ the fake answers each route with the statuses and shapes Vikunja v2.7.0 sent (106.64679ms) +✔ the recorded task bodies pass the checks S3 applies to every read (0.5836ms) +✔ the client works against the fake over real HTTP with the platform fetch (103.710016ms) +✔ a correct install starts, and the first reconcile records tasks that already exist (26.761043ms) +✔ verbs refuse while a business is starting and after startup refused it (17.920027ms) +✔ startup refuses a token that can do more than its role needs (38.784719ms) +✔ startup refuses an unsupported version and flags an untested one (29.454798ms) +✔ startup refuses a board that the runbook did not install (39.884452ms) +✔ startup refuses a project the sync bot cannot read (6.805487ms) +✔ startup refuses a configured label the pm bot cannot see (8.89736ms) +✔ startup refuses an expired credential and a missing sync credential (17.507495ms) +✔ an unreachable tracker refuses with tracker-unavailable (7.515047ms) +✔ an edit in the UI is recorded once, with the fields that changed (88.08522ms) +✔ a move between open buckets is seen on the board, though updated does not change (39.239909ms) +✔ a person's comment is counted and a bot's is not (70.85552ms) +✔ the hourly reconcile catches a comment through comment_count (79.111948ms) +✔ a task closed in the UI leaves the open view with its done bucket (112.694106ms) +✔ a task that leaves the board is recorded as deleted, moved or out of reach (66.256236ms) +✔ a poll that read before a verb wrote does not overwrite the verb (36.027847ms) +✔ a tracker fault during a tick is reported and the next tick catches up (36.364956ms) +✔ a malformed answer refuses the tick with tracker-shape (13.83003ms) +✔ no token value reaches the database, the log or a refusal (56.431652ms) +✔ the first look at a task counts only comments inside the window (35.769886ms) +✔ task.create needs a recorded human request and a requirement id (41.974426ms) +✔ only labels named in the business file can be written (26.40169ms) +✔ task.schedule sets and clears a due date and relations (34.940101ms) +✔ assign and reassign move the role bots and record task.assigned (59.327811ms) +✔ task.update.assigned is for the assignee and records task.state (53.98079ms) +✔ a wrong expected digest records task.conflict and writes nothing (17.784255ms) +✔ a cross-role verb needs a resolved decision, used once (22.522257ms) +✔ task.close needs a verdict; after it every verb refuses with task-done (23.350917ms) +✔ a lost answer is settled by a re-read and never retried (25.303349ms) +✔ a create whose answer is lost is reported uncertain, and the poll finds the task (35.162388ms) +✔ a task the sync bot cannot read refuses and records nothing (15.624289ms) +✔ verbs and polls for one business run one at a time (91.847876ms) +✔ a due date with milliseconds is written to the second (65.82116ms) +✔ every write landed and the final read failed: the verb succeeds and records what it wrote (44.19338ms) +✔ some writes landed and the final read failed: write-uncertain, and nothing is recorded (16.809815ms) +✔ a create whose final read fails succeeds and records task.created (35.830588ms) +✔ an edit between the last write and the final read shows as external on the next poll (38.316051ms) +✔ task.created is recorded when a later label write fails (15.830074ms) +ℹ tests 51 +ℹ suites 0 +ℹ pass 51 +ℹ fail 0 +ℹ cancelled 0 +ℹ skipped 0 +ℹ todo 0 +ℹ duration_ms 776.554894 diff --git a/agents/filbert/work/s4-follow-up-review/r1-cand-node-webui.txt b/agents/filbert/work/s4-follow-up-review/r1-cand-node-webui.txt new file mode 100644 index 00000000..63a46ef2 --- /dev/null +++ b/agents/filbert/work/s4-follow-up-review/r1-cand-node-webui.txt @@ -0,0 +1,23 @@ +✔ browser edge states: loading, empty, malformed, stale, hostile/long values, in-flight reply and appearance fallback (2593.877023ms) +Rendered contrast: {"failures":[],"count":330,"lowest":4.504658476260286} +✔ served Console browser: real board fixtures, keyboard, drafts, receipts, themes, 320px and failures (2819.101922ms) +✔ conversation view: full history, collapsed tools, hidden thinking, inert hostile content, malformed and reconcile markers (2487.022264ms) +✔ conversation view: a fork keeps the open branch, says so, and opens the new one on request (1409.838855ms) +✔ conversation view: a newer session with no readable history keeps the marker (1033.313886ms) +✔ conversation view: seats without history say so and offer no reply (510.394246ms) +✔ Discord row through real board/WebUI: independent brake/liveness, no Reply, literal content (2198.027385ms) +✔ return flow through the conversation view: send, tool call, delayed result, peer message, exact long answers, relaunch (53388.794719ms) +✔ both presentations replace old activity with relaunch notice, label retained history, then resume after new activity (2301.993318ms) +✔ reported return flow and relative Age: reply sent from the inspector, then the new answer appears there without manual refresh (21973.89591ms) +✔ loopback host and board origin fail closed (10.966556ms) +✔ real board fixture passes through WebUI; assets and isolated seen/reply work (111.519321ms) +✔ proxy preserves exact request bytes, status and receipt, rejects forms and malformed JSON, never follows redirect (60.651787ms) +✔ unreachable board reports URL; CLI rejects unsupported options (969.115867ms) +ℹ tests 14 +ℹ suites 0 +ℹ pass 14 +ℹ fail 0 +ℹ cancelled 0 +ℹ skipped 0 +ℹ todo 0 +ℹ duration_ms 53754.387454 diff --git a/agents/filbert/work/s4-follow-up-review/r1-cand-suite-auth.txt b/agents/filbert/work/s4-follow-up-review/r1-cand-suite-auth.txt new file mode 100644 index 00000000..ac228760 --- /dev/null +++ b/agents/filbert/work/s4-follow-up-review/r1-cand-suite-auth.txt @@ -0,0 +1,17 @@ +OK status with missing harness credential exits 3 and still lists accounts +OK status reports harness credential (read-only) + mosaic accounts +OK api key material never reaches output +OK oauth token material never reaches output +OK unparseable credential file exits 2 +OK symlinked credential file exits 4 +OK env-side credential names reported +OK env var values never reach output +OK accounts without an accounts dir reports none and creates nothing +OK accounts lists files and marks the active one +OK loose account perms flagged in listing +OK agent --auth with missing account file refuses (exit 4) +OK agent --auth with non-0600 account file refuses +OK agent --auth with invalid account name refuses +OK auth.sh without valid config refuses + +selftest: 15 passed, 0 failed diff --git a/agents/filbert/work/s4-follow-up-review/r1-cand-suite-conductor.txt b/agents/filbert/work/s4-follow-up-review/r1-cand-suite-conductor.txt new file mode 100644 index 00000000..a2128b27 --- /dev/null +++ b/agents/filbert/work/s4-follow-up-review/r1-cand-suite-conductor.txt @@ -0,0 +1,55 @@ +Note: switching to '521597bbe0cf51a0a1aa3b7a16f13fc7b59c98c6'. + +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 + +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 '521597bbe0cf51a0a1aa3b7a16f13fc7b59c98c6'. + +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 + +Or undo this operation with: + + git switch - + +Turn off this advice by setting config variable advice.detachedHead to false + +OK dry-run: allowed change, exit 0, nothing committed (exit 0) +OK dry-run committed nothing +OK apply: allowed change exits 0 (exit 0) +OK apply: attribution in commit subject +OK apply: target tree clean after commit +OK disallowed path refused (exit 1) +OK disallowed path: target untouched +OK syntax gate refused broken .mjs (exit 1) +OK syntax gate: target untouched +OK suite failure refused (exit 1) +OK suite failure: target reverted to clean +OK disabled policy refused (exit 2) +OK disabled policy: target untouched +OK failed run refused (exit 1) +OK failed run: target untouched +OK missing run exits 4 (exit 4) +OK invalid policy exits 2 (exit 2) + +selftest: 17 passed, 0 failed diff --git a/agents/filbert/work/s4-follow-up-review/r1-cand-suite-config.txt b/agents/filbert/work/s4-follow-up-review/r1-cand-suite-config.txt new file mode 100644 index 00000000..76c05ef7 --- /dev/null +++ b/agents/filbert/work/s4-follow-up-review/r1-cand-suite-config.txt @@ -0,0 +1,26 @@ +OK absent adapter defaults to pi +OK adapter mock validates (exit 0) +OK unsupported adapter exits 2 (exit 2) +OK env exports adapter +OK bootstrap creates default when absent (exit 0) +OK bootstrap wrote config file +OK bootstrap is idempotent on existing config (exit 0) +OK bootstrap did not rewrite existing config +OK validate missing config exits 3 (exit 3) +OK malformed JSON exits 2 (exit 2) +OK unsupported configVersion exits 2 (exit 2) +OK unknown top-level key exits 2 (exit 2) +OK unknown execution key exits 2 (exit 2) +OK unsupported backend exits 2 (exit 2) +OK unsupported environment exits 2 (exit 2) +OK relative dataRoot exits 2 (exit 2) +OK non-canonical dataRoot exits 2 (exit 2) +OK filesystem root dataRoot exits 2 (exit 2) +OK home directory dataRoot exits 2 (exit 2) +OK dataRoot containing config dir exits 2 (exit 2) +OK control character in provider exits 2 (exit 2) +OK symlinked config file exits 2 (exit 2) +OK env exports resolve correctly +OK failed validation modified nothing + +selftest: 24 passed, 0 failed diff --git a/agents/filbert/work/s4-follow-up-review/r1-cand-suite-discord.txt b/agents/filbert/work/s4-follow-up-review/r1-cand-suite-discord.txt new file mode 100644 index 00000000..b013f9ff --- /dev/null +++ b/agents/filbert/work/s4-follow-up-review/r1-cand-suite-discord.txt @@ -0,0 +1,70 @@ +toolchain: node v26.8.1 + +OK syntax: packages/discord/src/approvals.mjs +OK syntax: packages/discord/src/authorize.mjs +OK syntax: packages/discord/src/binding.mjs +OK syntax: packages/discord/src/cli.mjs +OK syntax: packages/discord/src/connector.mjs +OK syntax: packages/discord/src/context.mjs +OK syntax: packages/discord/src/engine-pi.mjs +OK syntax: packages/discord/src/errors.mjs +OK syntax: packages/discord/src/gateway.mjs +OK syntax: packages/discord/src/git.mjs +OK syntax: packages/discord/src/journal.mjs +OK syntax: packages/discord/src/notify.mjs +OK syntax: packages/discord/src/rest.mjs +OK syntax: packages/discord/src/setspark.mjs +OK syntax: packages/discord/src/tools.mjs +OK syntax: packages/discord/src/web.mjs +OK syntax: packages/discord/bin/git-credential.mjs +OK syntax: packages/discord/extension/tools.mjs +OK syntax: packages/discord/tests/approvals.test.mjs +OK syntax: packages/discord/tests/authorize.test.mjs +OK syntax: packages/discord/tests/binding.test.mjs +OK syntax: packages/discord/tests/connector.test.mjs +OK syntax: packages/discord/tests/context.test.mjs +OK syntax: packages/discord/tests/engine.test.mjs +OK syntax: packages/discord/tests/fake-pi.mjs +OK syntax: packages/discord/tests/gateway.test.mjs +OK syntax: packages/discord/tests/git.test.mjs +OK syntax: packages/discord/tests/helpers.mjs +OK syntax: packages/discord/tests/journal.test.mjs +OK syntax: packages/discord/tests/notify.test.mjs +OK syntax: packages/discord/tests/recover.test.mjs +OK syntax: packages/discord/tests/rest.test.mjs +OK syntax: packages/discord/tests/setspark.test.mjs +OK syntax: packages/discord/tests/tools.test.mjs +OK syntax: packages/discord/tests/web.test.mjs +OK syntax: packages/discord/fixtures/claim-worker.mjs +OK syntax: packages/discord/fixtures/legacy-owner-worker.mjs +OK syntax: scripts/discord.sh +OK syntax: scripts/discord-service.sh +OK packages/discord declares no dependencies +OK no bot-token-shaped string in packages/discord +OK fixture binding uses placeholder ids only +OK fixture binding validates +OK real pi with the extension exposes exactly list_dir, read_file, search and no built-in tool +OK real pi with a writable root exposes exactly the three reads plus write_file and edit_file, and writes nothing at start +OK real pi with a web key exposes the three reads plus web_fetch and web_search, and no write tool without a writable root +OK real pi with a git root exposes the reads, writes and the four git verbs, commits nothing at start, and never shows the token +OK real pi with protocol vault adds reserve_id to the git verbs +OK real pi with a setspark key exposes the reads and the eight record verbs, no counters, and never shows the key +OK real pi refuses a git key on a read-only root (fail closed) +OK real pi with the pilot flags (--no-tools) exposes no tool at all +OK real pi exits non-zero without MOSAIC_DISCORD_TOOLS: no session, no tools (fail closed) +OK a failing nested test fails the run under a parent runner's NODE_TEST_CONTEXT +OK node --test packages/discord/tests/ (ℹ pass 178) +OK scripts/discord.sh --help exits 0 +OK scripts/discord.sh check without a binding exits 4 +OK scripts/discord.sh recover without a binding exits 4 +OK scripts/discord.sh reload without a binding exits 4 +OK scripts/discord-service.sh without a command exits 4 +OK service unit renders with the repository path, a supervised run as the main process, exit 3 never retried, and reload as SIGHUP +OK service install writes the rendered unit (0644) and leaves no temp file +OK service install a second time reports unchanged +OK systemd-analyze verify accepts the rendered unit +OK service uninstall removes the unit file +OK service install with an unknown flag exits 4 +OK service install with USER unset finishes and names the account for lingering + +discord suite: 66 passed, 0 failed diff --git a/agents/filbert/work/s4-follow-up-review/r1-cand-suite-extension-package.txt b/agents/filbert/work/s4-follow-up-review/r1-cand-suite-extension-package.txt new file mode 100644 index 00000000..6dee2b67 --- /dev/null +++ b/agents/filbert/work/s4-follow-up-review/r1-cand-suite-extension-package.txt @@ -0,0 +1,21 @@ +OK initial ordinary-file install +OK installed tree matches canonical source +OK installed tree has no symlinks +OK check detects installation drift +OK sync refuses to overwrite installation drift +OK check detects an extra destination file +OK check detects an extra destination directory +OK check rejects a destination symlink +OK sync accepts a canonical source update +OK updated installation matches canonical source +scripts/test-extension-package.sh: line 14: 4117866 Killed "$@" > /dev/null 2>&1 +OK forced interruption kills the replacing process +OK next invocation recovers old consistent installation +OK interrupted replacement rolled back +OK sync succeeds after interruption recovery +OK unlocked stale lock file does not block +OK active lock refuses a concurrent sync +OK source symlink fails closed +OK nested second entrypoint fails closed + +extension package selftest: 18 passed, 0 failed diff --git a/agents/filbert/work/s4-follow-up-review/r1-cand-suite-foundation.txt b/agents/filbert/work/s4-follow-up-review/r1-cand-suite-foundation.txt new file mode 100644 index 00000000..6338e1e6 --- /dev/null +++ b/agents/filbert/work/s4-follow-up-review/r1-cand-suite-foundation.txt @@ -0,0 +1,53 @@ +toolchain: node v26.8.1, python 3.12.8, jsonschema 4.26.0 + +OK syntax: scripts/foundation-inspect.mjs +OK syntax: scripts/foundation/strict-json.mjs +OK syntax: scripts/foundation/canonical.mjs +OK syntax: scripts/foundation/resolve.mjs +OK syntax: scripts/foundation/validate-record.mjs +OK syntax: scripts/foundation/fixtures/build-fixtures.mjs +OK syntax: scripts/foundation/canonical.test.mjs +OK syntax: scripts/foundation/cli.test.mjs +OK syntax: scripts/foundation/fixtures.test.mjs +OK syntax: scripts/foundation/resolve.test.mjs +OK syntax: scripts/foundation/strict-json.test.mjs +OK syntax: scripts/foundation/verify-schema.py (ast only; no bytecode written) +OK fixture generator runs +OK checked-in fixtures/bundles equal a fresh generation +OK checked-in fixtures/raw equal a fresh generation +OK checked-in fixtures/index.json equal a fresh generation +OK checked-in demo bundles equal a fresh generation +OK a failing nested test fails the run under a parent runner's NODE_TEST_CONTEXT +OK node --test scripts/foundation/ (ℹ pass 80) +OK differential schema oracle: PASS: differential schema oracle (finite corpus; compatibility evidence, not equivalence proof) + platform witness: strftime('%Y') for year 999 -> '999' (pinned checker refuses years 0001..0999) + node v26.8.1; corpus 1568 records (38 pinned fixtures, 478 unique bundle records, 1052 typeCase/mutation/lexical cases) + schema column: agree-valid 540, agree-invalid 991, DISAGREEMENTS 0; strict-only (parser-bound) cases: 27; unsupported-kind records not schema-assessed by the inspector: 10 + profile column (schema-valid records only): profile-valid 510, profile-invalid 30 + profile refusals asserted: 30 schema-agreed-valid records refused only by the strict typed-string profile (rule profile-pattern-mismatch), 12 declared by name; 73 named probes verified against declared schema/profile columns +OK oracle: zero schema-column disagreements with the pinned checker +OK oracle: strict-only profile refusals are counted and asserted +OK demo: permitted read preview exits 0 (exit 0) +OK demo: permitted file.change preview exits 0 (exit 0) +OK demo: assignment.change proposal is unresolved (exit 3) (exit 3) +OK demo: revoked registration is refused (exit 3) (exit 3) +OK demo: message is not authority (exit 3) (exit 3) +OK usage: no arguments exits 2 (exit 2) +OK io: missing file exits 4 (exit 4) +OK io: directory exits 4 (exit 4) +OK io: symlink exits 4 (O_NOFOLLOW) (exit 4) +OK bound: oversize fixture exits 2 (exit 2) +OK profile: one final LF in a typed selection id is refused before admission (exit 2) (exit 2) +OK profile: two final LFs fail the schema pattern itself (exit 2) (exit 2) +OK profile: escaped newlines in free-form text stay allowed (exit 0) (exit 0) +OK profile refusal is invalid-request/profile-pattern-mismatch with selection and operation withheld, value not echoed +OK text output starts with the disclaimer +OK json output is valid JSON with result allowed and exactly the charter §7 fields +OK json golden matches byte-for-byte +OK sandboxed bundle run (env -i, PATH=/nonexistent) produced the unresolved proposal +OK sandbox inventory (path/type/size/mode/uid/gid/inode/mtime/sha256) unchanged by runs +OK canary never printed (bundle run and credential-file run) +OK a non-bundle JSON file is refused at the shape gate, not read into output +OK no field of the non-bundle file is echoed + +selftest: 44 passed, 0 failed diff --git a/agents/filbert/work/s4-follow-up-review/r1-cand-suite-queue.txt b/agents/filbert/work/s4-follow-up-review/r1-cand-suite-queue.txt new file mode 100644 index 00000000..03f365f2 --- /dev/null +++ b/agents/filbert/work/s4-follow-up-review/r1-cand-suite-queue.txt @@ -0,0 +1,35 @@ +toolchain: node v26.8.1, git version 2.55.0 + +OK syntax: packages/queue/src/cli.mjs +OK syntax: packages/queue/src/errors.mjs +OK syntax: packages/queue/src/io.mjs +OK syntax: packages/queue/src/lock.mjs +OK syntax: packages/queue/src/queue.mjs +OK syntax: packages/queue/src/review.mjs +OK syntax: packages/queue/src/store.mjs +OK syntax: packages/queue/tests/commit.test.mjs +OK syntax: packages/queue/tests/data.test.mjs +OK syntax: packages/queue/tests/dispatch.test.mjs +OK syntax: packages/queue/tests/helpers.mjs +OK syntax: packages/queue/tests/lock.test.mjs +OK syntax: packages/queue/tests/migration.test.mjs +OK syntax: packages/queue/tests/review.test.mjs +OK syntax: packages/queue/tests/store.test.mjs +OK syntax: packages/queue/tests/write.test.mjs +OK syntax: packages/queue/tests/fixtures/fake-gitea.mjs +OK syntax: packages/queue/tests/fixtures/kill-at.mjs +OK syntax: packages/queue/tests/fixtures/lock-child.mjs +OK syntax: packages/queue/tests/fixtures/mosaic-pre-a2.sh +OK syntax: scripts/queue-commit.sh +OK syntax: scripts/git-hooks/pre-commit +OK syntax: scripts/mosaic +OK queue-commit.sh, the guard and scripts/mosaic are executable +OK packages/queue declares no dependencies +ℹ tests 148 +ℹ pass 148 +ℹ fail 0 +OK node --test packages/queue/tests/ +OK scripts/mosaic queue help +skip queue verify and render --check: this checkout (/home/jwoltje/filbert-scratch/r45/cand) is not the queue's canonical root (/mnt/storage/src/mosaic-stack) + +queue suite: 27 passed, 0 failed diff --git a/agents/filbert/work/s4-follow-up-review/r1-cand-suite-release.txt b/agents/filbert/work/s4-follow-up-review/r1-cand-suite-release.txt new file mode 100644 index 00000000..1d6ed050 --- /dev/null +++ b/agents/filbert/work/s4-follow-up-review/r1-cand-suite-release.txt @@ -0,0 +1,7 @@ +OK valid RELEASE resolves (exit 0) +OK invalid RELEASE exits 1 (exit 1) +OK missing RELEASE exits 1 (exit 1) +OK valid RELEASE leaves image tag consistent with version +skip state-machine cases (docker daemon unavailable) + +selftest: 4 passed, 0 failed diff --git a/agents/filbert/work/s4-follow-up-review/r1-cand-suite-task.txt b/agents/filbert/work/s4-follow-up-review/r1-cand-suite-task.txt new file mode 100644 index 00000000..85aa46f0 --- /dev/null +++ b/agents/filbert/work/s4-follow-up-review/r1-cand-suite-task.txt @@ -0,0 +1,33 @@ +OK valid task validates (exit 0) +OK unknown task key exits 2 (exit 2) +OK unsupported taskVersion exits 2 (exit 2) +OK invalid task id exits 2 (exit 2) +OK empty prompt exits 2 (exit 2) +OK NUL in expectExact exits 2 (exit 2) +OK out-of-range timeout exits 2 (exit 2) +OK missing mission file exits 4 (exit 4) +OK task with valid mission validates (exit 0) +OK invalid mission exits 2 (exit 2) +OK validate missing task exits 4 (exit 4) +OK validation does not modify the task file +OK prune dry-run exits 0 (exit 0) +OK dry-run deleted nothing +OK prune --keep=2 --yes removes oldest (exit 0) +OK kept exactly 2 newest runs +OK newest run kept, oldest pruned +OK append-only receipt written (3 entries) +OK sessions/workspaces untouched by prune +OK prune with invalid keep exits 4 (exit 4) +skip adapter seam cases (docker daemon unavailable) +skip workspace/capability cases (docker daemon unavailable) +skip live task cases (docker unavailable) +OK onboard without name exits 4 (non-interactive) (exit 4) +OK onboard --name renders profile (exit 0) +OK profile written +OK canon structure: required filled, optional placeholdered +OK canon sections present +FAIL user recall run succeeds (exit 1) +FAIL recalled user name (response: ) +OK no agent identity on headless run + +selftest: 26 passed, 2 failed diff --git a/agents/filbert/work/s4-follow-up-review/r1-gate-summary.txt b/agents/filbert/work/s4-follow-up-review/r1-gate-summary.txt new file mode 100644 index 00000000..d107388b --- /dev/null +++ b/agents/filbert/work/s4-follow-up-review/r1-gate-summary.txt @@ -0,0 +1,42 @@ +r1-cand node-business exit 0 +r1-cand node-bus exit 0 +r1-cand node-cli exit 0 +r1-cand node-control-board exit 0 +r1-cand node-conversation exit 1 +r1-cand node-discord exit 0 +r1-cand node-ledger exit 0 +r1-cand node-mosaic exit 0 +r1-cand node-queue exit 0 +r1-cand node-seat exit 0 +r1-cand node-tasks exit 0 +r1-cand node-webui exit 0 +r1-cand suite-auth exit 0 load 5.12 +r1-cand suite-conductor exit 0 load 5.03 +r1-cand suite-config exit 0 load 4.63 +r1-cand suite-discord exit 0 load 4.58 +r1-cand suite-extension-package exit 0 load 4.58 +r1-cand suite-foundation exit 0 load 3.87 +r1-cand suite-queue exit 0 load 5.10 +r1-cand suite-release exit 0 load 5.10 +r1-cand suite-task exit 1 load 5.10 +r1-base node-business exit 0 +r1-base node-bus exit 0 +r1-base node-cli exit 0 +r1-base node-control-board exit 0 +r1-base node-conversation exit 1 +r1-base node-discord exit 0 +r1-base node-ledger exit 0 +r1-base node-mosaic exit 0 +r1-base node-queue exit 0 +r1-base node-seat exit 0 +r1-base node-tasks exit 0 +r1-base node-webui exit 0 +r1-base suite-auth exit 0 load 11.24 +r1-base suite-conductor exit 0 load 10.66 +r1-base suite-config exit 0 load 10.66 +r1-base suite-discord exit 0 load 11.32 +r1-base suite-extension-package exit 0 load 11.32 +r1-base suite-foundation exit 0 load 6.34 +r1-base suite-queue exit 0 load 5.93 +r1-base suite-release exit 0 load 5.54 +r1-base suite-task exit 1 load 5.54 diff --git a/agents/filbert/work/s4-follow-up-review/r1-manifest-mut-after.txt b/agents/filbert/work/s4-follow-up-review/r1-manifest-mut-after.txt new file mode 100644 index 00000000..25064faa --- /dev/null +++ b/agents/filbert/work/s4-follow-up-review/r1-manifest-mut-after.txt @@ -0,0 +1,8 @@ +packages/cli/README.md: OK +packages/cli/src/host.mjs: OK +packages/cli/src/notifier.mjs: OK +packages/cli/tests/host.test.mjs: OK +packages/cli/tests/notifier.test.mjs: OK +packages/cli/tests/trackers-boot.test.mjs: OK +packages/discord/tests/journal.test.mjs: OK +scripts/bus-service.sh: OK diff --git a/agents/filbert/work/s4-follow-up-review/r1-mut-summary.txt b/agents/filbert/work/s4-follow-up-review/r1-mut-summary.txt new file mode 100644 index 00000000..b5dc3f46 --- /dev/null +++ b/agents/filbert/work/s4-follow-up-review/r1-mut-summary.txt @@ -0,0 +1,32 @@ +N2 killed (fail 1, cancelled 0) +N4 killed (fail 1, cancelled 0) +N5 killed (fail 1, cancelled 0) +M28 killed (fail 1, cancelled 0) +G150 killed (fail 1, cancelled 0) +G144 killed (fail 1, cancelled 0) +F2a killed (fail 1, cancelled 0) +F2b killed (fail 1, cancelled 0) +F2c killed (fail 2, cancelled 0) +F2d killed (fail 1, cancelled 0) +F2e killed (fail 1, cancelled 0) +F2f killed (fail 1, cancelled 0) +J4a killed (fail 1, cancelled 0) +J4b killed (fail 1, cancelled 0) +J4c killed (fail 1, cancelled 0) +J4d killed (fail 1, cancelled 0) +E1 killed (fail 1, cancelled 0) +E2 killed (fail 1, cancelled 0) +X1 killed (fail 1, cancelled 0) +X2 SURVIVED +X3 killed (fail 3, cancelled 0) +X4 killed (fail 1, cancelled 0) +X5 killed (fail 1, cancelled 0) +X6 killed (fail 1, cancelled 0) +X7 killed (fail 1, cancelled 0) +X8 killed (fail 1, cancelled 0) +X9 SURVIVED +X10 killed (fail 1, cancelled 0) +X11 killed (fail 1, cancelled 0) +X12 killed (fail 3, cancelled 0) +X13 killed (fail 2, cancelled 0) +X14 SURVIVED diff --git a/agents/filbert/work/s4-follow-up-review/r1-node24.txt b/agents/filbert/work/s4-follow-up-review/r1-node24.txt new file mode 100644 index 00000000..8f03759a --- /dev/null +++ b/agents/filbert/work/s4-follow-up-review/r1-node24.txt @@ -0,0 +1,249 @@ +v24.21.0 +✔ inbox lists only decisions routed to the human, with what approving authorizes and how to decide (169.210077ms) +✔ decide resolves by id prefix with --yes, and the trail shows it in broker order with the task to follow (220.995357ms) +✔ decide refuses without a terminal or --yes, on an unknown option and on a short reference (221.951051ms) +✔ decide prints a declining choice as declining (167.525958ms) +✔ an unknown outcome is reported once and never resent (127.301829ms) +✔ a decision closed before the answer arrives exits 2 and points at its trail (161.285204ms) +✔ a prefix that matches two open decisions exits 2 and resolves neither (122.126437ms) +✔ without --business a command uses the live host's business, and a stale host.json is not a host (129.876238ms) +✔ every human command refuses inside an agent run before it touches the bus (119.673429ms) +✔ usage errors exit 4; no business and no host is a usage error (166.691645ms) +✔ agents and tasks print through the broker (186.413662ms) +✔ notify.json: missing, loose, malformed and extra keys refuse; a binding or null passes (13.333788ms) +✔ bootConfig builds the broker's boot message for one business, with no trackers key when no project names one (201.980972ms) +✔ trackers come from the tracker.* variables of the one project that names a tracker project (114.238503ms) +✔ with two projects, the one that sets tracker.project supplies the tracker and the other is no conflict (83.289259ms) +✔ two projects that each name a tracker project refuse, since the boot shape holds one (75.913435ms) +✔ a business without tracker.baseUrl gets no trackers entry (120.454836ms) +✔ an unknown business and a broken system config refuse with exit 3 (173.234133ms) +✔ empty views say so (2.235721ms) +✔ the trail keeps the broker's order and names a decision's task without its rows (2.711433ms) +✔ tasks print the tracker fields the snapshot carries (0.326825ms) +✔ the host boots the broker, binds a launch in process, and the notifier DMs a blocking decision exactly once (1420.380971ms) +✔ a notifier that dies takes the host down with exit 1, so the unit restarts the pair (526.895008ms) +✔ a second host for the same data root refuses with exit 3 while the first runs (205.432629ms) +✔ a notifier that refuses stops the broker and the host refuses with exit 3 (380.380756ms) +✔ a notifier that refuses after the broker died still refuses with exit 3, without a send to the dead broker (401.224717ms) +✔ a notifier that dies before it replies, after the broker died, still refuses, without a send to the dead broker (193.95973ms) +✔ watchChildren reports a child that died before it was called, and one that dies later (30.977871ms) +✔ bus stop refuses to signal a live pid that is not a bus host (206.813112ms) +✔ bus start refuses with exit 3 and the code when the broker refuses to boot; bus status names the lock (314.80728ms) +✔ bus start refuses with exit 3 without a notifier config (148.547702ms) +✔ bus start runs until bus stop; status reports it while it runs (917.572739ms) +✔ bus-service.sh renders the unit and installs it into a given directory (55.222104ms) +✔ zoned uses the IANA zone across DST (174.293637ms) +✔ each open blocking decision is DM'd once, across polls and a restart; non-blocking ones are not (189.689945ms) +✔ two blocking decisions get two DMs with different nonces (185.5709ms) +✔ the digest nonce differs per business and per day and fits Discord's 25 characters (0.474682ms) +✔ a failed DM is journaled, backs off, and is retried until it lands (228.801997ms) +✔ five definite refusals stop a DM: one gave-up line, one log line, and a restart keeps the count (185.603648ms) +✔ 429s, 5xx-style unknowns and refusals without a status never count toward the limit (160.573693ms) +✔ a crash between the fifth refusal and its gave-up line: the next poll appends it and sends nothing (128.228554ms) +✔ the digest goes at 08:00 Chicago once a day, with blocking ones marked as DM'd (155.668925ms) +✔ a late start with no digest for the day sends one at once; an empty inbox gets one line (167.201859ms) +✔ an inbox read failure is logged and the next poll retries (2.445857ms) +✔ no Discord id reaches the journal or the log (151.557087ms) +✔ the journal: a torn tail is copied out and truncated, so an append after it reopens cleanly (33.621183ms) +✔ the journal: a crash between the copy and the truncate leaves a tail the next open repairs (88.02178ms) +✔ the journal: a whole file that is one torn line truncates to empty (24.4463ms) +✔ the journal: a malformed complete line refuses and leaves the file and any torn tail alone (19.133951ms) +✔ the journal: a loose file mode, a loose directory or a symlinked journal refuses (5.649955ms) +✔ the journal: a line with a wrong type refuses with exit 3 and names the field (21.592611ms) +✔ the journal: a symlinked directory refuses and says it is a link (2.071451ms) +✔ the journal: an append after the file was swapped for a symlink refuses and writes nothing through it (3.932517ms) +✔ the journal: a directory it cannot write or create refuses with exit 3 and names the path (2.475167ms) +✔ digest content stays within Discord's 2000 characters (0.631769ms) +✔ runLoop never overlaps ticks and stops after the one in flight (124.297708ms) +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 (976.590424ms) +✔ the transport writes {business, verb, args} to the child and reads its JSON (209.432574ms) +✔ a bus code on stderr becomes the exit code; garbage and timeouts are outcome problems (2384.766754ms) +✔ busExit and refuseInsideAgent (0.613505ms) +✔ approvals: a request is validated before anything is posted; the rendering shows names and never ids (12.852656ms) +✔ approvals: the ledger is appended and folded into open requests with bind and approval states (5.782138ms) +✔ approvals: a reply approves only when it points at a request, says exactly approve, and comes from a listed approver once (0.867481ms) +✔ approvals: a button approves only on its own request message with the matching custom id (0.677824ms) +✔ approvals flow: a turn that opened a request posts the message with the button, records it, binds it, and both approvers approve (44.404919ms) +✔ approvals flow: a non-approver, a repeat, a wrong custom id and a service refusal each get their fixed line and a drop entry (24.440255ms) +✔ approvals flow: an invalid request from the model, a refused post, and no api client are recorded and post nothing (15.489524ms) +✔ approvals flow: start retries a bind and an approval left as unknown, under their original keys (27.563545ms) +✔ authorize: open channel, listed user (16.150485ms) +✔ authorize: wrong guild (0.28161ms) +✔ authorize: no guild (DM) (0.225398ms) +✔ authorize: unlisted channel (1.378769ms) +✔ authorize: unknown channel, no info (0.846771ms) +✔ authorize: thread of listed parent (0.639398ms) +✔ authorize: thread of unlisted parent (0.491093ms) +✔ authorize: text channel that is not a thread and not listed (0.248551ms) +✔ authorize: unlisted user (0.249877ms) +✔ authorize: no author (0.556257ms) +✔ authorize: bot author (listed id, bot flag) (0.192138ms) +✔ authorize: system author (0.166337ms) +✔ authorize: the bot itself (0.116806ms) +✔ authorize: webhook (0.113884ms) +✔ authorize: mention channel without mention (0.188505ms) +✔ authorize: mention channel with bot mention (0.203378ms) +✔ authorize: mention channel with @everyone only (0.169766ms) +✔ authorize: mention channel mentioning someone else (0.112607ms) +✔ authorize: mention channel, content says @bot but mentions empty (0.139715ms) +✔ authorize: private thread under mention channel, mentioned (0.12079ms) +✔ authorize: private thread under mention channel, not mentioned (0.100549ms) +✔ authorize: thread in another guild per channel info (0.242092ms) +✔ authorize: not an object (1.111897ms) +✔ authorize: no id (0.1492ms) +✔ authorize: oversize content is accepted and flagged (0.110482ms) +✔ authorize: exactly the limit is not oversize (0.086483ms) +✔ authorize: a user's channel allowlist drops them outside it, threads count as the parent, others are unaffected (0.684236ms) +✔ authorize: order puts wrong guild before user, and user before channel (no channel lookup for strangers) (0.216255ms) +✔ binding: a complete binding validates and is frozen (11.883208ms) +✔ binding: unknown key, missing field, wrong type refuse with exit 2 (7.763507ms) +✔ binding: empty allowlists refuse (0.506707ms) +✔ binding: a user's channel allowlist must be non-empty, listed and unique; absent means every listed channel (1.64819ms) +✔ reloadDiff: reloadable keys are summarised by id; every fixed key refuses with exit 2 (5.565414ms) +✔ binding: dmRecipient is optional, a snowflake, one of the listed users, and a fixed key (7.762208ms) +✔ binding: file must be 0600, regular, not a symlink (5.293905ms) +✔ binding: token file mode, symlink, emptiness and shape are checked; token never appears in errors (2.823072ms) +✔ cli: check refuses a non-0600 token file with exit 2 before any network use (220.470979ms) +✔ context files: absolute paths, traversal, symlinks and out-of-repo targets refuse; in-repo files resolve (3.235281ms) +✔ cli: check refuses a missing context file and a missing binding with exit 2; usage is exit 4 (885.135928ms) +✔ cli: reload validates the file first (exit 2), then needs a live owner (exit 1); usage is exit 4 (415.829812ms) +✔ cli: run refuses when STOP is present, before any network use (277.976045ms) +✔ binding: tools is optional, validated strictly, a fixed key for reload, and its roots are resolved against the data root (3.601177ms) +✔ binding: a git key is validated at load and reaches the extension whole, and only on a writable root (2.952646ms) +✔ 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 (37.062415ms) +✔ delivery: refused and unknown outcomes are journaled; a later chunk is not sent after a failure (48.925104ms) +✔ delivery: restart with an unknown entry re-sends the same nonce once and reconciles before accepting traffic (2.735505ms) +✔ delivery: an unknown entry older than the dedupe window is marked refused, not re-sent; a still-unknown one refuses start (4.826431ms) +✔ delivery: repeated unknown reconciliations never refresh the dedupe window; the original intent time decides (7.131981ms) +✔ turn: a failed engine turn posts the fixed line, never model output, and writes a failed record (7.448548ms) +✔ turn: a second message during a turn is held by the engine, both get their own reply and record (34.250691ms) +✔ turn: a thread under a listed channel is answered in the thread; an unknown thread is looked up once (6.149321ms) +✔ drop: an unlisted user gets silence and one drop line; no inbox entry, no REST call, no engine call (6.309747ms) +✔ drop: an oversize message is accepted into the inbox, answered with the fixed line and journaled as a drop (12.979224ms) +✔ restart: an inbox with three ids and a replay of the same three produces zero turns (48.344945ms) +✔ stop: STOP present refuses start; STOP written while running refuses new turns and the current one finishes (79.624664ms) +✔ ceiling: the ceiling plus one is refused and journaled; one fixed line per UTC day; a new day accepts again (12.828338ms) +✔ ceiling: a burst arriving while turns are still running cannot queue past the ceiling (12.537168ms) +✔ ceiling: a turn interrupted by a crash still counts after restart; admissions are durable (9.427092ms) +✔ ceiling: the daily notice survives a same-day restart; one delivery attempt in total, even when the first attempt crashed mid-flight (8.457426ms) +✔ duplicate: the same event delivered twice while the thread lookup is held yields one prompt, one admission and one reply (4.386104ms) +✔ journal: no token-shaped string and no model output on the drop path reaches disk (0.981586ms) +✔ 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.448548ms) +✔ receipt: Discord refusing the reaction leaves the turn intact and records ok false (5.770748ms) +✔ reload: a new user is silent before and answered after; a removed channel goes silent; a lower ceiling applies at once (16.068602ms) +✔ reload: a fixed key refuses with exit 2 and the old binding stays in force (2.733839ms) +✔ tools: with a tools binding the turn record lists every read and its outcome; without one the field is null (5.840293ms) +✔ context: the Discord block names the server, channels and modes, and states the rules from Q15 and Q16 (10.650128ms) +✔ context: with tools the block names the roots, keeps file content as data, and says to state refusals plainly (2.780979ms) +✔ context: a writable root adds the write rules and says a write is real only once Jason commits (4.29911ms) +✔ context: the envelope is one bracketed line then the text; names cannot break the line (1.58776ms) +✔ context: a git root swaps the terminal-commit line for the git verbs, and a vault root adds the id protocol (8.457708ms) +✔ context: assembleContext concatenates files in launcher format and appends the block; sha256 is stable (1.601151ms) +✔ context: splitReply keeps paragraphs together under the limit and splits long ones at lines, spaces, then hard (4.966235ms) +✔ engine: buildPiArgs carries the fixed flags, engine settings, session dir and prompt file (3.068728ms) +✔ engine: with tools, buildPiArgs turns pi's own tools off, loads the extension explicitly and allowlists exactly our three (0.97972ms) +✔ engine: a run with tool turns settles once, on the answer, with every tool call in the result (118.279655ms) +✔ engine: a run that ends on a tool-only turn fails the prompt as empty; a retried run settles on the real end (90.864625ms) +✔ engine: one prompt, one turn, text and usage come back (82.471638ms) +✔ engine: a prompt while streaming is held until pi settles, then sent as its own run, and answered in order (374.102779ms) +✔ engine: a held prompt that times out before pi settles fails on its own and is never sent (325.526012ms) +✔ engine: timeout sends abort and fails only that turn; the process stays (125.370647ms) +✔ engine: tool events from a run that outlived its timeout never land in the next prompt's record (266.818641ms) +✔ engine: a prompt after a turn that timed out before its agent_start waits for pi to settle instead of being refused (173.810103ms) +✔ engine: when pi has not started a timed-out turn by the end of the abort grace, the engine stops pi and fails held prompts (215.100324ms) +✔ engine: a timed-out turn pi starts only after the grace never answers a later prompt (622.159336ms) +✔ engine: late events of a run past its grace, before pi exits, answer nothing and nothing more is sent (early prompt response) (3.420612ms) +✔ engine: late events of a run past its grace, before pi exits, answer nothing and nothing more is sent (late prompt response) (1.316421ms) +✔ engine: a timed-out run pi did start outlives the grace; the next prompt goes out when it ends (466.085165ms) +✔ engine: a malformed JSONL line fails the turn, not the process (50.432037ms) +✔ engine: a turn that ends in error rejects with the error code; process exit fails pending turns (59.967268ms) +✔ gateway: hello -> identify with intents, ready, heartbeat with jitter, ack (3.410353ms) +✔ gateway: missed ack closes the socket and resumes with the last sequence (2.021625ms) +✔ gateway: op 7 reconnect resumes; op 9 non-resumable re-identifies (0.699336ms) +✔ gateway: op 9 resumable resumes (0.428487ms) +✔ gateway: close 4014 is fatal, reports the missing intent, never reconnects (1.052509ms) +✔ gateway: 4004 and 4013 are fatal too; 1006 reconnects with identify when no session (0.679693ms) +✔ gateway: close() is final and unparseable frames are ignored (0.502568ms) +✔ git: config validation is strict, needs write: true, a work tree and a private token file (285.019903ms) +✔ git: the child environment drops every host git config, names one helper, and carries the token path only for origin (136.347733ms) +✔ git: status reports the branch, ahead/behind and changed paths, and refuses off the named branch or mid-merge (272.428902ms) +✔ git: parseStatus reads porcelain v2 including renames and conflicts (1.350846ms) +✔ git: a commit stages exactly the named files, carries the seat author and the requester trailer, and pushes at once (397.671011ms) +✔ git: commit refusals: message, paths, requester, nothing to commit, and an index that already holds other work (284.970897ms) +✔ git: a commit whose push fails is still a commit, says so, and the next commit's push carries both (D6) (226.383507ms) +✔ git: pull is fast-forward only; a diverged origin or dirty local files refuse with nothing merged (405.146352ms) +✔ git: push pushes the named branch only and reports up to date (110.552937ms) +✔ git: no token value or token path ever reaches a git argument list; outputs are masked and capped (133.506908ms) +✔ git: the credential helper answers get over https from a private file and nothing else (356.776939ms) +✔ git: the vault protocol validates before a commit, honours another owner's lock, reserves ids, and locks around writes (608.652604ms) +✔ lock: the claim is exclusive; a second start against a live owner refuses (9.207667ms) +✔ lock: a stale lock (dead owner, reused pid, or record without start) refuses run and is never signaled; only unlock clears it (137.107672ms) +✔ lock: an incomplete claim (directory without owner record) is busy and refuses run; unlock clears it (1.449643ms) +✔ lock: an owner record that exists but cannot be read is invalid: never signaled, never removed, never claimed over (6.372436ms) +✔ lock: legacy upgrade; a live connector holding a {pid, start} record is unknown, unlock refuses and nothing changes; after it exits, unlock clears it (81.131754ms) +✔ 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 (821.907635ms) +✔ lock: identity syntax; only canonical unsigned decimal start ticks and lowercase boot uuids are identities (1.169369ms) +✔ lock: a process whose start marker or boot id cannot be read refuses to claim (44.588245ms) +✔ lock: a live pid whose identity cannot be read right now is unknown: never signaled, never removed, never claimed over (3.410371ms) +✔ lock: four processes racing for the same binding; exactly one claims it and the others refuse (102.949689ms) +✔ lock: stale handoff; concurrent starts over a stale lock all refuse, nothing reclaims, one unlock then exactly one live owner (258.084232ms) +✔ lock: four-party schedule; claims landing inside an unlock's gap never survive, one unlock leaves no owner and no residue (176.49723ms) +✔ notices: a kind is recorded per UTC day and found again (1.053503ms) +✔ notify: the DM channel opens once, every send carries the nonce, and only the message id comes back (10.557984ms) +✔ notify: refusals and unknowns surface as RestOutcome without ids; a refused send reopens the channel next time (3.7748ms) +✔ notify: no dmRecipient, a non-0600 binding or token, or a missing binding refuse before any network use (4.592249ms) +✔ recover: nothing to do is clean; a lock whose owner is gone or that has no record is cleared and STOP ends up absent (75.563709ms) +✔ recover: an operator STOP refuses with exit 3 and is never removed, whatever the lock says (61.586462ms) +✔ recover: a brake written during the unlock wins; STOP stays with both lines and the start is refused (56.812654ms) +✔ recover: a held binding refuses with exit 3 and writes no STOP: live owner, alive pid without verifiable identity, unreadable record (207.860034ms) +✔ cli: recover exits 0 when ready, 3 behind a brake or a held binding, and run's own STOP refusal is 3 (1510.849171ms) +✔ rest: createMessage sends nonce, enforce_nonce, empty allowed_mentions and a soft reply reference (5.531414ms) +✔ rest: 429 waits retry_after and retries; 4xx is refused; 5xx and socket errors are unknown (3.650468ms) +✔ rest: content and nonce limits are enforced locally; typing never throws (0.932379ms) +✔ rest: react PUTs the encoded emoji on the inbound message for @me; 2xx is true, anything else is false and never throws (0.954723ms) +✔ rest: createDm posts the recipient and resolves the channel id; 4xx refused, 5xx unknown, bad id never sent (2.00831ms) +✔ setspark config: a bare https or loopback origin, a private key file, a principal (10.254659ms) +✔ setspark config: reaches the tools config and the binding as a fixed key (4.714592ms) +✔ setspark config: the binding's key survives resolveToolRoots and the engine's JSON hand-off to the extension (2.900566ms) +✔ setspark config: approvers come from the binding's users, never from the binding's setspark key (3.163685ms) +✔ setspark verbs: required_approvers go out as discord ids from names and come back as names (84.247403ms) +✔ setspark verbs: no Discord user id reaches tool text, whatever shape the service returns it in (24.969462ms) +✔ setspark contract: a decision made with names opens a request the connector accepts; names stored by an old record still refuse (14.721499ms) +✔ setspark keys: read per call, one printable token per file, rotation without a restart (17.167329ms) +✔ setspark idempotency keys: principal, turn id, call index; connector keys name a step (3.160519ms) +✔ setspark http core: json in and out, bearer header, idempotency header, fixed user agent, no key anywhere else (9.572244ms) +✔ setspark http core: error bodies become fixed refusals with code and the 409 fields; server text is data, cut (1042.749224ms) +✔ setspark verbs: a setspark key enables the eight verbs and no counters (6.688099ms) +✔ 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 (13.997535ms) +✔ setspark verbs: no turn refuses every write before any request; bad arguments refuse before any request; reads still work (8.115785ms) +✔ setspark verbs: renderRecord caps long output and hides the accepted snapshot (1.376507ms) +✔ setspark api: bind, add_approval (button and reply) and get use integer request ids and the connector's keys (7.689174ms) +✔ tools: config refuses a missing, symlinked, dotted, non-directory or duplicate root and bad limits (8.854561ms) +✔ tools: every escape is refused with a fixed reason and nothing outside the root is read (17.340196ms) +✔ tools: happy paths list, read a window, and search case-insensitively; dotfiles and symlinks never appear (9.218508ms) +✔ tools: the tool set renders text for the model, records details for the journal, and enforces the per-run budget (10.983915ms) +✔ tools: listing and search caps hold (32.381356ms) +✔ tools: credential shapes are caught; ordinary prose and ids are not (7.784107ms) +✔ 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 (35.08534ms) +✔ tools: an unreadable file under the root is skipped by search and refused by read (6.387642ms) +✔ tools: config accepts write: true only as a boolean, and enables the write tools only then (6.48996ms) +✔ tools: every write outside the fence is refused before any byte lands, and no temp file remains (26.868965ms) +✔ tools: write_file leaves the exact bytes, edit_file replaces one exact match, and the set renders the change as uncommitted (15.200985ms) +✔ tools: a target that changed between the check and the rename is refused and the temp file is removed (6.559184ms) +✔ web: config takes an https or loopback-http SearXNG base url and a bounded fetch cap (13.287236ms) +✔ web: address rules refuse every private, loopback, link-local, mapped and multicast form (4.204019ms) +✔ web: web_fetch refuses bad urls, private hosts, rebinding names, non-https redirects, too many hops, error status, non-text bodies, and times out (1105.741841ms) +✔ web: web_fetch returns html as text with the title, follows an https redirect, keeps plain text and json, and cuts at the cap (16.202457ms) +✔ web: html to text drops scripts, styles and comments, decodes entities and keeps block breaks (0.804354ms) +✔ 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 (10.647487ms) +✔ web: the tool set enables the web tools only with a web key, counts them in the budget, and records url, status and hits (8.722026ms) +ℹ tests 238 +ℹ suites 0 +ℹ pass 238 +ℹ fail 0 +ℹ cancelled 0 +ℹ skipped 0 +ℹ todo 0 +ℹ duration_ms 5109.413025 diff --git a/agents/filbert/work/s4-follow-up-review/r1-probe.txt b/agents/filbert/work/s4-follow-up-review/r1-probe.txt new file mode 100644 index 00000000..46390769 --- /dev/null +++ b/agents/filbert/work/s4-follow-up-review/r1-probe.txt @@ -0,0 +1,25 @@ +PROBE T1 sends at +0s +30s +90s +210s +450s +PROBE T1 gave-up at +450s = 7.5 min +PROBE T2 after 4 h: outcomes refused,refused,refused,refused,refused,gave-up +PROBE T2 sends 5 +PROBE T3 sends before restart 2 after one tick post-restart 3 (same clock second) +PROBE T4 dm sends 17 gave-up false +PROBE T5 digest sends in 1 h 7 gave-up false +PROBE A1 tick {"dms":1,"digest":false,"failed":0} line {"at":"2026-10-08T05:00:00.000Z","kind":"dm","decision":"624d0407-56e6-4686-b5af-21dbc41002fb","outcome":"confirmed","messageId":123} +PROBE A1 reopen 3 notify journal line 1 is malformed (messageId): /notify/demo/sent.jsonl +PROBE A2 ok sent d1 days 2026-10-08 refusals [["d2",1]] +✔ F2-T1 time from first refusal to gave-up, polling every POLL_MS (86.95343ms) +✔ F2-T2 binding fixed after 60 min: the DM never lands (172.659536ms) +✔ F2-T3 restart drops the backoff: next attempt is immediate (60.500209ms) +✔ F2-T4 500s for 6 h: no gave-up (243.575372ms) +✔ F2-T5 digest refusals: retried, never gave-up (75.006372ms) +✔ J4-A1 append does not type-check: a numeric messageId bricks the next open (61.251173ms) +✔ J4-A2 a line the row 39 writer produced still opens (57.483971ms) +ℹ tests 7 +ℹ suites 0 +ℹ pass 7 +ℹ fail 0 +ℹ cancelled 0 +ℹ skipped 0 +ℹ todo 0 +ℹ duration_ms 844.895562 diff --git a/agents/filbert/work/s4-follow-up-review/r1-sendwindow.txt b/agents/filbert/work/s4-follow-up-review/r1-sendwindow.txt new file mode 100644 index 00000000..085c1d64 --- /dev/null +++ b/agents/filbert/work/s4-follow-up-review/r1-sendwindow.txt @@ -0,0 +1,33 @@ +nocb connected before send true +nocb process exit code 1 +node:events:505 + throw er; // Unhandled 'error' event + ^ + +-- exit 0 +nocb connected before send true +nocb process exit code 1 +node:events:505 + throw er; // Unhandled 'error' event + ^ + +-- exit 0 +nocb connected before send true +nocb process exit code 1 +node:events:505 + throw er; // Unhandled 'error' event + ^ + +-- exit 0 +cb connected before send true +cb callback got EPIPE +cb process exit code 3 +-- exit 0 +cb connected before send true +cb callback got EPIPE +cb process exit code 3 +-- exit 0 +cb connected before send true +cb callback got EPIPE +cb process exit code 3 +-- exit 0 diff --git a/agents/filbert/work/s4-follow-up-review/review-r1.md b/agents/filbert/work/s4-follow-up-review/review-r1.md new file mode 100644 index 00000000..3ac3bf6a --- /dev/null +++ b/agents/filbert/work/s4-follow-up-review/review-r1.md @@ -0,0 +1,212 @@ +# Row 45, S4 follow-up, round 1 review (Filbert) + +Issue #1527, request comment 26869, queue rev 207. Packet: +`agents/rocko/work/s4-follow-up/BUILD.md`. Candidate: manifest +`candidate-manifest.sha256`, sha256 +`b329fdcbdf8cb62659f239359570dfa9771559e3112b743df55429244769ed14`, 8 +files, over `521597bb` with `build.patch` (sha256 +`4ed9ff61b94b4c9d2426e5703c2e6c0dbcd6bc0c64a88d59347a8b47f4933408`). +Brief: `docs/plans/2026-10-09_s4-follow-up-and-cohort.md`, "S4 follow-up". +Ruling: lead decision 72. Second reviewer: Darkwing (comment 26872). +This verdict: comment 26880. + +Verdict: **changes.** There is one blocker, B1. With the current backoff, +five definite refusals arrive within 7.5 minutes, so a binding typo gives +up every blocking DM long before anyone can fix it. Decision 72 sets the +limit so that a typo fixed "within a few hours" is ridden out. I also ask +for the send callback (R1) in round 2. Everything else is a note. + +## Method + +- Detached worktrees at `521597bb` under `~/filbert-scratch/r45/`: base, + candidate and a mutant tree. `git apply build.patch`, then `sha256sum -c`: + 8 OK in both patched trees, and 8 OK in the mutant tree after the run. +- `gate.sh` runs every package's node tests and every `scripts/test-*.sh`, + one at a time, in the candidate and then the base, and tees each output. + `DOCKER_HOST` points at a socket that doesn't exist. +- `probe/probe.test.mjs` drives `createNotifier` with a fake clock and a + fake Discord (F2-T1 to T5, J4-A1 and A2). +- `probe/sendwindow.mjs` checks what `ChildProcess.send` does when the peer + is dead but this process has not yet seen the disconnect. +- `mutants.sh` reruns Rocko's 18 and adds X1 to X14. A run counts as + killed when a test fails or is cancelled, or the outer timeout fires. +- Node 24.21.0: `node:24`, no network, the tree mounted read-only, the host + uid. + +## Suites + +| Suite | Candidate | Base | +|---|---|---| +| node cli | 60/60 | 49/49 | +| node cli + discord (Node 24.21.0) | 238/238 | n/a | +| node discord | 178/178 | 178/178 | +| node bus | 67/67 | 67/67 | +| node business | 60/60 | 60/60 | +| node conversation | 149 pass, 3 fail | 149 pass, 3 fail | +| node control-board, ledger, mosaic, queue, seat, tasks, webui | all green | all green | +| test-auth, conductor, config, discord, extension-package, foundation, queue | all green | all green | +| test-release | 4/4, Docker cases skipped | same | +| test-task | 26 pass, 2 fail | 26 pass, 2 fail | + +The three conversation failures are K1, K3 and K10 in both trees (row 46). +Rocko saw 150/2 on the base, so one of the three passes there +intermittently. test-task fails "user recall run succeeds" and "recalled +user name" in both trees. + +## B1 (blocking): five refusals take 7.5 minutes, not a few hours + +The backoff for a refusal is the same as for an unknown outcome: 30 s, +doubling. Probe F2-T1 polls every `POLL_MS` with Discord answering 403 +every time: + +``` +sends at +0s +30s +90s +210s +450s +gave-up at +450s = 7.5 min +``` + +Probe F2-T2 fixes the binding 60 minutes after the first refusal. Four +hours later the journal reads `refused ×5, gave-up` and the DM was never +delivered. A typo in the binding hits every blocking decision at once, so +all of them give up within minutes. The only way back is to edit the +journal by hand. + +Decision 72 gives the reason for five: "Five is enough to ride out a binding +typo fixed within a few hours. Fewer would give up on a fixable mistake." +The candidate follows the brief's wording (five, from the journal, one +gave-up line), but at this pace five does not do what the ruling chose it +for. The new test can't see the problem because it advances the clock 31 +minutes between attempts. + +A restart makes it worse. The backoff lives in memory, so after a restart +the next attempt goes at once (probe F2-T3). The unit restarts after 15 s +on a failure exit, so a host that crashes in a loop spends the count even +faster. + +My round 1 note 7 on #1521 said a refused DM "retries at most every 30 +min". That is the cap, not the pace, and it may be where the "few hours" +estimate came from. That's my error. + +Fix: + +1. A definite refusal schedules the next attempt at `BACKOFF_MAX_MS` + instead of the doubling step. Five refusals then span at least 2 hours. +2. On open, take each decision's next attempt time from its last definite + refusal's `at` plus `BACKOFF_MAX_MS`, so a restart doesn't attempt early. + The journal already holds the timestamp. +3. Test: poll every `POLL_MS` with a permanent 403. Assert no gave-up line + before 2 hours and one by 2 hours plus a poll. Restart in the middle and + assert no early send. + +Sage owns the ruling. If five refusals at any pace is the intent, the +decision should say so, and B1 becomes a note. As written, the reason given +for five doesn't hold. + +## R1 (round 2): give both close sends a callback + +Rocko's open finding is real. `probe/sendwindow.mjs` SIGKILLs a child, +blocks the event loop until the child is dead, then sends: + +``` +nocb connected before send true +nocb process exit code 1 node:events: Unhandled 'error' event (EPIPE) +cb connected before send true +cb callback got EPIPE +cb process exit code 3 +``` + +It behaved the same in 3 of 3 runs for each mode. That is a bare fork. In +`startHost` the send is followed at once by `ended(broker)`, whose +`once(broker, "exit")` also listens for 'error'. So the EPIPE rejects +`ended`, `startHost` rejects with the raw error instead of the notifier's +CliError, and `cli.mjs` rethrows a non-CliError: exit 1 with a stack trace. +Darkwing measured this through `startHost` (5 of 80 runs, comment 26872). +At :150 the refusal (exit 3, kept down by `RestartPreventExitStatus`) +becomes exit 1, which the unit restarts every 15 s. At :144 the exit code +is 1 either way, but the operator loses the notifier's message. +Since round 2 is needed for B1 anyway, fold it in: +`broker.send({ op: "close" }, () => {})` at both places. A test is +optional: the window needs the event loop blocked between the kill and the +send. + +## Contested choices + +- J4 checks every complete line, not only confirmed ones. Sage accepted + that. Probe J4-A2 shows that every line shape the row 39 writer produces + still opens. +- A confirmed line needs a string `messageId`: **agree.** `rest.mjs` + `createMessage` throws `unknown` on a 2xx without a string id, so the + writer always has one. +- A digest line must not carry a decision: **agree.** The writer writes + `null`. +- `gave-up` only on a dm: **agree.** Only DMs give up. + +## Mutants + +All 18 of Rocko's are killed under my harness as well, each by a failing +test. Of my 14, 11 are killed. + +| Mutant | Result | +|---|---| +| N2, N4, N5, M28, G144, G150, F2a to F2f, J4a to J4d, E1, E2 | killed | +| X1 every refusal counts (no status check) | killed | +| X2 a 5xx refusal counts | survived; equivalent, `rest.mjs` never refuses with a 5xx | +| X3 the count is not rebuilt on open | killed | +| X4 a digest needs no `day` | killed | +| X5 `gave-up` allowed on a digest | killed | +| X6 no CliError when the directory can't be created | killed | +| X7 a digest may carry a decision | killed | +| X8 no symlink message on ELOOP | killed | +| X9 no CliError when `sent.jsonl` itself is unwritable | **survived**; test gap | +| X10 a non-confirmed `messageId` may be any type | killed | +| X11 any `kind` | killed | +| X12 gave-up lines not loaded | killed | +| X13 `giveUp` writes no line | killed | +| X14 the install message drops the `mkdir -m 0700` line | **survived**; test gap | + +X9 and X14 each need a one-line test (a 0400 `sent.jsonl`, and an +assertion on the install message). They are optional. + +## Notes (not blocking) + +1. **`append` doesn't type-check.** Probe J4-A1: a fake Discord that + returns `messageId: 123` gets a confirmed line written, and the next open + refuses with exit 3. The real writer can't produce that today (see + above), so this is hardening only: running `badField` before the write + would turn a future regression into a send error rather than a unit that + stays down. +2. **Recovery is undocumented.** After a gave-up, the README doesn't say + what a fixed binding does: nothing, the DM is final. One line would help: + the decision stays in `mosaic inbox` and the digest. +3. **Digest refusals have no limit** (probe F2-T5: 7 sends in an hour on a + permanent 403). That matches the ruling, which limits only DMs. +4. **`deadPid`** checks once that a reaped pid is free. In theory it could + be reused before the test uses it. That is far better than the fixed + number it replaces. +5. The brief's other items match: the reader-cap exposure check, `t.after` + in "a notifier that dies", the `broker.connected` guard on both closes, + the unwritable and symlink messages, the install line, and the trackers + boot test moved (byte-identical to Sage's copy). + +## Darkwing's review (comment 26872) + +Darkwing also asks for changes, and we found the same two things on our +own: their R1 is my R1, their R2 is my B1. I agree with their extra +points: + +- Add the callback to the `close()` sends at :184 and :188 too. They + predate this row and have the same window. +- A 401 from a bad token also counts as definite, which widens B1. +- Their notes 1 to 5 are not blocking. Note 2 is my X14. Note 3: the + `day` pattern accepts `2026-13-45`. + +## Files + +- `gate.sh`, `mutants.sh`, `probe/probe.test.mjs`, `probe/sendwindow.mjs` +- Output: + - `r1-gate-summary.txt` + - `r1-mut-summary.txt` + - `r1-node24.txt` + - `r1-probe.txt` + - `r1-sendwindow.txt` + - `r1-manifest-mut-after.txt` + - `r1-cand-*.txt` and `r1-base-*.txt` (each suite, teed)