diff --git a/agents/filbert/work/slice1-s4-review/gate.sh b/agents/filbert/work/slice1-s4-review/gate.sh new file mode 100755 index 00000000..588eae7b --- /dev/null +++ b/agents/filbert/work/slice1-s4-review/gate.sh @@ -0,0 +1,11 @@ +#!/usr/bin/env bash +# Sequential gate; $1 = tree, $2 = out prefix +T="$1"; P="$2"; O=~/filbert-scratch/r39/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-r39.sock timeout 900 bash "$s" > "$O/$P-suite-$n.txt" 2>&1; echo "$P suite-$n exit $?" +done diff --git a/agents/filbert/work/slice1-s4-review/mutants.sh b/agents/filbert/work/slice1-s4-review/mutants.sh new file mode 100644 index 00000000..dfd2650b --- /dev/null +++ b/agents/filbert/work/slice1-s4-review/mutants.sh @@ -0,0 +1,54 @@ +#!/usr/bin/env bash +# Row 39 mutants: one perl substitution each, restored after its run. +# 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 node --test 'packages/cli/tests/*.test.mjs' 'packages/discord/tests/*.test.mjs' > "$O/mut-$id.txt" 2>&1 + local f; f=$(grep -E '^ℹ fail ' "$O/mut-$id.txt" | awk '{print $3}') + if [ "${f:-?}" = 0 ]; then echo "$id SURVIVED"; else echo "$id killed ($f)"; 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)/' diff --git a/agents/filbert/work/slice1-s4-review/probe.mjs b/agents/filbert/work/slice1-s4-review/probe.mjs new file mode 100644 index 00000000..d47f318d --- /dev/null +++ b/agents/filbert/work/slice1-s4-review/probe.mjs @@ -0,0 +1,125 @@ +// Row 39 S4 round 1 probes (Filbert). Run from the candidate tree: +// PROBE_REPO= node probe.mjs +import { appendFileSync, chmodSync, mkdirSync, mkdtempSync, readFileSync, rmSync, statSync, symlinkSync, writeFileSync } from "node:fs"; +import { homedir } from "node:os"; +import { join } from "node:path"; +import { pathToFileURL } from "node:url"; + +const R = process.env.PROBE_REPO; +const N = await import(pathToFileURL(join(R, "packages/cli/src/notifier.mjs"))); +const base = mkdtempSync(join(homedir(), "filbert-scratch/r39/probe-")); +let pass = 0; +let fail = 0; +const check = (id, ok, what) => { + if (ok) pass++; + else fail++; + console.log(`${ok ? "PASS" : "FAIL"} ${id} ${what}`); +}; +const refuses = (fn) => { + try { + fn(); + return null; + } catch (e) { + return e; + } +}; + +// J1: torn final line, then one confirmed send, then a restart. +{ + const f = join(base, "j1", "sent.jsonl"); + mkdirSync(join(base, "j1"), { mode: 0o700 }); + writeFileSync(f, `${JSON.stringify({ at: "x", kind: "dm", decision: "d1", outcome: "confirmed", messageId: "m1" })}\n{"at":"x","kind":"dm","dec`, { mode: 0o600 }); + const logs = []; + const j = N.openJournal(f, { log: (l) => logs.push(l) }); + check("J1a", logs.length === 1 && j.sent.has("d1"), "first open skips the torn line and logs it"); + j.append({ at: "y", kind: "dm", decision: "d2", outcome: "confirmed", messageId: "m2" }); + const e = refuses(() => N.openJournal(f)); + console.log(` J1 file after append: ${JSON.stringify(readFileSync(f, "utf8").split("\n")[1].slice(0, 80))}`); + check("J1b", e === null, `restart after one more send opens the journal (got ${e ? `exit ${e.exitCode}: ${e.message}` : "ok"})`); +} + +// J2: journal directory with a loose mode. +{ + const d = join(base, "j2"); + mkdirSync(d, { mode: 0o755 }); + chmodSync(d, 0o755); + const e = refuses(() => N.openJournal(join(d, "sent.jsonl"))); + console.log(` J2 dir mode ${(statSync(d).mode & 0o777).toString(8)}; open ${e ? `refused: ${e.message}` : "accepted"}`); +} + +// J3: sent.jsonl is a symlink to a 0600 file elsewhere. +{ + const d = join(base, "j3"); + mkdirSync(d, { mode: 0o700 }); + const target = join(base, "elsewhere.jsonl"); + writeFileSync(target, "", { mode: 0o600 }); + symlinkSync(target, join(d, "sent.jsonl")); + const e = refuses(() => N.openJournal(join(d, "sent.jsonl"))); + console.log(` J3 symlinked journal: ${e ? `refused: ${e.message}` : "accepted"}`); +} + +// J4: a confirmed line with a non-string decision or a missing day loads. +{ + const d = join(base, "j4"); + mkdirSync(d, { mode: 0o700 }); + const f = join(d, "sent.jsonl"); + writeFileSync(f, `${JSON.stringify({ kind: "digest", outcome: "confirmed" })}\n${JSON.stringify({ kind: "dm", outcome: "confirmed", decision: 7 })}\n`, { mode: 0o600 }); + const e = refuses(() => N.openJournal(f)); + console.log(` J4 loose-typed confirmed lines: ${e ? `refused: ${e.message}` : "accepted"}`); +} + +// D1: the digest nonce ignores the business. +check("D1", N.digestNonce("2026-10-08") === "dg2026-10-08", `digest nonce is ${N.digestNonce("2026-10-08")} for every business`); + +// D2: digest and DM content stay within 1..2000 chars over a sweep. +{ + let worst = 0; + let bad = 0; + const mk = (i, qlen, blocking) => ({ id: `${String(i).padStart(8, "0")}-aaaa-bbbb`, action: "release.push", question: "q".repeat(qlen), blocking, options: [{ key: "yes", text: "y".repeat(200) }, { key: "no", text: "n" }], recommendation: "no", raised_by_role: "coder", task_ref: "T-1" }); + for (let n = 0; n <= 60; n++) { + for (const qlen of [1, 50, 159, 160, 161, 400, 5000]) { + const inbox = Array.from({ length: n }, (_, i) => mk(i, qlen, i % 2 === 0)); + const c = N.digestContent("acme", "2026-10-08", inbox, (id) => id.startsWith("0")); + worst = Math.max(worst, c.length); + if (c.length < 1 || c.length > 2000) bad++; + if (n > 0 && !c.includes("mosaic inbox")) bad++; + for (const d of inbox.slice(0, 2)) { + const m = N.dmContent("acme", d); + worst = Math.max(worst, m.length); + if (m.length < 1 || m.length > 2000) bad++; + } + } + } + check("D2", bad === 0, `digest/DM length sweep (427 inboxes), longest ${worst}`); +} + +// D3: DST: 08:00 local is 13:00Z in summer and 14:00Z in winter. +check("D3", N.zoned(new Date("2026-07-01T13:00:00Z")).hour === 8 && N.zoned(new Date("2026-12-01T14:00:00Z")).hour === 8 && N.zoned(new Date("2026-11-01T13:30:00Z")).hour === 7, "zoned 08:00 in CDT and CST; 2026-11-01 13:30Z is 07 CST"); + +// D4: digest catch-up: a tick at 07:59 local sends nothing; at 08:00 sends one; second tick none. +{ + const d = join(base, "d4"); + let t = new Date("2026-12-01T13:59:00Z"); + const sent = []; + const n = N.createNotifier({ business: "acme", dataRoot: d, inbox: async () => [], direct: { send: async (m) => (sent.push(m), { messageId: `m${sent.length}` }) }, now: () => t }); + await n.tick(); + const a = sent.length; + t = new Date("2026-12-01T14:00:00Z"); + await n.tick(); + await n.tick(); + check("D4", a === 0 && sent.length === 1 && sent[0].nonce === "dg2026-12-01", `digest edge: before ${a}, after ${sent.length}`); +} + +// D5: a send that throws a non-RestOutcome (a bug) is journaled unknown and retried, not fatal. +{ + const d = join(base, "d5"); + let t = new Date("2026-12-01T10:00:00Z"); + let calls = 0; + const n = N.createNotifier({ business: "acme", dataRoot: d, inbox: async () => [{ id: "dec-1", blocking: true, action: "a", question: "q", options: [{ key: "y", text: "y" }], recommendation: "y", raised_by_role: "coder" }], direct: { send: async () => { calls++; throw new TypeError("boom"); } }, now: () => t }); + const r = await n.tick(); + const lines = readFileSync(join(d, "notify", "acme", "sent.jsonl"), "utf8").trim().split("\n").map((l) => JSON.parse(l)); + check("D5", r.failed === 2 && lines.every((l) => l.outcome === "unknown"), `TypeError from send: failed ${r.failed}, outcomes ${lines.map((l) => l.outcome).join(",")}`); +} + +console.log(`\n${pass} PASS, ${fail} FAIL`); +rmSync(base, { recursive: true, force: true }); diff --git a/agents/filbert/work/slice1-s4-review/r1-base-node-bus.txt b/agents/filbert/work/slice1-s4-review/r1-base-node-bus.txt new file mode 100644 index 00000000..52e8d875 --- /dev/null +++ b/agents/filbert/work/slice1-s4-review/r1-base-node-bus.txt @@ -0,0 +1,66 @@ +✔ launch identity is stamped, payload identity is refused and stale holder cannot send (345.522871ms) +✔ decision classes route from policy; gated resolution is human-only, choice and target must match (427.048359ms) +✔ claim exclusion, holder release, gated revoke and rerouting to a new holder are atomic (441.566977ms) +✔ launch events require a human CLI capability; generic emit cannot forge authority events (283.44889ms) +✔ within-role decisions close atomically and invalid options or blocking omissions refuse (446.692328ms) +✔ observer capabilities read human inbox but cannot mutate or forge launch identity (170.66971ms) +✔ task action subjects and linked decision trail are complete and ordered (368.979812ms) +✔ launch binding is durable and reconnecting requires the identical trusted record (185.070975ms) +✔ business isolation includes inherited object names and cross-business message references (171.821867ms) +✔ authority never transfers between action, run, target, unresolved or replaced role holder (225.672529ms) +✔ task projection uses schema current view, skipping earlier and equal-start polls (139.363239ms) +✔ revocation permanently bars the old run from reclaiming first, including after broker restart (261.35952ms) +✔ empty message references refuse before storage; refusal-evidence failure stays a typed error (213.211117ms) +✔ both arbiters require human resolution when their cross-role route is themselves (336.739209ms) +✔ S1 adapter takes resolved limits and refs, rejects mismatched instance, never mutates input (3.073206ms) +✔ only validated broker references load; returned data and exceptions cannot expose a known token (4.972891ms) +✔ bad file modes, symlinks, repository/data paths, malformed tokens and missing dates refuse (5.252125ms) +✔ expiry refuses use and env references never become client data (1.207147ms) +✔ S1 parsed service refs work, service mismatch refuses, Gitea rotation due is a warning state (1.715701ms) +✔ opaque tokens shorter than 16 characters refuse before use (0.472508ms) +✔ human proof binds CLI entry, process start and nonce; agents and incomplete ancestry refuse (3.165414ms) +✔ process reader gets own kernel identity without exposing environment values (1.862049ms) +✔ EACCES ancestor environments skip only markers; commands and registered launches still refuse (3.701848ms) +✔ real pid 1 remains inspectable when its environment is protected (0.969262ms) +✔ within-role sends cite an open gated launch decision without spending it or naming it in grants (370.136447ms) +✔ missing and foreign-business citations refuse and roll back message and grant (330.928876ms) +✔ cross-role sends still need a matching resolved decision and consume it once (326.636028ms) +✔ broker process binds trusted launches, offers reader capabilities, refuses human mutation, closes cleanly (367.828924ms) +✔ startup token refusal returns safe code without value or partial listening broker (43.387676ms) +✔ loaded fixture token is absent from socket replies and SQLite, including refusal evidence (328.064225ms) +✔ killed broker leaves an explicit stale lock; another process cannot silently reclaim it (227.709519ms) +✔ trusted host registers later launches; socket clients never have a registration verb (312.078161ms) +✔ runtime excludes declared project roots even when host supplies no repoRoots (46.464111ms) +✔ a refused launch binding leaves the broker and existing capabilities alive; bad protocol stops it (246.870912ms) +✔ v3b prototype refusals, views and append-only mutations (1947.449264ms) +✔ gated approval authorizes once, survives store reopen, and fresh approval works (428.208356ms) +✔ another run cannot consume an approval; a failed check leaves it usable (375.265364ms) +✔ two scheduled callers have exactly one grant and one consumed refusal (237.250553ms) +✔ failed commit rolls consumption back; cross-role consumes and within-role stays reusable (661.669101ms) +✔ class drift gated to cross-role refuses before consumption (358.584232ms) +✔ class drift cross-role to gated refuses before consumption (410.520227ms) +✔ class drift gated to within-role refuses before consumption (287.157909ms) +✔ class drift cross-role to within-role refuses before consumption (205.354648ms) +✔ class drift within-role to gated refuses before consumption (169.87182ms) +✔ class drift within-role to cross-role refuses before consumption (228.552142ms) +✔ message.send consumes approval and prevents a later send or authorize (281.866321ms) +✔ role.revoke consumes approval and prevents a later revoke or authorize (371.574913ms) +✔ creates private WAL store and excludes a second writer until explicit close (293.50566ms) +✔ rollback is atomic and schema metadata is checked against trusted DDL, not just itself (223.16126ms) +✔ existing empty database and symlink runtime directory refuse, never initialize over damage (305.591335ms) +✔ crash during a transaction recovers no partial event after explicit fixture-only lock removal (319.186738ms) +✔ writer refuses mixed at/read_at forms atomically, even through trusted SQL helpers (160.264015ms) +✔ async transactions refuse before invoking their function (146.885032ms) +✔ socket capability stamps launch identity; shared views use wire, no SQL client (322.665011ms) +✔ two wire claims serialize; a lost reply never automatically retries (230.919199ms) +✔ malformed, oversized and identity-forging envelopes refuse without echoing input (227.869134ms) +✔ client preserves UTF-8 when a response divides a multibyte character (12.163385ms) +✔ committed mutation followed by dropped reply reports unknown and is never retried (192.340835ms) +ℹ tests 58 +ℹ suites 0 +ℹ pass 58 +ℹ fail 0 +ℹ cancelled 0 +ℹ skipped 0 +ℹ todo 0 +ℹ duration_ms 4141.695422 diff --git a/agents/filbert/work/slice1-s4-review/r1-base-node-business.txt b/agents/filbert/work/slice1-s4-review/r1-base-node-business.txt new file mode 100644 index 00000000..993f8ada --- /dev/null +++ b/agents/filbert/work/slice1-s4-review/r1-base-node-business.txt @@ -0,0 +1,68 @@ +✔ config directory and file path follow MOSAIC_CONFIG (2.121194ms) +✔ the fixture business validates and comes back frozen (6.452165ms) +✔ two instances may share a definition (2.134752ms) +✔ top-level refusals (5.199262ms) +✔ arbiters and projects (7.524685ms) +✔ role instances (3.757541ms) +✔ Vikunja bots (8.766374ms) +✔ a role without Vikunja takes no tracker block (2.142773ms) +✔ credential references match the definition's services (2.576937ms) +✔ launch (7.988747ms) +✔ loadBusiness: file checks (1.878581ms) +✔ loadBusiness: not a regular file (47.109419ms) +✔ loading writes nothing (1.228293ms) +✔ names that are Object.prototype properties don't count as declared (3.5501ms) +✔ the shipped example refuses as written and validates once filled in (0.969864ms) +✔ usage errors exit 4 (350.481125ms) +✔ validate: a good business exits 0 and prints instance digests (81.30678ms) +✔ validate: project files (502.453739ms) +✔ validate: missing files and a broken system config (314.663149ms) +✔ validate: credential reference problems exit 2 and name each one (105.671515ms) +✔ validate: a token file inside the repository is refused (91.773913ms) +✔ validate: role definitions come from MOSAIC_ROLES_DIR (319.588051ms) +✔ resolve: prints one instance's record (271.287856ms) +✔ resolve: refusals (576.865688ms) +✔ parse: exactly one of file or env, plus the service's date (3.132765ms) +✔ check: a good file has no problems (0.906848ms) +✔ check never opens the file: a write-only token passes (0.453124ms) +✔ check: file problems (1.182827ms) +✔ check: token files can't live in the repository or dataRoot, even through a linked directory (1.153042ms) +✔ check: dates and environment references (0.567197ms) +✔ path and load (3.146583ms) +✔ refusals (1.775462ms) +✔ systemVars flattens the validated config (2.734069ms) +✔ precedence: system, business, project, project role, agent (6.835112ms) +✔ limits narrow the definition and never widen it (3.133181ms) +✔ role.launch stays within-role only for the instance the launch block names (5.873857ms) +✔ limits.authority without role.launch leaves the launcher with no launch block (2.286532ms) +✔ limits.authority narrows cross-role actions too (1.172703ms) +✔ classify (1.436853ms) +✔ the record carries what the broker and launcher need (1.392019ms) +✔ digest: key order doesn't matter, any value change does (9.675767ms) +✔ refusals (2.094174ms) +✔ the four shipped version 2 roles load (4.860176ms) +✔ shipped role scopes match addendum B section 2 and the SR runbook (1.472529ms) +✔ shipped authority follows the note's table (0.794529ms) +✔ version 1 files keep loading with no authority (1.340498ms) +✔ the conductor policy isn't a role (0.308921ms) +✔ a missing role file is exit 4, a symbolic link too (0.480449ms) +✔ version 2 refusals (1.524426ms) +✔ authority: closed vocabulary, no gated-only action, no overlap (2.786759ms) +✔ credentials: Gitea scopes (0.933318ms) +✔ credentials: Vikunja scopes are a group-to-verbs map from the grantable list (1.156786ms) +✔ credentials: services (1.317671ms) +✔ contract: a non-empty regular Markdown file beside the role file (1.001296ms) +✔ every key names known layers and a merge rule (1.355683ms) +✔ unknown keys and wrong layers refuse (1.060639ms) +✔ types (2.698005ms) +✔ merge: defaults, then the most specific layer wins (0.462315ms) +✔ merge: limits only narrow, and provenance lists each source (0.53096ms) +✔ merge doesn't change its inputs (0.222347ms) +ℹ tests 60 +ℹ suites 0 +ℹ pass 60 +ℹ fail 0 +ℹ cancelled 0 +ℹ skipped 0 +ℹ todo 0 +ℹ duration_ms 2694.425501 diff --git a/agents/filbert/work/slice1-s4-review/r1-base-node-discord.txt b/agents/filbert/work/slice1-s4-review/r1-base-node-discord.txt new file mode 100644 index 00000000..9aa10273 --- /dev/null +++ b/agents/filbert/work/slice1-s4-review/r1-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.991866ms) +✔ approvals: the ledger is appended and folded into open requests with bind and approval states (1.853409ms) +✔ approvals: a reply approves only when it points at a request, says exactly approve, and comes from a listed approver once (0.768509ms) +✔ approvals: a button approves only on its own request message with the matching custom id (0.585816ms) +✔ approvals flow: a turn that opened a request posts the message with the button, records it, binds it, and both approvers approve (25.708904ms) +✔ approvals flow: a non-approver, a repeat, a wrong custom id and a service refusal each get their fixed line and a drop entry (7.727245ms) +✔ approvals flow: an invalid request from the model, a refused post, and no api client are recorded and post nothing (10.456405ms) +✔ approvals flow: start retries a bind and an approval left as unknown, under their original keys (3.946781ms) +✔ authorize: open channel, listed user (2.26399ms) +✔ authorize: wrong guild (0.211867ms) +✔ authorize: no guild (DM) (0.347613ms) +✔ authorize: unlisted channel (0.209452ms) +✔ authorize: unknown channel, no info (0.175509ms) +✔ authorize: thread of listed parent (0.205757ms) +✔ authorize: thread of unlisted parent (0.169478ms) +✔ authorize: text channel that is not a thread and not listed (0.162147ms) +✔ authorize: unlisted user (1.282016ms) +✔ authorize: no author (0.305478ms) +✔ authorize: bot author (listed id, bot flag) (0.137552ms) +✔ authorize: system author (0.124114ms) +✔ authorize: the bot itself (0.096306ms) +✔ authorize: webhook (0.156563ms) +✔ authorize: mention channel without mention (2.493763ms) +✔ authorize: mention channel with bot mention (5.400877ms) +✔ authorize: mention channel with @everyone only (0.357569ms) +✔ authorize: mention channel mentioning someone else (0.097817ms) +✔ authorize: mention channel, content says @bot but mentions empty (0.086567ms) +✔ authorize: private thread under mention channel, mentioned (0.107775ms) +✔ authorize: private thread under mention channel, not mentioned (0.072784ms) +✔ authorize: thread in another guild per channel info (0.083018ms) +✔ authorize: not an object (0.068265ms) +✔ authorize: no id (0.06516ms) +✔ authorize: oversize content is accepted and flagged (0.085947ms) +✔ authorize: exactly the limit is not oversize (0.066532ms) +✔ authorize: a user's channel allowlist drops them outside it, threads count as the parent, others are unaffected (0.543754ms) +✔ authorize: order puts wrong guild before user, and user before channel (no channel lookup for strangers) (0.178068ms) +✔ binding: a complete binding validates and is frozen (3.351194ms) +✔ binding: unknown key, missing field, wrong type refuse with exit 2 (1.721196ms) +✔ binding: empty allowlists refuse (0.430278ms) +✔ binding: a user's channel allowlist must be non-empty, listed and unique; absent means every listed channel (1.908215ms) +✔ reloadDiff: reloadable keys are summarised by id; every fixed key refuses with exit 2 (2.958372ms) +✔ binding: file must be 0600, regular, not a symlink (3.291355ms) +✔ binding: token file mode, symlink, emptiness and shape are checked; token never appears in errors (1.874695ms) +✔ cli: check refuses a non-0600 token file with exit 2 before any network use (114.845914ms) +✔ context files: absolute paths, traversal, symlinks and out-of-repo targets refuse; in-repo files resolve (1.922663ms) +✔ cli: check refuses a missing context file and a missing binding with exit 2; usage is exit 4 (436.525941ms) +✔ cli: reload validates the file first (exit 2), then needs a live owner (exit 1); usage is exit 4 (307.43185ms) +✔ cli: run refuses when STOP is present, before any network use (197.763683ms) +✔ binding: tools is optional, validated strictly, a fixed key for reload, and its roots are resolved against the data root (2.098613ms) +✔ binding: a git key is validated at load and reaches the extension whole, and only on a writable root (1.707678ms) +✔ 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.146472ms) +✔ delivery: refused and unknown outcomes are journaled; a later chunk is not sent after a failure (22.161727ms) +✔ delivery: restart with an unknown entry re-sends the same nonce once and reconciles before accepting traffic (1.922165ms) +✔ delivery: an unknown entry older than the dedupe window is marked refused, not re-sent; a still-unknown one refuses start (2.360403ms) +✔ delivery: repeated unknown reconciliations never refresh the dedupe window; the original intent time decides (1.5168ms) +✔ turn: a failed engine turn posts the fixed line, never model output, and writes a failed record (3.393632ms) +✔ turn: a second message during a turn is held by the engine, both get their own reply and record (32.764761ms) +✔ turn: a thread under a listed channel is answered in the thread; an unknown thread is looked up once (2.530436ms) +✔ drop: an unlisted user gets silence and one drop line; no inbox entry, no REST call, no engine call (1.106317ms) +✔ drop: an oversize message is accepted into the inbox, answered with the fixed line and journaled as a drop (1.162747ms) +✔ restart: an inbox with three ids and a replay of the same three produces zero turns (41.659505ms) +✔ stop: STOP present refuses start; STOP written while running refuses new turns and the current one finishes (31.900167ms) +✔ ceiling: the ceiling plus one is refused and journaled; one fixed line per UTC day; a new day accepts again (5.824945ms) +✔ ceiling: a burst arriving while turns are still running cannot queue past the ceiling (3.346ms) +✔ ceiling: a turn interrupted by a crash still counts after restart; admissions are durable (1.483835ms) +✔ ceiling: the daily notice survives a same-day restart; one delivery attempt in total, even when the first attempt crashed mid-flight (5.303309ms) +✔ duplicate: the same event delivered twice while the thread lookup is held yields one prompt, one admission and one reply (1.940904ms) +✔ journal: no token-shaped string and no model output on the drop path reaches disk (0.710878ms) +✔ receipt: an admitted message gets one eyes reaction on the inbound message; drops and refusals get none; a failed reaction is recorded and does not fail the turn (3.075694ms) +✔ receipt: Discord refusing the reaction leaves the turn intact and records ok false (5.40085ms) +✔ reload: a new user is silent before and answered after; a removed channel goes silent; a lower ceiling applies at once (3.977243ms) +✔ reload: a fixed key refuses with exit 2 and the old binding stays in force (2.820204ms) +✔ tools: with a tools binding the turn record lists every read and its outcome; without one the field is null (5.046271ms) +✔ context: the Discord block names the server, channels and modes, and states the rules from Q15 and Q16 (4.716101ms) +✔ context: with tools the block names the roots, keeps file content as data, and says to state refusals plainly (1.020369ms) +✔ context: a writable root adds the write rules and says a write is real only once Jason commits (2.643927ms) +✔ context: the envelope is one bracketed line then the text; names cannot break the line (2.288011ms) +✔ context: a git root swaps the terminal-commit line for the git verbs, and a vault root adds the id protocol (3.169459ms) +✔ context: assembleContext concatenates files in launcher format and appends the block; sha256 is stable (1.462494ms) +✔ context: splitReply keeps paragraphs together under the limit and splits long ones at lines, spaces, then hard (1.366629ms) +✔ engine: buildPiArgs carries the fixed flags, engine settings, session dir and prompt file (3.027103ms) +✔ engine: with tools, buildPiArgs turns pi's own tools off, loads the extension explicitly and allowlists exactly our three (0.542906ms) +✔ engine: a run with tool turns settles once, on the answer, with every tool call in the result (69.047219ms) +✔ engine: a run that ends on a tool-only turn fails the prompt as empty; a retried run settles on the real end (46.05589ms) +✔ engine: one prompt, one turn, text and usage come back (39.372748ms) +✔ engine: a prompt while streaming is held until pi settles, then sent as its own run, and answered in order (366.344826ms) +✔ engine: a held prompt that times out before pi settles fails on its own and is never sent (249.142072ms) +✔ engine: timeout sends abort and fails only that turn; the process stays (106.123841ms) +✔ engine: tool events from a run that outlived its timeout never land in the next prompt's record (243.439262ms) +✔ engine: a prompt after a turn that timed out before its agent_start waits for pi to settle instead of being refused (148.537966ms) +✔ 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.694936ms) +✔ engine: a timed-out turn pi starts only after the grace never answers a later prompt (617.856714ms) +✔ engine: late events of a run past its grace, before pi exits, answer nothing and nothing more is sent (early prompt response) (1.801167ms) +✔ engine: late events of a run past its grace, before pi exits, answer nothing and nothing more is sent (late prompt response) (0.643121ms) +✔ engine: a timed-out run pi did start outlives the grace; the next prompt goes out when it ends (435.916681ms) +✔ engine: a malformed JSONL line fails the turn, not the process (38.724015ms) +✔ engine: a turn that ends in error rejects with the error code; process exit fails pending turns (61.524282ms) +✔ gateway: hello -> identify with intents, ready, heartbeat with jitter, ack (3.259735ms) +✔ gateway: missed ack closes the socket and resumes with the last sequence (1.838789ms) +✔ gateway: op 7 reconnect resumes; op 9 non-resumable re-identifies (0.599133ms) +✔ gateway: op 9 resumable resumes (0.487412ms) +✔ gateway: close 4014 is fatal, reports the missing intent, never reconnects (1.09363ms) +✔ gateway: 4004 and 4013 are fatal too; 1006 reconnects with identify when no session (0.881424ms) +✔ gateway: close() is final and unparseable frames are ignored (0.595796ms) +✔ git: config validation is strict, needs write: true, a work tree and a private token file (71.066053ms) +✔ git: the child environment drops every host git config, names one helper, and carries the token path only for origin (48.306073ms) +✔ git: status reports the branch, ahead/behind and changed paths, and refuses off the named branch or mid-merge (103.092487ms) +✔ git: parseStatus reads porcelain v2 including renames and conflicts (0.504824ms) +✔ git: a commit stages exactly the named files, carries the seat author and the requester trailer, and pushes at once (109.074982ms) +✔ git: commit refusals: message, paths, requester, nothing to commit, and an index that already holds other work (115.846377ms) +✔ git: a commit whose push fails is still a commit, says so, and the next commit's push carries both (D6) (153.614912ms) +✔ git: pull is fast-forward only; a diverged origin or dirty local files refuse with nothing merged (310.727025ms) +✔ git: push pushes the named branch only and reports up to date (108.667033ms) +✔ git: no token value or token path ever reaches a git argument list; outputs are masked and capped (124.328012ms) +✔ git: the credential helper answers get over https from a private file and nothing else (314.682173ms) +✔ git: the vault protocol validates before a commit, honours another owner's lock, reserves ids, and locks around writes (1166.014042ms) +✔ lock: the claim is exclusive; a second start against a live owner refuses (6.20988ms) +✔ lock: a stale lock (dead owner, reused pid, or record without start) refuses run and is never signaled; only unlock clears it (6.640335ms) +✔ lock: an incomplete claim (directory without owner record) is busy and refuses run; unlock clears it (1.121802ms) +✔ lock: an owner record that exists but cannot be read is invalid: never signaled, never removed, never claimed over (3.627903ms) +✔ lock: legacy upgrade; a live connector holding a {pid, start} record is unknown, unlock refuses and nothing changes; after it exits, unlock clears it (56.459965ms) +✔ 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 (450.955141ms) +✔ lock: identity syntax; only canonical unsigned decimal start ticks and lowercase boot uuids are identities (0.565327ms) +✔ lock: a process whose start marker or boot id cannot be read refuses to claim (0.667884ms) +✔ lock: a live pid whose identity cannot be read right now is unknown: never signaled, never removed, never claimed over (1.405106ms) +✔ lock: four processes racing for the same binding; exactly one claims it and the others refuse (54.089469ms) +✔ lock: stale handoff; concurrent starts over a stale lock all refuse, nothing reclaims, one unlock then exactly one live owner (166.687895ms) +✔ lock: four-party schedule; claims landing inside an unlock's gap never survive, one unlock leaves no owner and no residue (99.520019ms) +✔ notices: a kind is recorded per UTC day and found again (0.804254ms) +✔ 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.038549ms) +✔ recover: an operator STOP refuses with exit 3 and is never removed, whatever the lock says (47.112719ms) +✔ recover: a brake written during the unlock wins; STOP stays with both lines and the start is refused (37.629128ms) +✔ recover: a held binding refuses with exit 3 and writes no STOP: live owner, alive pid without verifiable identity, unreadable record (105.740322ms) +✔ cli: recover exits 0 when ready, 3 behind a brake or a held binding, and run's own STOP refusal is 3 (939.945936ms) +✔ rest: createMessage sends nonce, enforce_nonce, empty allowed_mentions and a soft reply reference (4.101332ms) +✔ rest: 429 waits retry_after and retries; 4xx is refused; 5xx and socket errors are unknown (4.704323ms) +✔ rest: content and nonce limits are enforced locally; typing never throws (0.824992ms) +✔ rest: react PUTs the encoded emoji on the inbound message for @me; 2xx is true, anything else is false and never throws (0.999776ms) +✔ setspark config: a bare https or loopback origin, a private key file, a principal (7.995106ms) +✔ setspark config: reaches the tools config and the binding as a fixed key (3.76169ms) +✔ setspark config: the binding's key survives resolveToolRoots and the engine's JSON hand-off to the extension (1.858973ms) +✔ setspark config: approvers come from the binding's users, never from the binding's setspark key (2.738808ms) +✔ setspark verbs: required_approvers go out as discord ids from names and come back as names (25.907916ms) +✔ setspark verbs: no Discord user id reaches tool text, whatever shape the service returns it in (10.790342ms) +✔ setspark contract: a decision made with names opens a request the connector accepts; names stored by an old record still refuse (7.833831ms) +✔ setspark keys: read per call, one printable token per file, rotation without a restart (3.834458ms) +✔ setspark idempotency keys: principal, turn id, call index; connector keys name a step (0.67999ms) +✔ setspark http core: json in and out, bearer header, idempotency header, fixed user agent, no key anywhere else (3.042848ms) +✔ setspark http core: error bodies become fixed refusals with code and the 409 fields; server text is data, cut (1013.235865ms) +✔ setspark verbs: a setspark key enables the eight verbs and no counters (0.580633ms) +✔ 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 (9.002066ms) +✔ setspark verbs: no turn refuses every write before any request; bad arguments refuse before any request; reads still work (2.868128ms) +✔ setspark verbs: renderRecord caps long output and hides the accepted snapshot (0.361119ms) +✔ setspark api: bind, add_approval (button and reply) and get use integer request ids and the connector's keys (4.954567ms) +✔ tools: config refuses a missing, symlinked, dotted, non-directory or duplicate root and bad limits (4.206487ms) +✔ tools: every escape is refused with a fixed reason and nothing outside the root is read (3.772655ms) +✔ tools: happy paths list, read a window, and search case-insensitively; dotfiles and symlinks never appear (3.889209ms) +✔ tools: the tool set renders text for the model, records details for the journal, and enforces the per-run budget (2.541703ms) +✔ tools: listing and search caps hold (11.651533ms) +✔ tools: credential shapes are caught; ordinary prose and ids are not (0.902246ms) +✔ 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.101179ms) +✔ tools: an unreadable file under the root is skipped by search and refused by read (1.292173ms) +✔ tools: config accepts write: true only as a boolean, and enables the write tools only then (1.202218ms) +✔ tools: every write outside the fence is refused before any byte lands, and no temp file remains (6.193899ms) +✔ tools: write_file leaves the exact bytes, edit_file replaces one exact match, and the set renders the change as uncommitted (4.36968ms) +✔ tools: a target that changed between the check and the rename is refused and the temp file is removed (1.856538ms) +✔ web: config takes an https or loopback-http SearXNG base url and a bounded fetch cap (3.141386ms) +✔ web: address rules refuse every private, loopback, link-local, mapped and multicast form (2.277415ms) +✔ web: web_fetch refuses bad urls, private hosts, rebinding names, non-https redirects, too many hops, error status, non-text bodies, and times out (1034.014731ms) +✔ 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.187079ms) +✔ web: html to text drops scripts, styles and comments, decodes entities and keeps block breaks (0.364894ms) +✔ 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.636648ms) +✔ 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.196885ms) +ℹ tests 173 +ℹ suites 0 +ℹ pass 173 +ℹ fail 0 +ℹ cancelled 0 +ℹ skipped 0 +ℹ todo 0 +ℹ duration_ms 2867.645667 diff --git a/agents/filbert/work/slice1-s4-review/r1-base-suite-auth.txt b/agents/filbert/work/slice1-s4-review/r1-base-suite-auth.txt new file mode 100644 index 00000000..ac228760 --- /dev/null +++ b/agents/filbert/work/slice1-s4-review/r1-base-suite-auth.txt @@ -0,0 +1,17 @@ +OK status with missing harness credential exits 3 and still lists accounts +OK status reports harness credential (read-only) + mosaic accounts +OK api key material never reaches output +OK oauth token material never reaches output +OK unparseable credential file exits 2 +OK symlinked credential file exits 4 +OK env-side credential names reported +OK env var values never reach output +OK accounts without an accounts dir reports none and creates nothing +OK accounts lists files and marks the active one +OK loose account perms flagged in listing +OK agent --auth with missing account file refuses (exit 4) +OK agent --auth with non-0600 account file refuses +OK agent --auth with invalid account name refuses +OK auth.sh without valid config refuses + +selftest: 15 passed, 0 failed diff --git a/agents/filbert/work/slice1-s4-review/r1-base-suite-conductor.txt b/agents/filbert/work/slice1-s4-review/r1-base-suite-conductor.txt new file mode 100644 index 00000000..d6b7a1ee --- /dev/null +++ b/agents/filbert/work/slice1-s4-review/r1-base-suite-conductor.txt @@ -0,0 +1,55 @@ +Note: switching to 'd68aa20f278816f53583fb73bd3cb1e8cdc0fdb2'. + +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 'd68aa20f278816f53583fb73bd3cb1e8cdc0fdb2'. + +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/r1-base-suite-config.txt b/agents/filbert/work/slice1-s4-review/r1-base-suite-config.txt new file mode 100644 index 00000000..76c05ef7 --- /dev/null +++ b/agents/filbert/work/slice1-s4-review/r1-base-suite-config.txt @@ -0,0 +1,26 @@ +OK absent adapter defaults to pi +OK adapter mock validates (exit 0) +OK unsupported adapter exits 2 (exit 2) +OK env exports adapter +OK bootstrap creates default when absent (exit 0) +OK bootstrap wrote config file +OK bootstrap is idempotent on existing config (exit 0) +OK bootstrap did not rewrite existing config +OK validate missing config exits 3 (exit 3) +OK malformed JSON exits 2 (exit 2) +OK unsupported configVersion exits 2 (exit 2) +OK unknown top-level key exits 2 (exit 2) +OK unknown execution key exits 2 (exit 2) +OK unsupported backend exits 2 (exit 2) +OK unsupported environment exits 2 (exit 2) +OK relative dataRoot exits 2 (exit 2) +OK non-canonical dataRoot exits 2 (exit 2) +OK filesystem root dataRoot exits 2 (exit 2) +OK home directory dataRoot exits 2 (exit 2) +OK dataRoot containing config dir exits 2 (exit 2) +OK control character in provider exits 2 (exit 2) +OK symlinked config file exits 2 (exit 2) +OK env exports resolve correctly +OK failed validation modified nothing + +selftest: 24 passed, 0 failed diff --git a/agents/filbert/work/slice1-s4-review/r1-base-suite-discord.txt b/agents/filbert/work/slice1-s4-review/r1-base-suite-discord.txt new file mode 100644 index 00000000..c667a891 --- /dev/null +++ b/agents/filbert/work/slice1-s4-review/r1-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/r1-base-suite-extension-package.txt b/agents/filbert/work/slice1-s4-review/r1-base-suite-extension-package.txt new file mode 100644 index 00000000..0e346c8b --- /dev/null +++ b/agents/filbert/work/slice1-s4-review/r1-base-suite-extension-package.txt @@ -0,0 +1,21 @@ +OK initial ordinary-file install +OK installed tree matches canonical source +OK installed tree has no symlinks +OK check detects installation drift +OK sync refuses to overwrite installation drift +OK check detects an extra destination file +OK check detects an extra destination directory +OK check rejects a destination symlink +OK sync accepts a canonical source update +OK updated installation matches canonical source +scripts/test-extension-package.sh: line 14: 2271363 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/r1-base-suite-foundation.txt b/agents/filbert/work/slice1-s4-review/r1-base-suite-foundation.txt new file mode 100644 index 00000000..6338e1e6 --- /dev/null +++ b/agents/filbert/work/slice1-s4-review/r1-base-suite-foundation.txt @@ -0,0 +1,53 @@ +toolchain: node v26.8.1, python 3.12.8, jsonschema 4.26.0 + +OK syntax: scripts/foundation-inspect.mjs +OK syntax: scripts/foundation/strict-json.mjs +OK syntax: scripts/foundation/canonical.mjs +OK syntax: scripts/foundation/resolve.mjs +OK syntax: scripts/foundation/validate-record.mjs +OK syntax: scripts/foundation/fixtures/build-fixtures.mjs +OK syntax: scripts/foundation/canonical.test.mjs +OK syntax: scripts/foundation/cli.test.mjs +OK syntax: scripts/foundation/fixtures.test.mjs +OK syntax: scripts/foundation/resolve.test.mjs +OK syntax: scripts/foundation/strict-json.test.mjs +OK syntax: scripts/foundation/verify-schema.py (ast only; no bytecode written) +OK fixture generator runs +OK checked-in fixtures/bundles equal a fresh generation +OK checked-in fixtures/raw equal a fresh generation +OK checked-in fixtures/index.json equal a fresh generation +OK checked-in demo bundles equal a fresh generation +OK a failing nested test fails the run under a parent runner's NODE_TEST_CONTEXT +OK node --test scripts/foundation/ (ℹ pass 80) +OK differential schema oracle: PASS: differential schema oracle (finite corpus; compatibility evidence, not equivalence proof) + platform witness: strftime('%Y') for year 999 -> '999' (pinned checker refuses years 0001..0999) + node v26.8.1; corpus 1568 records (38 pinned fixtures, 478 unique bundle records, 1052 typeCase/mutation/lexical cases) + schema column: agree-valid 540, agree-invalid 991, DISAGREEMENTS 0; strict-only (parser-bound) cases: 27; unsupported-kind records not schema-assessed by the inspector: 10 + profile column (schema-valid records only): profile-valid 510, profile-invalid 30 + profile refusals asserted: 30 schema-agreed-valid records refused only by the strict typed-string profile (rule profile-pattern-mismatch), 12 declared by name; 73 named probes verified against declared schema/profile columns +OK oracle: zero schema-column disagreements with the pinned checker +OK oracle: strict-only profile refusals are counted and asserted +OK demo: permitted read preview exits 0 (exit 0) +OK demo: permitted file.change preview exits 0 (exit 0) +OK demo: assignment.change proposal is unresolved (exit 3) (exit 3) +OK demo: revoked registration is refused (exit 3) (exit 3) +OK demo: message is not authority (exit 3) (exit 3) +OK usage: no arguments exits 2 (exit 2) +OK io: missing file exits 4 (exit 4) +OK io: directory exits 4 (exit 4) +OK io: symlink exits 4 (O_NOFOLLOW) (exit 4) +OK bound: oversize fixture exits 2 (exit 2) +OK profile: one final LF in a typed selection id is refused before admission (exit 2) (exit 2) +OK profile: two final LFs fail the schema pattern itself (exit 2) (exit 2) +OK profile: escaped newlines in free-form text stay allowed (exit 0) (exit 0) +OK profile refusal is invalid-request/profile-pattern-mismatch with selection and operation withheld, value not echoed +OK text output starts with the disclaimer +OK json output is valid JSON with result allowed and exactly the charter §7 fields +OK json golden matches byte-for-byte +OK sandboxed bundle run (env -i, PATH=/nonexistent) produced the unresolved proposal +OK sandbox inventory (path/type/size/mode/uid/gid/inode/mtime/sha256) unchanged by runs +OK canary never printed (bundle run and credential-file run) +OK a non-bundle JSON file is refused at the shape gate, not read into output +OK no field of the non-bundle file is echoed + +selftest: 44 passed, 0 failed diff --git a/agents/filbert/work/slice1-s4-review/r1-base-suite-queue.txt b/agents/filbert/work/slice1-s4-review/r1-base-suite-queue.txt new file mode 100644 index 00000000..43c9adf4 --- /dev/null +++ b/agents/filbert/work/slice1-s4-review/r1-base-suite-queue.txt @@ -0,0 +1,35 @@ +toolchain: node v26.8.1, git version 2.55.0 + +OK syntax: packages/queue/src/cli.mjs +OK syntax: packages/queue/src/errors.mjs +OK syntax: packages/queue/src/io.mjs +OK syntax: packages/queue/src/lock.mjs +OK syntax: packages/queue/src/queue.mjs +OK syntax: packages/queue/src/review.mjs +OK syntax: packages/queue/src/store.mjs +OK syntax: packages/queue/tests/commit.test.mjs +OK syntax: packages/queue/tests/data.test.mjs +OK syntax: packages/queue/tests/dispatch.test.mjs +OK syntax: packages/queue/tests/helpers.mjs +OK syntax: packages/queue/tests/lock.test.mjs +OK syntax: packages/queue/tests/migration.test.mjs +OK syntax: packages/queue/tests/review.test.mjs +OK syntax: packages/queue/tests/store.test.mjs +OK syntax: packages/queue/tests/write.test.mjs +OK syntax: packages/queue/tests/fixtures/fake-gitea.mjs +OK syntax: packages/queue/tests/fixtures/kill-at.mjs +OK syntax: packages/queue/tests/fixtures/lock-child.mjs +OK syntax: packages/queue/tests/fixtures/mosaic-pre-a2.sh +OK syntax: scripts/queue-commit.sh +OK syntax: scripts/git-hooks/pre-commit +OK syntax: scripts/mosaic +OK queue-commit.sh, the guard and scripts/mosaic are executable +OK packages/queue declares no dependencies +ℹ tests 148 +ℹ pass 148 +ℹ fail 0 +OK node --test packages/queue/tests/ +OK scripts/mosaic queue help +skip queue verify and render --check: this checkout (/home/jwoltje/filbert-scratch/r39/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/r1-base-suite-release.txt b/agents/filbert/work/slice1-s4-review/r1-base-suite-release.txt new file mode 100644 index 00000000..1d6ed050 --- /dev/null +++ b/agents/filbert/work/slice1-s4-review/r1-base-suite-release.txt @@ -0,0 +1,7 @@ +OK valid RELEASE resolves (exit 0) +OK invalid RELEASE exits 1 (exit 1) +OK missing RELEASE exits 1 (exit 1) +OK valid RELEASE leaves image tag consistent with version +skip state-machine cases (docker daemon unavailable) + +selftest: 4 passed, 0 failed diff --git a/agents/filbert/work/slice1-s4-review/r1-base-suite-task.txt b/agents/filbert/work/slice1-s4-review/r1-base-suite-task.txt new file mode 100644 index 00000000..85aa46f0 --- /dev/null +++ b/agents/filbert/work/slice1-s4-review/r1-base-suite-task.txt @@ -0,0 +1,33 @@ +OK valid task validates (exit 0) +OK unknown task key exits 2 (exit 2) +OK unsupported taskVersion exits 2 (exit 2) +OK invalid task id exits 2 (exit 2) +OK empty prompt exits 2 (exit 2) +OK NUL in expectExact exits 2 (exit 2) +OK out-of-range timeout exits 2 (exit 2) +OK missing mission file exits 4 (exit 4) +OK task with valid mission validates (exit 0) +OK invalid mission exits 2 (exit 2) +OK validate missing task exits 4 (exit 4) +OK validation does not modify the task file +OK prune dry-run exits 0 (exit 0) +OK dry-run deleted nothing +OK prune --keep=2 --yes removes oldest (exit 0) +OK kept exactly 2 newest runs +OK newest run kept, oldest pruned +OK append-only receipt written (3 entries) +OK sessions/workspaces untouched by prune +OK prune with invalid keep exits 4 (exit 4) +skip adapter seam cases (docker daemon unavailable) +skip workspace/capability cases (docker daemon unavailable) +skip live task cases (docker unavailable) +OK onboard without name exits 4 (non-interactive) (exit 4) +OK onboard --name renders profile (exit 0) +OK profile written +OK canon structure: required filled, optional placeholdered +OK canon sections present +FAIL user recall run succeeds (exit 1) +FAIL recalled user name (response: ) +OK no agent identity on headless run + +selftest: 26 passed, 2 failed diff --git a/agents/filbert/work/slice1-s4-review/r1-cand-node-bus.txt b/agents/filbert/work/slice1-s4-review/r1-cand-node-bus.txt new file mode 100644 index 00000000..dfbd8e8c --- /dev/null +++ b/agents/filbert/work/slice1-s4-review/r1-cand-node-bus.txt @@ -0,0 +1,66 @@ +✔ launch identity is stamped, payload identity is refused and stale holder cannot send (157.589774ms) +✔ decision classes route from policy; gated resolution is human-only, choice and target must match (214.882734ms) +✔ claim exclusion, holder release, gated revoke and rerouting to a new holder are atomic (234.370396ms) +✔ launch events require a human CLI capability; generic emit cannot forge authority events (154.43658ms) +✔ within-role decisions close atomically and invalid options or blocking omissions refuse (147.744863ms) +✔ observer capabilities read human inbox but cannot mutate or forge launch identity (128.923406ms) +✔ task action subjects and linked decision trail are complete and ordered (135.580688ms) +✔ launch binding is durable and reconnecting requires the identical trusted record (70.784072ms) +✔ business isolation includes inherited object names and cross-business message references (128.216659ms) +✔ authority never transfers between action, run, target, unresolved or replaced role holder (214.293626ms) +✔ task projection uses schema current view, skipping earlier and equal-start polls (104.369741ms) +✔ revocation permanently bars the old run from reclaiming first, including after broker restart (202.700031ms) +✔ empty message references refuse before storage; refusal-evidence failure stays a typed error (113.034346ms) +✔ both arbiters require human resolution when their cross-role route is themselves (186.11149ms) +✔ S1 adapter takes resolved limits and refs, rejects mismatched instance, never mutates input (4.901157ms) +✔ only validated broker references load; returned data and exceptions cannot expose a known token (7.674124ms) +✔ bad file modes, symlinks, repository/data paths, malformed tokens and missing dates refuse (13.179581ms) +✔ expiry refuses use and env references never become client data (1.129997ms) +✔ S1 parsed service refs work, service mismatch refuses, Gitea rotation due is a warning state (2.104835ms) +✔ opaque tokens shorter than 16 characters refuse before use (0.516553ms) +✔ human proof binds CLI entry, process start and nonce; agents and incomplete ancestry refuse (3.290937ms) +✔ process reader gets own kernel identity without exposing environment values (2.69631ms) +✔ EACCES ancestor environments skip only markers; commands and registered launches still refuse (1.407953ms) +✔ real pid 1 remains inspectable when its environment is protected (0.446615ms) +✔ within-role sends cite an open gated launch decision without spending it or naming it in grants (179.466727ms) +✔ missing and foreign-business citations refuse and roll back message and grant (157.029434ms) +✔ cross-role sends still need a matching resolved decision and consume it once (216.213587ms) +✔ broker process binds trusted launches, offers reader capabilities, refuses human mutation, closes cleanly (191.273445ms) +✔ startup token refusal returns safe code without value or partial listening broker (44.184184ms) +✔ loaded fixture token is absent from socket replies and SQLite, including refusal evidence (163.394401ms) +✔ killed broker leaves an explicit stale lock; another process cannot silently reclaim it (172.087705ms) +✔ trusted host registers later launches; socket clients never have a registration verb (162.33158ms) +✔ runtime excludes declared project roots even when host supplies no repoRoots (43.79939ms) +✔ a refused launch binding leaves the broker and existing capabilities alive; bad protocol stops it (151.635091ms) +✔ v3b prototype refusals, views and append-only mutations (921.886122ms) +✔ gated approval authorizes once, survives store reopen, and fresh approval works (235.197428ms) +✔ another run cannot consume an approval; a failed check leaves it usable (188.114042ms) +✔ two scheduled callers have exactly one grant and one consumed refusal (145.100506ms) +✔ failed commit rolls consumption back; cross-role consumes and within-role stays reusable (262.999514ms) +✔ class drift gated to cross-role refuses before consumption (174.183825ms) +✔ class drift cross-role to gated refuses before consumption (161.959479ms) +✔ class drift gated to within-role refuses before consumption (140.823855ms) +✔ class drift cross-role to within-role refuses before consumption (165.128575ms) +✔ class drift within-role to gated refuses before consumption (149.257083ms) +✔ class drift within-role to cross-role refuses before consumption (164.919308ms) +✔ message.send consumes approval and prevents a later send or authorize (195.343532ms) +✔ role.revoke consumes approval and prevents a later revoke or authorize (207.352231ms) +✔ creates private WAL store and excludes a second writer until explicit close (115.38464ms) +✔ rollback is atomic and schema metadata is checked against trusted DDL, not just itself (145.673579ms) +✔ existing empty database and symlink runtime directory refuse, never initialize over damage (161.739595ms) +✔ crash during a transaction recovers no partial event after explicit fixture-only lock removal (158.645686ms) +✔ writer refuses mixed at/read_at forms atomically, even through trusted SQL helpers (88.416456ms) +✔ async transactions refuse before invoking their function (81.367723ms) +✔ socket capability stamps launch identity; shared views use wire, no SQL client (135.569067ms) +✔ two wire claims serialize; a lost reply never automatically retries (149.058021ms) +✔ malformed, oversized and identity-forging envelopes refuse without echoing input (101.144057ms) +✔ client preserves UTF-8 when a response divides a multibyte character (13.072058ms) +✔ committed mutation followed by dropped reply reports unknown and is never retried (120.466141ms) +ℹ tests 58 +ℹ suites 0 +ℹ pass 58 +ℹ fail 0 +ℹ cancelled 0 +ℹ skipped 0 +ℹ todo 0 +ℹ duration_ms 2334.386318 diff --git a/agents/filbert/work/slice1-s4-review/r1-cand-node-business.txt b/agents/filbert/work/slice1-s4-review/r1-cand-node-business.txt new file mode 100644 index 00000000..5a7875e5 --- /dev/null +++ b/agents/filbert/work/slice1-s4-review/r1-cand-node-business.txt @@ -0,0 +1,68 @@ +✔ config directory and file path follow MOSAIC_CONFIG (2.247029ms) +✔ the fixture business validates and comes back frozen (6.633818ms) +✔ two instances may share a definition (2.079058ms) +✔ top-level refusals (6.494276ms) +✔ arbiters and projects (9.593184ms) +✔ role instances (4.099484ms) +✔ Vikunja bots (9.407995ms) +✔ a role without Vikunja takes no tracker block (2.980678ms) +✔ credential references match the definition's services (4.080229ms) +✔ launch (13.062449ms) +✔ loadBusiness: file checks (2.443093ms) +✔ loadBusiness: not a regular file (60.332879ms) +✔ loading writes nothing (1.620853ms) +✔ names that are Object.prototype properties don't count as declared (5.165091ms) +✔ the shipped example refuses as written and validates once filled in (0.861003ms) +✔ usage errors exit 4 (422.043713ms) +✔ validate: a good business exits 0 and prints instance digests (105.035447ms) +✔ validate: project files (427.125782ms) +✔ validate: missing files and a broken system config (307.241615ms) +✔ validate: credential reference problems exit 2 and name each one (95.518816ms) +✔ validate: a token file inside the repository is refused (87.525855ms) +✔ validate: role definitions come from MOSAIC_ROLES_DIR (285.815308ms) +✔ resolve: prints one instance's record (278.379994ms) +✔ resolve: refusals (502.16823ms) +✔ parse: exactly one of file or env, plus the service's date (3.909755ms) +✔ check: a good file has no problems (0.982554ms) +✔ check never opens the file: a write-only token passes (0.391532ms) +✔ check: file problems (1.499342ms) +✔ check: token files can't live in the repository or dataRoot, even through a linked directory (1.002701ms) +✔ check: dates and environment references (0.599406ms) +✔ path and load (2.380308ms) +✔ refusals (1.438365ms) +✔ systemVars flattens the validated config (2.454743ms) +✔ precedence: system, business, project, project role, agent (6.249979ms) +✔ limits narrow the definition and never widen it (3.213516ms) +✔ role.launch stays within-role only for the instance the launch block names (9.064504ms) +✔ limits.authority without role.launch leaves the launcher with no launch block (4.036328ms) +✔ limits.authority narrows cross-role actions too (1.761735ms) +✔ classify (1.653353ms) +✔ the record carries what the broker and launcher need (1.368244ms) +✔ digest: key order doesn't matter, any value change does (10.405106ms) +✔ refusals (3.017749ms) +✔ the four shipped version 2 roles load (4.981049ms) +✔ shipped role scopes match addendum B section 2 and the SR runbook (1.460327ms) +✔ shipped authority follows the note's table (0.791107ms) +✔ version 1 files keep loading with no authority (1.248782ms) +✔ the conductor policy isn't a role (0.328608ms) +✔ a missing role file is exit 4, a symbolic link too (0.536317ms) +✔ version 2 refusals (1.944397ms) +✔ authority: closed vocabulary, no gated-only action, no overlap (2.922779ms) +✔ credentials: Gitea scopes (1.349151ms) +✔ credentials: Vikunja scopes are a group-to-verbs map from the grantable list (1.633512ms) +✔ credentials: services (0.882165ms) +✔ contract: a non-empty regular Markdown file beside the role file (1.18187ms) +✔ every key names known layers and a merge rule (0.886623ms) +✔ unknown keys and wrong layers refuse (1.149448ms) +✔ types (2.318627ms) +✔ merge: defaults, then the most specific layer wins (0.405798ms) +✔ merge: limits only narrow, and provenance lists each source (0.514157ms) +✔ merge doesn't change its inputs (0.234247ms) +ℹ tests 60 +ℹ suites 0 +ℹ pass 60 +ℹ fail 0 +ℹ cancelled 0 +ℹ skipped 0 +ℹ todo 0 +ℹ duration_ms 2597.243526 diff --git a/agents/filbert/work/slice1-s4-review/r1-cand-node-cli.txt b/agents/filbert/work/slice1-s4-review/r1-cand-node-cli.txt new file mode 100644 index 00000000..a00e3c6e --- /dev/null +++ b/agents/filbert/work/slice1-s4-review/r1-cand-node-cli.txt @@ -0,0 +1,45 @@ +✔ inbox lists only decisions routed to the human, with what approving authorizes and how to decide (98.278528ms) +✔ decide resolves by id prefix with --yes, and the trail shows it in broker order with the task to follow (125.163588ms) +✔ decide refuses without a terminal or --yes, on an unknown option and on a short reference (92.707937ms) +✔ decide prints a declining choice as declining (95.741332ms) +✔ an unknown outcome is reported once and never resent (81.962323ms) +✔ every human command refuses inside an agent run before it touches the bus (45.953551ms) +✔ usage errors exit 4; no business and no host is a usage error (46.449623ms) +✔ agents and tasks print through the broker (83.023299ms) +✔ notify.json: missing, loose, malformed and extra keys refuse; a binding or null passes (7.967045ms) +✔ bootConfig builds the broker's boot message for one business, with no trackers key when no project names one (64.626568ms) +✔ trackers come from the tracker.* variables of the one project that names a tracker project (50.171832ms) +✔ two projects that each name a tracker project refuse, since the boot shape holds one (42.368165ms) +✔ a business without tracker.baseUrl gets no trackers entry (42.066317ms) +✔ an unknown business and a broken system config refuse with exit 3 (78.66042ms) +✔ empty views say so (1.355696ms) +✔ the trail keeps the broker's order and names a decision's task without its rows (1.687364ms) +✔ tasks print the tracker fields the snapshot carries (0.278509ms) +✔ the host boots the broker, binds a launch in process, and the notifier DMs a blocking decision exactly once (979.495158ms) +✔ a notifier that dies takes the host down with exit 1, so the unit restarts the pair (224.208015ms) +✔ a notifier that refuses stops the broker and the host refuses with exit 3 (228.13232ms) +✔ bus start refuses with exit 3 and the code when the broker refuses to boot; bus status names the lock (272.223452ms) +✔ bus start refuses with exit 3 without a notifier config (120.733662ms) +✔ bus start runs until bus stop; status reports it while it runs (771.564129ms) +✔ bus-service.sh renders the unit and installs it into a given directory (50.72064ms) +✔ zoned uses the IANA zone across DST (24.109218ms) +✔ each open blocking decision is DM'd once, across polls and a restart; non-blocking ones are not (103.574053ms) +✔ a failed DM is journaled, backs off, and is retried until it lands (91.244207ms) +✔ the digest goes at 08:00 Chicago once a day, with blocking ones marked as DM'd (95.320613ms) +✔ a late start with no digest for the day sends one at once; an empty inbox gets one line (80.705267ms) +✔ an inbox read failure is logged and the next poll retries (0.857406ms) +✔ no Discord id reaches the journal or the log (93.864798ms) +✔ the journal: a torn last line is skipped, a malformed line or a loose mode refuses (1.377894ms) +✔ digest content stays within Discord's 2000 characters (0.386801ms) +✔ runLoop never overlaps ticks and stops after the one in flight (110.566277ms) +✔ the transport writes {business, verb, args} to the child and reads its JSON (45.086525ms) +✔ a bus code on stderr becomes the exit code; garbage and timeouts are outcome problems (2187.431879ms) +✔ busExit and refuseInsideAgent (0.520981ms) +ℹ tests 37 +ℹ suites 0 +ℹ pass 37 +ℹ fail 0 +ℹ cancelled 0 +ℹ skipped 0 +ℹ todo 0 +ℹ duration_ms 2803.844436 diff --git a/agents/filbert/work/slice1-s4-review/r1-cand-node-discord.txt b/agents/filbert/work/slice1-s4-review/r1-cand-node-discord.txt new file mode 100644 index 00000000..0c5d692c --- /dev/null +++ b/agents/filbert/work/slice1-s4-review/r1-cand-node-discord.txt @@ -0,0 +1,186 @@ +✔ approvals: a request is validated before anything is posted; the rendering shows names and never ids (12.706939ms) +✔ approvals: the ledger is appended and folded into open requests with bind and approval states (5.764051ms) +✔ approvals: a reply approves only when it points at a request, says exactly approve, and comes from a listed approver once (7.566468ms) +✔ approvals: a button approves only on its own request message with the matching custom id (0.995741ms) +✔ approvals flow: a turn that opened a request posts the message with the button, records it, binds it, and both approvers approve (38.336621ms) +✔ 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.395638ms) +✔ approvals flow: an invalid request from the model, a refused post, and no api client are recorded and post nothing (11.589773ms) +✔ approvals flow: start retries a bind and an approval left as unknown, under their original keys (6.470232ms) +✔ authorize: open channel, listed user (2.025499ms) +✔ authorize: wrong guild (0.32383ms) +✔ authorize: no guild (DM) (1.290476ms) +✔ authorize: unlisted channel (0.25834ms) +✔ authorize: unknown channel, no info (0.190158ms) +✔ authorize: thread of listed parent (0.569506ms) +✔ authorize: thread of unlisted parent (1.333381ms) +✔ authorize: text channel that is not a thread and not listed (4.451006ms) +✔ authorize: unlisted user (0.3146ms) +✔ authorize: no author (0.358961ms) +✔ authorize: bot author (listed id, bot flag) (0.292437ms) +✔ authorize: system author (0.158314ms) +✔ authorize: the bot itself (0.092842ms) +✔ authorize: webhook (0.148654ms) +✔ authorize: mention channel without mention (0.286772ms) +✔ authorize: mention channel with bot mention (0.772901ms) +✔ authorize: mention channel with @everyone only (0.419414ms) +✔ authorize: mention channel mentioning someone else (0.159232ms) +✔ authorize: mention channel, content says @bot but mentions empty (0.12284ms) +✔ authorize: private thread under mention channel, mentioned (0.112841ms) +✔ authorize: private thread under mention channel, not mentioned (0.086922ms) +✔ authorize: thread in another guild per channel info (0.092072ms) +✔ authorize: not an object (0.729575ms) +✔ authorize: no id (0.130448ms) +✔ authorize: oversize content is accepted and flagged (0.093947ms) +✔ authorize: exactly the limit is not oversize (0.078506ms) +✔ authorize: a user's channel allowlist drops them outside it, threads count as the parent, others are unaffected (1.364029ms) +✔ authorize: order puts wrong guild before user, and user before channel (no channel lookup for strangers) (0.232417ms) +✔ binding: a complete binding validates and is frozen (3.254847ms) +✔ binding: unknown key, missing field, wrong type refuse with exit 2 (2.041286ms) +✔ binding: empty allowlists refuse (0.716762ms) +✔ binding: a user's channel allowlist must be non-empty, listed and unique; absent means every listed channel (1.749186ms) +✔ reloadDiff: reloadable keys are summarised by id; every fixed key refuses with exit 2 (4.484196ms) +✔ binding: dmRecipient is optional, a snowflake, one of the listed users, and a fixed key (4.102947ms) +✔ binding: file must be 0600, regular, not a symlink (2.929102ms) +✔ binding: token file mode, symlink, emptiness and shape are checked; token never appears in errors (1.836278ms) +✔ cli: check refuses a non-0600 token file with exit 2 before any network use (150.934705ms) +✔ context files: absolute paths, traversal, symlinks and out-of-repo targets refuse; in-repo files resolve (2.045947ms) +✔ cli: check refuses a missing context file and a missing binding with exit 2; usage is exit 4 (409.016799ms) +✔ cli: reload validates the file first (exit 2), then needs a live owner (exit 1); usage is exit 4 (322.732436ms) +✔ cli: run refuses when STOP is present, before any network use (184.423251ms) +✔ binding: tools is optional, validated strictly, a fixed key for reload, and its roots are resolved against the data root (2.560055ms) +✔ binding: a git key is validated at load and reaches the extension whole, and only on a writable root (1.461387ms) +✔ delivery: an accepted message is in the inbox before the turn, the reply is chunked with one nonce per chunk, and the turn record is write-once (25.146486ms) +✔ delivery: refused and unknown outcomes are journaled; a later chunk is not sent after a failure (24.71943ms) +✔ delivery: restart with an unknown entry re-sends the same nonce once and reconciles before accepting traffic (1.744631ms) +✔ delivery: an unknown entry older than the dedupe window is marked refused, not re-sent; a still-unknown one refuses start (4.017534ms) +✔ delivery: repeated unknown reconciliations never refresh the dedupe window; the original intent time decides (3.374441ms) +✔ turn: a failed engine turn posts the fixed line, never model output, and writes a failed record (4.887526ms) +✔ turn: a second message during a turn is held by the engine, both get their own reply and record (32.529615ms) +✔ turn: a thread under a listed channel is answered in the thread; an unknown thread is looked up once (3.743256ms) +✔ drop: an unlisted user gets silence and one drop line; no inbox entry, no REST call, no engine call (1.0251ms) +✔ drop: an oversize message is accepted into the inbox, answered with the fixed line and journaled as a drop (1.075254ms) +✔ restart: an inbox with three ids and a replay of the same three produces zero turns (41.617425ms) +✔ stop: STOP present refuses start; STOP written while running refuses new turns and the current one finishes (32.581695ms) +✔ ceiling: the ceiling plus one is refused and journaled; one fixed line per UTC day; a new day accepts again (5.252804ms) +✔ ceiling: a burst arriving while turns are still running cannot queue past the ceiling (3.706118ms) +✔ ceiling: a turn interrupted by a crash still counts after restart; admissions are durable (4.125556ms) +✔ ceiling: the daily notice survives a same-day restart; one delivery attempt in total, even when the first attempt crashed mid-flight (4.789809ms) +✔ duplicate: the same event delivered twice while the thread lookup is held yields one prompt, one admission and one reply (2.730712ms) +✔ journal: no token-shaped string and no model output on the drop path reaches disk (0.526137ms) +✔ 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.952413ms) +✔ receipt: Discord refusing the reaction leaves the turn intact and records ok false (2.378462ms) +✔ reload: a new user is silent before and answered after; a removed channel goes silent; a lower ceiling applies at once (4.212779ms) +✔ reload: a fixed key refuses with exit 2 and the old binding stays in force (2.426159ms) +✔ tools: with a tools binding the turn record lists every read and its outcome; without one the field is null (3.09644ms) +✔ context: the Discord block names the server, channels and modes, and states the rules from Q15 and Q16 (12.891189ms) +✔ context: with tools the block names the roots, keeps file content as data, and says to state refusals plainly (3.647852ms) +✔ context: a writable root adds the write rules and says a write is real only once Jason commits (2.904691ms) +✔ context: the envelope is one bracketed line then the text; names cannot break the line (1.677825ms) +✔ context: a git root swaps the terminal-commit line for the git verbs, and a vault root adds the id protocol (4.853621ms) +✔ context: assembleContext concatenates files in launcher format and appends the block; sha256 is stable (4.947595ms) +✔ context: splitReply keeps paragraphs together under the limit and splits long ones at lines, spaces, then hard (0.849847ms) +✔ engine: buildPiArgs carries the fixed flags, engine settings, session dir and prompt file (5.368302ms) +✔ engine: with tools, buildPiArgs turns pi's own tools off, loads the extension explicitly and allowlists exactly our three (1.226371ms) +✔ engine: a run with tool turns settles once, on the answer, with every tool call in the result (107.033502ms) +✔ engine: a run that ends on a tool-only turn fails the prompt as empty; a retried run settles on the real end (63.818299ms) +✔ engine: one prompt, one turn, text and usage come back (42.255924ms) +✔ engine: a prompt while streaming is held until pi settles, then sent as its own run, and answered in order (344.755134ms) +✔ engine: a held prompt that times out before pi settles fails on its own and is never sent (251.990584ms) +✔ engine: timeout sends abort and fails only that turn; the process stays (108.724674ms) +✔ engine: tool events from a run that outlived its timeout never land in the next prompt's record (255.988873ms) +✔ engine: a prompt after a turn that timed out before its agent_start waits for pi to settle instead of being refused (136.893442ms) +✔ 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.99112ms) +✔ engine: a timed-out turn pi starts only after the grace never answers a later prompt (615.851085ms) +✔ engine: late events of a run past its grace, before pi exits, answer nothing and nothing more is sent (early prompt response) (1.513739ms) +✔ engine: late events of a run past its grace, before pi exits, answer nothing and nothing more is sent (late prompt response) (0.560912ms) +✔ engine: a timed-out run pi did start outlives the grace; the next prompt goes out when it ends (437.363964ms) +✔ engine: a malformed JSONL line fails the turn, not the process (30.347024ms) +✔ engine: a turn that ends in error rejects with the error code; process exit fails pending turns (51.504732ms) +✔ gateway: hello -> identify with intents, ready, heartbeat with jitter, ack (3.193741ms) +✔ gateway: missed ack closes the socket and resumes with the last sequence (2.285417ms) +✔ gateway: op 7 reconnect resumes; op 9 non-resumable re-identifies (0.739426ms) +✔ gateway: op 9 resumable resumes (0.589898ms) +✔ gateway: close 4014 is fatal, reports the missing intent, never reconnects (1.443946ms) +✔ gateway: 4004 and 4013 are fatal too; 1006 reconnects with identify when no session (0.655803ms) +✔ gateway: close() is final and unparseable frames are ignored (0.451018ms) +✔ git: config validation is strict, needs write: true, a work tree and a private token file (86.785692ms) +✔ git: the child environment drops every host git config, names one helper, and carries the token path only for origin (47.586388ms) +✔ git: status reports the branch, ahead/behind and changed paths, and refuses off the named branch or mid-merge (91.552876ms) +✔ git: parseStatus reads porcelain v2 including renames and conflicts (0.463205ms) +✔ git: a commit stages exactly the named files, carries the seat author and the requester trailer, and pushes at once (105.806954ms) +✔ git: commit refusals: message, paths, requester, nothing to commit, and an index that already holds other work (123.419333ms) +✔ git: a commit whose push fails is still a commit, says so, and the next commit's push carries both (D6) (147.237328ms) +✔ git: pull is fast-forward only; a diverged origin or dirty local files refuse with nothing merged (343.702406ms) +✔ git: push pushes the named branch only and reports up to date (94.888588ms) +✔ git: no token value or token path ever reaches a git argument list; outputs are masked and capped (116.824952ms) +✔ git: the credential helper answers get over https from a private file and nothing else (259.187461ms) +✔ git: the vault protocol validates before a commit, honours another owner's lock, reserves ids, and locks around writes (942.967811ms) +✔ lock: the claim is exclusive; a second start against a live owner refuses (8.778041ms) +✔ lock: a stale lock (dead owner, reused pid, or record without start) refuses run and is never signaled; only unlock clears it (11.200806ms) +✔ lock: an incomplete claim (directory without owner record) is busy and refuses run; unlock clears it (2.3718ms) +✔ lock: an owner record that exists but cannot be read is invalid: never signaled, never removed, never claimed over (7.75423ms) +✔ lock: legacy upgrade; a live connector holding a {pid, start} record is unknown, unlock refuses and nothing changes; after it exits, unlock clears it (81.257582ms) +✔ 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 (397.113082ms) +✔ lock: identity syntax; only canonical unsigned decimal start ticks and lowercase boot uuids are identities (0.404544ms) +✔ lock: a process whose start marker or boot id cannot be read refuses to claim (0.430983ms) +✔ lock: a live pid whose identity cannot be read right now is unknown: never signaled, never removed, never claimed over (0.86426ms) +✔ lock: four processes racing for the same binding; exactly one claims it and the others refuse (53.576304ms) +✔ lock: stale handoff; concurrent starts over a stale lock all refuse, nothing reclaims, one unlock then exactly one live owner (150.380081ms) +✔ lock: four-party schedule; claims landing inside an unlock's gap never survive, one unlock leaves no owner and no residue (110.971911ms) +✔ notices: a kind is recorded per UTC day and found again (1.076422ms) +✔ notify: the DM channel opens once, every send carries the nonce, and only the message id comes back (6.658024ms) +✔ notify: refusals and unknowns surface as RestOutcome without ids; a refused send reopens the channel next time (4.299482ms) +✔ notify: no dmRecipient, a non-0600 binding or token, or a missing binding refuse before any network use (1.824233ms) +✔ recover: nothing to do is clean; a lock whose owner is gone or that has no record is cleared and STOP ends up absent (63.925791ms) +✔ recover: an operator STOP refuses with exit 3 and is never removed, whatever the lock says (43.099931ms) +✔ recover: a brake written during the unlock wins; STOP stays with both lines and the start is refused (38.201778ms) +✔ recover: a held binding refuses with exit 3 and writes no STOP: live owner, alive pid without verifiable identity, unreadable record (88.834611ms) +✔ cli: recover exits 0 when ready, 3 behind a brake or a held binding, and run's own STOP refusal is 3 (953.281324ms) +✔ rest: createMessage sends nonce, enforce_nonce, empty allowed_mentions and a soft reply reference (5.076501ms) +✔ rest: 429 waits retry_after and retries; 4xx is refused; 5xx and socket errors are unknown (9.560275ms) +✔ rest: content and nonce limits are enforced locally; typing never throws (0.908581ms) +✔ rest: react PUTs the encoded emoji on the inbound message for @me; 2xx is true, anything else is false and never throws (0.95532ms) +✔ rest: createDm posts the recipient and resolves the channel id; 4xx refused, 5xx unknown, bad id never sent (1.953824ms) +✔ setspark config: a bare https or loopback origin, a private key file, a principal (8.777398ms) +✔ setspark config: reaches the tools config and the binding as a fixed key (3.703766ms) +✔ setspark config: the binding's key survives resolveToolRoots and the engine's JSON hand-off to the extension (2.325637ms) +✔ setspark config: approvers come from the binding's users, never from the binding's setspark key (3.231525ms) +✔ setspark verbs: required_approvers go out as discord ids from names and come back as names (33.277354ms) +✔ setspark verbs: no Discord user id reaches tool text, whatever shape the service returns it in (13.626319ms) +✔ setspark contract: a decision made with names opens a request the connector accepts; names stored by an old record still refuse (8.598593ms) +✔ setspark keys: read per call, one printable token per file, rotation without a restart (4.570522ms) +✔ setspark idempotency keys: principal, turn id, call index; connector keys name a step (0.54395ms) +✔ setspark http core: json in and out, bearer header, idempotency header, fixed user agent, no key anywhere else (3.444602ms) +✔ setspark http core: error bodies become fixed refusals with code and the 409 fields; server text is data, cut (1014.124706ms) +✔ setspark verbs: a setspark key enables the eight verbs and no counters (0.893219ms) +✔ 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 (9.777572ms) +✔ setspark verbs: no turn refuses every write before any request; bad arguments refuse before any request; reads still work (1.665942ms) +✔ setspark verbs: renderRecord caps long output and hides the accepted snapshot (0.275876ms) +✔ setspark api: bind, add_approval (button and reply) and get use integer request ids and the connector's keys (3.165245ms) +✔ tools: config refuses a missing, symlinked, dotted, non-directory or duplicate root and bad limits (4.637984ms) +✔ tools: every escape is refused with a fixed reason and nothing outside the root is read (5.022056ms) +✔ tools: happy paths list, read a window, and search case-insensitively; dotfiles and symlinks never appear (6.469087ms) +✔ tools: the tool set renders text for the model, records details for the journal, and enforces the per-run budget (5.531425ms) +✔ tools: listing and search caps hold (13.958999ms) +✔ tools: credential shapes are caught; ordinary prose and ids are not (0.942619ms) +✔ 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 (6.371998ms) +✔ tools: an unreadable file under the root is skipped by search and refused by read (1.383966ms) +✔ tools: config accepts write: true only as a boolean, and enables the write tools only then (1.125258ms) +✔ tools: every write outside the fence is refused before any byte lands, and no temp file remains (7.337062ms) +✔ tools: write_file leaves the exact bytes, edit_file replaces one exact match, and the set renders the change as uncommitted (3.499635ms) +✔ tools: a target that changed between the check and the rename is refused and the temp file is removed (1.996341ms) +✔ web: config takes an https or loopback-http SearXNG base url and a bounded fetch cap (3.790844ms) +✔ web: address rules refuse every private, loopback, link-local, mapped and multicast form (2.953583ms) +✔ web: web_fetch refuses bad urls, private hosts, rebinding names, non-https redirects, too many hops, error status, non-text bodies, and times out (1040.353738ms) +✔ 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.394779ms) +✔ web: html to text drops scripts, styles and comments, decodes entities and keeps block breaks (0.501611ms) +✔ 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.758476ms) +✔ 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.847279ms) +ℹ tests 178 +ℹ suites 0 +ℹ pass 178 +ℹ fail 0 +ℹ cancelled 0 +ℹ skipped 0 +ℹ todo 0 +ℹ duration_ms 2819.110201 diff --git a/agents/filbert/work/slice1-s4-review/r1-cand-suite-auth.txt b/agents/filbert/work/slice1-s4-review/r1-cand-suite-auth.txt new file mode 100644 index 00000000..ac228760 --- /dev/null +++ b/agents/filbert/work/slice1-s4-review/r1-cand-suite-auth.txt @@ -0,0 +1,17 @@ +OK status with missing harness credential exits 3 and still lists accounts +OK status reports harness credential (read-only) + mosaic accounts +OK api key material never reaches output +OK oauth token material never reaches output +OK unparseable credential file exits 2 +OK symlinked credential file exits 4 +OK env-side credential names reported +OK env var values never reach output +OK accounts without an accounts dir reports none and creates nothing +OK accounts lists files and marks the active one +OK loose account perms flagged in listing +OK agent --auth with missing account file refuses (exit 4) +OK agent --auth with non-0600 account file refuses +OK agent --auth with invalid account name refuses +OK auth.sh without valid config refuses + +selftest: 15 passed, 0 failed diff --git a/agents/filbert/work/slice1-s4-review/r1-cand-suite-conductor.txt b/agents/filbert/work/slice1-s4-review/r1-cand-suite-conductor.txt new file mode 100644 index 00000000..d6b7a1ee --- /dev/null +++ b/agents/filbert/work/slice1-s4-review/r1-cand-suite-conductor.txt @@ -0,0 +1,55 @@ +Note: switching to 'd68aa20f278816f53583fb73bd3cb1e8cdc0fdb2'. + +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 'd68aa20f278816f53583fb73bd3cb1e8cdc0fdb2'. + +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/r1-cand-suite-config.txt b/agents/filbert/work/slice1-s4-review/r1-cand-suite-config.txt new file mode 100644 index 00000000..76c05ef7 --- /dev/null +++ b/agents/filbert/work/slice1-s4-review/r1-cand-suite-config.txt @@ -0,0 +1,26 @@ +OK absent adapter defaults to pi +OK adapter mock validates (exit 0) +OK unsupported adapter exits 2 (exit 2) +OK env exports adapter +OK bootstrap creates default when absent (exit 0) +OK bootstrap wrote config file +OK bootstrap is idempotent on existing config (exit 0) +OK bootstrap did not rewrite existing config +OK validate missing config exits 3 (exit 3) +OK malformed JSON exits 2 (exit 2) +OK unsupported configVersion exits 2 (exit 2) +OK unknown top-level key exits 2 (exit 2) +OK unknown execution key exits 2 (exit 2) +OK unsupported backend exits 2 (exit 2) +OK unsupported environment exits 2 (exit 2) +OK relative dataRoot exits 2 (exit 2) +OK non-canonical dataRoot exits 2 (exit 2) +OK filesystem root dataRoot exits 2 (exit 2) +OK home directory dataRoot exits 2 (exit 2) +OK dataRoot containing config dir exits 2 (exit 2) +OK control character in provider exits 2 (exit 2) +OK symlinked config file exits 2 (exit 2) +OK env exports resolve correctly +OK failed validation modified nothing + +selftest: 24 passed, 0 failed diff --git a/agents/filbert/work/slice1-s4-review/r1-cand-suite-discord.txt b/agents/filbert/work/slice1-s4-review/r1-cand-suite-discord.txt new file mode 100644 index 00000000..b013f9ff --- /dev/null +++ b/agents/filbert/work/slice1-s4-review/r1-cand-suite-discord.txt @@ -0,0 +1,70 @@ +toolchain: node v26.8.1 + +OK syntax: packages/discord/src/approvals.mjs +OK syntax: packages/discord/src/authorize.mjs +OK syntax: packages/discord/src/binding.mjs +OK syntax: packages/discord/src/cli.mjs +OK syntax: packages/discord/src/connector.mjs +OK syntax: packages/discord/src/context.mjs +OK syntax: packages/discord/src/engine-pi.mjs +OK syntax: packages/discord/src/errors.mjs +OK syntax: packages/discord/src/gateway.mjs +OK syntax: packages/discord/src/git.mjs +OK syntax: packages/discord/src/journal.mjs +OK syntax: packages/discord/src/notify.mjs +OK syntax: packages/discord/src/rest.mjs +OK syntax: packages/discord/src/setspark.mjs +OK syntax: packages/discord/src/tools.mjs +OK syntax: packages/discord/src/web.mjs +OK syntax: packages/discord/bin/git-credential.mjs +OK syntax: packages/discord/extension/tools.mjs +OK syntax: packages/discord/tests/approvals.test.mjs +OK syntax: packages/discord/tests/authorize.test.mjs +OK syntax: packages/discord/tests/binding.test.mjs +OK syntax: packages/discord/tests/connector.test.mjs +OK syntax: packages/discord/tests/context.test.mjs +OK syntax: packages/discord/tests/engine.test.mjs +OK syntax: packages/discord/tests/fake-pi.mjs +OK syntax: packages/discord/tests/gateway.test.mjs +OK syntax: packages/discord/tests/git.test.mjs +OK syntax: packages/discord/tests/helpers.mjs +OK syntax: packages/discord/tests/journal.test.mjs +OK syntax: packages/discord/tests/notify.test.mjs +OK syntax: packages/discord/tests/recover.test.mjs +OK syntax: packages/discord/tests/rest.test.mjs +OK syntax: packages/discord/tests/setspark.test.mjs +OK syntax: packages/discord/tests/tools.test.mjs +OK syntax: packages/discord/tests/web.test.mjs +OK syntax: packages/discord/fixtures/claim-worker.mjs +OK syntax: packages/discord/fixtures/legacy-owner-worker.mjs +OK syntax: scripts/discord.sh +OK syntax: scripts/discord-service.sh +OK packages/discord declares no dependencies +OK no bot-token-shaped string in packages/discord +OK fixture binding uses placeholder ids only +OK fixture binding validates +OK real pi with the extension exposes exactly list_dir, read_file, search and no built-in tool +OK real pi with a writable root exposes exactly the three reads plus write_file and edit_file, and writes nothing at start +OK real pi with a web key exposes the three reads plus web_fetch and web_search, and no write tool without a writable root +OK real pi with a git root exposes the reads, writes and the four git verbs, commits nothing at start, and never shows the token +OK real pi with protocol vault adds reserve_id to the git verbs +OK real pi with a setspark key exposes the reads and the eight record verbs, no counters, and never shows the key +OK real pi refuses a git key on a read-only root (fail closed) +OK real pi with the pilot flags (--no-tools) exposes no tool at all +OK real pi exits non-zero without MOSAIC_DISCORD_TOOLS: no session, no tools (fail closed) +OK a failing nested test fails the run under a parent runner's NODE_TEST_CONTEXT +OK node --test packages/discord/tests/ (ℹ pass 178) +OK scripts/discord.sh --help exits 0 +OK scripts/discord.sh check without a binding exits 4 +OK scripts/discord.sh recover without a binding exits 4 +OK scripts/discord.sh reload without a binding exits 4 +OK scripts/discord-service.sh without a command exits 4 +OK service unit renders with the repository path, a supervised run as the main process, exit 3 never retried, and reload as SIGHUP +OK service install writes the rendered unit (0644) and leaves no temp file +OK service install a second time reports unchanged +OK systemd-analyze verify accepts the rendered unit +OK service uninstall removes the unit file +OK service install with an unknown flag exits 4 +OK service install with USER unset finishes and names the account for lingering + +discord suite: 66 passed, 0 failed diff --git a/agents/filbert/work/slice1-s4-review/r1-cand-suite-extension-package.txt b/agents/filbert/work/slice1-s4-review/r1-cand-suite-extension-package.txt new file mode 100644 index 00000000..32b44172 --- /dev/null +++ b/agents/filbert/work/slice1-s4-review/r1-cand-suite-extension-package.txt @@ -0,0 +1,21 @@ +OK initial ordinary-file install +OK installed tree matches canonical source +OK installed tree has no symlinks +OK check detects installation drift +OK sync refuses to overwrite installation drift +OK check detects an extra destination file +OK check detects an extra destination directory +OK check rejects a destination symlink +OK sync accepts a canonical source update +OK updated installation matches canonical source +scripts/test-extension-package.sh: line 14: 2204219 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/r1-cand-suite-foundation.txt b/agents/filbert/work/slice1-s4-review/r1-cand-suite-foundation.txt new file mode 100644 index 00000000..6338e1e6 --- /dev/null +++ b/agents/filbert/work/slice1-s4-review/r1-cand-suite-foundation.txt @@ -0,0 +1,53 @@ +toolchain: node v26.8.1, python 3.12.8, jsonschema 4.26.0 + +OK syntax: scripts/foundation-inspect.mjs +OK syntax: scripts/foundation/strict-json.mjs +OK syntax: scripts/foundation/canonical.mjs +OK syntax: scripts/foundation/resolve.mjs +OK syntax: scripts/foundation/validate-record.mjs +OK syntax: scripts/foundation/fixtures/build-fixtures.mjs +OK syntax: scripts/foundation/canonical.test.mjs +OK syntax: scripts/foundation/cli.test.mjs +OK syntax: scripts/foundation/fixtures.test.mjs +OK syntax: scripts/foundation/resolve.test.mjs +OK syntax: scripts/foundation/strict-json.test.mjs +OK syntax: scripts/foundation/verify-schema.py (ast only; no bytecode written) +OK fixture generator runs +OK checked-in fixtures/bundles equal a fresh generation +OK checked-in fixtures/raw equal a fresh generation +OK checked-in fixtures/index.json equal a fresh generation +OK checked-in demo bundles equal a fresh generation +OK a failing nested test fails the run under a parent runner's NODE_TEST_CONTEXT +OK node --test scripts/foundation/ (ℹ pass 80) +OK differential schema oracle: PASS: differential schema oracle (finite corpus; compatibility evidence, not equivalence proof) + platform witness: strftime('%Y') for year 999 -> '999' (pinned checker refuses years 0001..0999) + node v26.8.1; corpus 1568 records (38 pinned fixtures, 478 unique bundle records, 1052 typeCase/mutation/lexical cases) + schema column: agree-valid 540, agree-invalid 991, DISAGREEMENTS 0; strict-only (parser-bound) cases: 27; unsupported-kind records not schema-assessed by the inspector: 10 + profile column (schema-valid records only): profile-valid 510, profile-invalid 30 + profile refusals asserted: 30 schema-agreed-valid records refused only by the strict typed-string profile (rule profile-pattern-mismatch), 12 declared by name; 73 named probes verified against declared schema/profile columns +OK oracle: zero schema-column disagreements with the pinned checker +OK oracle: strict-only profile refusals are counted and asserted +OK demo: permitted read preview exits 0 (exit 0) +OK demo: permitted file.change preview exits 0 (exit 0) +OK demo: assignment.change proposal is unresolved (exit 3) (exit 3) +OK demo: revoked registration is refused (exit 3) (exit 3) +OK demo: message is not authority (exit 3) (exit 3) +OK usage: no arguments exits 2 (exit 2) +OK io: missing file exits 4 (exit 4) +OK io: directory exits 4 (exit 4) +OK io: symlink exits 4 (O_NOFOLLOW) (exit 4) +OK bound: oversize fixture exits 2 (exit 2) +OK profile: one final LF in a typed selection id is refused before admission (exit 2) (exit 2) +OK profile: two final LFs fail the schema pattern itself (exit 2) (exit 2) +OK profile: escaped newlines in free-form text stay allowed (exit 0) (exit 0) +OK profile refusal is invalid-request/profile-pattern-mismatch with selection and operation withheld, value not echoed +OK text output starts with the disclaimer +OK json output is valid JSON with result allowed and exactly the charter §7 fields +OK json golden matches byte-for-byte +OK sandboxed bundle run (env -i, PATH=/nonexistent) produced the unresolved proposal +OK sandbox inventory (path/type/size/mode/uid/gid/inode/mtime/sha256) unchanged by runs +OK canary never printed (bundle run and credential-file run) +OK a non-bundle JSON file is refused at the shape gate, not read into output +OK no field of the non-bundle file is echoed + +selftest: 44 passed, 0 failed diff --git a/agents/filbert/work/slice1-s4-review/r1-cand-suite-queue.txt b/agents/filbert/work/slice1-s4-review/r1-cand-suite-queue.txt new file mode 100644 index 00000000..41dc5093 --- /dev/null +++ b/agents/filbert/work/slice1-s4-review/r1-cand-suite-queue.txt @@ -0,0 +1,35 @@ +toolchain: node v26.8.1, git version 2.55.0 + +OK syntax: packages/queue/src/cli.mjs +OK syntax: packages/queue/src/errors.mjs +OK syntax: packages/queue/src/io.mjs +OK syntax: packages/queue/src/lock.mjs +OK syntax: packages/queue/src/queue.mjs +OK syntax: packages/queue/src/review.mjs +OK syntax: packages/queue/src/store.mjs +OK syntax: packages/queue/tests/commit.test.mjs +OK syntax: packages/queue/tests/data.test.mjs +OK syntax: packages/queue/tests/dispatch.test.mjs +OK syntax: packages/queue/tests/helpers.mjs +OK syntax: packages/queue/tests/lock.test.mjs +OK syntax: packages/queue/tests/migration.test.mjs +OK syntax: packages/queue/tests/review.test.mjs +OK syntax: packages/queue/tests/store.test.mjs +OK syntax: packages/queue/tests/write.test.mjs +OK syntax: packages/queue/tests/fixtures/fake-gitea.mjs +OK syntax: packages/queue/tests/fixtures/kill-at.mjs +OK syntax: packages/queue/tests/fixtures/lock-child.mjs +OK syntax: packages/queue/tests/fixtures/mosaic-pre-a2.sh +OK syntax: scripts/queue-commit.sh +OK syntax: scripts/git-hooks/pre-commit +OK syntax: scripts/mosaic +OK queue-commit.sh, the guard and scripts/mosaic are executable +OK packages/queue declares no dependencies +ℹ tests 148 +ℹ pass 148 +ℹ fail 0 +OK node --test packages/queue/tests/ +OK scripts/mosaic queue help +skip queue verify and render --check: this checkout (/home/jwoltje/filbert-scratch/r39/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/r1-cand-suite-release.txt b/agents/filbert/work/slice1-s4-review/r1-cand-suite-release.txt new file mode 100644 index 00000000..1d6ed050 --- /dev/null +++ b/agents/filbert/work/slice1-s4-review/r1-cand-suite-release.txt @@ -0,0 +1,7 @@ +OK valid RELEASE resolves (exit 0) +OK invalid RELEASE exits 1 (exit 1) +OK missing RELEASE exits 1 (exit 1) +OK valid RELEASE leaves image tag consistent with version +skip state-machine cases (docker daemon unavailable) + +selftest: 4 passed, 0 failed diff --git a/agents/filbert/work/slice1-s4-review/r1-cand-suite-task.txt b/agents/filbert/work/slice1-s4-review/r1-cand-suite-task.txt new file mode 100644 index 00000000..85aa46f0 --- /dev/null +++ b/agents/filbert/work/slice1-s4-review/r1-cand-suite-task.txt @@ -0,0 +1,33 @@ +OK valid task validates (exit 0) +OK unknown task key exits 2 (exit 2) +OK unsupported taskVersion exits 2 (exit 2) +OK invalid task id exits 2 (exit 2) +OK empty prompt exits 2 (exit 2) +OK NUL in expectExact exits 2 (exit 2) +OK out-of-range timeout exits 2 (exit 2) +OK missing mission file exits 4 (exit 4) +OK task with valid mission validates (exit 0) +OK invalid mission exits 2 (exit 2) +OK validate missing task exits 4 (exit 4) +OK validation does not modify the task file +OK prune dry-run exits 0 (exit 0) +OK dry-run deleted nothing +OK prune --keep=2 --yes removes oldest (exit 0) +OK kept exactly 2 newest runs +OK newest run kept, oldest pruned +OK append-only receipt written (3 entries) +OK sessions/workspaces untouched by prune +OK prune with invalid keep exits 4 (exit 4) +skip adapter seam cases (docker daemon unavailable) +skip workspace/capability cases (docker daemon unavailable) +skip live task cases (docker unavailable) +OK onboard without name exits 4 (non-interactive) (exit 4) +OK onboard --name renders profile (exit 0) +OK profile written +OK canon structure: required filled, optional placeholdered +OK canon sections present +FAIL user recall run succeeds (exit 1) +FAIL recalled user name (response: ) +OK no agent identity on headless run + +selftest: 26 passed, 2 failed diff --git a/agents/filbert/work/slice1-s4-review/r1-gate-summary.txt b/agents/filbert/work/slice1-s4-review/r1-gate-summary.txt new file mode 100644 index 00000000..4c6e71e7 --- /dev/null +++ b/agents/filbert/work/slice1-s4-review/r1-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 +cand suite-conductor exit 0 +cand suite-config exit 0 +cand suite-discord exit 0 +cand suite-extension-package exit 0 +cand suite-foundation exit 0 +cand suite-queue exit 0 +cand suite-release exit 0 +cand suite-task exit 1 +base node-bus exit 0 +base node-business exit 0 +base node-discord exit 0 +base suite-auth exit 0 +base suite-conductor exit 0 +base suite-config exit 0 +base suite-discord exit 0 +base suite-extension-package exit 0 +base suite-foundation exit 0 +base suite-queue exit 0 +base suite-release exit 0 +base suite-task exit 1 diff --git a/agents/filbert/work/slice1-s4-review/r1-mut-summary.txt b/agents/filbert/work/slice1-s4-review/r1-mut-summary.txt new file mode 100644 index 00000000..dea2a84a --- /dev/null +++ b/agents/filbert/work/slice1-s4-review/r1-mut-summary.txt @@ -0,0 +1,34 @@ +M1 killed (1) +M2 killed (1) +M3 killed (2) +M4 killed (1) +M5 killed (1) +M6 killed (9) +M7 killed (4) +M8 SURVIVED +M9 killed (1) +M10 killed (1) +M11 killed (1) +M12 killed (1) +M13 killed (2) +M14 killed (1) +M15 killed (2) +M16 killed (2) +M17 killed (1) +M18 SURVIVED +M19 killed (1) +M20 killed (1) +M21 killed (1) +M22 SURVIVED +M23 SURVIVED +M24 SURVIVED +M25 killed (1) +M26 SURVIVED +M27 SURVIVED +M28 SURVIVED +M29 SURVIVED +M30 killed (1) +M31 killed (1) +M32 killed (2) +M33 SURVIVED +M34 killed (1) diff --git a/agents/filbert/work/slice1-s4-review/r1-node24.txt b/agents/filbert/work/slice1-s4-review/r1-node24.txt new file mode 100644 index 00000000..b84afbf2 --- /dev/null +++ b/agents/filbert/work/slice1-s4-review/r1-node24.txt @@ -0,0 +1,224 @@ +v24.21.0 +✔ inbox lists only decisions routed to the human, with what approving authorizes and how to decide (150.435882ms) +✔ decide resolves by id prefix with --yes, and the trail shows it in broker order with the task to follow (200.178803ms) +✔ decide refuses without a terminal or --yes, on an unknown option and on a short reference (118.123019ms) +✔ decide prints a declining choice as declining (134.534ms) +✔ an unknown outcome is reported once and never resent (128.982594ms) +✔ every human command refuses inside an agent run before it touches the bus (123.245098ms) +✔ usage errors exit 4; no business and no host is a usage error (76.722535ms) +✔ agents and tasks print through the broker (70.33817ms) +✔ notify.json: missing, loose, malformed and extra keys refuse; a binding or null passes (2.89813ms) +✔ bootConfig builds the broker's boot message for one business, with no trackers key when no project names one (110.715533ms) +✔ trackers come from the tracker.* variables of the one project that names a tracker project (64.50296ms) +✔ two projects that each name a tracker project refuse, since the boot shape holds one (64.507858ms) +✔ a business without tracker.baseUrl gets no trackers entry (47.644944ms) +✔ an unknown business and a broken system config refuse with exit 3 (116.847144ms) +✔ empty views say so (3.14604ms) +✔ the trail keeps the broker's order and names a decision's task without its rows (4.254618ms) +✔ tasks print the tracker fields the snapshot carries (0.681733ms) +✔ the host boots the broker, binds a launch in process, and the notifier DMs a blocking decision exactly once (1142.966359ms) +✔ a notifier that dies takes the host down with exit 1, so the unit restarts the pair (238.34223ms) +✔ a notifier that refuses stops the broker and the host refuses with exit 3 (269.794327ms) +✔ bus start refuses with exit 3 and the code when the broker refuses to boot; bus status names the lock (211.442714ms) +✔ bus start refuses with exit 3 without a notifier config (88.527484ms) +✔ bus start runs until bus stop; status reports it while it runs (767.121333ms) +✔ bus-service.sh renders the unit and installs it into a given directory (121.357435ms) +✔ zoned uses the IANA zone across DST (58.838911ms) +✔ each open blocking decision is DM'd once, across polls and a restart; non-blocking ones are not (152.651058ms) +✔ a failed DM is journaled, backs off, and is retried until it lands (196.852785ms) +✔ the digest goes at 08:00 Chicago once a day, with blocking ones marked as DM'd (126.049679ms) +✔ a late start with no digest for the day sends one at once; an empty inbox gets one line (118.867316ms) +✔ an inbox read failure is logged and the next poll retries (1.372407ms) +✔ no Discord id reaches the journal or the log (134.687969ms) +✔ the journal: a torn last line is skipped, a malformed line or a loose mode refuses (1.840228ms) +✔ digest content stays within Discord's 2000 characters (0.364783ms) +✔ runLoop never overlaps ticks and stops after the one in flight (112.057763ms) +✔ the transport writes {business, verb, args} to the child and reads its JSON (60.499405ms) +✔ a bus code on stderr becomes the exit code; garbage and timeouts are outcome problems (2287.730105ms) +✔ busExit and refuseInsideAgent (0.344819ms) +✔ approvals: a request is validated before anything is posted; the rendering shows names and never ids (3.904371ms) +✔ approvals: the ledger is appended and folded into open requests with bind and approval states (1.869811ms) +✔ approvals: a reply approves only when it points at a request, says exactly approve, and comes from a listed approver once (0.668165ms) +✔ approvals: a button approves only on its own request message with the matching custom id (0.568448ms) +✔ approvals flow: a turn that opened a request posts the message with the button, records it, binds it, and both approvers approve (29.744837ms) +✔ approvals flow: a non-approver, a repeat, a wrong custom id and a service refusal each get their fixed line and a drop entry (10.792007ms) +✔ approvals flow: an invalid request from the model, a refused post, and no api client are recorded and post nothing (12.091111ms) +✔ approvals flow: start retries a bind and an approval left as unknown, under their original keys (4.453013ms) +✔ authorize: open channel, listed user (2.631297ms) +✔ authorize: wrong guild (0.215547ms) +✔ authorize: no guild (DM) (0.203643ms) +✔ authorize: unlisted channel (0.203563ms) +✔ authorize: unknown channel, no info (0.198187ms) +✔ authorize: thread of listed parent (0.228421ms) +✔ authorize: thread of unlisted parent (0.158194ms) +✔ authorize: text channel that is not a thread and not listed (0.144199ms) +✔ authorize: unlisted user (1.375392ms) +✔ authorize: no author (0.429395ms) +✔ authorize: bot author (listed id, bot flag) (0.354272ms) +✔ authorize: system author (0.382177ms) +✔ authorize: the bot itself (0.105437ms) +✔ authorize: webhook (0.119066ms) +✔ authorize: mention channel without mention (0.761486ms) +✔ authorize: mention channel with bot mention (0.154281ms) +✔ authorize: mention channel with @everyone only (0.255599ms) +✔ authorize: mention channel mentioning someone else (0.105281ms) +✔ authorize: mention channel, content says @bot but mentions empty (0.095571ms) +✔ authorize: private thread under mention channel, mentioned (0.095101ms) +✔ authorize: private thread under mention channel, not mentioned (0.086938ms) +✔ authorize: thread in another guild per channel info (0.084159ms) +✔ authorize: not an object (0.074414ms) +✔ authorize: no id (0.072953ms) +✔ authorize: oversize content is accepted and flagged (0.081435ms) +✔ authorize: exactly the limit is not oversize (0.083942ms) +✔ authorize: a user's channel allowlist drops them outside it, threads count as the parent, others are unaffected (0.542027ms) +✔ authorize: order puts wrong guild before user, and user before channel (no channel lookup for strangers) (0.169126ms) +✔ binding: a complete binding validates and is frozen (3.336891ms) +✔ binding: unknown key, missing field, wrong type refuse with exit 2 (2.35773ms) +✔ binding: empty allowlists refuse (3.591544ms) +✔ binding: a user's channel allowlist must be non-empty, listed and unique; absent means every listed channel (3.436071ms) +✔ reloadDiff: reloadable keys are summarised by id; every fixed key refuses with exit 2 (5.557911ms) +✔ binding: dmRecipient is optional, a snowflake, one of the listed users, and a fixed key (1.793604ms) +✔ binding: file must be 0600, regular, not a symlink (2.449567ms) +✔ binding: token file mode, symlink, emptiness and shape are checked; token never appears in errors (3.969886ms) +✔ cli: check refuses a non-0600 token file with exit 2 before any network use (156.289692ms) +✔ context files: absolute paths, traversal, symlinks and out-of-repo targets refuse; in-repo files resolve (2.362516ms) +✔ cli: check refuses a missing context file and a missing binding with exit 2; usage is exit 4 (449.667173ms) +✔ cli: reload validates the file first (exit 2), then needs a live owner (exit 1); usage is exit 4 (219.503697ms) +✔ cli: run refuses when STOP is present, before any network use (129.18445ms) +✔ binding: tools is optional, validated strictly, a fixed key for reload, and its roots are resolved against the data root (3.395845ms) +✔ binding: a git key is validated at load and reaches the extension whole, and only on a writable root (1.395129ms) +✔ 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.669603ms) +✔ delivery: refused and unknown outcomes are journaled; a later chunk is not sent after a failure (38.455002ms) +✔ delivery: restart with an unknown entry re-sends the same nonce once and reconciles before accepting traffic (3.515252ms) +✔ delivery: an unknown entry older than the dedupe window is marked refused, not re-sent; a still-unknown one refuses start (1.892221ms) +✔ delivery: repeated unknown reconciliations never refresh the dedupe window; the original intent time decides (1.942892ms) +✔ turn: a failed engine turn posts the fixed line, never model output, and writes a failed record (6.720968ms) +✔ turn: a second message during a turn is held by the engine, both get their own reply and record (34.793536ms) +✔ turn: a thread under a listed channel is answered in the thread; an unknown thread is looked up once (5.684398ms) +✔ drop: an unlisted user gets silence and one drop line; no inbox entry, no REST call, no engine call (3.760396ms) +✔ drop: an oversize message is accepted into the inbox, answered with the fixed line and journaled as a drop (4.591196ms) +✔ restart: an inbox with three ids and a replay of the same three produces zero turns (43.702178ms) +✔ stop: STOP present refuses start; STOP written while running refuses new turns and the current one finishes (33.167424ms) +✔ ceiling: the ceiling plus one is refused and journaled; one fixed line per UTC day; a new day accepts again (7.065196ms) +✔ ceiling: a burst arriving while turns are still running cannot queue past the ceiling (5.348704ms) +✔ ceiling: a turn interrupted by a crash still counts after restart; admissions are durable (3.188081ms) +✔ ceiling: the daily notice survives a same-day restart; one delivery attempt in total, even when the first attempt crashed mid-flight (5.924764ms) +✔ duplicate: the same event delivered twice while the thread lookup is held yields one prompt, one admission and one reply (3.004252ms) +✔ journal: no token-shaped string and no model output on the drop path reaches disk (1.342169ms) +✔ receipt: an admitted message gets one eyes reaction on the inbound message; drops and refusals get none; a failed reaction is recorded and does not fail the turn (3.204885ms) +✔ receipt: Discord refusing the reaction leaves the turn intact and records ok false (5.018006ms) +✔ reload: a new user is silent before and answered after; a removed channel goes silent; a lower ceiling applies at once (8.783793ms) +✔ reload: a fixed key refuses with exit 2 and the old binding stays in force (2.410026ms) +✔ tools: with a tools binding the turn record lists every read and its outcome; without one the field is null (6.741756ms) +✔ context: the Discord block names the server, channels and modes, and states the rules from Q15 and Q16 (3.597169ms) +✔ context: with tools the block names the roots, keeps file content as data, and says to state refusals plainly (0.977235ms) +✔ context: a writable root adds the write rules and says a write is real only once Jason commits (3.142584ms) +✔ context: the envelope is one bracketed line then the text; names cannot break the line (1.884853ms) +✔ context: a git root swaps the terminal-commit line for the git verbs, and a vault root adds the id protocol (2.199674ms) +✔ context: assembleContext concatenates files in launcher format and appends the block; sha256 is stable (1.445207ms) +✔ context: splitReply keeps paragraphs together under the limit and splits long ones at lines, spaces, then hard (0.928253ms) +✔ engine: buildPiArgs carries the fixed flags, engine settings, session dir and prompt file (2.806951ms) +✔ engine: with tools, buildPiArgs turns pi's own tools off, loads the extension explicitly and allowlists exactly our three (0.643047ms) +✔ engine: a run with tool turns settles once, on the answer, with every tool call in the result (67.856538ms) +✔ engine: a run that ends on a tool-only turn fails the prompt as empty; a retried run settles on the real end (53.284192ms) +✔ engine: one prompt, one turn, text and usage come back (53.025172ms) +✔ engine: a prompt while streaming is held until pi settles, then sent as its own run, and answered in order (346.769288ms) +✔ engine: a held prompt that times out before pi settles fails on its own and is never sent (241.93361ms) +✔ engine: timeout sends abort and fails only that turn; the process stays (106.049225ms) +✔ engine: tool events from a run that outlived its timeout never land in the next prompt's record (240.039179ms) +✔ engine: a prompt after a turn that timed out before its agent_start waits for pi to settle instead of being refused (132.438308ms) +✔ 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.508216ms) +✔ engine: a timed-out turn pi starts only after the grace never answers a later prompt (617.115448ms) +✔ engine: late events of a run past its grace, before pi exits, answer nothing and nothing more is sent (early prompt response) (1.456684ms) +✔ engine: late events of a run past its grace, before pi exits, answer nothing and nothing more is sent (late prompt response) (0.665191ms) +✔ engine: a timed-out run pi did start outlives the grace; the next prompt goes out when it ends (426.354012ms) +✔ engine: a malformed JSONL line fails the turn, not the process (23.667112ms) +✔ engine: a turn that ends in error rejects with the error code; process exit fails pending turns (45.734903ms) +✔ gateway: hello -> identify with intents, ready, heartbeat with jitter, ack (5.207451ms) +✔ gateway: missed ack closes the socket and resumes with the last sequence (1.91995ms) +✔ gateway: op 7 reconnect resumes; op 9 non-resumable re-identifies (0.679031ms) +✔ gateway: op 9 resumable resumes (0.482662ms) +✔ gateway: close 4014 is fatal, reports the missing intent, never reconnects (1.235122ms) +✔ gateway: 4004 and 4013 are fatal too; 1006 reconnects with identify when no session (0.765713ms) +✔ gateway: close() is final and unparseable frames are ignored (0.578199ms) +✔ git: config validation is strict, needs write: true, a work tree and a private token file (172.457051ms) +✔ git: the child environment drops every host git config, names one helper, and carries the token path only for origin (98.62783ms) +✔ git: status reports the branch, ahead/behind and changed paths, and refuses off the named branch or mid-merge (133.981392ms) +✔ git: parseStatus reads porcelain v2 including renames and conflicts (0.309823ms) +✔ git: a commit stages exactly the named files, carries the seat author and the requester trailer, and pushes at once (115.185496ms) +✔ git: commit refusals: message, paths, requester, nothing to commit, and an index that already holds other work (112.794483ms) +✔ git: a commit whose push fails is still a commit, says so, and the next commit's push carries both (D6) (112.277117ms) +✔ git: pull is fast-forward only; a diverged origin or dirty local files refuse with nothing merged (203.637551ms) +✔ git: push pushes the named branch only and reports up to date (66.93841ms) +✔ git: no token value or token path ever reaches a git argument list; outputs are masked and capped (104.082841ms) +✔ git: the credential helper answers get over https from a private file and nothing else (204.297929ms) +✔ git: the vault protocol validates before a commit, honours another owner's lock, reserves ids, and locks around writes (512.581464ms) +✔ lock: the claim is exclusive; a second start against a live owner refuses (5.592493ms) +✔ lock: a stale lock (dead owner, reused pid, or record without start) refuses run and is never signaled; only unlock clears it (7.19046ms) +✔ lock: an incomplete claim (directory without owner record) is busy and refuses run; unlock clears it (2.568321ms) +✔ lock: an owner record that exists but cannot be read is invalid: never signaled, never removed, never claimed over (20.07439ms) +✔ 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.344777ms) +✔ 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 (422.903726ms) +✔ lock: identity syntax; only canonical unsigned decimal start ticks and lowercase boot uuids are identities (0.457342ms) +✔ lock: a process whose start marker or boot id cannot be read refuses to claim (0.517485ms) +✔ lock: a live pid whose identity cannot be read right now is unknown: never signaled, never removed, never claimed over (0.975483ms) +✔ lock: four processes racing for the same binding; exactly one claims it and the others refuse (49.395811ms) +✔ lock: stale handoff; concurrent starts over a stale lock all refuse, nothing reclaims, one unlock then exactly one live owner (130.119921ms) +✔ lock: four-party schedule; claims landing inside an unlock's gap never survive, one unlock leaves no owner and no residue (70.542802ms) +✔ notices: a kind is recorded per UTC day and found again (0.556458ms) +✔ notify: the DM channel opens once, every send carries the nonce, and only the message id comes back (7.224783ms) +✔ notify: refusals and unknowns surface as RestOutcome without ids; a refused send reopens the channel next time (2.877674ms) +✔ notify: no dmRecipient, a non-0600 binding or token, or a missing binding refuse before any network use (1.789212ms) +✔ recover: nothing to do is clean; a lock whose owner is gone or that has no record is cleared and STOP ends up absent (47.696558ms) +✔ recover: an operator STOP refuses with exit 3 and is never removed, whatever the lock says (58.960783ms) +✔ recover: a brake written during the unlock wins; STOP stays with both lines and the start is refused (77.434184ms) +✔ recover: a held binding refuses with exit 3 and writes no STOP: live owner, alive pid without verifiable identity, unreadable record (82.63822ms) +✔ cli: recover exits 0 when ready, 3 behind a brake or a held binding, and run's own STOP refusal is 3 (739.571771ms) +✔ rest: createMessage sends nonce, enforce_nonce, empty allowed_mentions and a soft reply reference (3.8089ms) +✔ rest: 429 waits retry_after and retries; 4xx is refused; 5xx and socket errors are unknown (3.466246ms) +✔ rest: content and nonce limits are enforced locally; typing never throws (0.862413ms) +✔ rest: react PUTs the encoded emoji on the inbound message for @me; 2xx is true, anything else is false and never throws (0.916757ms) +✔ rest: createDm posts the recipient and resolves the channel id; 4xx refused, 5xx unknown, bad id never sent (1.312148ms) +✔ setspark config: a bare https or loopback origin, a private key file, a principal (7.18167ms) +✔ setspark config: reaches the tools config and the binding as a fixed key (3.947701ms) +✔ setspark config: the binding's key survives resolveToolRoots and the engine's JSON hand-off to the extension (2.145815ms) +✔ setspark config: approvers come from the binding's users, never from the binding's setspark key (3.191331ms) +✔ setspark verbs: required_approvers go out as discord ids from names and come back as names (52.161187ms) +✔ setspark verbs: no Discord user id reaches tool text, whatever shape the service returns it in (24.261416ms) +✔ setspark contract: a decision made with names opens a request the connector accepts; names stored by an old record still refuse (9.563071ms) +✔ setspark keys: read per call, one printable token per file, rotation without a restart (4.746867ms) +✔ setspark idempotency keys: principal, turn id, call index; connector keys name a step (0.72222ms) +✔ setspark http core: json in and out, bearer header, idempotency header, fixed user agent, no key anywhere else (3.763035ms) +✔ setspark http core: error bodies become fixed refusals with code and the 409 fields; server text is data, cut (1026.486885ms) +✔ setspark verbs: a setspark key enables the eight verbs and no counters (0.558047ms) +✔ 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.889988ms) +✔ setspark verbs: no turn refuses every write before any request; bad arguments refuse before any request; reads still work (2.382002ms) +✔ setspark verbs: renderRecord caps long output and hides the accepted snapshot (0.237737ms) +✔ setspark api: bind, add_approval (button and reply) and get use integer request ids and the connector's keys (3.045548ms) +✔ tools: config refuses a missing, symlinked, dotted, non-directory or duplicate root and bad limits (3.900058ms) +✔ tools: every escape is refused with a fixed reason and nothing outside the root is read (4.356126ms) +✔ tools: happy paths list, read a window, and search case-insensitively; dotfiles and symlinks never appear (5.103007ms) +✔ tools: the tool set renders text for the model, records details for the journal, and enforces the per-run budget (3.264669ms) +✔ tools: listing and search caps hold (17.80897ms) +✔ tools: credential shapes are caught; ordinary prose and ids are not (1.040674ms) +✔ 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 (30.477334ms) +✔ tools: an unreadable file under the root is skipped by search and refused by read (1.583548ms) +✔ tools: config accepts write: true only as a boolean, and enables the write tools only then (1.419993ms) +✔ tools: every write outside the fence is refused before any byte lands, and no temp file remains (10.106202ms) +✔ tools: write_file leaves the exact bytes, edit_file replaces one exact match, and the set renders the change as uncommitted (3.714621ms) +✔ tools: a target that changed between the check and the rename is refused and the temp file is removed (2.740889ms) +✔ web: config takes an https or loopback-http SearXNG base url and a bounded fetch cap (4.166067ms) +✔ web: address rules refuse every private, loopback, link-local, mapped and multicast form (3.305128ms) +✔ web: web_fetch refuses bad urls, private hosts, rebinding names, non-https redirects, too many hops, error status, non-text bodies, and times out (1042.751174ms) +✔ 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.436221ms) +✔ web: html to text drops scripts, styles and comments, decodes entities and keeps block breaks (0.303589ms) +✔ 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.9654ms) +✔ 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.465095ms) +ℹ tests 215 +ℹ suites 0 +ℹ pass 215 +ℹ fail 0 +ℹ cancelled 0 +ℹ skipped 0 +ℹ todo 0 +ℹ duration_ms 3008.126265 diff --git a/agents/filbert/work/slice1-s4-review/r1-probe.txt b/agents/filbert/work/slice1-s4-review/r1-probe.txt new file mode 100644 index 00000000..0df44b5e --- /dev/null +++ b/agents/filbert/work/slice1-s4-review/r1-probe.txt @@ -0,0 +1,13 @@ +PASS J1a first open skips the torn line and logs it + J1 file after append: "{\"at\":\"x\",\"kind\":\"dm\",\"dec{\"at\":\"y\",\"kind\":\"dm\",\"decision\":\"d2\",\"outcome\":\"confi" +FAIL J1b restart after one more send opens the journal (got exit 3: notify journal line 2 is malformed: /home/jwoltje/filbert-scratch/r39/probe-jkXow7/j1/sent.jsonl) + J2 dir mode 755; open accepted + J3 symlinked journal: accepted + J4 loose-typed confirmed lines: accepted +PASS D1 digest nonce is dg2026-10-08 for every business +PASS D2 digest/DM length sweep (427 inboxes), longest 1997 +PASS D3 zoned 08:00 in CDT and CST; 2026-11-01 13:30Z is 07 CST +PASS D4 digest edge: before 0, after 1 +FAIL D5 TypeError from send: failed 1, outcomes unknown + +5 PASS, 2 FAIL diff --git a/agents/filbert/work/slice1-s4-review/review-r1.md b/agents/filbert/work/slice1-s4-review/review-r1.md new file mode 100644 index 00000000..86811c21 --- /dev/null +++ b/agents/filbert/work/slice1-s4-review/review-r1.md @@ -0,0 +1,169 @@ +# Slice 1 S4, row 39, round 1 review (Filbert) + +Issue #1521, request comment 26841, queue rev 176. Packet: +`agents/rocko/work/slice1-s4/BUILD.md`. Candidate: manifest +`candidate-manifest.sha256`, sha256 +`895a7021805ad3e8d3c47df88a8ceb03d6345ac3bc95059558691c981ea837be`, 29 +files, over `d68aa20f` with `build.patch` (sha256 +`b725998c92c3dae3ceea93059d854778999e27f4ccf908d82e61b47d0c0b872a`). +Brief: `docs/plans/2026-10-04_slice-1.md`, S4. Ruling: lead decision 70. +Second reviewer: Darkwing, comment 26843 (approve, F1 to F4). + +Verdict: **changes.** There is one blocker, B1. A torn journal line turns +into a permanent exit-3 refusal after the next send, which is the case +choice 6 exists to handle. The fix is small. I also ask for Darkwing's F1 +and one test (R2) in round 2. Everything else is a note. + +## Method + +- Detached worktrees at `d68aa20f`, one for the base and one for the + candidate, under `~/filbert-scratch/r39/`. I ran `git apply build.patch` + in the candidate and then `sha256sum -c`: 29 OK. After the mutant run + the tree checks clean against the manifest again. +- `gate.sh` runs the suites one at a time in both trees and tees each + output. `DOCKER_HOST` points at a socket that doesn't exist, so the + Docker cases skip the same way in both. +- `probe.mjs` exercises the notifier journal and digest/DM code directly + (J1 to J4, D1 to D5). +- `mutants.sh` applies 34 single perl substitutions to `packages/cli/src` + and `packages/discord/src`. For each one it runs the cli and discord + node tests, then restores the file. +- Node 24.21.0 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) | 37/37 | n/a | +| node cli + discord (Node 24.21.0) | 215/215 | 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". That matches Rocko's and Sage's +numbers. + +## B1 (blocking): a torn line breaks the journal on the next append + +`openJournal` pops the torn final line, logs it and skips it. It leaves +the file as it is. The next `append` then writes onto the end of the +fragment: + +``` +{"at":"x","kind":"dm","dec{"at":"y","kind":"dm","decision":"d2","outcome":"confi... +``` + +The next start reads that as a malformed middle line and refuses with exit +3 ("notify journal line 2 is malformed"). Probe J1b shows this. Three +things follow: + +- The confirmed record of the d2 send is lost inside the bad line. +- `RestartPreventExitStatus=2 3 4` keeps the unit down, so blocking DMs + and the digest stop until someone edits the journal by hand. +- Choice 6 and the README say a torn final line "is skipped". In practice + it is skipped once, then breaks the journal on the next append. + +The existing test, "the journal: a torn last line is skipped...", only +checks the first open. + +Fix: when the tail is torn, truncate the file to the last newline (or +write a `"\n"`) before the first append, and log either way. Add a test +that opens a journal with a torn tail, appends once and reopens: the +reopen must succeed and must load the appended record. + +## Asked for in round 2 (not blocking on their own) + +- **F1 (Darkwing):** the journal directory is created at 0700, but an + existing 0755 directory is accepted (probe J2). Check the directory mode + as the file mode is checked, or document the exception. +- **R2, a test that DM nonces differ per decision.** Mutant M8 makes + `dmNonce` a constant and survives. The fake Discord doesn't model nonce + dedupe. Under M8, Discord would return the first message for a second + decision's DM inside its dedupe window, and the notifier would journal + it as confirmed. Every DM after the first would then go missing with no + error. Asserting that two decisions get different nonces is one line. + +## Notes (not blocking) + +1. **D1, the digest nonce has no business in it.** The nonce is + `dg`. Two businesses with the same bot and recipient would send + the same nonce on the same day. Discord dedupes the second digest and + the notifier journals it as confirmed. Slice 1 runs one business, so + this doesn't bite yet. `"dg" + sha256(business + day)` truncated would + avoid it. +2. **Host startup race.** `broker.on("exit")` and `notify.on("exit")` are + attached only after the notifier's start reply. If the broker dies + while the notifier starts (up to `START_TIMEOUT_MS`), no listener sees + it, and the host runs with a dead broker. Checking + `broker.exitCode !== null` after attaching the listeners closes the + window. +3. **J3:** a symlinked `sent.jsonl` is accepted, because `openSync(file, + "a")` follows links. The mode and uid checks run on the target. The + directory is the user's own, so this records the boundary only. +4. **J4:** confirmed lines with loose types load, for example a + `decision` that is a number or a digest line with no `day`. They do no + harm, since lookups are by string. +5. **Reader-cap exposure test.** host.test checks that the launch cap is + absent from `/proc//cmdline` and `environ`. It doesn't check the + reader cap sent to the notifier. By inspection it travels only over + IPC. +6. **`dmRecipient` is a FIXED_KEY.** After the binding edit for the live + run, a connector reload refuses the change and keeps the old binding. + The notifier reads the binding fresh at its own start, so it is + unaffected. The running connector keeps its old binding, including any + other change made in the same edit, until it restarts. Sage should know + this before the live run. +7. **F2 and F3 (Darkwing):** I agree they don't block. A refused DM retries + at most every 30 min, forever. `network-online.target` does nothing in + the user manager. +8. **human-cli timeout.** The `spawnSync` timeout kills the direct child. + An inner re-exec child that keeps the pipes open could hold the call + past it. This is untested and lives in the bus shim, not in this row. + +## Mutants + +20 of 34 killed. M19 hung on a stdin prompt under the mutant. I counted +it as killed when I stopped it. + +| Mutant | Result | +|---|---| +| M1 to M7, M9 to M17, M20, M21, M25, M30 to M32, M34 | killed | +| M8 constant DM nonce | **survived** (R2) | +| M18 transport drops `status === null` | survived; equivalent, a signalled child still ends as invalid-response, exit 1 | +| M19 decide drops the no-TTY check | killed (hang) | +| M22 cli drops the `decision-closed` message | survived; test gap, the generic error still exits nonzero | +| M23 cli drops the ambiguous-prefix message | survived; test gap, it falls through to "no open decision", exit 2 | +| M24 `businessFor` uses a stale host.json | survived; test gap, no test for a dead host's state file | +| M26 `trackerFor` counts projects without `tracker.project` | survived; test gap, no fixture has a project without one | +| M27 host exits 0 on child death | survived; near-equivalent, `close()` already maps a nonzero child end to 1 | +| M28 `startHost` skips the live-host check | survived; near-equivalent, the broker's `writer.lock` refuses a second boot | +| M29 `stopHost` skips the cmdline check | survived; test gap, the start-time check already guards pid reuse | +| M33 notifier drops its SIGTERM handler | survived; near-equivalent, the default action still ends it, and KillMode=mixed signals the host only | + +Tests for M22 to M24, M26 and M29 would each be short. They are optional. + +## Darkwing's F4 + +The S1 `baseUrl` validator accepts a path. Darkwing is documenting that in +`packages/tasks`, so it stays out of this row. + +## Files + +- `probe.mjs`, `mutants.sh`, `gate.sh` +- Output: + - `r1-probe.txt` + - `r1-mut-summary.txt` + - `r1-node24.txt` + - `r1-gate-summary.txt` + - `r1-cand-*.txt` and `r1-base-*.txt` (each suite, teed)