diff --git a/agents/filbert/work/slice1-s4-review/gate-r2.sh b/agents/filbert/work/slice1-s4-review/gate-r2.sh new file mode 100755 index 00000000..fa8d002a --- /dev/null +++ b/agents/filbert/work/slice1-s4-review/gate-r2.sh @@ -0,0 +1,11 @@ +#!/usr/bin/env bash +# Sequential gate; $1 = tree, $2 = out prefix +T="$1"; P="$2"; O=~/filbert-scratch/r39b/out; cd "$T" +for p in cli bus business discord; do + [ -d packages/$p/tests ] || continue + env -u NODE_TEST_CONTEXT node --test "packages/$p/tests/*.test.mjs" > "$O/$P-node-$p.txt" 2>&1; echo "$P node-$p exit $?" +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-r39b.sock timeout 900 bash "$s" > "$O/$P-suite-$n.txt" 2>&1; echo "$P suite-$n exit $? load $(cut -d" " -f1 /proc/loadavg)" +done diff --git a/agents/filbert/work/slice1-s4-review/mutants-r2.sh b/agents/filbert/work/slice1-s4-review/mutants-r2.sh new file mode 100755 index 00000000..2ccb9482 --- /dev/null +++ b/agents/filbert/work/slice1-s4-review/mutants-r2.sh @@ -0,0 +1,78 @@ +#!/usr/bin/env bash +# Row 39 round 2 mutants: Filbert's round 1 set (M1-M34), Rocko's round 2 +# set (R5-F3, from agents/rocko/work/slice1-s4/mutants-r2.sh) and N1-N6. +# One perl substitution each, restored after its run. A run counts as +# killed when any test fails or is cancelled; --test-timeout turns a hang +# (M19 waits on a readline prompt) into a cancellation. +# 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 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 +run M1 $NT 's/if \(!d\.blocking \|\| journal\.sent\.has\(d\.id\)\) continue;/if (journal.sent.has(d.id)) continue;/' +run M2 $NT 's/hour >= DIGEST_HOUR/hour > DIGEST_HOUR/' +run M3 $NT 's/!journal\.days\.has\(day\) &&/true \&\&/' +run M4 $NT 's/return b !== undefined && t < b\.next;/return false;/' +run M5 $NT 's/if \(st\.uid !== process\.getuid\(\) \|\| \(st\.mode & 0o777\) !== 0o600\)/if (false)/' +run M6 $NT 's/const torn = lines\.pop\(\);/const torn = "";/' +run M7 $NT 's/export const ZONE = "America\/Chicago";/export const ZONE = "UTC";/' +run M8 $NT 's/export const dmNonce = \(id\) => `dm\$\{[^;]*;/export const dmNonce = (id) => "dmfixed";/' +run M9 $NT 's/\(dmSent\(d\.id\) \? "\[blocking, DM sent\] " : "\[blocking, DM pending\] "\)/"[blocking] "/' +run M10 $NT 's/Math\.min\(BACKOFF_MS \* 2 \*\* n, BACKOFF_MAX_MS\) \}\);/BACKOFF_MS });/' +run M11 packages/discord/src/notify.mjs 's/if \(err\.kind === "refused"\) channel = null;//' +run M12 packages/discord/src/notify.mjs 's/throw new RestOutcome\(err\.kind, `dm: \$\{err\.kind\}`/throw new RestOutcome(err.kind, err.message/' +run M13 packages/discord/src/notify.mjs 's/if \(binding\.dmRecipient === null\)[^\n]*\n//' +run M14 packages/discord/src/binding.mjs 's/if \(!users\.some\(\(u\) => u\.id === dmRecipient\)\)[^\n]*\n//' +run M15 packages/discord/src/binding.mjs 's/"tokenFile", "dmRecipient", "engine", "context"/"tokenFile", "engine", "context"/' +TR=packages/cli/src/transport.mjs +run M16 $TR 's/if \(found\.length > 0\) \{/if (false) {/' +run M17 $TR 's/"outcome-unknown": 1,/"outcome-unknown": 2,/' +run M18 $TR 's/if \(proc\.error \|\| proc\.status === null\)/if (proc.error)/' +CL=packages/cli/src/cli.mjs +run M19 $CL 's/if \(!io\.stdin\.isTTY\) throw usage/if (false) throw usage/' +run M20 $CL 's/if \(ref\.length < 8\)/if (ref.length < 1)/' +run M21 $CL 's/\|\| \(st\.mode & 0o777\) !== 0o600\)/)/' +run M22 $CL 's/if \(e\.code === "decision-closed"\)[^\n]*\n//' +run M23 $CL 's/if \(hits\.length > 1\)[^\n]*\n//' +run M24 $CL 's/if \(state\?\.live\) return state\.business;/if (state) return state.business;/' +CF=packages/cli/src/config.mjs +run M25 $CF 's/if \(named\.length > 1\)/if (named.length > 2)/' +run M26 $CF 's/if \(vars\["tracker\.project"\] !== undefined\) named\.push/named.push/' +HO=packages/cli/src/host.mjs +run M27 $HO 's/close\(1\);\n \};/close(0);\n };/' +run M28 $HO 's/if \(prior\?\.live\) throw/if (false) throw/' +run M29 $HO 's/if \(i < 0 \|\| argv\[i \+ 1\] !== "bus" \|\| argv\[i \+ 2\] !== "start"\)/if (false)/' +run M30 $HO 's/return fields\[0\] === "Z" \? null : fields\[19\];/return fields[19];/' +run M31 $HO 's/if \(ready\?\.ok !== true\) \{/if (ready?.ok === "never") {/' +run M32 packages/cli/src/format.mjs 's/if \(decision\?\.task_ref\) out\.push[^\n]*\n//' +run M33 packages/cli/src/notifier-process.mjs 's/process\.on\("SIGTERM", \(\) => stop\(0\)\);//' +run M34 packages/discord/src/rest.mjs 's/if \(typeof recipientId !== "string" \|\| !\/\^\[0-9\]\{17,20\}\$\/\.test\(recipientId\)\)/if (false)/' +run R5 $NT 's/if \(!st\.isFile\(\) \|\| st\.uid !== process\.getuid\(\) \|\| \(st\.mode & 0o777\) !== 0o600\)/if (false)/' +run R6 $NT 's/if \(end < bytes\.length\) \{/if (false) {/' +run R6b $NT 's/ftruncateSync\(fd, end\);//' +run R6c $NT 's/const name = copyTorn\(dir, bytes\.subarray\(end\), now\(\)\);/const name = "none";/' +run F1 $NT 's/if \(!ds\.isDirectory\(\) \|\| ds\.uid !== process\.getuid\(\) \|\| \(ds\.mode & 0o077\) !== 0\)/if (false)/' +run J3 $NT 's/O_RDWR \| O_APPEND \| O_CREAT \| O_NOFOLLOW/O_RDWR | O_APPEND | O_CREAT/' +run D1 $NT 's/hash23\(`\$\{business\}\\n\$\{day\}`\)/hash23(day)/' +run RACE packages/cli/src/host.mjs 's/if \(child\.exitCode !== null \|\| child\.signalCode !== null\) onDeath\(name, child\.exitCode, child\.signalCode\);\n\s*else //' +run F3 packages/cli/systemd/mosaic-bus.service.in 's/\[Unit\]\n/[Unit]\nAfter=network-online.target\n/' +# Round 2, Filbert +run N1 $NT 's/O_WRONLY \| O_CREAT \| constants\.O_EXCL \| O_NOFOLLOW/O_WRONLY | O_CREAT | O_NOFOLLOW/' +run N2 $NT 's/lstatSync, mkdirSync/lstatSync, statSync, mkdirSync/; s/const ds = lstatSync\(dir\);/const ds = statSync(dir);/' +run N3 $NT 's/const dfd = openSync\(dir, constants\.O_RDONLY\);\n try \{\n fsyncSync\(dfd\);/const dfd = openSync(dir, constants.O_RDONLY);\n try {\n 0;/' +run N4 $NT 's/openSync\(file, O_WRONLY \| O_APPEND \| O_NOFOLLOW\)/openSync(file, O_WRONLY | O_APPEND)/' +run N5 packages/cli/src/host.mjs 's/if \(child\.exitCode !== null \|\| child\.signalCode !== null\) onDeath/if (child.exitCode !== null) onDeath/' +run N6 $NT 's/ftruncateSync\(fd, end\);/ftruncateSync(fd, end > 0 ? end - 1 : 0);/' diff --git a/agents/filbert/work/slice1-s4-review/probe-r2.mjs b/agents/filbert/work/slice1-s4-review/probe-r2.mjs new file mode 100644 index 00000000..511764c4 --- /dev/null +++ b/agents/filbert/work/slice1-s4-review/probe-r2.mjs @@ -0,0 +1,41 @@ +// Filbert, row 39 r2: notify journal edge cases against the candidate. +// Usage: node probe.mjs +import { appendFileSync, chmodSync, mkdirSync, mkdtempSync, readdirSync, readFileSync, rmSync, symlinkSync, writeFileSync } from "node:fs"; +import { homedir } from "node:os"; +import { dirname, join } from "node:path"; +const { openJournal, journalPath } = await import(process.argv[2] + "/packages/cli/src/notifier.mjs"); +const scratch = join(homedir(), "filbert-scratch", "r39b", "probe", "tmp"); +mkdirSync(scratch, { recursive: true }); +const fresh = () => journalPath(mkdtempSync(join(scratch, "j-")), "demo"); +const torn = (f) => readdirSync(dirname(f)).filter((n) => n.startsWith("torn-")).sort(); +const tryOpen = (f, o) => { try { return openJournal(f, o); } catch (e) { return { error: `${e.exitCode ?? e.code}: ${e.message.replace(scratch, "")}` }; } }; +const rec = (d) => JSON.stringify({ at: "x", kind: "dm", decision: d, outcome: "confirmed", messageId: "1" }); + +// T1: a confirmed record whose newline never landed is treated as torn: copied out, dropped, so the decision is DM'd again. +{ const f = fresh(); openJournal(f).append(JSON.parse(rec("a"))); appendFileSync(f, rec("b")); + const logs = []; const j = openJournal(f, { log: (l) => logs.push(l) }); + console.log("T1 sent after open", JSON.stringify([...j.sent]), "torn copies", torn(f).length, "logs", logs.length); } +// T2: the round 1 B1 sequence: torn tail, append, reopen. +{ const f = fresh(); openJournal(f).append(JSON.parse(rec("a"))); appendFileSync(f, '{"at":"x","kind":"dm","dec'); + openJournal(f).append(JSON.parse(rec("b"))); const r = tryOpen(f); + console.log("T2 reopen", r.error ?? JSON.stringify([...r.sent])); } +// T3: torn tail plus a malformed middle line: refuses, file and directory untouched. +{ const f = fresh(); openJournal(f); writeFileSync(f, `${rec("a")}\nnot json\n${rec("b").slice(0, 10)}`); const before = readFileSync(f, "utf8"); + console.log("T3", tryOpen(f).error, "| unchanged", readFileSync(f, "utf8") === before, "| torn copies", torn(f).length); } +// T4: torn tail with a loose file mode: refuses before any repair. +{ const f = fresh(); openJournal(f); writeFileSync(f, "{\"a"); chmodSync(f, 0o644); + console.log("T4", tryOpen(f).error, "| torn copies", torn(f).length); } +// T5: the journal directory is a symlink to a 0700 directory. +{ const root = mkdtempSync(join(scratch, "j-")); const real = join(root, "real"); mkdirSync(real, { mode: 0o700 }); + mkdirSync(join(root, "notify"), { mode: 0o700 }); symlinkSync(real, join(root, "notify", "demo")); + console.log("T5", tryOpen(journalPath(root, "demo")).error ?? "accepted"); } +// T6: three crashes between copy and truncate, then a clean open: one copy per attempt, none overwritten. +{ const f = fresh(); openJournal(f); writeFileSync(f, "{\"torn"); const now = () => new Date("2026-10-09T01:00:00Z"); + for (let i = 0; i < 3; i++) tryOpen(f, { now, log: () => { throw new Error("crash"); } }); + const j = openJournal(f, { now }); console.log("T6 copies", JSON.stringify(torn(f)), "journal bytes", readFileSync(f).length, "sent", j.sent.size); } +// T7: a torn tail in a directory the user cannot write (0500): what refuses, and how. +{ const f = fresh(); openJournal(f); writeFileSync(f, "{\"torn"); chmodSync(dirname(f), 0o500); + console.log("T7", tryOpen(f).error ?? "opened", "| journal", JSON.stringify(readFileSync(f, "utf8"))); chmodSync(dirname(f), 0o700); } +// T8: a blank complete line (e.g. "\n\n" from a hand edit). +{ const f = fresh(); openJournal(f); writeFileSync(f, `${rec("a")}\n\n`); console.log("T8", tryOpen(f).error ?? "opened"); } +rmSync(scratch, { recursive: true, force: true }); diff --git a/agents/filbert/work/slice1-s4-review/r2-base-node-bus.txt b/agents/filbert/work/slice1-s4-review/r2-base-node-bus.txt new file mode 100644 index 00000000..7d2f551d --- /dev/null +++ b/agents/filbert/work/slice1-s4-review/r2-base-node-bus.txt @@ -0,0 +1,66 @@ +✔ launch identity is stamped, payload identity is refused and stale holder cannot send (217.310414ms) +✔ decision classes route from policy; gated resolution is human-only, choice and target must match (303.537957ms) +✔ claim exclusion, holder release, gated revoke and rerouting to a new holder are atomic (253.286695ms) +✔ launch events require a human CLI capability; generic emit cannot forge authority events (160.225541ms) +✔ within-role decisions close atomically and invalid options or blocking omissions refuse (162.19786ms) +✔ observer capabilities read human inbox but cannot mutate or forge launch identity (128.801264ms) +✔ task action subjects and linked decision trail are complete and ordered (149.430351ms) +✔ launch binding is durable and reconnecting requires the identical trusted record (86.051671ms) +✔ business isolation includes inherited object names and cross-business message references (157.309435ms) +✔ authority never transfers between action, run, target, unresolved or replaced role holder (223.035126ms) +✔ task projection uses schema current view, skipping earlier and equal-start polls (210.412718ms) +✔ revocation permanently bars the old run from reclaiming first, including after broker restart (191.349274ms) +✔ empty message references refuse before storage; refusal-evidence failure stays a typed error (222.442927ms) +✔ both arbiters require human resolution when their cross-role route is themselves (195.17119ms) +✔ S1 adapter takes resolved limits and refs, rejects mismatched instance, never mutates input (2.246744ms) +✔ only validated broker references load; returned data and exceptions cannot expose a known token (4.360273ms) +✔ bad file modes, symlinks, repository/data paths, malformed tokens and missing dates refuse (1.628386ms) +✔ expiry refuses use and env references never become client data (0.592553ms) +✔ S1 parsed service refs work, service mismatch refuses, Gitea rotation due is a warning state (1.278882ms) +✔ opaque tokens shorter than 16 characters refuse before use (0.323611ms) +✔ human proof binds CLI entry, process start and nonce; agents and incomplete ancestry refuse (2.685349ms) +✔ process reader gets own kernel identity without exposing environment values (1.400191ms) +✔ EACCES ancestor environments skip only markers; commands and registered launches still refuse (0.8581ms) +✔ real pid 1 remains inspectable when its environment is protected (0.305429ms) +✔ within-role sends cite an open gated launch decision without spending it or naming it in grants (260.83156ms) +✔ missing and foreign-business citations refuse and roll back message and grant (226.690882ms) +✔ cross-role sends still need a matching resolved decision and consume it once (233.511399ms) +✔ broker process binds trusted launches, offers reader capabilities, refuses human mutation, closes cleanly (248.645504ms) +✔ startup token refusal returns safe code without value or partial listening broker (41.71346ms) +✔ loaded fixture token is absent from socket replies and SQLite, including refusal evidence (198.391657ms) +✔ killed broker leaves an explicit stale lock; another process cannot silently reclaim it (157.488841ms) +✔ trusted host registers later launches; socket clients never have a registration verb (173.653461ms) +✔ runtime excludes declared project roots even when host supplies no repoRoots (37.15333ms) +✔ a refused launch binding leaves the broker and existing capabilities alive; bad protocol stops it (165.733757ms) +✔ v3b prototype refusals, views and append-only mutations (1094.861222ms) +✔ gated approval authorizes once, survives store reopen, and fresh approval works (343.625879ms) +✔ another run cannot consume an approval; a failed check leaves it usable (233.997111ms) +✔ two scheduled callers have exactly one grant and one consumed refusal (150.27752ms) +✔ failed commit rolls consumption back; cross-role consumes and within-role stays reusable (294.068958ms) +✔ class drift gated to cross-role refuses before consumption (169.324257ms) +✔ class drift cross-role to gated refuses before consumption (181.47066ms) +✔ class drift gated to within-role refuses before consumption (174.542442ms) +✔ class drift cross-role to within-role refuses before consumption (182.622361ms) +✔ class drift within-role to gated refuses before consumption (167.596658ms) +✔ class drift within-role to cross-role refuses before consumption (237.783601ms) +✔ message.send consumes approval and prevents a later send or authorize (307.546305ms) +✔ role.revoke consumes approval and prevents a later revoke or authorize (211.423445ms) +✔ creates private WAL store and excludes a second writer until explicit close (171.415558ms) +✔ rollback is atomic and schema metadata is checked against trusted DDL, not just itself (234.689651ms) +✔ existing empty database and symlink runtime directory refuse, never initialize over damage (188.953806ms) +✔ crash during a transaction recovers no partial event after explicit fixture-only lock removal (164.016855ms) +✔ writer refuses mixed at/read_at forms atomically, even through trusted SQL helpers (86.356441ms) +✔ async transactions refuse before invoking their function (83.160309ms) +✔ socket capability stamps launch identity; shared views use wire, no SQL client (205.097157ms) +✔ two wire claims serialize; a lost reply never automatically retries (215.068339ms) +✔ malformed, oversized and identity-forging envelopes refuse without echoing input (127.551648ms) +✔ client preserves UTF-8 when a response divides a multibyte character (11.827025ms) +✔ committed mutation followed by dropped reply reports unknown and is never retried (118.23409ms) +ℹ tests 58 +ℹ suites 0 +ℹ pass 58 +ℹ fail 0 +ℹ cancelled 0 +ℹ skipped 0 +ℹ todo 0 +ℹ duration_ms 2739.556368 diff --git a/agents/filbert/work/slice1-s4-review/r2-base-node-business.txt b/agents/filbert/work/slice1-s4-review/r2-base-node-business.txt new file mode 100644 index 00000000..8caece19 --- /dev/null +++ b/agents/filbert/work/slice1-s4-review/r2-base-node-business.txt @@ -0,0 +1,68 @@ +✔ config directory and file path follow MOSAIC_CONFIG (1.261412ms) +✔ the fixture business validates and comes back frozen (3.486499ms) +✔ two instances may share a definition (1.135312ms) +✔ top-level refusals (4.052468ms) +✔ arbiters and projects (5.50727ms) +✔ role instances (2.908438ms) +✔ Vikunja bots (5.357349ms) +✔ a role without Vikunja takes no tracker block (1.918244ms) +✔ credential references match the definition's services (2.41167ms) +✔ launch (7.652372ms) +✔ loadBusiness: file checks (1.760046ms) +✔ loadBusiness: not a regular file (43.811309ms) +✔ loading writes nothing (1.147463ms) +✔ names that are Object.prototype properties don't count as declared (3.088735ms) +✔ the shipped example refuses as written and validates once filled in (0.806055ms) +✔ usage errors exit 4 (281.842061ms) +✔ validate: a good business exits 0 and prints instance digests (65.70548ms) +✔ validate: project files (343.187247ms) +✔ validate: missing files and a broken system config (246.116384ms) +✔ validate: credential reference problems exit 2 and name each one (68.667591ms) +✔ validate: a token file inside the repository is refused (67.922015ms) +✔ validate: role definitions come from MOSAIC_ROLES_DIR (214.225914ms) +✔ resolve: prints one instance's record (208.183465ms) +✔ resolve: refusals (390.711309ms) +✔ parse: exactly one of file or env, plus the service's date (2.300609ms) +✔ check: a good file has no problems (0.624398ms) +✔ check never opens the file: a write-only token passes (0.309092ms) +✔ check: file problems (0.677698ms) +✔ check: token files can't live in the repository or dataRoot, even through a linked directory (0.619586ms) +✔ check: dates and environment references (0.321745ms) +✔ path and load (1.70873ms) +✔ refusals (1.06147ms) +✔ systemVars flattens the validated config (1.602672ms) +✔ precedence: system, business, project, project role, agent (4.096363ms) +✔ limits narrow the definition and never widen it (1.855891ms) +✔ role.launch stays within-role only for the instance the launch block names (3.74717ms) +✔ limits.authority without role.launch leaves the launcher with no launch block (1.445106ms) +✔ limits.authority narrows cross-role actions too (0.934994ms) +✔ classify (1.225729ms) +✔ the record carries what the broker and launcher need (0.879546ms) +✔ digest: key order doesn't matter, any value change does (5.684839ms) +✔ refusals (1.786089ms) +✔ the four shipped version 2 roles load (2.972336ms) +✔ shipped role scopes match addendum B section 2 and the SR runbook (1.142959ms) +✔ shipped authority follows the note's table (0.654999ms) +✔ version 1 files keep loading with no authority (0.911567ms) +✔ the conductor policy isn't a role (0.193819ms) +✔ a missing role file is exit 4, a symbolic link too (0.337323ms) +✔ version 2 refusals (1.328539ms) +✔ authority: closed vocabulary, no gated-only action, no overlap (2.030509ms) +✔ credentials: Gitea scopes (0.828626ms) +✔ credentials: Vikunja scopes are a group-to-verbs map from the grantable list (1.097888ms) +✔ credentials: services (0.512348ms) +✔ contract: a non-empty regular Markdown file beside the role file (0.606346ms) +✔ every key names known layers and a merge rule (0.66841ms) +✔ unknown keys and wrong layers refuse (0.557532ms) +✔ types (1.552146ms) +✔ merge: defaults, then the most specific layer wins (0.269319ms) +✔ merge: limits only narrow, and provenance lists each source (0.340672ms) +✔ merge doesn't change its inputs (0.136302ms) +ℹ tests 60 +ℹ suites 0 +ℹ pass 60 +ℹ fail 0 +ℹ cancelled 0 +ℹ skipped 0 +ℹ todo 0 +ℹ duration_ms 1946.375781 diff --git a/agents/filbert/work/slice1-s4-review/r2-base-node-discord.txt b/agents/filbert/work/slice1-s4-review/r2-base-node-discord.txt new file mode 100644 index 00000000..1a6eef37 --- /dev/null +++ b/agents/filbert/work/slice1-s4-review/r2-base-node-discord.txt @@ -0,0 +1,181 @@ +✔ approvals: a request is validated before anything is posted; the rendering shows names and never ids (3.311357ms) +✔ approvals: the ledger is appended and folded into open requests with bind and approval states (1.701935ms) +✔ approvals: a reply approves only when it points at a request, says exactly approve, and comes from a listed approver once (0.666129ms) +✔ approvals: a button approves only on its own request message with the matching custom id (0.466461ms) +✔ approvals flow: a turn that opened a request posts the message with the button, records it, binds it, and both approvers approve (16.530003ms) +✔ approvals flow: a non-approver, a repeat, a wrong custom id and a service refusal each get their fixed line and a drop entry (8.97202ms) +✔ approvals flow: an invalid request from the model, a refused post, and no api client are recorded and post nothing (9.876476ms) +✔ approvals flow: start retries a bind and an approval left as unknown, under their original keys (3.614364ms) +✔ authorize: open channel, listed user (1.918309ms) +✔ authorize: wrong guild (0.209469ms) +✔ authorize: no guild (DM) (0.183666ms) +✔ authorize: unlisted channel (0.244643ms) +✔ authorize: unknown channel, no info (0.196908ms) +✔ authorize: thread of listed parent (0.202889ms) +✔ authorize: thread of unlisted parent (0.191015ms) +✔ authorize: text channel that is not a thread and not listed (0.153491ms) +✔ authorize: unlisted user (0.204601ms) +✔ authorize: no author (0.916465ms) +✔ authorize: bot author (listed id, bot flag) (0.173926ms) +✔ authorize: system author (0.120911ms) +✔ authorize: the bot itself (0.10735ms) +✔ authorize: webhook (0.100761ms) +✔ authorize: mention channel without mention (0.140699ms) +✔ authorize: mention channel with bot mention (0.151843ms) +✔ authorize: mention channel with @everyone only (0.167888ms) +✔ authorize: mention channel mentioning someone else (0.089486ms) +✔ authorize: mention channel, content says @bot but mentions empty (0.089341ms) +✔ authorize: private thread under mention channel, mentioned (0.084556ms) +✔ authorize: private thread under mention channel, not mentioned (0.05975ms) +✔ authorize: thread in another guild per channel info (0.069707ms) +✔ authorize: not an object (0.061778ms) +✔ authorize: no id (0.07487ms) +✔ authorize: oversize content is accepted and flagged (0.075356ms) +✔ authorize: exactly the limit is not oversize (0.081014ms) +✔ authorize: a user's channel allowlist drops them outside it, threads count as the parent, others are unaffected (0.486988ms) +✔ authorize: order puts wrong guild before user, and user before channel (no channel lookup for strangers) (0.204973ms) +✔ binding: a complete binding validates and is frozen (2.928165ms) +✔ binding: unknown key, missing field, wrong type refuse with exit 2 (1.414856ms) +✔ binding: empty allowlists refuse (0.496834ms) +✔ binding: a user's channel allowlist must be non-empty, listed and unique; absent means every listed channel (1.373232ms) +✔ reloadDiff: reloadable keys are summarised by id; every fixed key refuses with exit 2 (2.044495ms) +✔ binding: file must be 0600, regular, not a symlink (1.679612ms) +✔ binding: token file mode, symlink, emptiness and shape are checked; token never appears in errors (2.276414ms) +✔ cli: check refuses a non-0600 token file with exit 2 before any network use (102.613686ms) +✔ context files: absolute paths, traversal, symlinks and out-of-repo targets refuse; in-repo files resolve (2.50081ms) +✔ cli: check refuses a missing context file and a missing binding with exit 2; usage is exit 4 (354.541628ms) +✔ cli: reload validates the file first (exit 2), then needs a live owner (exit 1); usage is exit 4 (214.315306ms) +✔ cli: run refuses when STOP is present, before any network use (121.851384ms) +✔ binding: tools is optional, validated strictly, a fixed key for reload, and its roots are resolved against the data root (1.735489ms) +✔ binding: a git key is validated at load and reaches the extension whole, and only on a writable root (1.184618ms) +✔ delivery: an accepted message is in the inbox before the turn, the reply is chunked with one nonce per chunk, and the turn record is write-once (18.267254ms) +✔ delivery: refused and unknown outcomes are journaled; a later chunk is not sent after a failure (16.980222ms) +✔ delivery: restart with an unknown entry re-sends the same nonce once and reconciles before accepting traffic (1.475552ms) +✔ delivery: an unknown entry older than the dedupe window is marked refused, not re-sent; a still-unknown one refuses start (1.498543ms) +✔ delivery: repeated unknown reconciliations never refresh the dedupe window; the original intent time decides (1.464203ms) +✔ turn: a failed engine turn posts the fixed line, never model output, and writes a failed record (3.505835ms) +✔ turn: a second message during a turn is held by the engine, both get their own reply and record (33.203656ms) +✔ turn: a thread under a listed channel is answered in the thread; an unknown thread is looked up once (3.334043ms) +✔ drop: an unlisted user gets silence and one drop line; no inbox entry, no REST call, no engine call (0.96087ms) +✔ drop: an oversize message is accepted into the inbox, answered with the fixed line and journaled as a drop (1.052453ms) +✔ restart: an inbox with three ids and a replay of the same three produces zero turns (41.357704ms) +✔ stop: STOP present refuses start; STOP written while running refuses new turns and the current one finishes (32.2268ms) +✔ ceiling: the ceiling plus one is refused and journaled; one fixed line per UTC day; a new day accepts again (5.648472ms) +✔ ceiling: a burst arriving while turns are still running cannot queue past the ceiling (4.041924ms) +✔ ceiling: a turn interrupted by a crash still counts after restart; admissions are durable (3.401109ms) +✔ ceiling: the daily notice survives a same-day restart; one delivery attempt in total, even when the first attempt crashed mid-flight (5.260933ms) +✔ duplicate: the same event delivered twice while the thread lookup is held yields one prompt, one admission and one reply (2.889649ms) +✔ journal: no token-shaped string and no model output on the drop path reaches disk (0.694598ms) +✔ receipt: an admitted message gets one eyes reaction on the inbound message; drops and refusals get none; a failed reaction is recorded and does not fail the turn (1.741159ms) +✔ receipt: Discord refusing the reaction leaves the turn intact and records ok false (2.260447ms) +✔ reload: a new user is silent before and answered after; a removed channel goes silent; a lower ceiling applies at once (4.2361ms) +✔ reload: a fixed key refuses with exit 2 and the old binding stays in force (2.330372ms) +✔ tools: with a tools binding the turn record lists every read and its outcome; without one the field is null (2.893545ms) +✔ context: the Discord block names the server, channels and modes, and states the rules from Q15 and Q16 (2.642891ms) +✔ context: with tools the block names the roots, keeps file content as data, and says to state refusals plainly (0.830227ms) +✔ context: a writable root adds the write rules and says a write is real only once Jason commits (1.927868ms) +✔ context: the envelope is one bracketed line then the text; names cannot break the line (1.4971ms) +✔ context: a git root swaps the terminal-commit line for the git verbs, and a vault root adds the id protocol (1.540519ms) +✔ context: assembleContext concatenates files in launcher format and appends the block; sha256 is stable (1.506593ms) +✔ context: splitReply keeps paragraphs together under the limit and splits long ones at lines, spaces, then hard (0.924662ms) +✔ engine: buildPiArgs carries the fixed flags, engine settings, session dir and prompt file (2.701325ms) +✔ engine: with tools, buildPiArgs turns pi's own tools off, loads the extension explicitly and allowlists exactly our three (0.471719ms) +✔ engine: a run with tool turns settles once, on the answer, with every tool call in the result (67.931376ms) +✔ engine: a run that ends on a tool-only turn fails the prompt as empty; a retried run settles on the real end (45.990579ms) +✔ engine: one prompt, one turn, text and usage come back (35.765858ms) +✔ engine: a prompt while streaming is held until pi settles, then sent as its own run, and answered in order (353.640991ms) +✔ engine: a held prompt that times out before pi settles fails on its own and is never sent (231.414967ms) +✔ engine: timeout sends abort and fails only that turn; the process stays (103.36315ms) +✔ engine: tool events from a run that outlived its timeout never land in the next prompt's record (233.842944ms) +✔ engine: a prompt after a turn that timed out before its agent_start waits for pi to settle instead of being refused (131.995471ms) +✔ 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.759436ms) +✔ engine: a timed-out turn pi starts only after the grace never answers a later prompt (616.153458ms) +✔ engine: late events of a run past its grace, before pi exits, answer nothing and nothing more is sent (early prompt response) (1.412333ms) +✔ engine: late events of a run past its grace, before pi exits, answer nothing and nothing more is sent (late prompt response) (0.447298ms) +✔ engine: a timed-out run pi did start outlives the grace; the next prompt goes out when it ends (427.505852ms) +✔ engine: a malformed JSONL line fails the turn, not the process (25.976253ms) +✔ engine: a turn that ends in error rejects with the error code; process exit fails pending turns (48.518719ms) +✔ gateway: hello -> identify with intents, ready, heartbeat with jitter, ack (2.727944ms) +✔ gateway: missed ack closes the socket and resumes with the last sequence (1.902931ms) +✔ gateway: op 7 reconnect resumes; op 9 non-resumable re-identifies (0.711452ms) +✔ gateway: op 9 resumable resumes (0.386186ms) +✔ gateway: close 4014 is fatal, reports the missing intent, never reconnects (0.977284ms) +✔ gateway: 4004 and 4013 are fatal too; 1006 reconnects with identify when no session (0.593665ms) +✔ gateway: close() is final and unparseable frames are ignored (0.626696ms) +✔ git: config validation is strict, needs write: true, a work tree and a private token file (75.657153ms) +✔ git: the child environment drops every host git config, names one helper, and carries the token path only for origin (45.88357ms) +✔ git: status reports the branch, ahead/behind and changed paths, and refuses off the named branch or mid-merge (87.721754ms) +✔ git: parseStatus reads porcelain v2 including renames and conflicts (0.302868ms) +✔ git: a commit stages exactly the named files, carries the seat author and the requester trailer, and pushes at once (88.384999ms) +✔ git: commit refusals: message, paths, requester, nothing to commit, and an index that already holds other work (90.045981ms) +✔ git: a commit whose push fails is still a commit, says so, and the next commit's push carries both (D6) (103.560019ms) +✔ git: pull is fast-forward only; a diverged origin or dirty local files refuse with nothing merged (206.388678ms) +✔ git: push pushes the named branch only and reports up to date (65.616181ms) +✔ git: no token value or token path ever reaches a git argument list; outputs are masked and capped (92.830705ms) +✔ git: the credential helper answers get over https from a private file and nothing else (213.591577ms) +✔ git: the vault protocol validates before a commit, honours another owner's lock, reserves ids, and locks around writes (837.248736ms) +✔ lock: the claim is exclusive; a second start against a live owner refuses (4.220185ms) +✔ lock: a stale lock (dead owner, reused pid, or record without start) refuses run and is never signaled; only unlock clears it (4.518775ms) +✔ lock: an incomplete claim (directory without owner record) is busy and refuses run; unlock clears it (1.422894ms) +✔ lock: an owner record that exists but cannot be read is invalid: never signaled, never removed, never claimed over (4.582692ms) +✔ lock: legacy upgrade; a live connector holding a {pid, start} record is unknown, unlock refuses and nothing changes; after it exits, unlock clears it (63.8715ms) +✔ 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 (344.239416ms) +✔ lock: identity syntax; only canonical unsigned decimal start ticks and lowercase boot uuids are identities (0.336098ms) +✔ lock: a process whose start marker or boot id cannot be read refuses to claim (0.362061ms) +✔ lock: a live pid whose identity cannot be read right now is unknown: never signaled, never removed, never claimed over (0.76214ms) +✔ lock: four processes racing for the same binding; exactly one claims it and the others refuse (39.224352ms) +✔ lock: stale handoff; concurrent starts over a stale lock all refuse, nothing reclaims, one unlock then exactly one live owner (129.362888ms) +✔ lock: four-party schedule; claims landing inside an unlock's gap never survive, one unlock leaves no owner and no residue (63.552354ms) +✔ notices: a kind is recorded per UTC day and found again (0.51231ms) +✔ recover: nothing to do is clean; a lock whose owner is gone or that has no record is cleared and STOP ends up absent (53.144558ms) +✔ recover: an operator STOP refuses with exit 3 and is never removed, whatever the lock says (47.151395ms) +✔ recover: a brake written during the unlock wins; STOP stays with both lines and the start is refused (32.2825ms) +✔ recover: a held binding refuses with exit 3 and writes no STOP: live owner, alive pid without verifiable identity, unreadable record (82.540159ms) +✔ cli: recover exits 0 when ready, 3 behind a brake or a held binding, and run's own STOP refusal is 3 (679.325926ms) +✔ rest: createMessage sends nonce, enforce_nonce, empty allowed_mentions and a soft reply reference (2.657912ms) +✔ rest: 429 waits retry_after and retries; 4xx is refused; 5xx and socket errors are unknown (6.281283ms) +✔ rest: content and nonce limits are enforced locally; typing never throws (3.152613ms) +✔ rest: react PUTs the encoded emoji on the inbound message for @me; 2xx is true, anything else is false and never throws (0.761836ms) +✔ setspark config: a bare https or loopback origin, a private key file, a principal (5.390986ms) +✔ setspark config: reaches the tools config and the binding as a fixed key (2.644296ms) +✔ setspark config: the binding's key survives resolveToolRoots and the engine's JSON hand-off to the extension (1.538722ms) +✔ setspark config: approvers come from the binding's users, never from the binding's setspark key (2.010351ms) +✔ setspark verbs: required_approvers go out as discord ids from names and come back as names (40.502527ms) +✔ setspark verbs: no Discord user id reaches tool text, whatever shape the service returns it in (9.130128ms) +✔ setspark contract: a decision made with names opens a request the connector accepts; names stored by an old record still refuse (5.5993ms) +✔ setspark keys: read per call, one printable token per file, rotation without a restart (3.048084ms) +✔ setspark idempotency keys: principal, turn id, call index; connector keys name a step (0.504142ms) +✔ setspark http core: json in and out, bearer header, idempotency header, fixed user agent, no key anywhere else (3.120153ms) +✔ setspark http core: error bodies become fixed refusals with code and the 409 fields; server text is data, cut (1009.602815ms) +✔ setspark verbs: a setspark key enables the eight verbs and no counters (0.504453ms) +✔ 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.184188ms) +✔ setspark verbs: no turn refuses every write before any request; bad arguments refuse before any request; reads still work (1.273923ms) +✔ setspark verbs: renderRecord caps long output and hides the accepted snapshot (0.202957ms) +✔ setspark api: bind, add_approval (button and reply) and get use integer request ids and the connector's keys (2.283008ms) +✔ tools: config refuses a missing, symlinked, dotted, non-directory or duplicate root and bad limits (4.339522ms) +✔ tools: every escape is refused with a fixed reason and nothing outside the root is read (4.333603ms) +✔ tools: happy paths list, read a window, and search case-insensitively; dotfiles and symlinks never appear (4.855722ms) +✔ tools: the tool set renders text for the model, records details for the journal, and enforces the per-run budget (3.752295ms) +✔ tools: listing and search caps hold (21.069267ms) +✔ tools: credential shapes are caught; ordinary prose and ids are not (0.870034ms) +✔ 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 (8.328982ms) +✔ tools: an unreadable file under the root is skipped by search and refused by read (1.063217ms) +✔ tools: config accepts write: true only as a boolean, and enables the write tools only then (1.163075ms) +✔ tools: every write outside the fence is refused before any byte lands, and no temp file remains (5.921007ms) +✔ tools: write_file leaves the exact bytes, edit_file replaces one exact match, and the set renders the change as uncommitted (2.552488ms) +✔ tools: a target that changed between the check and the rename is refused and the temp file is removed (1.46398ms) +✔ web: config takes an https or loopback-http SearXNG base url and a bounded fetch cap (3.339588ms) +✔ web: address rules refuse every private, loopback, link-local, mapped and multicast form (2.329664ms) +✔ web: web_fetch refuses bad urls, private hosts, rebinding names, non-https redirects, too many hops, error status, non-text bodies, and times out (1033.562992ms) +✔ 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.6957ms) +✔ web: html to text drops scripts, styles and comments, decodes entities and keeps block breaks (0.268031ms) +✔ 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.797272ms) +✔ 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.368338ms) +ℹ tests 173 +ℹ suites 0 +ℹ pass 173 +ℹ fail 0 +ℹ cancelled 0 +ℹ skipped 0 +ℹ todo 0 +ℹ duration_ms 2671.315407 diff --git a/agents/filbert/work/slice1-s4-review/r2-base-suite-auth.txt b/agents/filbert/work/slice1-s4-review/r2-base-suite-auth.txt new file mode 100644 index 00000000..ac228760 --- /dev/null +++ b/agents/filbert/work/slice1-s4-review/r2-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/slice1-s4-review/r2-base-suite-conductor.txt b/agents/filbert/work/slice1-s4-review/r2-base-suite-conductor.txt new file mode 100644 index 00000000..9c66e563 --- /dev/null +++ b/agents/filbert/work/slice1-s4-review/r2-base-suite-conductor.txt @@ -0,0 +1,55 @@ +Note: switching to 'b9b6cf008a8d7083676fc4a2760ae50fd43ad9b0'. + +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 'b9b6cf008a8d7083676fc4a2760ae50fd43ad9b0'. + +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/slice1-s4-review/r2-base-suite-config.txt b/agents/filbert/work/slice1-s4-review/r2-base-suite-config.txt new file mode 100644 index 00000000..76c05ef7 --- /dev/null +++ b/agents/filbert/work/slice1-s4-review/r2-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/slice1-s4-review/r2-base-suite-discord.txt b/agents/filbert/work/slice1-s4-review/r2-base-suite-discord.txt new file mode 100644 index 00000000..c667a891 --- /dev/null +++ b/agents/filbert/work/slice1-s4-review/r2-base-suite-discord.txt @@ -0,0 +1,68 @@ +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/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/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 173) +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: 64 passed, 0 failed diff --git a/agents/filbert/work/slice1-s4-review/r2-base-suite-extension-package.txt b/agents/filbert/work/slice1-s4-review/r2-base-suite-extension-package.txt new file mode 100644 index 00000000..4b35ef60 --- /dev/null +++ b/agents/filbert/work/slice1-s4-review/r2-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: 192975 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/slice1-s4-review/r2-base-suite-foundation.txt b/agents/filbert/work/slice1-s4-review/r2-base-suite-foundation.txt new file mode 100644 index 00000000..6338e1e6 --- /dev/null +++ b/agents/filbert/work/slice1-s4-review/r2-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/slice1-s4-review/r2-base-suite-queue.txt b/agents/filbert/work/slice1-s4-review/r2-base-suite-queue.txt new file mode 100644 index 00000000..f3afdf1d --- /dev/null +++ b/agents/filbert/work/slice1-s4-review/r2-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/r39b/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/slice1-s4-review/r2-base-suite-release.txt b/agents/filbert/work/slice1-s4-review/r2-base-suite-release.txt new file mode 100644 index 00000000..1d6ed050 --- /dev/null +++ b/agents/filbert/work/slice1-s4-review/r2-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/slice1-s4-review/r2-base-suite-task.txt b/agents/filbert/work/slice1-s4-review/r2-base-suite-task.txt new file mode 100644 index 00000000..85aa46f0 --- /dev/null +++ b/agents/filbert/work/slice1-s4-review/r2-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/slice1-s4-review/r2-cand-node-bus.txt b/agents/filbert/work/slice1-s4-review/r2-cand-node-bus.txt new file mode 100644 index 00000000..61b87264 --- /dev/null +++ b/agents/filbert/work/slice1-s4-review/r2-cand-node-bus.txt @@ -0,0 +1,66 @@ +✔ launch identity is stamped, payload identity is refused and stale holder cannot send (139.654375ms) +✔ decision classes route from policy; gated resolution is human-only, choice and target must match (247.063501ms) +✔ claim exclusion, holder release, gated revoke and rerouting to a new holder are atomic (228.702372ms) +✔ launch events require a human CLI capability; generic emit cannot forge authority events (152.589158ms) +✔ within-role decisions close atomically and invalid options or blocking omissions refuse (154.294352ms) +✔ observer capabilities read human inbox but cannot mutate or forge launch identity (128.295573ms) +✔ task action subjects and linked decision trail are complete and ordered (137.15036ms) +✔ launch binding is durable and reconnecting requires the identical trusted record (64.285079ms) +✔ business isolation includes inherited object names and cross-business message references (146.396237ms) +✔ authority never transfers between action, run, target, unresolved or replaced role holder (243.938983ms) +✔ task projection uses schema current view, skipping earlier and equal-start polls (94.170395ms) +✔ revocation permanently bars the old run from reclaiming first, including after broker restart (161.232705ms) +✔ empty message references refuse before storage; refusal-evidence failure stays a typed error (101.449267ms) +✔ both arbiters require human resolution when their cross-role route is themselves (201.868218ms) +✔ S1 adapter takes resolved limits and refs, rejects mismatched instance, never mutates input (1.792956ms) +✔ only validated broker references load; returned data and exceptions cannot expose a known token (3.145636ms) +✔ bad file modes, symlinks, repository/data paths, malformed tokens and missing dates refuse (1.852726ms) +✔ expiry refuses use and env references never become client data (1.151507ms) +✔ S1 parsed service refs work, service mismatch refuses, Gitea rotation due is a warning state (1.274286ms) +✔ opaque tokens shorter than 16 characters refuse before use (0.249982ms) +✔ human proof binds CLI entry, process start and nonce; agents and incomplete ancestry refuse (1.518729ms) +✔ process reader gets own kernel identity without exposing environment values (1.029208ms) +✔ EACCES ancestor environments skip only markers; commands and registered launches still refuse (0.603923ms) +✔ real pid 1 remains inspectable when its environment is protected (0.304814ms) +✔ within-role sends cite an open gated launch decision without spending it or naming it in grants (170.40665ms) +✔ missing and foreign-business citations refuse and roll back message and grant (167.569254ms) +✔ cross-role sends still need a matching resolved decision and consume it once (202.66731ms) +✔ broker process binds trusted launches, offers reader capabilities, refuses human mutation, closes cleanly (170.952763ms) +✔ startup token refusal returns safe code without value or partial listening broker (36.821787ms) +✔ loaded fixture token is absent from socket replies and SQLite, including refusal evidence (165.196727ms) +✔ killed broker leaves an explicit stale lock; another process cannot silently reclaim it (170.727787ms) +✔ trusted host registers later launches; socket clients never have a registration verb (164.592257ms) +✔ runtime excludes declared project roots even when host supplies no repoRoots (32.806422ms) +✔ a refused launch binding leaves the broker and existing capabilities alive; bad protocol stops it (153.089197ms) +✔ v3b prototype refusals, views and append-only mutations (947.370703ms) +✔ gated approval authorizes once, survives store reopen, and fresh approval works (241.796595ms) +✔ another run cannot consume an approval; a failed check leaves it usable (198.279698ms) +✔ two scheduled callers have exactly one grant and one consumed refusal (141.759733ms) +✔ failed commit rolls consumption back; cross-role consumes and within-role stays reusable (271.282029ms) +✔ class drift gated to cross-role refuses before consumption (168.101142ms) +✔ class drift cross-role to gated refuses before consumption (161.738213ms) +✔ class drift gated to within-role refuses before consumption (157.073495ms) +✔ class drift cross-role to within-role refuses before consumption (206.009333ms) +✔ class drift within-role to gated refuses before consumption (144.380123ms) +✔ class drift within-role to cross-role refuses before consumption (121.658912ms) +✔ message.send consumes approval and prevents a later send or authorize (166.58839ms) +✔ role.revoke consumes approval and prevents a later revoke or authorize (221.729859ms) +✔ creates private WAL store and excludes a second writer until explicit close (115.826599ms) +✔ rollback is atomic and schema metadata is checked against trusted DDL, not just itself (156.703344ms) +✔ existing empty database and symlink runtime directory refuse, never initialize over damage (170.059617ms) +✔ crash during a transaction recovers no partial event after explicit fixture-only lock removal (152.580868ms) +✔ writer refuses mixed at/read_at forms atomically, even through trusted SQL helpers (92.676541ms) +✔ async transactions refuse before invoking their function (77.571318ms) +✔ socket capability stamps launch identity; shared views use wire, no SQL client (136.661151ms) +✔ two wire claims serialize; a lost reply never automatically retries (177.631582ms) +✔ malformed, oversized and identity-forging envelopes refuse without echoing input (106.21344ms) +✔ client preserves UTF-8 when a response divides a multibyte character (12.320555ms) +✔ committed mutation followed by dropped reply reports unknown and is never retried (120.72807ms) +ℹ tests 58 +ℹ suites 0 +ℹ pass 58 +ℹ fail 0 +ℹ cancelled 0 +ℹ skipped 0 +ℹ todo 0 +ℹ duration_ms 2281.14019 diff --git a/agents/filbert/work/slice1-s4-review/r2-cand-node-business.txt b/agents/filbert/work/slice1-s4-review/r2-cand-node-business.txt new file mode 100644 index 00000000..170829dc --- /dev/null +++ b/agents/filbert/work/slice1-s4-review/r2-cand-node-business.txt @@ -0,0 +1,68 @@ +✔ config directory and file path follow MOSAIC_CONFIG (1.341014ms) +✔ the fixture business validates and comes back frozen (3.783753ms) +✔ two instances may share a definition (1.283933ms) +✔ top-level refusals (3.513758ms) +✔ arbiters and projects (6.980478ms) +✔ role instances (2.481886ms) +✔ Vikunja bots (5.977217ms) +✔ a role without Vikunja takes no tracker block (2.187817ms) +✔ credential references match the definition's services (2.491705ms) +✔ launch (7.223763ms) +✔ loadBusiness: file checks (1.762953ms) +✔ loadBusiness: not a regular file (41.927398ms) +✔ loading writes nothing (0.980735ms) +✔ names that are Object.prototype properties don't count as declared (2.715734ms) +✔ the shipped example refuses as written and validates once filled in (0.550139ms) +✔ usage errors exit 4 (284.740895ms) +✔ validate: a good business exits 0 and prints instance digests (63.480045ms) +✔ validate: project files (309.702262ms) +✔ validate: missing files and a broken system config (244.883943ms) +✔ validate: credential reference problems exit 2 and name each one (78.619778ms) +✔ validate: a token file inside the repository is refused (83.544263ms) +✔ validate: role definitions come from MOSAIC_ROLES_DIR (347.298703ms) +✔ resolve: prints one instance's record (220.944754ms) +✔ resolve: refusals (568.395168ms) +✔ parse: exactly one of file or env, plus the service's date (2.488417ms) +✔ check: a good file has no problems (0.727746ms) +✔ check never opens the file: a write-only token passes (0.321555ms) +✔ check: file problems (0.837081ms) +✔ check: token files can't live in the repository or dataRoot, even through a linked directory (0.729554ms) +✔ check: dates and environment references (0.373624ms) +✔ path and load (2.09583ms) +✔ refusals (1.025402ms) +✔ systemVars flattens the validated config (1.734096ms) +✔ precedence: system, business, project, project role, agent (4.324111ms) +✔ limits narrow the definition and never widen it (2.066702ms) +✔ role.launch stays within-role only for the instance the launch block names (4.116684ms) +✔ limits.authority without role.launch leaves the launcher with no launch block (1.570418ms) +✔ limits.authority narrows cross-role actions too (1.247099ms) +✔ classify (1.04595ms) +✔ the record carries what the broker and launcher need (0.868636ms) +✔ digest: key order doesn't matter, any value change does (5.570897ms) +✔ refusals (1.953933ms) +✔ the four shipped version 2 roles load (3.020071ms) +✔ shipped role scopes match addendum B section 2 and the SR runbook (0.911031ms) +✔ shipped authority follows the note's table (0.503269ms) +✔ version 1 files keep loading with no authority (0.976922ms) +✔ the conductor policy isn't a role (0.20077ms) +✔ a missing role file is exit 4, a symbolic link too (0.346796ms) +✔ version 2 refusals (1.510228ms) +✔ authority: closed vocabulary, no gated-only action, no overlap (1.956643ms) +✔ credentials: Gitea scopes (0.842503ms) +✔ credentials: Vikunja scopes are a group-to-verbs map from the grantable list (0.965281ms) +✔ credentials: services (0.507288ms) +✔ contract: a non-empty regular Markdown file beside the role file (0.60658ms) +✔ every key names known layers and a merge rule (1.074622ms) +✔ unknown keys and wrong layers refuse (0.713932ms) +✔ types (1.651585ms) +✔ merge: defaults, then the most specific layer wins (0.235419ms) +✔ merge: limits only narrow, and provenance lists each source (0.392174ms) +✔ merge doesn't change its inputs (0.172929ms) +ℹ tests 60 +ℹ suites 0 +ℹ pass 60 +ℹ fail 0 +ℹ cancelled 0 +ℹ skipped 0 +ℹ todo 0 +ℹ duration_ms 2270.726837 diff --git a/agents/filbert/work/slice1-s4-review/r2-cand-node-cli.txt b/agents/filbert/work/slice1-s4-review/r2-cand-node-cli.txt new file mode 100644 index 00000000..057c1a3d --- /dev/null +++ b/agents/filbert/work/slice1-s4-review/r2-cand-node-cli.txt @@ -0,0 +1,57 @@ +✔ inbox lists only decisions routed to the human, with what approving authorizes and how to decide (100.078936ms) +✔ decide resolves by id prefix with --yes, and the trail shows it in broker order with the task to follow (107.319875ms) +✔ decide refuses without a terminal or --yes, on an unknown option and on a short reference (99.730956ms) +✔ decide prints a declining choice as declining (90.510381ms) +✔ an unknown outcome is reported once and never resent (112.00473ms) +✔ a decision closed before the answer arrives exits 2 and points at its trail (108.740493ms) +✔ a prefix that matches two open decisions exits 2 and resolves neither (77.352582ms) +✔ without --business a command uses the live host's business, and a stale host.json is not a host (45.635253ms) +✔ every human command refuses inside an agent run before it touches the bus (45.464533ms) +✔ usage errors exit 4; no business and no host is a usage error (45.159304ms) +✔ agents and tasks print through the broker (53.456446ms) +✔ notify.json: missing, loose, malformed and extra keys refuse; a binding or null passes (1.27951ms) +✔ bootConfig builds the broker's boot message for one business, with no trackers key when no project names one (37.49377ms) +✔ trackers come from the tracker.* variables of the one project that names a tracker project (36.732668ms) +✔ with two projects, the one that sets tracker.project supplies the tracker and the other is no conflict (36.58984ms) +✔ two projects that each name a tracker project refuse, since the boot shape holds one (28.492158ms) +✔ a business without tracker.baseUrl gets no trackers entry (29.24871ms) +✔ an unknown business and a broken system config refuse with exit 3 (55.884577ms) +✔ empty views say so (1.044953ms) +✔ the trail keeps the broker's order and names a decision's task without its rows (1.380369ms) +✔ tasks print the tracker fields the snapshot carries (0.224637ms) +✔ the host boots the broker, binds a launch in process, and the notifier DMs a blocking decision exactly once (859.449566ms) +✔ a notifier that dies takes the host down with exit 1, so the unit restarts the pair (187.04901ms) +✔ a notifier that refuses stops the broker and the host refuses with exit 3 (173.229976ms) +✔ watchChildren reports a child that died before it was called, and one that dies later (22.622042ms) +✔ bus stop refuses to signal a live pid that is not a bus host (202.886448ms) +✔ bus start refuses with exit 3 and the code when the broker refuses to boot; bus status names the lock (210.010048ms) +✔ bus start refuses with exit 3 without a notifier config (84.912019ms) +✔ bus start runs until bus stop; status reports it while it runs (650.965231ms) +✔ bus-service.sh renders the unit and installs it into a given directory (27.10127ms) +✔ zoned uses the IANA zone across DST (13.969441ms) +✔ each open blocking decision is DM'd once, across polls and a restart; non-blocking ones are not (94.732038ms) +✔ two blocking decisions get two DMs with different nonces (109.219033ms) +✔ the digest nonce differs per business and per day and fits Discord's 25 characters (0.214105ms) +✔ a failed DM is journaled, backs off, and is retried until it lands (87.262675ms) +✔ the digest goes at 08:00 Chicago once a day, with blocking ones marked as DM'd (91.765134ms) +✔ a late start with no digest for the day sends one at once; an empty inbox gets one line (90.620084ms) +✔ an inbox read failure is logged and the next poll retries (0.714447ms) +✔ no Discord id reaches the journal or the log (97.129267ms) +✔ the journal: a torn tail is copied out and truncated, so an append after it reopens cleanly (28.777778ms) +✔ the journal: a crash between the copy and the truncate leaves a tail the next open repairs (29.548192ms) +✔ the journal: a whole file that is one torn line truncates to empty (14.661147ms) +✔ the journal: a malformed complete line refuses and leaves the file and any torn tail alone (1.862905ms) +✔ the journal: a loose file mode, a loose directory or a symlinked journal refuses (0.5764ms) +✔ digest content stays within Discord's 2000 characters (0.315122ms) +✔ runLoop never overlaps ticks and stops after the one in flight (111.13999ms) +✔ the transport writes {business, verb, args} to the child and reads its JSON (33.566934ms) +✔ a bus code on stderr becomes the exit code; garbage and timeouts are outcome problems (2140.027835ms) +✔ busExit and refuseInsideAgent (0.491405ms) +ℹ tests 49 +ℹ suites 0 +ℹ pass 49 +ℹ fail 0 +ℹ cancelled 0 +ℹ skipped 0 +ℹ todo 0 +ℹ duration_ms 2517.736153 diff --git a/agents/filbert/work/slice1-s4-review/r2-cand-node-discord.txt b/agents/filbert/work/slice1-s4-review/r2-cand-node-discord.txt new file mode 100644 index 00000000..18bf19b0 --- /dev/null +++ b/agents/filbert/work/slice1-s4-review/r2-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.542746ms) +✔ approvals: the ledger is appended and folded into open requests with bind and approval states (1.934868ms) +✔ approvals: a reply approves only when it points at a request, says exactly approve, and comes from a listed approver once (1.475863ms) +✔ approvals: a button approves only on its own request message with the matching custom id (0.538308ms) +✔ approvals flow: a turn that opened a request posts the message with the button, records it, binds it, and both approvers approve (25.448134ms) +✔ approvals flow: a non-approver, a repeat, a wrong custom id and a service refusal each get their fixed line and a drop entry (11.685966ms) +✔ approvals flow: an invalid request from the model, a refused post, and no api client are recorded and post nothing (10.841998ms) +✔ approvals flow: start retries a bind and an approval left as unknown, under their original keys (5.170841ms) +✔ authorize: open channel, listed user (22.287105ms) +✔ authorize: wrong guild (0.267614ms) +✔ authorize: no guild (DM) (0.191727ms) +✔ authorize: unlisted channel (0.222855ms) +✔ authorize: unknown channel, no info (0.316141ms) +✔ authorize: thread of listed parent (0.250805ms) +✔ authorize: thread of unlisted parent (0.208692ms) +✔ authorize: text channel that is not a thread and not listed (0.149837ms) +✔ authorize: unlisted user (1.531999ms) +✔ authorize: no author (0.293545ms) +✔ authorize: bot author (listed id, bot flag) (0.132914ms) +✔ authorize: system author (2.617179ms) +✔ authorize: the bot itself (0.129547ms) +✔ authorize: webhook (0.690146ms) +✔ authorize: mention channel without mention (0.243579ms) +✔ authorize: mention channel with bot mention (0.238357ms) +✔ authorize: mention channel with @everyone only (0.236778ms) +✔ authorize: mention channel mentioning someone else (0.081781ms) +✔ authorize: mention channel, content says @bot but mentions empty (0.081841ms) +✔ authorize: private thread under mention channel, mentioned (0.076594ms) +✔ authorize: private thread under mention channel, not mentioned (0.081193ms) +✔ authorize: thread in another guild per channel info (0.089329ms) +✔ authorize: not an object (0.052593ms) +✔ authorize: no id (0.052998ms) +✔ authorize: oversize content is accepted and flagged (0.05717ms) +✔ authorize: exactly the limit is not oversize (0.049367ms) +✔ authorize: a user's channel allowlist drops them outside it, threads count as the parent, others are unaffected (0.411752ms) +✔ authorize: order puts wrong guild before user, and user before channel (no channel lookup for strangers) (0.121684ms) +✔ binding: a complete binding validates and is frozen (5.790531ms) +✔ binding: unknown key, missing field, wrong type refuse with exit 2 (1.767833ms) +✔ binding: empty allowlists refuse (0.530678ms) +✔ binding: a user's channel allowlist must be non-empty, listed and unique; absent means every listed channel (1.434008ms) +✔ reloadDiff: reloadable keys are summarised by id; every fixed key refuses with exit 2 (2.160588ms) +✔ binding: dmRecipient is optional, a snowflake, one of the listed users, and a fixed key (1.286517ms) +✔ binding: file must be 0600, regular, not a symlink (1.69439ms) +✔ binding: token file mode, symlink, emptiness and shape are checked; token never appears in errors (1.417494ms) +✔ cli: check refuses a non-0600 token file with exit 2 before any network use (104.652492ms) +✔ context files: absolute paths, traversal, symlinks and out-of-repo targets refuse; in-repo files resolve (2.813887ms) +✔ cli: check refuses a missing context file and a missing binding with exit 2; usage is exit 4 (366.266935ms) +✔ cli: reload validates the file first (exit 2), then needs a live owner (exit 1); usage is exit 4 (207.600874ms) +✔ cli: run refuses when STOP is present, before any network use (120.080515ms) +✔ binding: tools is optional, validated strictly, a fixed key for reload, and its roots are resolved against the data root (1.565436ms) +✔ binding: a git key is validated at load and reaches the extension whole, and only on a writable root (1.016626ms) +✔ 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 (24.754313ms) +✔ delivery: refused and unknown outcomes are journaled; a later chunk is not sent after a failure (23.517154ms) +✔ delivery: restart with an unknown entry re-sends the same nonce once and reconciles before accepting traffic (4.521681ms) +✔ delivery: an unknown entry older than the dedupe window is marked refused, not re-sent; a still-unknown one refuses start (3.510393ms) +✔ delivery: repeated unknown reconciliations never refresh the dedupe window; the original intent time decides (1.836254ms) +✔ turn: a failed engine turn posts the fixed line, never model output, and writes a failed record (4.523989ms) +✔ turn: a second message during a turn is held by the engine, both get their own reply and record (33.772732ms) +✔ turn: a thread under a listed channel is answered in the thread; an unknown thread is looked up once (6.500024ms) +✔ drop: an unlisted user gets silence and one drop line; no inbox entry, no REST call, no engine call (1.770107ms) +✔ drop: an oversize message is accepted into the inbox, answered with the fixed line and journaled as a drop (2.071734ms) +✔ restart: an inbox with three ids and a replay of the same three produces zero turns (42.380346ms) +✔ stop: STOP present refuses start; STOP written while running refuses new turns and the current one finishes (32.375624ms) +✔ ceiling: the ceiling plus one is refused and journaled; one fixed line per UTC day; a new day accepts again (8.859983ms) +✔ ceiling: a burst arriving while turns are still running cannot queue past the ceiling (10.699491ms) +✔ ceiling: a turn interrupted by a crash still counts after restart; admissions are durable (2.121998ms) +✔ ceiling: the daily notice survives a same-day restart; one delivery attempt in total, even when the first attempt crashed mid-flight (5.571623ms) +✔ duplicate: the same event delivered twice while the thread lookup is held yields one prompt, one admission and one reply (2.913785ms) +✔ journal: no token-shaped string and no model output on the drop path reaches disk (0.765934ms) +✔ receipt: an admitted message gets one eyes reaction on the inbound message; drops and refusals get none; a failed reaction is recorded and does not fail the turn (1.938226ms) +✔ receipt: Discord refusing the reaction leaves the turn intact and records ok false (1.278103ms) +✔ reload: a new user is silent before and answered after; a removed channel goes silent; a lower ceiling applies at once (9.799634ms) +✔ reload: a fixed key refuses with exit 2 and the old binding stays in force (2.248465ms) +✔ tools: with a tools binding the turn record lists every read and its outcome; without one the field is null (3.792951ms) +✔ context: the Discord block names the server, channels and modes, and states the rules from Q15 and Q16 (2.49622ms) +✔ context: with tools the block names the roots, keeps file content as data, and says to state refusals plainly (0.897878ms) +✔ context: a writable root adds the write rules and says a write is real only once Jason commits (2.224316ms) +✔ context: the envelope is one bracketed line then the text; names cannot break the line (1.331116ms) +✔ context: a git root swaps the terminal-commit line for the git verbs, and a vault root adds the id protocol (1.57512ms) +✔ context: assembleContext concatenates files in launcher format and appends the block; sha256 is stable (1.340266ms) +✔ context: splitReply keeps paragraphs together under the limit and splits long ones at lines, spaces, then hard (0.711663ms) +✔ engine: buildPiArgs carries the fixed flags, engine settings, session dir and prompt file (2.660918ms) +✔ engine: with tools, buildPiArgs turns pi's own tools off, loads the extension explicitly and allowlists exactly our three (0.597104ms) +✔ engine: a run with tool turns settles once, on the answer, with every tool call in the result (66.981725ms) +✔ engine: a run that ends on a tool-only turn fails the prompt as empty; a retried run settles on the real end (46.315019ms) +✔ engine: one prompt, one turn, text and usage come back (38.85986ms) +✔ engine: a prompt while streaming is held until pi settles, then sent as its own run, and answered in order (362.520383ms) +✔ engine: a held prompt that times out before pi settles fails on its own and is never sent (232.404059ms) +✔ engine: timeout sends abort and fails only that turn; the process stays (105.101303ms) +✔ engine: tool events from a run that outlived its timeout never land in the next prompt's record (227.882056ms) +✔ engine: a prompt after a turn that timed out before its agent_start waits for pi to settle instead of being refused (124.917471ms) +✔ 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.301179ms) +✔ engine: a timed-out turn pi starts only after the grace never answers a later prompt (614.943458ms) +✔ engine: late events of a run past its grace, before pi exits, answer nothing and nothing more is sent (early prompt response) (1.407506ms) +✔ engine: late events of a run past its grace, before pi exits, answer nothing and nothing more is sent (late prompt response) (0.63402ms) +✔ engine: a timed-out run pi did start outlives the grace; the next prompt goes out when it ends (435.256752ms) +✔ engine: a malformed JSONL line fails the turn, not the process (28.489293ms) +✔ engine: a turn that ends in error rejects with the error code; process exit fails pending turns (46.291897ms) +✔ gateway: hello -> identify with intents, ready, heartbeat with jitter, ack (2.731491ms) +✔ gateway: missed ack closes the socket and resumes with the last sequence (1.78848ms) +✔ gateway: op 7 reconnect resumes; op 9 non-resumable re-identifies (0.694244ms) +✔ gateway: op 9 resumable resumes (0.387033ms) +✔ gateway: close 4014 is fatal, reports the missing intent, never reconnects (1.046497ms) +✔ gateway: 4004 and 4013 are fatal too; 1006 reconnects with identify when no session (0.620206ms) +✔ gateway: close() is final and unparseable frames are ignored (0.539526ms) +✔ git: config validation is strict, needs write: true, a work tree and a private token file (106.5672ms) +✔ git: the child environment drops every host git config, names one helper, and carries the token path only for origin (66.381881ms) +✔ git: status reports the branch, ahead/behind and changed paths, and refuses off the named branch or mid-merge (89.732409ms) +✔ git: parseStatus reads porcelain v2 including renames and conflicts (0.306919ms) +✔ git: a commit stages exactly the named files, carries the seat author and the requester trailer, and pushes at once (81.547628ms) +✔ git: commit refusals: message, paths, requester, nothing to commit, and an index that already holds other work (93.304949ms) +✔ git: a commit whose push fails is still a commit, says so, and the next commit's push carries both (D6) (114.704235ms) +✔ git: pull is fast-forward only; a diverged origin or dirty local files refuse with nothing merged (201.165005ms) +✔ git: push pushes the named branch only and reports up to date (60.465398ms) +✔ git: no token value or token path ever reaches a git argument list; outputs are masked and capped (83.600115ms) +✔ git: the credential helper answers get over https from a private file and nothing else (218.04961ms) +✔ git: the vault protocol validates before a commit, honours another owner's lock, reserves ids, and locks around writes (888.492779ms) +✔ lock: the claim is exclusive; a second start against a live owner refuses (5.649702ms) +✔ lock: a stale lock (dead owner, reused pid, or record without start) refuses run and is never signaled; only unlock clears it (5.603631ms) +✔ lock: an incomplete claim (directory without owner record) is busy and refuses run; unlock clears it (1.088287ms) +✔ lock: an owner record that exists but cannot be read is invalid: never signaled, never removed, never claimed over (2.778693ms) +✔ lock: legacy upgrade; a live connector holding a {pid, start} record is unknown, unlock refuses and nothing changes; after it exits, unlock clears it (60.984081ms) +✔ 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 (342.062302ms) +✔ lock: identity syntax; only canonical unsigned decimal start ticks and lowercase boot uuids are identities (0.42599ms) +✔ lock: a process whose start marker or boot id cannot be read refuses to claim (0.443703ms) +✔ lock: a live pid whose identity cannot be read right now is unknown: never signaled, never removed, never claimed over (0.79832ms) +✔ lock: four processes racing for the same binding; exactly one claims it and the others refuse (49.089317ms) +✔ lock: stale handoff; concurrent starts over a stale lock all refuse, nothing reclaims, one unlock then exactly one live owner (131.551284ms) +✔ lock: four-party schedule; claims landing inside an unlock's gap never survive, one unlock leaves no owner and no residue (69.87592ms) +✔ notices: a kind is recorded per UTC day and found again (0.708582ms) +✔ notify: the DM channel opens once, every send carries the nonce, and only the message id comes back (5.667109ms) +✔ notify: refusals and unknowns surface as RestOutcome without ids; a refused send reopens the channel next time (2.177148ms) +✔ notify: no dmRecipient, a non-0600 binding or token, or a missing binding refuse before any network use (1.87754ms) +✔ recover: nothing to do is clean; a lock whose owner is gone or that has no record is cleared and STOP ends up absent (50.94603ms) +✔ recover: an operator STOP refuses with exit 3 and is never removed, whatever the lock says (43.95032ms) +✔ recover: a brake written during the unlock wins; STOP stays with both lines and the start is refused (50.103263ms) +✔ recover: a held binding refuses with exit 3 and writes no STOP: live owner, alive pid without verifiable identity, unreadable record (72.273143ms) +✔ cli: recover exits 0 when ready, 3 behind a brake or a held binding, and run's own STOP refusal is 3 (677.205774ms) +✔ rest: createMessage sends nonce, enforce_nonce, empty allowed_mentions and a soft reply reference (2.518193ms) +✔ rest: 429 waits retry_after and retries; 4xx is refused; 5xx and socket errors are unknown (3.114429ms) +✔ rest: content and nonce limits are enforced locally; typing never throws (0.643462ms) +✔ rest: react PUTs the encoded emoji on the inbound message for @me; 2xx is true, anything else is false and never throws (1.020396ms) +✔ rest: createDm posts the recipient and resolves the channel id; 4xx refused, 5xx unknown, bad id never sent (1.044026ms) +✔ setspark config: a bare https or loopback origin, a private key file, a principal (6.027816ms) +✔ setspark config: reaches the tools config and the binding as a fixed key (3.325777ms) +✔ setspark config: the binding's key survives resolveToolRoots and the engine's JSON hand-off to the extension (1.78746ms) +✔ setspark config: approvers come from the binding's users, never from the binding's setspark key (3.620258ms) +✔ setspark verbs: required_approvers go out as discord ids from names and come back as names (29.582174ms) +✔ setspark verbs: no Discord user id reaches tool text, whatever shape the service returns it in (13.508143ms) +✔ setspark contract: a decision made with names opens a request the connector accepts; names stored by an old record still refuse (7.497698ms) +✔ setspark keys: read per call, one printable token per file, rotation without a restart (3.755965ms) +✔ setspark idempotency keys: principal, turn id, call index; connector keys name a step (1.068823ms) +✔ setspark http core: json in and out, bearer header, idempotency header, fixed user agent, no key anywhere else (3.314254ms) +✔ setspark http core: error bodies become fixed refusals with code and the 409 fields; server text is data, cut (1012.527762ms) +✔ setspark verbs: a setspark key enables the eight verbs and no counters (0.434897ms) +✔ setspark verbs: writes carry the turn's key and the asserted requester, reads carry no key, and the api key never appears in text or details (5.922125ms) +✔ setspark verbs: no turn refuses every write before any request; bad arguments refuse before any request; reads still work (1.195623ms) +✔ setspark verbs: renderRecord caps long output and hides the accepted snapshot (0.197563ms) +✔ setspark api: bind, add_approval (button and reply) and get use integer request ids and the connector's keys (2.298838ms) +✔ tools: config refuses a missing, symlinked, dotted, non-directory or duplicate root and bad limits (4.450242ms) +✔ tools: every escape is refused with a fixed reason and nothing outside the root is read (4.223885ms) +✔ tools: happy paths list, read a window, and search case-insensitively; dotfiles and symlinks never appear (5.4028ms) +✔ tools: the tool set renders text for the model, records details for the journal, and enforces the per-run budget (4.476365ms) +✔ tools: listing and search caps hold (17.774841ms) +✔ tools: credential shapes are caught; ordinary prose and ids are not (1.049228ms) +✔ 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 (9.337057ms) +✔ tools: an unreadable file under the root is skipped by search and refused by read (1.339143ms) +✔ tools: config accepts write: true only as a boolean, and enables the write tools only then (1.20303ms) +✔ tools: every write outside the fence is refused before any byte lands, and no temp file remains (5.917216ms) +✔ tools: write_file leaves the exact bytes, edit_file replaces one exact match, and the set renders the change as uncommitted (3.149135ms) +✔ tools: a target that changed between the check and the rename is refused and the temp file is removed (1.826738ms) +✔ web: config takes an https or loopback-http SearXNG base url and a bounded fetch cap (3.567972ms) +✔ web: address rules refuse every private, loopback, link-local, mapped and multicast form (2.262031ms) +✔ web: web_fetch refuses bad urls, private hosts, rebinding names, non-https redirects, too many hops, error status, non-text bodies, and times out (1029.97115ms) +✔ web: web_fetch returns html as text with the title, follows an https redirect, keeps plain text and json, and cuts at the cap (6.108577ms) +✔ web: html to text drops scripts, styles and comments, decodes entities and keeps block breaks (0.337669ms) +✔ 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.857954ms) +✔ 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.362929ms) +ℹ tests 178 +ℹ suites 0 +ℹ pass 178 +ℹ fail 0 +ℹ cancelled 0 +ℹ skipped 0 +ℹ todo 0 +ℹ duration_ms 2744.162363 diff --git a/agents/filbert/work/slice1-s4-review/r2-cand-suite-auth.txt b/agents/filbert/work/slice1-s4-review/r2-cand-suite-auth.txt new file mode 100644 index 00000000..ac228760 --- /dev/null +++ b/agents/filbert/work/slice1-s4-review/r2-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/slice1-s4-review/r2-cand-suite-conductor.txt b/agents/filbert/work/slice1-s4-review/r2-cand-suite-conductor.txt new file mode 100644 index 00000000..9c66e563 --- /dev/null +++ b/agents/filbert/work/slice1-s4-review/r2-cand-suite-conductor.txt @@ -0,0 +1,55 @@ +Note: switching to 'b9b6cf008a8d7083676fc4a2760ae50fd43ad9b0'. + +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 'b9b6cf008a8d7083676fc4a2760ae50fd43ad9b0'. + +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/slice1-s4-review/r2-cand-suite-config.txt b/agents/filbert/work/slice1-s4-review/r2-cand-suite-config.txt new file mode 100644 index 00000000..76c05ef7 --- /dev/null +++ b/agents/filbert/work/slice1-s4-review/r2-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/slice1-s4-review/r2-cand-suite-discord.txt b/agents/filbert/work/slice1-s4-review/r2-cand-suite-discord.txt new file mode 100644 index 00000000..b013f9ff --- /dev/null +++ b/agents/filbert/work/slice1-s4-review/r2-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/slice1-s4-review/r2-cand-suite-extension-package.txt b/agents/filbert/work/slice1-s4-review/r2-cand-suite-extension-package.txt new file mode 100644 index 00000000..805974db --- /dev/null +++ b/agents/filbert/work/slice1-s4-review/r2-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: 141359 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/slice1-s4-review/r2-cand-suite-foundation.txt b/agents/filbert/work/slice1-s4-review/r2-cand-suite-foundation.txt new file mode 100644 index 00000000..6338e1e6 --- /dev/null +++ b/agents/filbert/work/slice1-s4-review/r2-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/slice1-s4-review/r2-cand-suite-queue.txt b/agents/filbert/work/slice1-s4-review/r2-cand-suite-queue.txt new file mode 100644 index 00000000..4ea8963a --- /dev/null +++ b/agents/filbert/work/slice1-s4-review/r2-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/r39b/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/slice1-s4-review/r2-cand-suite-release.txt b/agents/filbert/work/slice1-s4-review/r2-cand-suite-release.txt new file mode 100644 index 00000000..1d6ed050 --- /dev/null +++ b/agents/filbert/work/slice1-s4-review/r2-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/slice1-s4-review/r2-cand-suite-task.txt b/agents/filbert/work/slice1-s4-review/r2-cand-suite-task.txt new file mode 100644 index 00000000..85aa46f0 --- /dev/null +++ b/agents/filbert/work/slice1-s4-review/r2-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/slice1-s4-review/r2-gate-summary.txt b/agents/filbert/work/slice1-s4-review/r2-gate-summary.txt new file mode 100644 index 00000000..2b640566 --- /dev/null +++ b/agents/filbert/work/slice1-s4-review/r2-gate-summary.txt @@ -0,0 +1,25 @@ +cand node-cli exit 0 +cand node-bus exit 0 +cand node-business exit 0 +cand node-discord exit 0 +cand suite-auth exit 0 load 3.88 +cand suite-conductor exit 0 load 4.13 +cand suite-config exit 0 load 4.13 +cand suite-discord exit 0 load 4.12 +cand suite-extension-package exit 0 load 3.87 +cand suite-foundation exit 0 load 3.46 +cand suite-queue exit 0 load 3.97 +cand suite-release exit 0 load 3.97 +cand suite-task exit 1 load 3.97 +base node-bus exit 0 +base node-business exit 0 +base node-discord exit 0 +base suite-auth exit 0 load 4.29 +base suite-conductor exit 0 load 5.31 +base suite-config exit 0 load 5.31 +base suite-discord exit 0 load 5.47 +base suite-extension-package exit 0 load 5.47 +base suite-foundation exit 0 load 4.24 +base suite-queue exit 0 load 4.43 +base suite-release exit 0 load 4.43 +base suite-task exit 1 load 4.72 diff --git a/agents/filbert/work/slice1-s4-review/r2-m13-host.txt b/agents/filbert/work/slice1-s4-review/r2-m13-host.txt new file mode 100644 index 00000000..c654b609 --- /dev/null +++ b/agents/filbert/work/slice1-s4-review/r2-m13-host.txt @@ -0,0 +1,34 @@ +✔ the host boots the broker, binds a launch in process, and the notifier DMs a blocking decision exactly once (851.881094ms) +✔ a notifier that dies takes the host down with exit 1, so the unit restarts the pair (171.408869ms) +mosaic-notify: notify: poll failed: ENOENT: no such file or directory, open '/tmp/mosaic-cli-RnYDTZ/data/notify/acme/sent.jsonl' +✖ a notifier that refuses stops the broker and the host refuses with exit 3 (163.293325ms) +✔ watchChildren reports a child that died before it was called, and one that dies later (22.529171ms) +✔ bus stop refuses to signal a live pid that is not a bus host (202.458765ms) +✔ bus start refuses with exit 3 and the code when the broker refuses to boot; bus status names the lock (219.699321ms) +✔ bus start refuses with exit 3 without a notifier config (89.542554ms) +✔ bus start runs until bus stop; status reports it while it runs (685.795533ms) +✔ bus-service.sh renders the unit and installs it into a given directory (40.826807ms) +ℹ tests 9 +ℹ suites 0 +ℹ pass 8 +ℹ fail 1 +ℹ cancelled 0 +ℹ skipped 0 +ℹ todo 0 +ℹ duration_ms 2532.392133 + +✖ failing tests: + +test at packages/cli/tests/host.test.mjs:117:1 +✖ a notifier that refuses stops the broker and the host refuses with exit 3 (163.293325ms) + AssertionError [ERR_ASSERTION]: Missing expected rejection. + at process.processTicksAndRejections (node:internal/process/task_queues:104:5) + at async TestContext. (file:///home/jwoltje/filbert-scratch/r39b/mut/packages/cli/tests/host.test.mjs:125:3) + 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: undefined, + operator: 'rejects', + diff: 'simple' + } diff --git a/agents/filbert/work/slice1-s4-review/r2-m19-timeout.txt b/agents/filbert/work/slice1-s4-review/r2-m19-timeout.txt new file mode 100644 index 00000000..da65b366 --- /dev/null +++ b/agents/filbert/work/slice1-s4-review/r2-m19-timeout.txt @@ -0,0 +1,26 @@ +✔ inbox lists only decisions routed to the human, with what approving authorizes and how to decide (87.216667ms) +✔ decide resolves by id prefix with --yes, and the trail shows it in broker order with the task to follow (72.518856ms) +✖ decide refuses without a terminal or --yes, on an unknown option and on a short reference (60001.70155ms) +✔ decide prints a declining choice as declining (71.997826ms) +✔ an unknown outcome is reported once and never resent (54.943141ms) +✔ a decision closed before the answer arrives exits 2 and points at its trail (50.941742ms) +✔ a prefix that matches two open decisions exits 2 and resolves neither (51.85766ms) +✔ without --business a command uses the live host's business, and a stale host.json is not a host (45.937668ms) +✔ every human command refuses inside an agent run before it touches the bus (48.960227ms) +✔ usage errors exit 4; no business and no host is a usage error (51.712577ms) +✔ agents and tasks print through the broker (49.316379ms) +✔ notify.json: missing, loose, malformed and extra keys refuse; a binding or null passes (1.177967ms) +ℹ tests 12 +ℹ suites 0 +ℹ pass 11 +ℹ fail 0 +ℹ cancelled 1 +ℹ skipped 0 +ℹ todo 0 +ℹ duration_ms 60677.507287 + +✖ failing tests: + +test at packages/cli/tests/cli.test.mjs:66:1 +✖ decide refuses without a terminal or --yes, on an unknown option and on a short reference (60001.70155ms) + 'test timed out after 60000ms' diff --git a/agents/filbert/work/slice1-s4-review/r2-mut-M28-rerun1.txt b/agents/filbert/work/slice1-s4-review/r2-mut-M28-rerun1.txt new file mode 100644 index 00000000..9e654a83 --- /dev/null +++ b/agents/filbert/work/slice1-s4-review/r2-mut-M28-rerun1.txt @@ -0,0 +1,235 @@ +✔ inbox lists only decisions routed to the human, with what approving authorizes and how to decide (128.195384ms) +✔ decide resolves by id prefix with --yes, and the trail shows it in broker order with the task to follow (173.03349ms) +✔ decide refuses without a terminal or --yes, on an unknown option and on a short reference (101.725873ms) +✔ decide prints a declining choice as declining (211.031203ms) +✔ an unknown outcome is reported once and never resent (168.845654ms) +✔ a decision closed before the answer arrives exits 2 and points at its trail (197.827882ms) +✔ a prefix that matches two open decisions exits 2 and resolves neither (142.434566ms) +✔ without --business a command uses the live host's business, and a stale host.json is not a host (105.022882ms) +✔ every human command refuses inside an agent run before it touches the bus (114.596595ms) +✔ usage errors exit 4; no business and no host is a usage error (66.603429ms) +✔ agents and tasks print through the broker (68.059725ms) +✔ notify.json: missing, loose, malformed and extra keys refuse; a binding or null passes (1.285612ms) +✔ bootConfig builds the broker's boot message for one business, with no trackers key when no project names one (92.375276ms) +✔ trackers come from the tracker.* variables of the one project that names a tracker project (61.819103ms) +✔ with two projects, the one that sets tracker.project supplies the tracker and the other is no conflict (60.559355ms) +✔ two projects that each name a tracker project refuse, since the boot shape holds one (49.117414ms) +✔ a business without tracker.baseUrl gets no trackers entry (53.852451ms) +✔ an unknown business and a broken system config refuse with exit 3 (87.694477ms) +✔ empty views say so (1.285418ms) +✔ the trail keeps the broker's order and names a decision's task without its rows (1.478037ms) +✔ tasks print the tracker fields the snapshot carries (0.28882ms) +✔ the host boots the broker, binds a launch in process, and the notifier DMs a blocking decision exactly once (1033.632014ms) +✔ a notifier that dies takes the host down with exit 1, so the unit restarts the pair (262.045607ms) +✔ a notifier that refuses stops the broker and the host refuses with exit 3 (203.472241ms) +✔ watchChildren reports a child that died before it was called, and one that dies later (22.806465ms) +✔ bus stop refuses to signal a live pid that is not a bus host (203.474754ms) +✔ bus start refuses with exit 3 and the code when the broker refuses to boot; bus status names the lock (245.182997ms) +✔ bus start refuses with exit 3 without a notifier config (101.641085ms) +✔ bus start runs until bus stop; status reports it while it runs (687.392239ms) +✔ bus-service.sh renders the unit and installs it into a given directory (27.544558ms) +✔ zoned uses the IANA zone across DST (26.513988ms) +✔ each open blocking decision is DM'd once, across polls and a restart; non-blocking ones are not (103.232685ms) +✔ two blocking decisions get two DMs with different nonces (166.594618ms) +✔ the digest nonce differs per business and per day and fits Discord's 25 characters (0.542796ms) +✔ a failed DM is journaled, backs off, and is retried until it lands (101.736767ms) +✔ the digest goes at 08:00 Chicago once a day, with blocking ones marked as DM'd (176.495893ms) +✔ a late start with no digest for the day sends one at once; an empty inbox gets one line (175.331443ms) +✔ an inbox read failure is logged and the next poll retries (1.349032ms) +✔ no Discord id reaches the journal or the log (176.712746ms) +✔ the journal: a torn tail is copied out and truncated, so an append after it reopens cleanly (56.520073ms) +✔ the journal: a crash between the copy and the truncate leaves a tail the next open repairs (49.840375ms) +✔ the journal: a whole file that is one torn line truncates to empty (22.119255ms) +✔ the journal: a malformed complete line refuses and leaves the file and any torn tail alone (2.003867ms) +✔ the journal: a loose file mode, a loose directory or a symlinked journal refuses (0.994243ms) +✔ digest content stays within Discord's 2000 characters (0.486197ms) +✔ runLoop never overlaps ticks and stops after the one in flight (110.970108ms) +✔ the transport writes {business, verb, args} to the child and reads its JSON (54.522158ms) +✔ a bus code on stderr becomes the exit code; garbage and timeouts are outcome problems (2267.743264ms) +✔ busExit and refuseInsideAgent (0.466679ms) +✔ approvals: a request is validated before anything is posted; the rendering shows names and never ids (3.446995ms) +✔ approvals: the ledger is appended and folded into open requests with bind and approval states (1.777098ms) +✔ approvals: a reply approves only when it points at a request, says exactly approve, and comes from a listed approver once (0.701757ms) +✔ approvals: a button approves only on its own request message with the matching custom id (0.514843ms) +✔ approvals flow: a turn that opened a request posts the message with the button, records it, binds it, and both approvers approve (19.376227ms) +✔ approvals flow: a non-approver, a repeat, a wrong custom id and a service refusal each get their fixed line and a drop entry (6.783828ms) +✔ approvals flow: an invalid request from the model, a refused post, and no api client are recorded and post nothing (11.817459ms) +✔ approvals flow: start retries a bind and an approval left as unknown, under their original keys (2.623937ms) +✔ authorize: open channel, listed user (1.991359ms) +✔ authorize: wrong guild (0.234897ms) +✔ authorize: no guild (DM) (0.176042ms) +✔ authorize: unlisted channel (0.199873ms) +✔ authorize: unknown channel, no info (0.184158ms) +✔ authorize: thread of listed parent (0.238941ms) +✔ authorize: thread of unlisted parent (0.155529ms) +✔ authorize: text channel that is not a thread and not listed (0.183767ms) +✔ authorize: unlisted user (0.190022ms) +✔ authorize: no author (0.365207ms) +✔ authorize: bot author (listed id, bot flag) (0.173026ms) +✔ authorize: system author (0.118875ms) +✔ authorize: the bot itself (0.099871ms) +✔ authorize: webhook (0.095675ms) +✔ authorize: mention channel without mention (0.149411ms) +✔ authorize: mention channel with bot mention (0.17239ms) +✔ authorize: mention channel with @everyone only (0.113569ms) +✔ authorize: mention channel mentioning someone else (0.096703ms) +✔ authorize: mention channel, content says @bot but mentions empty (0.094882ms) +✔ authorize: private thread under mention channel, mentioned (0.098952ms) +✔ authorize: private thread under mention channel, not mentioned (0.105221ms) +✔ authorize: thread in another guild per channel info (0.086125ms) +✔ authorize: not an object (0.080474ms) +✔ authorize: no id (0.08ms) +✔ authorize: oversize content is accepted and flagged (0.083916ms) +✔ authorize: exactly the limit is not oversize (0.072872ms) +✔ authorize: a user's channel allowlist drops them outside it, threads count as the parent, others are unaffected (0.48684ms) +✔ authorize: order puts wrong guild before user, and user before channel (no channel lookup for strangers) (0.164021ms) +✔ binding: a complete binding validates and is frozen (3.221593ms) +✔ binding: unknown key, missing field, wrong type refuse with exit 2 (1.664308ms) +✔ binding: empty allowlists refuse (0.489484ms) +✔ binding: a user's channel allowlist must be non-empty, listed and unique; absent means every listed channel (1.530283ms) +✔ reloadDiff: reloadable keys are summarised by id; every fixed key refuses with exit 2 (2.331597ms) +✔ binding: dmRecipient is optional, a snowflake, one of the listed users, and a fixed key (1.395098ms) +✔ binding: file must be 0600, regular, not a symlink (2.156108ms) +✔ binding: token file mode, symlink, emptiness and shape are checked; token never appears in errors (3.713437ms) +✔ cli: check refuses a non-0600 token file with exit 2 before any network use (115.304298ms) +✔ context files: absolute paths, traversal, symlinks and out-of-repo targets refuse; in-repo files resolve (3.29538ms) +✔ cli: check refuses a missing context file and a missing binding with exit 2; usage is exit 4 (434.958653ms) +✔ cli: reload validates the file first (exit 2), then needs a live owner (exit 1); usage is exit 4 (238.946386ms) +✔ cli: run refuses when STOP is present, before any network use (152.871089ms) +✔ binding: tools is optional, validated strictly, a fixed key for reload, and its roots are resolved against the data root (3.135857ms) +✔ binding: a git key is validated at load and reaches the extension whole, and only on a writable root (2.122184ms) +✔ delivery: an accepted message is in the inbox before the turn, the reply is chunked with one nonce per chunk, and the turn record is write-once (18.934291ms) +✔ delivery: refused and unknown outcomes are journaled; a later chunk is not sent after a failure (30.088217ms) +✔ delivery: restart with an unknown entry re-sends the same nonce once and reconciles before accepting traffic (3.499336ms) +✔ delivery: an unknown entry older than the dedupe window is marked refused, not re-sent; a still-unknown one refuses start (2.520585ms) +✔ delivery: repeated unknown reconciliations never refresh the dedupe window; the original intent time decides (1.451321ms) +✔ turn: a failed engine turn posts the fixed line, never model output, and writes a failed record (3.229017ms) +✔ turn: a second message during a turn is held by the engine, both get their own reply and record (33.681902ms) +✔ turn: a thread under a listed channel is answered in the thread; an unknown thread is looked up once (3.973696ms) +✔ drop: an unlisted user gets silence and one drop line; no inbox entry, no REST call, no engine call (1.294851ms) +✔ drop: an oversize message is accepted into the inbox, answered with the fixed line and journaled as a drop (1.799863ms) +✔ restart: an inbox with three ids and a replay of the same three produces zero turns (45.065449ms) +✔ stop: STOP present refuses start; STOP written while running refuses new turns and the current one finishes (33.457106ms) +✔ ceiling: the ceiling plus one is refused and journaled; one fixed line per UTC day; a new day accepts again (5.686495ms) +✔ ceiling: a burst arriving while turns are still running cannot queue past the ceiling (4.778305ms) +✔ ceiling: a turn interrupted by a crash still counts after restart; admissions are durable (2.148577ms) +✔ ceiling: the daily notice survives a same-day restart; one delivery attempt in total, even when the first attempt crashed mid-flight (6.217326ms) +✔ duplicate: the same event delivered twice while the thread lookup is held yields one prompt, one admission and one reply (4.351708ms) +✔ journal: no token-shaped string and no model output on the drop path reaches disk (0.873163ms) +✔ 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.348064ms) +✔ receipt: Discord refusing the reaction leaves the turn intact and records ok false (3.501793ms) +✔ reload: a new user is silent before and answered after; a removed channel goes silent; a lower ceiling applies at once (4.052151ms) +✔ reload: a fixed key refuses with exit 2 and the old binding stays in force (2.393233ms) +✔ tools: with a tools binding the turn record lists every read and its outcome; without one the field is null (5.212187ms) +✔ context: the Discord block names the server, channels and modes, and states the rules from Q15 and Q16 (2.823545ms) +✔ context: with tools the block names the roots, keeps file content as data, and says to state refusals plainly (0.82393ms) +✔ context: a writable root adds the write rules and says a write is real only once Jason commits (2.13179ms) +✔ context: the envelope is one bracketed line then the text; names cannot break the line (1.298018ms) +✔ context: a git root swaps the terminal-commit line for the git verbs, and a vault root adds the id protocol (1.744131ms) +✔ context: assembleContext concatenates files in launcher format and appends the block; sha256 is stable (1.733223ms) +✔ context: splitReply keeps paragraphs together under the limit and splits long ones at lines, spaces, then hard (0.762688ms) +✔ engine: buildPiArgs carries the fixed flags, engine settings, session dir and prompt file (2.944535ms) +✔ engine: with tools, buildPiArgs turns pi's own tools off, loads the extension explicitly and allowlists exactly our three (0.465626ms) +✔ engine: a run with tool turns settles once, on the answer, with every tool call in the result (62.822814ms) +✔ engine: a run that ends on a tool-only turn fails the prompt as empty; a retried run settles on the real end (61.145295ms) +✔ engine: one prompt, one turn, text and usage come back (65.381089ms) +✔ engine: a prompt while streaming is held until pi settles, then sent as its own run, and answered in order (351.693041ms) +✔ engine: a held prompt that times out before pi settles fails on its own and is never sent (252.49447ms) +✔ engine: timeout sends abort and fails only that turn; the process stays (104.961156ms) +✔ engine: tool events from a run that outlived its timeout never land in the next prompt's record (236.952688ms) +✔ engine: a prompt after a turn that timed out before its agent_start waits for pi to settle instead of being refused (136.060149ms) +✔ 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.56674ms) +✔ engine: a timed-out turn pi starts only after the grace never answers a later prompt (614.013047ms) +✔ engine: late events of a run past its grace, before pi exits, answer nothing and nothing more is sent (early prompt response) (1.50905ms) +✔ engine: late events of a run past its grace, before pi exits, answer nothing and nothing more is sent (late prompt response) (0.982612ms) +✔ engine: a timed-out run pi did start outlives the grace; the next prompt goes out when it ends (438.371393ms) +✔ engine: a malformed JSONL line fails the turn, not the process (25.024093ms) +✔ engine: a turn that ends in error rejects with the error code; process exit fails pending turns (44.214839ms) +✔ gateway: hello -> identify with intents, ready, heartbeat with jitter, ack (2.80776ms) +✔ gateway: missed ack closes the socket and resumes with the last sequence (1.836378ms) +✔ gateway: op 7 reconnect resumes; op 9 non-resumable re-identifies (0.562911ms) +✔ gateway: op 9 resumable resumes (0.397066ms) +✔ gateway: close 4014 is fatal, reports the missing intent, never reconnects (1.091203ms) +✔ gateway: 4004 and 4013 are fatal too; 1006 reconnects with identify when no session (0.585441ms) +✔ gateway: close() is final and unparseable frames are ignored (0.509514ms) +✔ git: config validation is strict, needs write: true, a work tree and a private token file (81.630624ms) +✔ git: the child environment drops every host git config, names one helper, and carries the token path only for origin (55.182558ms) +✔ git: status reports the branch, ahead/behind and changed paths, and refuses off the named branch or mid-merge (104.588538ms) +✔ git: parseStatus reads porcelain v2 including renames and conflicts (0.386886ms) +✔ git: a commit stages exactly the named files, carries the seat author and the requester trailer, and pushes at once (129.509564ms) +✔ git: commit refusals: message, paths, requester, nothing to commit, and an index that already holds other work (129.242538ms) +✔ git: a commit whose push fails is still a commit, says so, and the next commit's push carries both (D6) (128.941211ms) +✔ git: pull is fast-forward only; a diverged origin or dirty local files refuse with nothing merged (270.059409ms) +✔ git: push pushes the named branch only and reports up to date (108.38127ms) +✔ git: no token value or token path ever reaches a git argument list; outputs are masked and capped (120.832286ms) +✔ git: the credential helper answers get over https from a private file and nothing else (235.297232ms) +✔ git: the vault protocol validates before a commit, honours another owner's lock, reserves ids, and locks around writes (870.316574ms) +✔ lock: the claim is exclusive; a second start against a live owner refuses (5.461445ms) +✔ lock: a stale lock (dead owner, reused pid, or record without start) refuses run and is never signaled; only unlock clears it (6.077463ms) +✔ lock: an incomplete claim (directory without owner record) is busy and refuses run; unlock clears it (1.154073ms) +✔ lock: an owner record that exists but cannot be read is invalid: never signaled, never removed, never claimed over (2.958004ms) +✔ lock: legacy upgrade; a live connector holding a {pid, start} record is unknown, unlock refuses and nothing changes; after it exits, unlock clears it (68.497107ms) +✔ 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 (414.276972ms) +✔ lock: identity syntax; only canonical unsigned decimal start ticks and lowercase boot uuids are identities (0.568485ms) +✔ lock: a process whose start marker or boot id cannot be read refuses to claim (0.622695ms) +✔ lock: a live pid whose identity cannot be read right now is unknown: never signaled, never removed, never claimed over (1.24818ms) +✔ lock: four processes racing for the same binding; exactly one claims it and the others refuse (65.895538ms) +✔ lock: stale handoff; concurrent starts over a stale lock all refuse, nothing reclaims, one unlock then exactly one live owner (136.975315ms) +✔ lock: four-party schedule; claims landing inside an unlock's gap never survive, one unlock leaves no owner and no residue (62.784536ms) +✔ notices: a kind is recorded per UTC day and found again (0.47959ms) +✔ notify: the DM channel opens once, every send carries the nonce, and only the message id comes back (6.202273ms) +✔ notify: refusals and unknowns surface as RestOutcome without ids; a refused send reopens the channel next time (2.347778ms) +✔ notify: no dmRecipient, a non-0600 binding or token, or a missing binding refuse before any network use (2.088161ms) +✔ recover: nothing to do is clean; a lock whose owner is gone or that has no record is cleared and STOP ends up absent (46.846093ms) +✔ recover: an operator STOP refuses with exit 3 and is never removed, whatever the lock says (46.862321ms) +✔ recover: a brake written during the unlock wins; STOP stays with both lines and the start is refused (36.118273ms) +✔ recover: a held binding refuses with exit 3 and writes no STOP: live owner, alive pid without verifiable identity, unreadable record (85.679261ms) +✔ cli: recover exits 0 when ready, 3 behind a brake or a held binding, and run's own STOP refusal is 3 (786.96409ms) +✔ rest: createMessage sends nonce, enforce_nonce, empty allowed_mentions and a soft reply reference (2.469546ms) +✔ rest: 429 waits retry_after and retries; 4xx is refused; 5xx and socket errors are unknown (6.499215ms) +✔ rest: content and nonce limits are enforced locally; typing never throws (0.663236ms) +✔ rest: react PUTs the encoded emoji on the inbound message for @me; 2xx is true, anything else is false and never throws (1.10301ms) +✔ rest: createDm posts the recipient and resolves the channel id; 4xx refused, 5xx unknown, bad id never sent (1.019735ms) +✔ setspark config: a bare https or loopback origin, a private key file, a principal (5.327066ms) +✔ setspark config: reaches the tools config and the binding as a fixed key (2.605257ms) +✔ setspark config: the binding's key survives resolveToolRoots and the engine's JSON hand-off to the extension (1.225081ms) +✔ setspark config: approvers come from the binding's users, never from the binding's setspark key (2.048754ms) +✔ setspark verbs: required_approvers go out as discord ids from names and come back as names (28.04122ms) +✔ setspark verbs: no Discord user id reaches tool text, whatever shape the service returns it in (12.465965ms) +✔ setspark contract: a decision made with names opens a request the connector accepts; names stored by an old record still refuse (8.034413ms) +✔ setspark keys: read per call, one printable token per file, rotation without a restart (4.868421ms) +✔ setspark idempotency keys: principal, turn id, call index; connector keys name a step (0.667357ms) +✔ setspark http core: json in and out, bearer header, idempotency header, fixed user agent, no key anywhere else (2.697976ms) +✔ setspark http core: error bodies become fixed refusals with code and the 409 fields; server text is data, cut (1011.422397ms) +✔ setspark verbs: a setspark key enables the eight verbs and no counters (0.574031ms) +✔ 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 (7.422913ms) +✔ setspark verbs: no turn refuses every write before any request; bad arguments refuse before any request; reads still work (1.467746ms) +✔ setspark verbs: renderRecord caps long output and hides the accepted snapshot (0.250436ms) +✔ setspark api: bind, add_approval (button and reply) and get use integer request ids and the connector's keys (2.738467ms) +✔ tools: config refuses a missing, symlinked, dotted, non-directory or duplicate root and bad limits (4.390017ms) +✔ tools: every escape is refused with a fixed reason and nothing outside the root is read (4.716188ms) +✔ tools: happy paths list, read a window, and search case-insensitively; dotfiles and symlinks never appear (5.057023ms) +✔ tools: the tool set renders text for the model, records details for the journal, and enforces the per-run budget (3.659219ms) +✔ tools: listing and search caps hold (12.247961ms) +✔ tools: credential shapes are caught; ordinary prose and ids are not (0.942743ms) +✔ 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.486088ms) +✔ tools: an unreadable file under the root is skipped by search and refused by read (1.284782ms) +✔ tools: config accepts write: true only as a boolean, and enables the write tools only then (1.243556ms) +✔ tools: every write outside the fence is refused before any byte lands, and no temp file remains (5.044765ms) +✔ tools: write_file leaves the exact bytes, edit_file replaces one exact match, and the set renders the change as uncommitted (2.66857ms) +✔ tools: a target that changed between the check and the rename is refused and the temp file is removed (1.687135ms) +✔ web: config takes an https or loopback-http SearXNG base url and a bounded fetch cap (3.829048ms) +✔ web: address rules refuse every private, loopback, link-local, mapped and multicast form (2.407129ms) +✔ web: web_fetch refuses bad urls, private hosts, rebinding names, non-https redirects, too many hops, error status, non-text bodies, and times out (1029.680966ms) +✔ web: web_fetch returns html as text with the title, follows an https redirect, keeps plain text and json, and cuts at the cap (5.749177ms) +✔ web: html to text drops scripts, styles and comments, decodes entities and keeps block breaks (0.345888ms) +✔ 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 (3.21551ms) +✔ 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.689858ms) +ℹ tests 227 +ℹ suites 0 +ℹ pass 227 +ℹ fail 0 +ℹ cancelled 0 +ℹ skipped 0 +ℹ todo 0 +ℹ duration_ms 2941.501687 diff --git a/agents/filbert/work/slice1-s4-review/r2-mut-M28-rerun2.txt b/agents/filbert/work/slice1-s4-review/r2-mut-M28-rerun2.txt new file mode 100644 index 00000000..a78efe5d --- /dev/null +++ b/agents/filbert/work/slice1-s4-review/r2-mut-M28-rerun2.txt @@ -0,0 +1,235 @@ +✔ inbox lists only decisions routed to the human, with what approving authorizes and how to decide (127.753251ms) +✔ decide resolves by id prefix with --yes, and the trail shows it in broker order with the task to follow (129.025855ms) +✔ decide refuses without a terminal or --yes, on an unknown option and on a short reference (96.418066ms) +✔ decide prints a declining choice as declining (100.853056ms) +✔ an unknown outcome is reported once and never resent (96.778988ms) +✔ a decision closed before the answer arrives exits 2 and points at its trail (105.734474ms) +✔ a prefix that matches two open decisions exits 2 and resolves neither (93.012442ms) +✔ without --business a command uses the live host's business, and a stale host.json is not a host (53.206865ms) +✔ every human command refuses inside an agent run before it touches the bus (62.921536ms) +✔ usage errors exit 4; no business and no host is a usage error (56.550824ms) +✔ agents and tasks print through the broker (57.582994ms) +✔ notify.json: missing, loose, malformed and extra keys refuse; a binding or null passes (1.307361ms) +✔ bootConfig builds the broker's boot message for one business, with no trackers key when no project names one (64.984295ms) +✔ trackers come from the tracker.* variables of the one project that names a tracker project (65.53152ms) +✔ with two projects, the one that sets tracker.project supplies the tracker and the other is no conflict (62.872752ms) +✔ two projects that each name a tracker project refuse, since the boot shape holds one (53.003211ms) +✔ a business without tracker.baseUrl gets no trackers entry (50.542782ms) +✔ an unknown business and a broken system config refuse with exit 3 (74.0262ms) +✔ empty views say so (2.944357ms) +✔ the trail keeps the broker's order and names a decision's task without its rows (3.005385ms) +✔ tasks print the tracker fields the snapshot carries (0.30078ms) +✔ the host boots the broker, binds a launch in process, and the notifier DMs a blocking decision exactly once (974.684957ms) +✔ a notifier that dies takes the host down with exit 1, so the unit restarts the pair (214.521088ms) +✔ a notifier that refuses stops the broker and the host refuses with exit 3 (203.644138ms) +✔ watchChildren reports a child that died before it was called, and one that dies later (25.221184ms) +✔ bus stop refuses to signal a live pid that is not a bus host (203.41222ms) +✔ bus start refuses with exit 3 and the code when the broker refuses to boot; bus status names the lock (215.480332ms) +✔ bus start refuses with exit 3 without a notifier config (93.644985ms) +✔ bus start runs until bus stop; status reports it while it runs (651.972183ms) +✔ bus-service.sh renders the unit and installs it into a given directory (26.333963ms) +✔ zoned uses the IANA zone across DST (30.582528ms) +✔ each open blocking decision is DM'd once, across polls and a restart; non-blocking ones are not (126.420982ms) +✔ two blocking decisions get two DMs with different nonces (123.200882ms) +✔ the digest nonce differs per business and per day and fits Discord's 25 characters (0.488688ms) +✔ a failed DM is journaled, backs off, and is retried until it lands (100.613569ms) +✔ the digest goes at 08:00 Chicago once a day, with blocking ones marked as DM'd (102.035032ms) +✔ a late start with no digest for the day sends one at once; an empty inbox gets one line (86.894435ms) +✔ an inbox read failure is logged and the next poll retries (1.01765ms) +✔ no Discord id reaches the journal or the log (102.020379ms) +✔ the journal: a torn tail is copied out and truncated, so an append after it reopens cleanly (25.701484ms) +✔ the journal: a crash between the copy and the truncate leaves a tail the next open repairs (38.403667ms) +✔ the journal: a whole file that is one torn line truncates to empty (20.953522ms) +✔ the journal: a malformed complete line refuses and leaves the file and any torn tail alone (2.164213ms) +✔ the journal: a loose file mode, a loose directory or a symlinked journal refuses (0.676866ms) +✔ digest content stays within Discord's 2000 characters (0.322469ms) +✔ runLoop never overlaps ticks and stops after the one in flight (111.200287ms) +✔ the transport writes {business, verb, args} to the child and reads its JSON (82.057379ms) +✔ a bus code on stderr becomes the exit code; garbage and timeouts are outcome problems (2253.399727ms) +✔ busExit and refuseInsideAgent (0.42075ms) +✔ approvals: a request is validated before anything is posted; the rendering shows names and never ids (3.628277ms) +✔ approvals: the ledger is appended and folded into open requests with bind and approval states (1.75015ms) +✔ approvals: a reply approves only when it points at a request, says exactly approve, and comes from a listed approver once (0.789884ms) +✔ approvals: a button approves only on its own request message with the matching custom id (0.528425ms) +✔ approvals flow: a turn that opened a request posts the message with the button, records it, binds it, and both approvers approve (17.730053ms) +✔ approvals flow: a non-approver, a repeat, a wrong custom id and a service refusal each get their fixed line and a drop entry (8.531053ms) +✔ approvals flow: an invalid request from the model, a refused post, and no api client are recorded and post nothing (8.368222ms) +✔ approvals flow: start retries a bind and an approval left as unknown, under their original keys (2.490163ms) +✔ authorize: open channel, listed user (2.163248ms) +✔ authorize: wrong guild (0.413078ms) +✔ authorize: no guild (DM) (0.246179ms) +✔ authorize: unlisted channel (0.226812ms) +✔ authorize: unknown channel, no info (0.216533ms) +✔ authorize: thread of listed parent (0.244115ms) +✔ authorize: thread of unlisted parent (0.18617ms) +✔ authorize: text channel that is not a thread and not listed (0.212158ms) +✔ authorize: unlisted user (0.216673ms) +✔ authorize: no author (1.07892ms) +✔ authorize: bot author (listed id, bot flag) (0.556837ms) +✔ authorize: system author (0.132411ms) +✔ authorize: the bot itself (0.124679ms) +✔ authorize: webhook (0.092331ms) +✔ authorize: mention channel without mention (0.367416ms) +✔ authorize: mention channel with bot mention (0.15319ms) +✔ authorize: mention channel with @everyone only (0.094163ms) +✔ authorize: mention channel mentioning someone else (0.106898ms) +✔ authorize: mention channel, content says @bot but mentions empty (0.07215ms) +✔ authorize: private thread under mention channel, mentioned (0.107247ms) +✔ authorize: private thread under mention channel, not mentioned (0.076093ms) +✔ authorize: thread in another guild per channel info (0.081567ms) +✔ authorize: not an object (0.078452ms) +✔ authorize: no id (0.087671ms) +✔ authorize: oversize content is accepted and flagged (0.088343ms) +✔ authorize: exactly the limit is not oversize (0.103414ms) +✔ authorize: a user's channel allowlist drops them outside it, threads count as the parent, others are unaffected (0.510951ms) +✔ authorize: order puts wrong guild before user, and user before channel (no channel lookup for strangers) (0.151798ms) +✔ binding: a complete binding validates and is frozen (2.811979ms) +✔ binding: unknown key, missing field, wrong type refuse with exit 2 (1.534343ms) +✔ binding: empty allowlists refuse (0.489052ms) +✔ binding: a user's channel allowlist must be non-empty, listed and unique; absent means every listed channel (1.332179ms) +✔ reloadDiff: reloadable keys are summarised by id; every fixed key refuses with exit 2 (1.730779ms) +✔ binding: dmRecipient is optional, a snowflake, one of the listed users, and a fixed key (0.479475ms) +✔ binding: file must be 0600, regular, not a symlink (1.561869ms) +✔ binding: token file mode, symlink, emptiness and shape are checked; token never appears in errors (1.360705ms) +✔ cli: check refuses a non-0600 token file with exit 2 before any network use (118.459641ms) +✔ context files: absolute paths, traversal, symlinks and out-of-repo targets refuse; in-repo files resolve (5.138223ms) +✔ cli: check refuses a missing context file and a missing binding with exit 2; usage is exit 4 (390.500298ms) +✔ cli: reload validates the file first (exit 2), then needs a live owner (exit 1); usage is exit 4 (225.842844ms) +✔ cli: run refuses when STOP is present, before any network use (127.056523ms) +✔ binding: tools is optional, validated strictly, a fixed key for reload, and its roots are resolved against the data root (1.86736ms) +✔ binding: a git key is validated at load and reaches the extension whole, and only on a writable root (1.263217ms) +✔ 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 (19.600503ms) +✔ delivery: refused and unknown outcomes are journaled; a later chunk is not sent after a failure (22.072709ms) +✔ delivery: restart with an unknown entry re-sends the same nonce once and reconciles before accepting traffic (1.633239ms) +✔ delivery: an unknown entry older than the dedupe window is marked refused, not re-sent; a still-unknown one refuses start (1.375273ms) +✔ delivery: repeated unknown reconciliations never refresh the dedupe window; the original intent time decides (1.379997ms) +✔ turn: a failed engine turn posts the fixed line, never model output, and writes a failed record (3.479605ms) +✔ turn: a second message during a turn is held by the engine, both get their own reply and record (35.877674ms) +✔ turn: a thread under a listed channel is answered in the thread; an unknown thread is looked up once (6.498832ms) +✔ drop: an unlisted user gets silence and one drop line; no inbox entry, no REST call, no engine call (1.019322ms) +✔ drop: an oversize message is accepted into the inbox, answered with the fixed line and journaled as a drop (1.114521ms) +✔ restart: an inbox with three ids and a replay of the same three produces zero turns (42.243936ms) +✔ stop: STOP present refuses start; STOP written while running refuses new turns and the current one finishes (31.749582ms) +✔ ceiling: the ceiling plus one is refused and journaled; one fixed line per UTC day; a new day accepts again (6.897904ms) +✔ ceiling: a burst arriving while turns are still running cannot queue past the ceiling (4.729896ms) +✔ ceiling: a turn interrupted by a crash still counts after restart; admissions are durable (3.503019ms) +✔ ceiling: the daily notice survives a same-day restart; one delivery attempt in total, even when the first attempt crashed mid-flight (5.841041ms) +✔ duplicate: the same event delivered twice while the thread lookup is held yields one prompt, one admission and one reply (3.776015ms) +✔ journal: no token-shaped string and no model output on the drop path reaches disk (0.802936ms) +✔ 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 (6.148514ms) +✔ receipt: Discord refusing the reaction leaves the turn intact and records ok false (2.668272ms) +✔ reload: a new user is silent before and answered after; a removed channel goes silent; a lower ceiling applies at once (5.084101ms) +✔ reload: a fixed key refuses with exit 2 and the old binding stays in force (3.3259ms) +✔ tools: with a tools binding the turn record lists every read and its outcome; without one the field is null (6.035844ms) +✔ context: the Discord block names the server, channels and modes, and states the rules from Q15 and Q16 (2.65906ms) +✔ context: with tools the block names the roots, keeps file content as data, and says to state refusals plainly (0.859541ms) +✔ context: a writable root adds the write rules and says a write is real only once Jason commits (2.287212ms) +✔ context: the envelope is one bracketed line then the text; names cannot break the line (1.651253ms) +✔ context: a git root swaps the terminal-commit line for the git verbs, and a vault root adds the id protocol (1.766928ms) +✔ context: assembleContext concatenates files in launcher format and appends the block; sha256 is stable (1.413465ms) +✔ context: splitReply keeps paragraphs together under the limit and splits long ones at lines, spaces, then hard (0.742759ms) +✔ engine: buildPiArgs carries the fixed flags, engine settings, session dir and prompt file (2.827857ms) +✔ engine: with tools, buildPiArgs turns pi's own tools off, loads the extension explicitly and allowlists exactly our three (0.491479ms) +✔ engine: a run with tool turns settles once, on the answer, with every tool call in the result (70.86549ms) +✔ engine: a run that ends on a tool-only turn fails the prompt as empty; a retried run settles on the real end (69.620553ms) +✔ engine: one prompt, one turn, text and usage come back (51.782501ms) +✔ engine: a prompt while streaming is held until pi settles, then sent as its own run, and answered in order (359.437182ms) +✔ engine: a held prompt that times out before pi settles fails on its own and is never sent (238.14282ms) +✔ engine: timeout sends abort and fails only that turn; the process stays (104.388246ms) +✔ engine: tool events from a run that outlived its timeout never land in the next prompt's record (227.259601ms) +✔ engine: a prompt after a turn that timed out before its agent_start waits for pi to settle instead of being refused (126.831582ms) +✔ 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.412083ms) +✔ engine: a timed-out turn pi starts only after the grace never answers a later prompt (615.408499ms) +✔ engine: late events of a run past its grace, before pi exits, answer nothing and nothing more is sent (early prompt response) (1.270054ms) +✔ engine: late events of a run past its grace, before pi exits, answer nothing and nothing more is sent (late prompt response) (0.489266ms) +✔ engine: a timed-out run pi did start outlives the grace; the next prompt goes out when it ends (427.920283ms) +✔ engine: a malformed JSONL line fails the turn, not the process (26.809844ms) +✔ engine: a turn that ends in error rejects with the error code; process exit fails pending turns (45.871482ms) +✔ gateway: hello -> identify with intents, ready, heartbeat with jitter, ack (4.042801ms) +✔ gateway: missed ack closes the socket and resumes with the last sequence (1.783841ms) +✔ gateway: op 7 reconnect resumes; op 9 non-resumable re-identifies (0.518696ms) +✔ gateway: op 9 resumable resumes (0.356799ms) +✔ gateway: close 4014 is fatal, reports the missing intent, never reconnects (0.956958ms) +✔ gateway: 4004 and 4013 are fatal too; 1006 reconnects with identify when no session (0.57353ms) +✔ gateway: close() is final and unparseable frames are ignored (0.404589ms) +✔ git: config validation is strict, needs write: true, a work tree and a private token file (89.033224ms) +✔ git: the child environment drops every host git config, names one helper, and carries the token path only for origin (63.115487ms) +✔ git: status reports the branch, ahead/behind and changed paths, and refuses off the named branch or mid-merge (104.076686ms) +✔ git: parseStatus reads porcelain v2 including renames and conflicts (0.349054ms) +✔ git: a commit stages exactly the named files, carries the seat author and the requester trailer, and pushes at once (92.960511ms) +✔ git: commit refusals: message, paths, requester, nothing to commit, and an index that already holds other work (110.702274ms) +✔ git: a commit whose push fails is still a commit, says so, and the next commit's push carries both (D6) (126.24749ms) +✔ git: pull is fast-forward only; a diverged origin or dirty local files refuse with nothing merged (201.410346ms) +✔ git: push pushes the named branch only and reports up to date (79.932738ms) +✔ git: no token value or token path ever reaches a git argument list; outputs are masked and capped (93.41013ms) +✔ git: the credential helper answers get over https from a private file and nothing else (240.864545ms) +✔ git: the vault protocol validates before a commit, honours another owner's lock, reserves ids, and locks around writes (836.648473ms) +✔ lock: the claim is exclusive; a second start against a live owner refuses (4.93703ms) +✔ lock: a stale lock (dead owner, reused pid, or record without start) refuses run and is never signaled; only unlock clears it (5.34184ms) +✔ lock: an incomplete claim (directory without owner record) is busy and refuses run; unlock clears it (1.049106ms) +✔ lock: an owner record that exists but cannot be read is invalid: never signaled, never removed, never claimed over (2.501652ms) +✔ lock: legacy upgrade; a live connector holding a {pid, start} record is unknown, unlock refuses and nothing changes; after it exits, unlock clears it (61.604453ms) +✔ 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 (388.69257ms) +✔ lock: identity syntax; only canonical unsigned decimal start ticks and lowercase boot uuids are identities (0.431042ms) +✔ lock: a process whose start marker or boot id cannot be read refuses to claim (0.413009ms) +✔ lock: a live pid whose identity cannot be read right now is unknown: never signaled, never removed, never claimed over (0.859556ms) +✔ lock: four processes racing for the same binding; exactly one claims it and the others refuse (40.072031ms) +✔ lock: stale handoff; concurrent starts over a stale lock all refuse, nothing reclaims, one unlock then exactly one live owner (131.561811ms) +✔ lock: four-party schedule; claims landing inside an unlock's gap never survive, one unlock leaves no owner and no residue (69.87073ms) +✔ notices: a kind is recorded per UTC day and found again (0.616648ms) +✔ notify: the DM channel opens once, every send carries the nonce, and only the message id comes back (6.579462ms) +✔ notify: refusals and unknowns surface as RestOutcome without ids; a refused send reopens the channel next time (4.713692ms) +✔ notify: no dmRecipient, a non-0600 binding or token, or a missing binding refuse before any network use (4.738148ms) +✔ recover: nothing to do is clean; a lock whose owner is gone or that has no record is cleared and STOP ends up absent (48.62245ms) +✔ recover: an operator STOP refuses with exit 3 and is never removed, whatever the lock says (47.091302ms) +✔ recover: a brake written during the unlock wins; STOP stays with both lines and the start is refused (33.514263ms) +✔ recover: a held binding refuses with exit 3 and writes no STOP: live owner, alive pid without verifiable identity, unreadable record (92.247189ms) +✔ cli: recover exits 0 when ready, 3 behind a brake or a held binding, and run's own STOP refusal is 3 (725.566027ms) +✔ rest: createMessage sends nonce, enforce_nonce, empty allowed_mentions and a soft reply reference (2.80118ms) +✔ rest: 429 waits retry_after and retries; 4xx is refused; 5xx and socket errors are unknown (3.155356ms) +✔ rest: content and nonce limits are enforced locally; typing never throws (0.665527ms) +✔ rest: react PUTs the encoded emoji on the inbound message for @me; 2xx is true, anything else is false and never throws (1.148698ms) +✔ rest: createDm posts the recipient and resolves the channel id; 4xx refused, 5xx unknown, bad id never sent (1.866994ms) +✔ setspark config: a bare https or loopback origin, a private key file, a principal (4.907299ms) +✔ setspark config: reaches the tools config and the binding as a fixed key (2.885344ms) +✔ setspark config: the binding's key survives resolveToolRoots and the engine's JSON hand-off to the extension (1.415831ms) +✔ setspark config: approvers come from the binding's users, never from the binding's setspark key (1.676002ms) +✔ setspark verbs: required_approvers go out as discord ids from names and come back as names (26.652602ms) +✔ setspark verbs: no Discord user id reaches tool text, whatever shape the service returns it in (11.608155ms) +✔ setspark contract: a decision made with names opens a request the connector accepts; names stored by an old record still refuse (7.134591ms) +✔ setspark keys: read per call, one printable token per file, rotation without a restart (3.890858ms) +✔ setspark idempotency keys: principal, turn id, call index; connector keys name a step (2.733054ms) +✔ setspark http core: json in and out, bearer header, idempotency header, fixed user agent, no key anywhere else (3.599123ms) +✔ setspark http core: error bodies become fixed refusals with code and the 409 fields; server text is data, cut (1011.42976ms) +✔ setspark verbs: a setspark key enables the eight verbs and no counters (0.570767ms) +✔ 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.650093ms) +✔ setspark verbs: no turn refuses every write before any request; bad arguments refuse before any request; reads still work (1.427899ms) +✔ setspark verbs: renderRecord caps long output and hides the accepted snapshot (0.226813ms) +✔ setspark api: bind, add_approval (button and reply) and get use integer request ids and the connector's keys (2.807204ms) +✔ tools: config refuses a missing, symlinked, dotted, non-directory or duplicate root and bad limits (4.286692ms) +✔ tools: every escape is refused with a fixed reason and nothing outside the root is read (3.667324ms) +✔ tools: happy paths list, read a window, and search case-insensitively; dotfiles and symlinks never appear (5.102094ms) +✔ tools: the tool set renders text for the model, records details for the journal, and enforces the per-run budget (4.099343ms) +✔ tools: listing and search caps hold (11.508786ms) +✔ tools: credential shapes are caught; ordinary prose and ids are not (0.881486ms) +✔ 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.835094ms) +✔ tools: an unreadable file under the root is skipped by search and refused by read (1.19701ms) +✔ tools: config accepts write: true only as a boolean, and enables the write tools only then (1.125946ms) +✔ tools: every write outside the fence is refused before any byte lands, and no temp file remains (5.987562ms) +✔ tools: write_file leaves the exact bytes, edit_file replaces one exact match, and the set renders the change as uncommitted (2.708844ms) +✔ tools: a target that changed between the check and the rename is refused and the temp file is removed (1.627306ms) +✔ web: config takes an https or loopback-http SearXNG base url and a bounded fetch cap (3.544969ms) +✔ web: address rules refuse every private, loopback, link-local, mapped and multicast form (2.000021ms) +✔ 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.136539ms) +✔ web: web_fetch returns html as text with the title, follows an https redirect, keeps plain text and json, and cuts at the cap (5.463547ms) +✔ web: html to text drops scripts, styles and comments, decodes entities and keeps block breaks (0.314305ms) +✔ 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 (3.573597ms) +✔ web: the tool set enables the web tools only with a web key, counts them in the budget, and records url, status and hits (3.249626ms) +ℹ tests 227 +ℹ suites 0 +ℹ pass 227 +ℹ fail 0 +ℹ cancelled 0 +ℹ skipped 0 +ℹ todo 0 +ℹ duration_ms 2765.910563 diff --git a/agents/filbert/work/slice1-s4-review/r2-mut-summary.txt b/agents/filbert/work/slice1-s4-review/r2-mut-summary.txt new file mode 100644 index 00000000..e46234dc --- /dev/null +++ b/agents/filbert/work/slice1-s4-review/r2-mut-summary.txt @@ -0,0 +1,50 @@ +M1 killed (fail 1, cancelled 0) +M2 killed (fail 1, cancelled 0) +M3 killed (fail 2, cancelled 0) +M4 killed (fail 1, cancelled 0) +M5 NOT-APPLIED packages/cli/src/notifier.mjs +M6 NOT-APPLIED packages/cli/src/notifier.mjs +M7 killed (fail 5, cancelled 0) +M8 killed (fail 1, cancelled 0) +M9 killed (fail 1, cancelled 0) +M10 killed (fail 1, cancelled 0) +M11 killed (fail 1, cancelled 0) +M12 killed (fail 1, cancelled 0) +M13 killed (fail 2, cancelled 0) +M14 killed (fail 1, cancelled 0) +M15 killed (fail 2, cancelled 0) +M16 killed (fail 2, cancelled 0) +M17 killed (fail 1, cancelled 0) +M18 SURVIVED +M19 killed (fail 0, cancelled 1) +M20 killed (fail 1, cancelled 0) +M21 killed (fail 1, cancelled 0) +M22 killed (fail 1, cancelled 0) +M23 killed (fail 1, cancelled 0) +M24 killed (fail 1, cancelled 0) +M25 killed (fail 1, cancelled 0) +M26 killed (fail 1, cancelled 0) +M27 NOT-APPLIED packages/cli/src/host.mjs +M28 SURVIVED +M29 killed (fail 1, cancelled 0) +M30 killed (fail 1, cancelled 0) +M31 killed (fail 1, cancelled 0) +M32 killed (fail 2, cancelled 0) +M33 SURVIVED +M34 killed (fail 1, cancelled 0) +R5 killed (fail 1, cancelled 0) +R6 killed (fail 3, cancelled 0) +R6b killed (fail 3, cancelled 0) +R6c killed (fail 3, cancelled 0) +F1 killed (fail 1, cancelled 0) +J3 killed (fail 1, cancelled 0) +D1 killed (fail 1, cancelled 0) +RACE killed (fail 1, cancelled 0) +F3 killed (fail 1, cancelled 0) +N1 killed (fail 1, cancelled 0) +N2 SURVIVED +N3 SURVIVED +N4 SURVIVED +N5 SURVIVED +N6 killed (fail 2, cancelled 0) +manifest exit 0 diff --git a/agents/filbert/work/slice1-s4-review/r2-node24.txt b/agents/filbert/work/slice1-s4-review/r2-node24.txt new file mode 100644 index 00000000..b141f9a6 --- /dev/null +++ b/agents/filbert/work/slice1-s4-review/r2-node24.txt @@ -0,0 +1,236 @@ +v24.21.0 +✔ inbox lists only decisions routed to the human, with what approving authorizes and how to decide (133.148875ms) +✔ decide resolves by id prefix with --yes, and the trail shows it in broker order with the task to follow (144.284714ms) +✔ decide refuses without a terminal or --yes, on an unknown option and on a short reference (105.577091ms) +✔ decide prints a declining choice as declining (149.14756ms) +✔ an unknown outcome is reported once and never resent (98.973182ms) +✔ a decision closed before the answer arrives exits 2 and points at its trail (116.155887ms) +✔ a prefix that matches two open decisions exits 2 and resolves neither (86.731111ms) +✔ without --business a command uses the live host's business, and a stale host.json is not a host (60.683256ms) +✔ every human command refuses inside an agent run before it touches the bus (59.372134ms) +✔ usage errors exit 4; no business and no host is a usage error (59.391836ms) +✔ agents and tasks print through the broker (52.619851ms) +✔ notify.json: missing, loose, malformed and extra keys refuse; a binding or null passes (1.306544ms) +✔ bootConfig builds the broker's boot message for one business, with no trackers key when no project names one (70.290138ms) +✔ trackers come from the tracker.* variables of the one project that names a tracker project (45.218875ms) +✔ with two projects, the one that sets tracker.project supplies the tracker and the other is no conflict (73.190968ms) +✔ two projects that each name a tracker project refuse, since the boot shape holds one (50.949454ms) +✔ a business without tracker.baseUrl gets no trackers entry (49.646488ms) +✔ an unknown business and a broken system config refuse with exit 3 (73.975271ms) +✔ empty views say so (1.324689ms) +✔ the trail keeps the broker's order and names a decision's task without its rows (2.356319ms) +✔ tasks print the tracker fields the snapshot carries (0.221546ms) +✔ the host boots the broker, binds a launch in process, and the notifier DMs a blocking decision exactly once (1034.927582ms) +✔ a notifier that dies takes the host down with exit 1, so the unit restarts the pair (186.210396ms) +✔ a notifier that refuses stops the broker and the host refuses with exit 3 (174.291356ms) +✔ watchChildren reports a child that died before it was called, and one that dies later (22.270706ms) +✔ bus stop refuses to signal a live pid that is not a bus host (202.662447ms) +✔ bus start refuses with exit 3 and the code when the broker refuses to boot; bus status names the lock (201.680155ms) +✔ bus start refuses with exit 3 without a notifier config (90.929576ms) +✔ bus start runs until bus stop; status reports it while it runs (654.588193ms) +✔ bus-service.sh renders the unit and installs it into a given directory (37.869352ms) +✔ zoned uses the IANA zone across DST (55.713246ms) +✔ each open blocking decision is DM'd once, across polls and a restart; non-blocking ones are not (135.192226ms) +✔ two blocking decisions get two DMs with different nonces (126.131514ms) +✔ the digest nonce differs per business and per day and fits Discord's 25 characters (0.357865ms) +✔ a failed DM is journaled, backs off, and is retried until it lands (97.327268ms) +✔ the digest goes at 08:00 Chicago once a day, with blocking ones marked as DM'd (145.621758ms) +✔ a late start with no digest for the day sends one at once; an empty inbox gets one line (97.606994ms) +✔ an inbox read failure is logged and the next poll retries (1.289062ms) +✔ no Discord id reaches the journal or the log (108.828729ms) +✔ the journal: a torn tail is copied out and truncated, so an append after it reopens cleanly (19.13901ms) +✔ the journal: a crash between the copy and the truncate leaves a tail the next open repairs (39.211452ms) +✔ the journal: a whole file that is one torn line truncates to empty (24.705091ms) +✔ the journal: a malformed complete line refuses and leaves the file and any torn tail alone (1.989135ms) +✔ the journal: a loose file mode, a loose directory or a symlinked journal refuses (0.748042ms) +✔ digest content stays within Discord's 2000 characters (0.320981ms) +✔ runLoop never overlaps ticks and stops after the one in flight (111.21029ms) +✔ the transport writes {business, verb, args} to the child and reads its JSON (58.297057ms) +✔ a bus code on stderr becomes the exit code; garbage and timeouts are outcome problems (2219.381688ms) +✔ busExit and refuseInsideAgent (0.347476ms) +✔ approvals: a request is validated before anything is posted; the rendering shows names and never ids (3.867876ms) +✔ approvals: the ledger is appended and folded into open requests with bind and approval states (1.930998ms) +✔ approvals: a reply approves only when it points at a request, says exactly approve, and comes from a listed approver once (0.622318ms) +✔ approvals: a button approves only on its own request message with the matching custom id (0.745477ms) +✔ approvals flow: a turn that opened a request posts the message with the button, records it, binds it, and both approvers approve (22.317701ms) +✔ approvals flow: a non-approver, a repeat, a wrong custom id and a service refusal each get their fixed line and a drop entry (8.212755ms) +✔ approvals flow: an invalid request from the model, a refused post, and no api client are recorded and post nothing (6.007654ms) +✔ approvals flow: start retries a bind and an approval left as unknown, under their original keys (2.234751ms) +✔ authorize: open channel, listed user (1.91511ms) +✔ authorize: wrong guild (0.197809ms) +✔ authorize: no guild (DM) (0.329677ms) +✔ authorize: unlisted channel (0.237793ms) +✔ authorize: unknown channel, no info (0.170099ms) +✔ authorize: thread of listed parent (0.207444ms) +✔ authorize: thread of unlisted parent (0.174588ms) +✔ authorize: text channel that is not a thread and not listed (0.146914ms) +✔ authorize: unlisted user (2.273145ms) +✔ authorize: no author (0.330478ms) +✔ authorize: bot author (listed id, bot flag) (0.15287ms) +✔ authorize: system author (0.134099ms) +✔ authorize: the bot itself (0.094939ms) +✔ authorize: webhook (0.101119ms) +✔ authorize: mention channel without mention (0.156474ms) +✔ authorize: mention channel with bot mention (0.153772ms) +✔ authorize: mention channel with @everyone only (0.108938ms) +✔ authorize: mention channel mentioning someone else (0.093841ms) +✔ authorize: mention channel, content says @bot but mentions empty (3.283104ms) +✔ authorize: private thread under mention channel, mentioned (0.142484ms) +✔ authorize: private thread under mention channel, not mentioned (0.081361ms) +✔ authorize: thread in another guild per channel info (0.072228ms) +✔ authorize: not an object (0.369775ms) +✔ authorize: no id (0.096019ms) +✔ authorize: oversize content is accepted and flagged (0.080047ms) +✔ authorize: exactly the limit is not oversize (0.064125ms) +✔ authorize: a user's channel allowlist drops them outside it, threads count as the parent, others are unaffected (0.537813ms) +✔ authorize: order puts wrong guild before user, and user before channel (no channel lookup for strangers) (0.166175ms) +✔ binding: a complete binding validates and is frozen (2.683269ms) +✔ binding: unknown key, missing field, wrong type refuse with exit 2 (1.858271ms) +✔ binding: empty allowlists refuse (0.698964ms) +✔ binding: a user's channel allowlist must be non-empty, listed and unique; absent means every listed channel (2.117376ms) +✔ reloadDiff: reloadable keys are summarised by id; every fixed key refuses with exit 2 (2.08131ms) +✔ binding: dmRecipient is optional, a snowflake, one of the listed users, and a fixed key (1.115265ms) +✔ binding: file must be 0600, regular, not a symlink (1.68097ms) +✔ binding: token file mode, symlink, emptiness and shape are checked; token never appears in errors (3.174229ms) +✔ cli: check refuses a non-0600 token file with exit 2 before any network use (117.285167ms) +✔ context files: absolute paths, traversal, symlinks and out-of-repo targets refuse; in-repo files resolve (1.909175ms) +✔ cli: check refuses a missing context file and a missing binding with exit 2; usage is exit 4 (377.041478ms) +✔ cli: reload validates the file first (exit 2), then needs a live owner (exit 1); usage is exit 4 (216.295324ms) +✔ cli: run refuses when STOP is present, before any network use (115.048351ms) +✔ binding: tools is optional, validated strictly, a fixed key for reload, and its roots are resolved against the data root (2.467178ms) +✔ binding: a git key is validated at load and reaches the extension whole, and only on a writable root (1.09963ms) +✔ delivery: an accepted message is in the inbox before the turn, the reply is chunked with one nonce per chunk, and the turn record is write-once (21.316467ms) +✔ delivery: refused and unknown outcomes are journaled; a later chunk is not sent after a failure (33.272973ms) +✔ delivery: restart with an unknown entry re-sends the same nonce once and reconciles before accepting traffic (1.688757ms) +✔ delivery: an unknown entry older than the dedupe window is marked refused, not re-sent; a still-unknown one refuses start (1.275857ms) +✔ delivery: repeated unknown reconciliations never refresh the dedupe window; the original intent time decides (1.39992ms) +✔ turn: a failed engine turn posts the fixed line, never model output, and writes a failed record (2.560595ms) +✔ turn: a second message during a turn is held by the engine, both get their own reply and record (36.07126ms) +✔ turn: a thread under a listed channel is answered in the thread; an unknown thread is looked up once (4.736815ms) +✔ drop: an unlisted user gets silence and one drop line; no inbox entry, no REST call, no engine call (3.359329ms) +✔ drop: an oversize message is accepted into the inbox, answered with the fixed line and journaled as a drop (3.802886ms) +✔ restart: an inbox with three ids and a replay of the same three produces zero turns (46.352161ms) +✔ stop: STOP present refuses start; STOP written while running refuses new turns and the current one finishes (33.3766ms) +✔ ceiling: the ceiling plus one is refused and journaled; one fixed line per UTC day; a new day accepts again (7.460856ms) +✔ ceiling: a burst arriving while turns are still running cannot queue past the ceiling (3.778733ms) +✔ ceiling: a turn interrupted by a crash still counts after restart; admissions are durable (3.070541ms) +✔ ceiling: the daily notice survives a same-day restart; one delivery attempt in total, even when the first attempt crashed mid-flight (6.080508ms) +✔ duplicate: the same event delivered twice while the thread lookup is held yields one prompt, one admission and one reply (2.213655ms) +✔ journal: no token-shaped string and no model output on the drop path reaches disk (0.63942ms) +✔ receipt: an admitted message gets one eyes reaction on the inbound message; drops and refusals get none; a failed reaction is recorded and does not fail the turn (1.918608ms) +✔ receipt: Discord refusing the reaction leaves the turn intact and records ok false (2.440224ms) +✔ reload: a new user is silent before and answered after; a removed channel goes silent; a lower ceiling applies at once (5.739841ms) +✔ reload: a fixed key refuses with exit 2 and the old binding stays in force (1.712085ms) +✔ tools: with a tools binding the turn record lists every read and its outcome; without one the field is null (4.645544ms) +✔ context: the Discord block names the server, channels and modes, and states the rules from Q15 and Q16 (3.595148ms) +✔ context: with tools the block names the roots, keeps file content as data, and says to state refusals plainly (0.816121ms) +✔ context: a writable root adds the write rules and says a write is real only once Jason commits (3.90803ms) +✔ context: the envelope is one bracketed line then the text; names cannot break the line (1.357139ms) +✔ context: a git root swaps the terminal-commit line for the git verbs, and a vault root adds the id protocol (2.524516ms) +✔ context: assembleContext concatenates files in launcher format and appends the block; sha256 is stable (1.16571ms) +✔ context: splitReply keeps paragraphs together under the limit and splits long ones at lines, spaces, then hard (0.69284ms) +✔ engine: buildPiArgs carries the fixed flags, engine settings, session dir and prompt file (2.867992ms) +✔ engine: with tools, buildPiArgs turns pi's own tools off, loads the extension explicitly and allowlists exactly our three (0.456893ms) +✔ engine: a run with tool turns settles once, on the answer, with every tool call in the result (53.731313ms) +✔ engine: a run that ends on a tool-only turn fails the prompt as empty; a retried run settles on the real end (55.72278ms) +✔ engine: one prompt, one turn, text and usage come back (51.932374ms) +✔ engine: a prompt while streaming is held until pi settles, then sent as its own run, and answered in order (345.279587ms) +✔ engine: a held prompt that times out before pi settles fails on its own and is never sent (244.214588ms) +✔ engine: timeout sends abort and fails only that turn; the process stays (105.437654ms) +✔ engine: tool events from a run that outlived its timeout never land in the next prompt's record (224.117287ms) +✔ engine: a prompt after a turn that timed out before its agent_start waits for pi to settle instead of being refused (126.738318ms) +✔ engine: when pi has not started a timed-out turn by the end of the abort grace, the engine stops pi and fails held prompts (212.59562ms) +✔ engine: a timed-out turn pi starts only after the grace never answers a later prompt (615.157126ms) +✔ engine: late events of a run past its grace, before pi exits, answer nothing and nothing more is sent (early prompt response) (1.351493ms) +✔ engine: late events of a run past its grace, before pi exits, answer nothing and nothing more is sent (late prompt response) (0.556148ms) +✔ engine: a timed-out run pi did start outlives the grace; the next prompt goes out when it ends (427.018428ms) +✔ engine: a malformed JSONL line fails the turn, not the process (25.43066ms) +✔ engine: a turn that ends in error rejects with the error code; process exit fails pending turns (46.951295ms) +✔ gateway: hello -> identify with intents, ready, heartbeat with jitter, ack (2.69792ms) +✔ gateway: missed ack closes the socket and resumes with the last sequence (1.869035ms) +✔ gateway: op 7 reconnect resumes; op 9 non-resumable re-identifies (0.533233ms) +✔ gateway: op 9 resumable resumes (0.383641ms) +✔ gateway: close 4014 is fatal, reports the missing intent, never reconnects (1.340036ms) +✔ gateway: 4004 and 4013 are fatal too; 1006 reconnects with identify when no session (0.636877ms) +✔ gateway: close() is final and unparseable frames are ignored (0.462875ms) +✔ git: config validation is strict, needs write: true, a work tree and a private token file (160.392307ms) +✔ git: the child environment drops every host git config, names one helper, and carries the token path only for origin (59.185612ms) +✔ git: status reports the branch, ahead/behind and changed paths, and refuses off the named branch or mid-merge (99.969922ms) +✔ git: parseStatus reads porcelain v2 including renames and conflicts (0.307647ms) +✔ git: a commit stages exactly the named files, carries the seat author and the requester trailer, and pushes at once (80.090709ms) +✔ git: commit refusals: message, paths, requester, nothing to commit, and an index that already holds other work (126.0413ms) +✔ git: a commit whose push fails is still a commit, says so, and the next commit's push carries both (D6) (109.954892ms) +✔ git: pull is fast-forward only; a diverged origin or dirty local files refuse with nothing merged (167.839313ms) +✔ git: push pushes the named branch only and reports up to date (53.632654ms) +✔ git: no token value or token path ever reaches a git argument list; outputs are masked and capped (67.062539ms) +✔ git: the credential helper answers get over https from a private file and nothing else (175.014557ms) +✔ git: the vault protocol validates before a commit, honours another owner's lock, reserves ids, and locks around writes (259.931632ms) +✔ lock: the claim is exclusive; a second start against a live owner refuses (4.113238ms) +✔ lock: a stale lock (dead owner, reused pid, or record without start) refuses run and is never signaled; only unlock clears it (5.474769ms) +✔ lock: an incomplete claim (directory without owner record) is busy and refuses run; unlock clears it (1.085455ms) +✔ lock: an owner record that exists but cannot be read is invalid: never signaled, never removed, never claimed over (16.807399ms) +✔ lock: legacy upgrade; a live connector holding a {pid, start} record is unknown, unlock refuses and nothing changes; after it exits, unlock clears it (65.916029ms) +✔ 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 (344.449758ms) +✔ lock: identity syntax; only canonical unsigned decimal start ticks and lowercase boot uuids are identities (0.348967ms) +✔ lock: a process whose start marker or boot id cannot be read refuses to claim (0.431166ms) +✔ lock: a live pid whose identity cannot be read right now is unknown: never signaled, never removed, never claimed over (0.868604ms) +✔ lock: four processes racing for the same binding; exactly one claims it and the others refuse (44.707965ms) +✔ lock: stale handoff; concurrent starts over a stale lock all refuse, nothing reclaims, one unlock then exactly one live owner (130.97353ms) +✔ lock: four-party schedule; claims landing inside an unlock's gap never survive, one unlock leaves no owner and no residue (63.553437ms) +✔ notices: a kind is recorded per UTC day and found again (0.634182ms) +✔ notify: the DM channel opens once, every send carries the nonce, and only the message id comes back (5.771808ms) +✔ notify: refusals and unknowns surface as RestOutcome without ids; a refused send reopens the channel next time (2.633108ms) +✔ notify: no dmRecipient, a non-0600 binding or token, or a missing binding refuse before any network use (1.727567ms) +✔ recover: nothing to do is clean; a lock whose owner is gone or that has no record is cleared and STOP ends up absent (43.414226ms) +✔ recover: an operator STOP refuses with exit 3 and is never removed, whatever the lock says (36.289292ms) +✔ recover: a brake written during the unlock wins; STOP stays with both lines and the start is refused (44.93181ms) +✔ recover: a held binding refuses with exit 3 and writes no STOP: live owner, alive pid without verifiable identity, unreadable record (78.616995ms) +✔ cli: recover exits 0 when ready, 3 behind a brake or a held binding, and run's own STOP refusal is 3 (654.649528ms) +✔ rest: createMessage sends nonce, enforce_nonce, empty allowed_mentions and a soft reply reference (2.318177ms) +✔ rest: 429 waits retry_after and retries; 4xx is refused; 5xx and socket errors are unknown (2.7292ms) +✔ rest: content and nonce limits are enforced locally; typing never throws (1.019023ms) +✔ rest: react PUTs the encoded emoji on the inbound message for @me; 2xx is true, anything else is false and never throws (1.822671ms) +✔ rest: createDm posts the recipient and resolves the channel id; 4xx refused, 5xx unknown, bad id never sent (2.16405ms) +✔ setspark config: a bare https or loopback origin, a private key file, a principal (6.461151ms) +✔ setspark config: reaches the tools config and the binding as a fixed key (3.264288ms) +✔ setspark config: the binding's key survives resolveToolRoots and the engine's JSON hand-off to the extension (1.646026ms) +✔ setspark config: approvers come from the binding's users, never from the binding's setspark key (2.251922ms) +✔ setspark verbs: required_approvers go out as discord ids from names and come back as names (33.26389ms) +✔ setspark verbs: no Discord user id reaches tool text, whatever shape the service returns it in (10.908174ms) +✔ setspark contract: a decision made with names opens a request the connector accepts; names stored by an old record still refuse (7.971595ms) +✔ setspark keys: read per call, one printable token per file, rotation without a restart (5.757372ms) +✔ setspark idempotency keys: principal, turn id, call index; connector keys name a step (6.206062ms) +✔ setspark http core: json in and out, bearer header, idempotency header, fixed user agent, no key anywhere else (11.202128ms) +✔ setspark http core: error bodies become fixed refusals with code and the 409 fields; server text is data, cut (1013.46248ms) +✔ setspark verbs: a setspark key enables the eight verbs and no counters (0.431636ms) +✔ setspark verbs: writes carry the turn's key and the asserted requester, reads carry no key, and the api key never appears in text or details (5.826749ms) +✔ setspark verbs: no turn refuses every write before any request; bad arguments refuse before any request; reads still work (2.37378ms) +✔ setspark verbs: renderRecord caps long output and hides the accepted snapshot (0.201864ms) +✔ setspark api: bind, add_approval (button and reply) and get use integer request ids and the connector's keys (2.484639ms) +✔ tools: config refuses a missing, symlinked, dotted, non-directory or duplicate root and bad limits (4.284492ms) +✔ tools: every escape is refused with a fixed reason and nothing outside the root is read (3.842823ms) +✔ tools: happy paths list, read a window, and search case-insensitively; dotfiles and symlinks never appear (9.336853ms) +✔ tools: the tool set renders text for the model, records details for the journal, and enforces the per-run budget (2.946598ms) +✔ tools: listing and search caps hold (15.272918ms) +✔ tools: credential shapes are caught; ordinary prose and ids are not (0.876088ms) +✔ 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 (14.495331ms) +✔ tools: an unreadable file under the root is skipped by search and refused by read (1.811072ms) +✔ tools: config accepts write: true only as a boolean, and enables the write tools only then (1.434261ms) +✔ tools: every write outside the fence is refused before any byte lands, and no temp file remains (4.796861ms) +✔ tools: write_file leaves the exact bytes, edit_file replaces one exact match, and the set renders the change as uncommitted (2.933489ms) +✔ tools: a target that changed between the check and the rename is refused and the temp file is removed (3.509719ms) +✔ web: config takes an https or loopback-http SearXNG base url and a bounded fetch cap (3.647114ms) +✔ web: address rules refuse every private, loopback, link-local, mapped and multicast form (2.47267ms) +✔ web: web_fetch refuses bad urls, private hosts, rebinding names, non-https redirects, too many hops, error status, non-text bodies, and times out (1039.582548ms) +✔ 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.443289ms) +✔ web: html to text drops scripts, styles and comments, decodes entities and keeps block breaks (0.308404ms) +✔ 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.850135ms) +✔ 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.433448ms) +ℹ tests 227 +ℹ suites 0 +ℹ pass 227 +ℹ fail 0 +ℹ cancelled 0 +ℹ skipped 0 +ℹ todo 0 +ℹ duration_ms 2755.78809 diff --git a/agents/filbert/work/slice1-s4-review/r2-probe.txt b/agents/filbert/work/slice1-s4-review/r2-probe.txt new file mode 100644 index 00000000..4a83286e --- /dev/null +++ b/agents/filbert/work/slice1-s4-review/r2-probe.txt @@ -0,0 +1,8 @@ +T1 sent after open ["a"] torn copies 1 logs 2 +T2 reopen ["a","b"] +T3 3: notify journal line 2 is malformed: /j-OP2V6r/notify/demo/sent.jsonl | unchanged true | torn copies 0 +T4 3: notify journal must be a regular file, mode 0600, owned by this user: /j-ohiVsv/notify/demo/sent.jsonl | torn copies 0 +T5 3: notify journal directory must be mode 0700 and owned by this user: /j-h6fq3N/notify/demo +T6 copies ["torn-20261009T010000000Z-1.bin","torn-20261009T010000000Z-2.bin","torn-20261009T010000000Z-3.bin","torn-20261009T010000000Z.bin"] journal bytes 0 sent 0 +T7 EACCES: EACCES: permission denied, open '/j-famMXt/notify/demo/torn-20261009T010503790Z.bin' | journal "{\"torn" +T8 3: notify journal line 2 is malformed: /j-Pv0zPl/notify/demo/sent.jsonl diff --git a/agents/filbert/work/slice1-s4-review/r2-sendclosed.txt b/agents/filbert/work/slice1-s4-review/r2-sendclosed.txt new file mode 100644 index 00000000..b7c956b3 --- /dev/null +++ b/agents/filbert/work/slice1-s4-review/r2-sendclosed.txt @@ -0,0 +1,19 @@ +connected false exitCode 0 +send returned without throwing +process exit code 1 +node:events:505 + throw er; // Unhandled 'error' event + ^ + +Error [ERR_IPC_CHANNEL_CLOSED]: Channel closed + at target.send (node:internal/child_process:778:16) + at file:///home/jwoltje/filbert-scratch/r39b/probe/sendclosed.mjs:10:9 + at process.processTicksAndRejections (node:internal/process/task_queues:104:5) +Emitted 'error' event on ChildProcess instance at: + at node:internal/child_process:782:35 + at process.processTicksAndRejections (node:internal/process/task_queues:85:11) { + code: 'ERR_IPC_CHANNEL_CLOSED' +} + +Node.js v26.8.1 +shell exit 1 diff --git a/agents/filbert/work/slice1-s4-review/r2-w0-host.txt b/agents/filbert/work/slice1-s4-review/r2-w0-host.txt new file mode 100644 index 00000000..83087d13 --- /dev/null +++ b/agents/filbert/work/slice1-s4-review/r2-w0-host.txt @@ -0,0 +1,32 @@ +✔ the host boots the broker, binds a launch in process, and the notifier DMs a blocking decision exactly once (873.048242ms) +✖ a notifier that dies takes the host down with exit 1, so the unit restarts the pair (60005.00757ms) +✔ a notifier that refuses stops the broker and the host refuses with exit 3 (185.620796ms) +✔ watchChildren reports a child that died before it was called, and one that dies later (20.748664ms) +✔ bus stop refuses to signal a live pid that is not a bus host (202.751381ms) +✔ bus start refuses with exit 3 and the code when the broker refuses to boot; bus status names the lock (257.772528ms) +✔ bus start refuses with exit 3 without a notifier config (105.611141ms) +✔ bus start runs until bus stop; status reports it while it runs (673.656974ms) +✔ bus-service.sh renders the unit and installs it into a given directory (27.783457ms) + +Interrupted while running: + +⚠ packages/cli/tests/host.test.mjs (packages/cli/tests/host.test.mjs:1:1) +✖ packages/cli/tests/host.test.mjs (239968.123988ms) +ℹ tests 10 +ℹ suites 0 +ℹ pass 8 +ℹ fail 0 +ℹ cancelled 2 +ℹ skipped 0 +ℹ todo 0 +ℹ duration_ms 239973.857677 + +✖ failing tests: + +test at packages/cli/tests/host.test.mjs:102:1 +✖ a notifier that dies takes the host down with exit 1, so the unit restarts the pair (60005.00757ms) + 'test timed out after 60000ms' + +test at packages/cli/tests/host.test.mjs:1:1 +✖ packages/cli/tests/host.test.mjs (239968.123988ms) + 'Promise resolution is still pending but the event loop has already resolved' diff --git a/agents/filbert/work/slice1-s4-review/review-r2.md b/agents/filbert/work/slice1-s4-review/review-r2.md new file mode 100644 index 00000000..c99f8ea7 --- /dev/null +++ b/agents/filbert/work/slice1-s4-review/review-r2.md @@ -0,0 +1,247 @@ +# Slice 1 S4, row 39, round 2 review (Filbert) + +Issue #1521, request comment 26854, queue rev 191, pushed at `26bd829c`. +Packet: `agents/rocko/work/slice1-s4/` (`packet-manifest.sha256` +`fef8a758d0c75c380294591a59c865c9ecc8c8e634b0e11b012a3dd481c86ec4`, +44 OK). Candidate: manifest sha256 +`e858504e54d5b06581ec8b7168b82cd4895491c980aae1c62bd8a8be5e4f2f17`, +29 files, over `b9b6cf00` with `build.patch` (sha256 +`b52f7d6800538cdaf9df29300ad37672d00552e7086f496606722df3967e88f8`). +Scope: lead decision 71. Round 1: `review-r1.md` here. + +Verdict: **approve.** B1 is fixed the way decision 71 lays it out, and +F1, R2, D1, the startup race, J3 and F3 are fixed and tested. So are +the round 1 test gaps M22 to M24, M26 and M29. F2 and J4 stay +follow-ups, as decision 71 says. The notes below don't block. + +## Method + +- Detached worktrees at `b9b6cf00` under `~/filbert-scratch/r39b/`: base, + candidate, and a third candidate copy for mutants. In both candidate + trees I ran `git apply build.patch` and then `sha256sum -c`: 29 OK. + After the mutant run the mutant tree checks clean again. +- No code differs between `b9b6cf00` and `26bd829c` outside `agents/` + and `docs/`. +- `gate-r2.sh` runs the suites one at a time in both trees, tees each + output and logs the load average. As in round 1, `DOCKER_HOST` points + at a socket that doesn't exist, so no Docker case runs. I touched no + Docker network. +- `probe-r2.mjs` exercises `openJournal` directly (T1 to T8). +- `sendclosed.mjs` checks what `ChildProcess.send` does on a closed + channel. +- `mutants-r2.sh` runs my round 1 mutants M1 to M34, Rocko's round 2 set + (R5 to F3, from `mutants-r2.sh` in the packet), and N1 to N6, which + target the round 2 code. Each run is the cli and discord node tests, + 227 tests, under `--test-timeout=90000` and an outer `timeout 900`. A + run counts as killed on any failure, any cancellation, or the outer + timeout. My round 1 harness counted only failures, which is why M19 + showed SURVIVED in Rocko's run of it. +- Node 24 run: `node:24` with no network, the tree mounted read-only, and + the host uid. + +## Suites + +| Suite | Candidate | Base | +|---|---|---| +| node cli (Node 26.8.1) | 49/49 | n/a | +| node cli + discord (Node 24.21.0) | 227/227 | n/a | +| node bus | 58/58 | 58/58 | +| node business | 60/60 | 60/60 | +| node discord | 178/178 | 173/173 | +| test-auth | 15/15 | 15/15 | +| test-conductor | 17/17 | 17/17 | +| test-config | 24/24 | 24/24 | +| test-discord | 66/66 | 64/64 | +| test-extension-package | 18/18 | 18/18 | +| test-foundation | 44/44 | 44/44 | +| test-queue | 27/27 | 27/27 | +| test-release | 4/4, Docker cases skipped | 4/4, same | +| test-task | 26 pass, 2 fail | 26 pass, 2 fail | + +Base and candidate fail the same two test-task cases, "user recall run +succeeds" and "recalled user name", as in round 1 and row 38. That +matches Rocko's 26/2. I saw no 429 and no address-pool error. + +## Sage's three questions + +### 1. M19: confirmed, it never passes + +M19 removes decide's no-TTY check. The test's stdin is a PassThrough +with `isTTY` false that never ends, so readline waits forever. Run alone +with `--test-timeout=60000` (`r2-m19-timeout.txt`): + +``` +ℹ pass 11 +ℹ fail 0 +ℹ cancelled 1 +✖ decide refuses without a terminal or --yes, on an unknown option and on a short reference (60001.70155ms) + 'test timed out after 60000ms' +``` + +The test is cancelled, never passed. My round 1 harness logged +SURVIVED because it read only the `fail` count. In the round 2 harness +M19 is killed (fail 0, cancelled 1). Rocko's reading is right. + +### 2. M13's host: closed on every path + +M13 drops the `dmRecipient === null` refusal in `notify.mjs`, so the +refusal test's `startHost` resolves a running host. Rocko's hook: + +```js +t.after(async () => (await started.catch(() => null))?.close(0)); +``` + +- **The refusal holds** (the normal case). `started` rejects, the hook + gets null and does nothing. Nothing is left to close: `startHost` + stopped the notifier and the broker before it threw. +- **The refusal regresses** (M13). `started` resolves and the hook + closes the host. +- **A different rejection.** Same as the first path: every throw in + `startHost` after a fork ends the child first. + +Under M13 the test fails (fail 1), host.test ends in about 3 s, and no +process is left over (`r2-m13-host.txt`). + +One gap of the same kind, in a different test. Under a mutant that +never wires `watchChildren` (W0, `watchChildren({}, …)`), "a notifier +that dies takes the host down with exit 1" waits on `host.done` +forever. That test has no `t.after`, so the broker stays up and the file +ran until my 240 s outer timeout (`r2-w0-host.txt`). The test still +fails, so the mutant is caught, but slowly. `t.after(() => host.close(0))` +would fix it. This is not blocking. + +### 3. Parse first, then repair: that's the right order + +`openJournal` checks the directory and the file, parses every complete +line, and only then copies and truncates a torn tail. Decision 71 fixes +the two repair steps and the exit-3 refusal, but not their order. This +order is the better one: + +- A refusal changes nothing on disk. The human who fixes the bad line + sees the file as it was, torn tail included. +- The next open after the fix repairs the tail as usual. +- Repairing first would write a torn copy and truncate a journal that is + about to be refused anyway. A human would then be looking at a changed + file. + +Probe T3 (a bad middle line plus a torn tail): exit 3 "notify journal +line 2 is malformed", file unchanged, no torn copy. Probe T4 (mode 0644) +also refuses before any repair. + +## Decision 71, item by item + +- **B1, torn tail.** `copyTorn` writes the torn bytes to + `torn-.bin`: `O_EXCL`, 0600, the file fsynced, then the + directory. A name that exists gets `-1`, `-2`, and so on. Then + `ftruncateSync` to the last newline and `fsyncSync`, and each step is + logged. Probes: + - T2 is the round 1 B1 sequence: torn tail, append, reopen. It now + reopens with `["a","b"]`. + - T6 replays a crash between the steps three times. That gives four + copies, none overwritten, and an empty journal. + - Mutants R6, R6b, R6c, N1 (no `O_EXCL`) and N6 (truncate one byte + short) are killed. +- **F1.** An existing directory with any group or other bit refuses with + exit 3. Mutant F1 is killed. +- **R2.** A test asserts different DM nonces for two decisions. Round + 1's M8 (constant nonce), which survived, is now killed. +- **D1.** `digestNonce(business, day)` is `"dg"` + 23 hex characters of + sha256 of business and day. It stays within Discord's 25-character + limit. Mutant D1 is killed. +- **Startup race.** `watchChildren` checks `exitCode` and `signalCode` + before it attaches `once("exit")`. It runs after `queue` exists. + Mutant RACE is killed. +- **J3.** The journal opens with `O_NOFOLLOW`, `ELOOP` refuses with exit + 3, and `fstat` checks the opened file. `append` reopens with + `O_NOFOLLOW` and no `O_CREAT`. Mutant J3 is killed. +- **F3.** `network-online.target` is gone from the unit. Mutant F3 is + killed. +- **M22 to M24, M26, M29.** Each now has a test and is killed, one + failure each. + +## Notes (not blocking) + +1. **Unguarded `broker.send` in `startHost`'s notifier-failure path** + (`host.mjs:144` and `:150`). `close()` checks `broker.connected`, but + these two lines don't. `sendclosed.mjs` shows what happens if the + broker is already gone: + - `send` returns. + - An unhandled `ERR_IPC_CHANNEL_CLOSED` follows on the next tick. + - The process exits 1, even with `exitCode` set to 3. + + So a broker that dies while the notifier starts turns a refusal (exit + 3, the unit stays down) into exit 1 (the unit restarts). That is a + narrow window, and a restart is not wrong there. `if + (broker.connected)` on both lines would match `close()`. + `bindLaunch` has the same exposure, which is S6's to settle. +2. **The "notifier that dies" test has no `t.after`** (see question 2). +3. **M28 in BUILD.md.** Rocko's table lists M28 as killed. In my run it + survived three times out of three: the combined run plus two reruns, + 227/227 pass each (`r2-mut-M28-rerun1.txt`, `r2-mut-M28-rerun2.txt`). + No test covers the "a bus host already runs" refusal. Rocko's single + failure was probably a flaky test. Round 1 rated M28 near-equivalent, + because `writer.lock` refuses a second broker, and I still do. Only + the packet's record is wrong. +4. **N2 survives.** It swaps the directory's `lstatSync` for `statSync`, + so a symlinked notify directory is accepted. Probe T5 shows the + candidate refuses one. No test does, though. +5. **N4 survives.** It drops `O_NOFOLLOW` from `append`, so a symlink + swapped in after the open would be followed. Like J3 in round 1, this + sits behind the 0700 directory and records the boundary only. +6. **N5 survives.** It makes `watchChildren`'s early check ignore + `signalCode`. The early-death test exits with code 7, and no test + kills a child with a signal before the watch. +7. **N3 survives.** It drops the directory fsync in `copyTorn`. That + can't be tested in-process. Equivalent for the suite. +8. **T1, a confirmed line that lost only its newline.** It is treated as + torn and dropped, so the decision is DM'd again with the same nonce. + Decision 71 accepts this: a duplicate costs less than a miss, and the + nonce folds a retry inside Discord's dedupe window. +9. **T5 wording.** A symlinked directory refuses with "must be mode 0700 + and owned by this user". That is true, but it doesn't say the + directory is a link. +10. **T7, a read-only journal directory.** `copyTorn` throws a raw + `EACCES`, not a `CliError`. The notifier still refuses to start, the + host exits 3, and the journal is untouched. Fail-closed, with a less + clear message. +11. **Live run.** Rocko documents that an existing notify directory + looser than 0700 needs `chmod 700` before the first start. Round 1 + note 6 (`dmRecipient` is a FIXED_KEY) still applies to the binding + edit. +12. F2 and J4 stay follow-ups (decision 71). Round 1 notes 5 and 8 still + apply. + +## Mutants + +| Mutant | Result | +|---|---| +| M1 to M4, M7 to M17, M20 to M26, M29 to M32, M34 | killed | +| M8 constant DM nonce | killed (R2 test; survived in round 1) | +| M19 decide drops the no-TTY check | killed (cancelled 1; see question 1) | +| M22, M23, M24, M26, M29 | killed (new tests; survived in round 1) | +| M5, M6, M27 | not applied; round 2 rewrote those lines | +| M18, M33 | survived; equivalent and near-equivalent, as in round 1 | +| M28 | survived three times; near-equivalent, as in round 1 (note 3) | +| R5, R6, R6b, R6c, F1, J3, D1, RACE, F3 (Rocko) | killed | +| N1 `copyTorn` without `O_EXCL` | killed | +| N2 directory `statSync`, not `lstatSync` | **survived** (note 4) | +| N3 no directory fsync in `copyTorn` | survived; not testable in-process (note 7) | +| N4 `append` without `O_NOFOLLOW` | **survived** (note 5) | +| N5 early check ignores `signalCode` | **survived** (note 6) | +| N6 truncate to `end - 1` | killed | + +46 applied: 39 killed, 7 survived. + +## Files + +- `mutants-r2.sh`, `gate-r2.sh`, `probe-r2.mjs`, `sendclosed.mjs` (the + round 1 scripts stay as they were) +- Output: + - `r2-probe.txt` + - `r2-sendclosed.txt` + - `r2-mut-summary.txt` + - `r2-m19-timeout.txt`, `r2-m13-host.txt`, `r2-w0-host.txt` + - `r2-mut-M28-rerun1.txt`, `r2-mut-M28-rerun2.txt` + - `r2-node24.txt` + - `r2-gate-summary.txt` + - `r2-cand-*.txt` and `r2-base-*.txt` (each suite, teed) diff --git a/agents/filbert/work/slice1-s4-review/sendclosed.mjs b/agents/filbert/work/slice1-s4-review/sendclosed.mjs new file mode 100644 index 00000000..43dde704 --- /dev/null +++ b/agents/filbert/work/slice1-s4-review/sendclosed.mjs @@ -0,0 +1,12 @@ +// Filbert, row 39 r2: what ChildProcess.send() does on a child that has exited, as +// startHost's notifier-failure path would call broker.send({op: "close"}) after the broker died. +import { fork } from "node:child_process"; +import { once } from "node:events"; +import { fileURLToPath } from "node:url"; +if (process.argv[2] === "child") process.exit(0); +const c = fork(fileURLToPath(import.meta.url), ["child"], { stdio: ["ignore", "inherit", "inherit", "ipc"] }); +await once(c, "exit"); +console.log("connected", c.connected, "exitCode", c.exitCode); +try { c.send({ op: "close" }); console.log("send returned without throwing"); } catch (e) { console.log("send threw", e.code); } +process.on("exit", (code) => console.log("process exit code", code)); +process.exitCode = 3; // what the CLI sets for a refusal