diff --git a/agents/filbert/work/slice1-s3-review/gate.sh b/agents/filbert/work/slice1-s3-review/gate.sh index cd8a229b..f2ea878a 100755 --- a/agents/filbert/work/slice1-s3-review/gate.sh +++ b/agents/filbert/work/slice1-s3-review/gate.sh @@ -1,11 +1,11 @@ #!/usr/bin/env bash # Row 38 sequential gate; $1 = tree, $2 = out prefix -T="$1"; P="$2"; O=~/filbert-scratch/r38/out; cd "$T" +T="$1"; P="$2"; O=~/filbert-scratch/r38b/out; cd "$T" for p in tasks 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 $? load $(cut -d' ' -f1 /proc/loadavg)" 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-r38.sock timeout 900 bash "$s" > "$O/$P-suite-$n.txt" 2>&1; echo "$P suite-$n exit $? load $(cut -d' ' -f1 /proc/loadavg)" + env -u NODE_TEST_CONTEXT DOCKER_HOST=unix:///nonexistent-filbert-r38b.sock timeout 900 bash "$s" > "$O/$P-suite-$n.txt" 2>&1; echo "$P suite-$n exit $? load $(cut -d' ' -f1 /proc/loadavg)" done diff --git a/agents/filbert/work/slice1-s3-review/mutants.sh b/agents/filbert/work/slice1-s3-review/mutants.sh index 4de21cd3..7fe97d0f 100755 --- a/agents/filbert/work/slice1-s3-review/mutants.sh +++ b/agents/filbert/work/slice1-s3-review/mutants.sh @@ -86,3 +86,10 @@ run B6 $B "s/this\.#secretCheck\(result\);\n return result;/return result; SV=packages/bus/src/server.mjs run B7 $SV "s/socket\.setTimeout\(tasks\.timeout\);//" run B8 $SV "s/if \(own\) broker\.disconnect\(own\);//" +# Round 2 additions (the r2 fixes) +run N1 $V "s/throw landed \? new BusError\('write-uncertain'\) : failure;/throw failure;/" +run N2 $V "s/await step\(role, method, path, body, id, done\);\n(\s*)landed\+\+;/await step(role, method, path, body, id, done);/" +run N3 $V "s/const fields = \{ \.\.\.taskFields\(r\.json, ctx\.view\.ids\.todo\), labels: sorted\(plan\.labels\) \};/const fields = taskFields(r.json, ctx.view.ids.todo);/" +run N4 $V "s/return x === null \? null : \`\\$\{x\.slice\(0, 19\)\}\.000Z\`;/return x;/" +run N5 $V "s/if \(!after && failure\) throw/if (failure) throw/" +run N6 $V "s/if \(!after && !failure\) \{/if (false) {/" diff --git a/agents/filbert/work/slice1-s3-review/probe.test.mjs b/agents/filbert/work/slice1-s3-review/probe.test.mjs index 54b01ea1..548a4e51 100644 --- a/agents/filbert/work/slice1-s3-review/probe.test.mjs +++ b/agents/filbert/work/slice1-s3-review/probe.test.mjs @@ -1,4 +1,4 @@ -// Filbert, row 38 round 1 probes. Run from the candidate tree: +// Filbert, row 38 probes (round 1, with round 2 additions P1 to P3 at the end; D1 now expects the fix). Run from the candidate tree: // node --test ~/filbert-scratch/r38/probe.test.mjs (with CAND set to the candidate root) import test from 'node:test'; import assert from 'node:assert/strict'; @@ -45,7 +45,7 @@ test('D1 schedule with milliseconds: the poll reports the bot\'s own due date as // The pm's next call with the digest it was handed now conflicts. const c = await code(w.call(pm, 'task.priority.change', { task_ref, priority: 2, expect: r.digest, decision: 'x' })); console.log('D1 expect=returned digest ->', c); - assert.equal(ext.length, 1, 'false external change'); + assert.equal(ext.length, 0, 'r2: no false external change'); }); test('D2 whole seconds: no external change (control)', async (t) => { @@ -125,3 +125,77 @@ test('M1 one task with a 500 in missing() stalls the tick; T1 the tick after rec console.log('T1 next tick ->', await code(w.adapter.tick('demo')), 'external', w.events('task.changed.external').length, 'missing', w.events('task.missing').length); void a, b; }); + +// Round 2. P1: a create with a label and a relation whose final read fails. The fallback snapshot +// must digest the same as the next poll's read, or the poll reports the bot's create as external. +test('P1 create with a label and a relation, final read fails: snapshot matches the poll', async (t) => { + const fake = new FakeVikunja(); + let armed = false; + const wrap = async (u, i = {}) => { + const res = await fake.fetch(u, i); + if (armed && (i.method ?? 'GET') === 'POST' && /\/tasks\/2\/relations$/.test(new URL(u).pathname)) { + armed = false; + fake.fault('GET', /^\/tasks\/2$/, 0); + } + return res; + }; + const w = await ready(t, { fake, fetch: wrap }); + const other = await create(w); + armed = true; + const r = await create(w, { labels: [w.label], relations: [{ kind: 'related', task_ref: other.task_ref }], due_date: '2026-11-01T10:20:30.789Z' }); + const s = w.snapshots(r.task_ref).at(-1); + console.log('P1 create ->', r.task_ref, 'snapshot', s?.source, 'labels', JSON.stringify(s?.fields.labels), 'due', s?.fields.due_date, 'digest==returned', s?.digest === r.digest, 'task.created', w.events('task.created').length); + await new Promise((x) => setTimeout(x, 20)); + await w.adapter.tick('demo'); + const ext = w.events('task.changed.external').filter((e) => e.subject === r.task_ref); + console.log('P1 poll external events for the new task', JSON.stringify(ext.map((e) => e.body.changed))); + assert.equal(ext.length, 0); +}); + +// P2: a PATCH that lands but answers 500 is settled by the step's re-read; then the final read +// fails. landed counts the settled step, so the verb succeeds. +test('P2 step settled by re-read, then the final read fails: success', async (t) => { + const fake = new FakeVikunja(); + let state = 0; + const wrap = async (u, i = {}) => { + const m = i.method ?? 'GET'; + const p = new URL(u).pathname; + const res = await fake.fetch(u, i); + if (state === 1 && m === 'PATCH' && /\/tasks\/1$/.test(p)) state = 2; + else if (state === 2 && m === 'GET' && /\/tasks\/1$/.test(p)) { + state = 3; + fake.fault('GET', /^\/tasks\/1$/, 0); + } + return res; + }; + const w = await ready(t, { fake, fetch: wrap }); + const { task_ref } = await create(w); + fake.fault('PATCH', /^\/tasks\/1$/, 500, { apply: true }); + state = 1; + const c = await code(w.call(w.caps.pm, 'task.schedule', { task_ref, due_date: '2026-12-02T00:00:00.000Z' })); + console.log('P2 caller sees', c, 'state', state, 'due in tracker', fake.task(1).due, 'schedule events', w.events('task.scheduled').length, 'last snapshot', w.snapshots(task_ref).at(-1).source, w.snapshots(task_ref).at(-1).fields.due_date); + assert.equal(c, 'ok'); +}); + +// P3: a verb with nothing to write (the due date it already has) and a failed final read. +test('P3 nothing to write, final read fails', async (t) => { + const fake = new FakeVikunja(); + let armed = false; + let gets = 0; + const wrap = async (u, i = {}) => { + const m = i.method ?? 'GET'; + if (armed && m === 'GET' && /\/tasks\/1$/.test(new URL(u).pathname) && ++gets === 1) { + const res = await fake.fetch(u, i); + fake.fault('GET', /^\/tasks\/1$/, 0); + armed = false; + return res; + } + return fake.fetch(u, i); + }; + const w = await ready(t, { fake, fetch: wrap }); + const { task_ref } = await create(w, { due_date: '2026-11-01T00:00:00.000Z' }); + const patches = fake.requests.filter((x) => x.method === 'PATCH').length; + armed = true; + const c = await code(w.call(w.caps.pm, 'task.schedule', { task_ref, due_date: '2026-11-01T00:00:00.000Z' })); + console.log('P3 caller sees', c, 'PATCHes sent', fake.requests.filter((x) => x.method === 'PATCH').length - patches); +}); diff --git a/agents/filbert/work/slice1-s3-review/r2-base-node-bus.txt b/agents/filbert/work/slice1-s3-review/r2-base-node-bus.txt new file mode 100644 index 00000000..7d414c87 --- /dev/null +++ b/agents/filbert/work/slice1-s3-review/r2-base-node-bus.txt @@ -0,0 +1,66 @@ +✔ launch identity is stamped, payload identity is refused and stale holder cannot send (161.99682ms) +✔ decision classes route from policy; gated resolution is human-only, choice and target must match (260.733424ms) +✔ claim exclusion, holder release, gated revoke and rerouting to a new holder are atomic (281.486187ms) +✔ launch events require a human CLI capability; generic emit cannot forge authority events (162.822388ms) +✔ within-role decisions close atomically and invalid options or blocking omissions refuse (153.87044ms) +✔ observer capabilities read human inbox but cannot mutate or forge launch identity (134.51572ms) +✔ task action subjects and linked decision trail are complete and ordered (143.892404ms) +✔ launch binding is durable and reconnecting requires the identical trusted record (91.916717ms) +✔ business isolation includes inherited object names and cross-business message references (164.453315ms) +✔ authority never transfers between action, run, target, unresolved or replaced role holder (230.37595ms) +✔ task projection uses schema current view, skipping earlier and equal-start polls (120.937737ms) +✔ revocation permanently bars the old run from reclaiming first, including after broker restart (224.1436ms) +✔ empty message references refuse before storage; refusal-evidence failure stays a typed error (128.822107ms) +✔ both arbiters require human resolution when their cross-role route is themselves (222.91092ms) +✔ S1 adapter takes resolved limits and refs, rejects mismatched instance, never mutates input (2.440593ms) +✔ only validated broker references load; returned data and exceptions cannot expose a known token (5.2307ms) +✔ bad file modes, symlinks, repository/data paths, malformed tokens and missing dates refuse (4.462634ms) +✔ expiry refuses use and env references never become client data (0.735329ms) +✔ S1 parsed service refs work, service mismatch refuses, Gitea rotation due is a warning state (1.490388ms) +✔ opaque tokens shorter than 16 characters refuse before use (0.343695ms) +✔ human proof binds CLI entry, process start and nonce; agents and incomplete ancestry refuse (2.828976ms) +✔ process reader gets own kernel identity without exposing environment values (1.590136ms) +✔ EACCES ancestor environments skip only markers; commands and registered launches still refuse (0.916616ms) +✔ real pid 1 remains inspectable when its environment is protected (0.365469ms) +✔ within-role sends cite an open gated launch decision without spending it or naming it in grants (199.022873ms) +✔ missing and foreign-business citations refuse and roll back message and grant (194.019149ms) +✔ cross-role sends still need a matching resolved decision and consume it once (258.433362ms) +✔ broker process binds trusted launches, offers reader capabilities, refuses human mutation, closes cleanly (196.956701ms) +✔ startup token refusal returns safe code without value or partial listening broker (42.075334ms) +✔ loaded fixture token is absent from socket replies and SQLite, including refusal evidence (189.10371ms) +✔ killed broker leaves an explicit stale lock; another process cannot silently reclaim it (175.754188ms) +✔ trusted host registers later launches; socket clients never have a registration verb (175.881257ms) +✔ runtime excludes declared project roots even when host supplies no repoRoots (40.458729ms) +✔ a refused launch binding leaves the broker and existing capabilities alive; bad protocol stops it (144.862401ms) +✔ v3b prototype refusals, views and append-only mutations (1024.796612ms) +✔ gated approval authorizes once, survives store reopen, and fresh approval works (244.998208ms) +✔ another run cannot consume an approval; a failed check leaves it usable (241.336493ms) +✔ two scheduled callers have exactly one grant and one consumed refusal (165.127797ms) +✔ failed commit rolls consumption back; cross-role consumes and within-role stays reusable (283.616106ms) +✔ class drift gated to cross-role refuses before consumption (183.3628ms) +✔ class drift cross-role to gated refuses before consumption (179.228674ms) +✔ class drift gated to within-role refuses before consumption (178.688578ms) +✔ class drift cross-role to within-role refuses before consumption (197.355563ms) +✔ class drift within-role to gated refuses before consumption (155.541641ms) +✔ class drift within-role to cross-role refuses before consumption (174.222869ms) +✔ message.send consumes approval and prevents a later send or authorize (214.551278ms) +✔ role.revoke consumes approval and prevents a later revoke or authorize (248.854374ms) +✔ creates private WAL store and excludes a second writer until explicit close (111.036837ms) +✔ rollback is atomic and schema metadata is checked against trusted DDL, not just itself (174.625817ms) +✔ existing empty database and symlink runtime directory refuse, never initialize over damage (193.415895ms) +✔ crash during a transaction recovers no partial event after explicit fixture-only lock removal (173.846124ms) +✔ writer refuses mixed at/read_at forms atomically, even through trusted SQL helpers (91.901517ms) +✔ async transactions refuse before invoking their function (71.450858ms) +✔ socket capability stamps launch identity; shared views use wire, no SQL client (158.003584ms) +✔ two wire claims serialize; a lost reply never automatically retries (180.553678ms) +✔ malformed, oversized and identity-forging envelopes refuse without echoing input (128.998455ms) +✔ client preserves UTF-8 when a response divides a multibyte character (11.872033ms) +✔ committed mutation followed by dropped reply reports unknown and is never retried (144.718933ms) +ℹ tests 58 +ℹ suites 0 +ℹ pass 58 +ℹ fail 0 +ℹ cancelled 0 +ℹ skipped 0 +ℹ todo 0 +ℹ duration_ms 2568.827198 diff --git a/agents/filbert/work/slice1-s3-review/r2-base-node-business.txt b/agents/filbert/work/slice1-s3-review/r2-base-node-business.txt new file mode 100644 index 00000000..8f0972c4 --- /dev/null +++ b/agents/filbert/work/slice1-s3-review/r2-base-node-business.txt @@ -0,0 +1,68 @@ +✔ config directory and file path follow MOSAIC_CONFIG (2.210719ms) +✔ the fixture business validates and comes back frozen (8.770712ms) +✔ two instances may share a definition (2.177367ms) +✔ top-level refusals (8.827406ms) +✔ arbiters and projects (14.50992ms) +✔ role instances (4.839355ms) +✔ Vikunja bots (11.538611ms) +✔ a role without Vikunja takes no tracker block (3.793442ms) +✔ credential references match the definition's services (4.868786ms) +✔ launch (14.960448ms) +✔ loadBusiness: file checks (3.236296ms) +✔ loadBusiness: not a regular file (91.08637ms) +✔ loading writes nothing (2.57653ms) +✔ names that are Object.prototype properties don't count as declared (6.890756ms) +✔ the shipped example refuses as written and validates once filled in (2.883122ms) +✔ usage errors exit 4 (869.470778ms) +✔ validate: a good business exits 0 and prints instance digests (82.042246ms) +✔ validate: project files (417.1794ms) +✔ validate: missing files and a broken system config (390.376836ms) +✔ validate: credential reference problems exit 2 and name each one (99.3234ms) +✔ validate: a token file inside the repository is refused (103.756822ms) +✔ validate: role definitions come from MOSAIC_ROLES_DIR (234.927357ms) +✔ resolve: prints one instance's record (281.877257ms) +✔ resolve: refusals (989.310152ms) +✔ parse: exactly one of file or env, plus the service's date (4.549447ms) +✔ check: a good file has no problems (1.209331ms) +✔ check never opens the file: a write-only token passes (0.804481ms) +✔ check: file problems (1.630366ms) +✔ check: token files can't live in the repository or dataRoot, even through a linked directory (1.247451ms) +✔ check: dates and environment references (0.70132ms) +✔ path and load (3.972788ms) +✔ refusals (2.078689ms) +✔ systemVars flattens the validated config (3.4031ms) +✔ precedence: system, business, project, project role, agent (9.73379ms) +✔ limits narrow the definition and never widen it (5.213511ms) +✔ role.launch stays within-role only for the instance the launch block names (9.175749ms) +✔ limits.authority without role.launch leaves the launcher with no launch block (3.165563ms) +✔ limits.authority narrows cross-role actions too (2.155587ms) +✔ classify (2.074589ms) +✔ the record carries what the broker and launcher need (1.773542ms) +✔ digest: key order doesn't matter, any value change does (11.515402ms) +✔ refusals (3.850074ms) +✔ the four shipped version 2 roles load (5.81714ms) +✔ shipped role scopes match addendum B section 2 and the SR runbook (2.413725ms) +✔ shipped authority follows the note's table (1.528904ms) +✔ version 1 files keep loading with no authority (1.516579ms) +✔ the conductor policy isn't a role (0.505683ms) +✔ a missing role file is exit 4, a symbolic link too (0.688851ms) +✔ version 2 refusals (2.788584ms) +✔ authority: closed vocabulary, no gated-only action, no overlap (3.696945ms) +✔ credentials: Gitea scopes (1.798267ms) +✔ credentials: Vikunja scopes are a group-to-verbs map from the grantable list (1.959897ms) +✔ credentials: services (0.894557ms) +✔ contract: a non-empty regular Markdown file beside the role file (1.458286ms) +✔ every key names known layers and a merge rule (2.305419ms) +✔ unknown keys and wrong layers refuse (1.12706ms) +✔ types (2.631133ms) +✔ merge: defaults, then the most specific layer wins (0.412703ms) +✔ merge: limits only narrow, and provenance lists each source (0.911658ms) +✔ merge doesn't change its inputs (0.224637ms) +ℹ tests 60 +ℹ suites 0 +ℹ pass 60 +ℹ fail 0 +ℹ cancelled 0 +ℹ skipped 0 +ℹ todo 0 +ℹ duration_ms 3610.609773 diff --git a/agents/filbert/work/slice1-s3-review/r2-base-node-discord.txt b/agents/filbert/work/slice1-s3-review/r2-base-node-discord.txt new file mode 100644 index 00000000..fc24551a --- /dev/null +++ b/agents/filbert/work/slice1-s3-review/r2-base-node-discord.txt @@ -0,0 +1,181 @@ +✔ approvals: a request is validated before anything is posted; the rendering shows names and never ids (34.487862ms) +✔ approvals: the ledger is appended and folded into open requests with bind and approval states (14.074347ms) +✔ approvals: a reply approves only when it points at a request, says exactly approve, and comes from a listed approver once (6.773058ms) +✔ approvals: a button approves only on its own request message with the matching custom id (12.400304ms) +✔ approvals flow: a turn that opened a request posts the message with the button, records it, binds it, and both approvers approve (43.608983ms) +✔ approvals flow: a non-approver, a repeat, a wrong custom id and a service refusal each get their fixed line and a drop entry (22.126058ms) +✔ approvals flow: an invalid request from the model, a refused post, and no api client are recorded and post nothing (27.270723ms) +✔ approvals flow: start retries a bind and an approval left as unknown, under their original keys (13.726781ms) +✔ authorize: open channel, listed user (17.885431ms) +✔ authorize: wrong guild (0.292072ms) +✔ authorize: no guild (DM) (0.202845ms) +✔ authorize: unlisted channel (3.905942ms) +✔ authorize: unknown channel, no info (0.210231ms) +✔ authorize: thread of listed parent (0.248472ms) +✔ authorize: thread of unlisted parent (0.177088ms) +✔ authorize: text channel that is not a thread and not listed (0.190345ms) +✔ authorize: unlisted user (0.448978ms) +✔ authorize: no author (0.333759ms) +✔ authorize: bot author (listed id, bot flag) (0.164449ms) +✔ authorize: system author (0.153064ms) +✔ authorize: the bot itself (0.111149ms) +✔ authorize: webhook (0.122779ms) +✔ authorize: mention channel without mention (3.295562ms) +✔ authorize: mention channel with bot mention (0.193551ms) +✔ authorize: mention channel with @everyone only (0.143018ms) +✔ authorize: mention channel mentioning someone else (0.097916ms) +✔ authorize: mention channel, content says @bot but mentions empty (0.127677ms) +✔ authorize: private thread under mention channel, mentioned (0.108796ms) +✔ authorize: private thread under mention channel, not mentioned (0.093467ms) +✔ authorize: thread in another guild per channel info (3.951563ms) +✔ authorize: not an object (0.132099ms) +✔ authorize: no id (0.096086ms) +✔ authorize: oversize content is accepted and flagged (0.0958ms) +✔ authorize: exactly the limit is not oversize (0.075696ms) +✔ authorize: a user's channel allowlist drops them outside it, threads count as the parent, others are unaffected (6.783025ms) +✔ authorize: order puts wrong guild before user, and user before channel (no channel lookup for strangers) (4.049927ms) +✔ binding: a complete binding validates and is frozen (3.220184ms) +✔ binding: unknown key, missing field, wrong type refuse with exit 2 (1.45178ms) +✔ binding: empty allowlists refuse (0.424632ms) +✔ binding: a user's channel allowlist must be non-empty, listed and unique; absent means every listed channel (1.460828ms) +✔ reloadDiff: reloadable keys are summarised by id; every fixed key refuses with exit 2 (1.863865ms) +✔ binding: file must be 0600, regular, not a symlink (4.287306ms) +✔ binding: token file mode, symlink, emptiness and shape are checked; token never appears in errors (3.011523ms) +✔ cli: check refuses a non-0600 token file with exit 2 before any network use (217.82549ms) +✔ context files: absolute paths, traversal, symlinks and out-of-repo targets refuse; in-repo files resolve (15.349608ms) +✔ cli: check refuses a missing context file and a missing binding with exit 2; usage is exit 4 (1080.69582ms) +✔ cli: reload validates the file first (exit 2), then needs a live owner (exit 1); usage is exit 4 (307.79078ms) +✔ cli: run refuses when STOP is present, before any network use (172.208585ms) +✔ binding: tools is optional, validated strictly, a fixed key for reload, and its roots are resolved against the data root (1.897057ms) +✔ binding: a git key is validated at load and reaches the extension whole, and only on a writable root (1.342074ms) +✔ 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 (81.604347ms) +✔ delivery: refused and unknown outcomes are journaled; a later chunk is not sent after a failure (84.640489ms) +✔ delivery: restart with an unknown entry re-sends the same nonce once and reconciles before accepting traffic (1.998392ms) +✔ delivery: an unknown entry older than the dedupe window is marked refused, not re-sent; a still-unknown one refuses start (2.084332ms) +✔ delivery: repeated unknown reconciliations never refresh the dedupe window; the original intent time decides (1.962625ms) +✔ turn: a failed engine turn posts the fixed line, never model output, and writes a failed record (7.880217ms) +✔ turn: a second message during a turn is held by the engine, both get their own reply and record (37.152437ms) +✔ turn: a thread under a listed channel is answered in the thread; an unknown thread is looked up once (7.685887ms) +✔ drop: an unlisted user gets silence and one drop line; no inbox entry, no REST call, no engine call (1.67433ms) +✔ drop: an oversize message is accepted into the inbox, answered with the fixed line and journaled as a drop (2.167776ms) +✔ restart: an inbox with three ids and a replay of the same three produces zero turns (43.464381ms) +✔ stop: STOP present refuses start; STOP written while running refuses new turns and the current one finishes (34.229383ms) +✔ ceiling: the ceiling plus one is refused and journaled; one fixed line per UTC day; a new day accepts again (30.796295ms) +✔ ceiling: a burst arriving while turns are still running cannot queue past the ceiling (32.221013ms) +✔ ceiling: a turn interrupted by a crash still counts after restart; admissions are durable (2.485179ms) +✔ ceiling: the daily notice survives a same-day restart; one delivery attempt in total, even when the first attempt crashed mid-flight (14.687224ms) +✔ duplicate: the same event delivered twice while the thread lookup is held yields one prompt, one admission and one reply (29.243855ms) +✔ journal: no token-shaped string and no model output on the drop path reaches disk (11.767537ms) +✔ 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 (5.128386ms) +✔ receipt: Discord refusing the reaction leaves the turn intact and records ok false (8.727482ms) +✔ reload: a new user is silent before and answered after; a removed channel goes silent; a lower ceiling applies at once (26.132094ms) +✔ reload: a fixed key refuses with exit 2 and the old binding stays in force (2.494167ms) +✔ tools: with a tools binding the turn record lists every read and its outcome; without one the field is null (7.685619ms) +✔ context: the Discord block names the server, channels and modes, and states the rules from Q15 and Q16 (2.833757ms) +✔ context: with tools the block names the roots, keeps file content as data, and says to state refusals plainly (3.939416ms) +✔ context: a writable root adds the write rules and says a write is real only once Jason commits (3.074028ms) +✔ context: the envelope is one bracketed line then the text; names cannot break the line (1.362179ms) +✔ context: a git root swaps the terminal-commit line for the git verbs, and a vault root adds the id protocol (4.817406ms) +✔ context: assembleContext concatenates files in launcher format and appends the block; sha256 is stable (3.082078ms) +✔ context: splitReply keeps paragraphs together under the limit and splits long ones at lines, spaces, then hard (0.867042ms) +✔ engine: buildPiArgs carries the fixed flags, engine settings, session dir and prompt file (3.319401ms) +✔ engine: with tools, buildPiArgs turns pi's own tools off, loads the extension explicitly and allowlists exactly our three (0.48708ms) +✔ engine: a run with tool turns settles once, on the answer, with every tool call in the result (114.787619ms) +✔ engine: a run that ends on a tool-only turn fails the prompt as empty; a retried run settles on the real end (96.012621ms) +✔ engine: one prompt, one turn, text and usage come back (117.267651ms) +✔ engine: a prompt while streaming is held until pi settles, then sent as its own run, and answered in order (385.920468ms) +✔ engine: a held prompt that times out before pi settles fails on its own and is never sent (307.654865ms) +✔ engine: timeout sends abort and fails only that turn; the process stays (146.711305ms) +✔ engine: tool events from a run that outlived its timeout never land in the next prompt's record (299.092131ms) +✔ engine: a prompt after a turn that timed out before its agent_start waits for pi to settle instead of being refused (166.825666ms) +✔ 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.598765ms) +✔ engine: a timed-out turn pi starts only after the grace never answers a later prompt (616.243685ms) +✔ engine: late events of a run past its grace, before pi exits, answer nothing and nothing more is sent (early prompt response) (9.382248ms) +✔ engine: late events of a run past its grace, before pi exits, answer nothing and nothing more is sent (late prompt response) (1.804695ms) +✔ engine: a timed-out run pi did start outlives the grace; the next prompt goes out when it ends (455.497693ms) +✔ engine: a malformed JSONL line fails the turn, not the process (33.520961ms) +✔ engine: a turn that ends in error rejects with the error code; process exit fails pending turns (54.1439ms) +✔ gateway: hello -> identify with intents, ready, heartbeat with jitter, ack (3.468422ms) +✔ gateway: missed ack closes the socket and resumes with the last sequence (6.755832ms) +✔ gateway: op 7 reconnect resumes; op 9 non-resumable re-identifies (4.39287ms) +✔ gateway: op 9 resumable resumes (0.558088ms) +✔ gateway: close 4014 is fatal, reports the missing intent, never reconnects (6.634387ms) +✔ gateway: 4004 and 4013 are fatal too; 1006 reconnects with identify when no session (0.663017ms) +✔ gateway: close() is final and unparseable frames are ignored (0.45109ms) +✔ git: config validation is strict, needs write: true, a work tree and a private token file (180.855233ms) +✔ git: the child environment drops every host git config, names one helper, and carries the token path only for origin (195.087231ms) +✔ git: status reports the branch, ahead/behind and changed paths, and refuses off the named branch or mid-merge (331.606799ms) +✔ git: parseStatus reads porcelain v2 including renames and conflicts (0.516461ms) +✔ git: a commit stages exactly the named files, carries the seat author and the requester trailer, and pushes at once (376.392144ms) +✔ git: commit refusals: message, paths, requester, nothing to commit, and an index that already holds other work (190.005319ms) +✔ git: a commit whose push fails is still a commit, says so, and the next commit's push carries both (D6) (181.910749ms) +✔ git: pull is fast-forward only; a diverged origin or dirty local files refuse with nothing merged (281.704081ms) +✔ git: push pushes the named branch only and reports up to date (74.740746ms) +✔ git: no token value or token path ever reaches a git argument list; outputs are masked and capped (94.007326ms) +✔ git: the credential helper answers get over https from a private file and nothing else (376.924827ms) +✔ git: the vault protocol validates before a commit, honours another owner's lock, reserves ids, and locks around writes (1001.713929ms) +✔ lock: the claim is exclusive; a second start against a live owner refuses (5.634528ms) +✔ lock: a stale lock (dead owner, reused pid, or record without start) refuses run and is never signaled; only unlock clears it (35.483959ms) +✔ lock: an incomplete claim (directory without owner record) is busy and refuses run; unlock clears it (6.841195ms) +✔ lock: an owner record that exists but cannot be read is invalid: never signaled, never removed, never claimed over (3.49952ms) +✔ lock: legacy upgrade; a live connector holding a {pid, start} record is unknown, unlock refuses and nothing changes; after it exits, unlock clears it (194.544821ms) +✔ 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 (1035.119501ms) +✔ lock: identity syntax; only canonical unsigned decimal start ticks and lowercase boot uuids are identities (0.661416ms) +✔ lock: a process whose start marker or boot id cannot be read refuses to claim (0.807626ms) +✔ lock: a live pid whose identity cannot be read right now is unknown: never signaled, never removed, never claimed over (1.801628ms) +✔ lock: four processes racing for the same binding; exactly one claims it and the others refuse (69.853531ms) +✔ lock: stale handoff; concurrent starts over a stale lock all refuse, nothing reclaims, one unlock then exactly one live owner (179.73331ms) +✔ lock: four-party schedule; claims landing inside an unlock's gap never survive, one unlock leaves no owner and no residue (91.055965ms) +✔ notices: a kind is recorded per UTC day and found again (0.736817ms) +✔ recover: nothing to do is clean; a lock whose owner is gone or that has no record is cleared and STOP ends up absent (107.432405ms) +✔ recover: an operator STOP refuses with exit 3 and is never removed, whatever the lock says (132.449148ms) +✔ recover: a brake written during the unlock wins; STOP stays with both lines and the start is refused (131.450452ms) +✔ recover: a held binding refuses with exit 3 and writes no STOP: live owner, alive pid without verifiable identity, unreadable record (274.897047ms) +✔ cli: recover exits 0 when ready, 3 behind a brake or a held binding, and run's own STOP refusal is 3 (1279.582667ms) +✔ rest: createMessage sends nonce, enforce_nonce, empty allowed_mentions and a soft reply reference (2.874588ms) +✔ rest: 429 waits retry_after and retries; 4xx is refused; 5xx and socket errors are unknown (3.848359ms) +✔ rest: content and nonce limits are enforced locally; typing never throws (0.849946ms) +✔ rest: react PUTs the encoded emoji on the inbound message for @me; 2xx is true, anything else is false and never throws (0.861657ms) +✔ setspark config: a bare https or loopback origin, a private key file, a principal (27.493778ms) +✔ setspark config: reaches the tools config and the binding as a fixed key (13.504085ms) +✔ setspark config: the binding's key survives resolveToolRoots and the engine's JSON hand-off to the extension (3.85414ms) +✔ setspark config: approvers come from the binding's users, never from the binding's setspark key (4.34759ms) +✔ setspark verbs: required_approvers go out as discord ids from names and come back as names (38.399576ms) +✔ setspark verbs: no Discord user id reaches tool text, whatever shape the service returns it in (143.161052ms) +✔ setspark contract: a decision made with names opens a request the connector accepts; names stored by an old record still refuse (14.455155ms) +✔ setspark keys: read per call, one printable token per file, rotation without a restart (7.034683ms) +✔ setspark idempotency keys: principal, turn id, call index; connector keys name a step (1.032953ms) +✔ setspark http core: json in and out, bearer header, idempotency header, fixed user agent, no key anywhere else (5.837698ms) +✔ setspark http core: error bodies become fixed refusals with code and the 409 fields; server text is data, cut (1019.925443ms) +✔ setspark verbs: a setspark key enables the eight verbs and no counters (0.838506ms) +✔ 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 (11.626826ms) +✔ setspark verbs: no turn refuses every write before any request; bad arguments refuse before any request; reads still work (2.453047ms) +✔ setspark verbs: renderRecord caps long output and hides the accepted snapshot (0.406606ms) +✔ setspark api: bind, add_approval (button and reply) and get use integer request ids and the connector's keys (4.624686ms) +✔ tools: config refuses a missing, symlinked, dotted, non-directory or duplicate root and bad limits (4.093436ms) +✔ tools: every escape is refused with a fixed reason and nothing outside the root is read (4.352767ms) +✔ tools: happy paths list, read a window, and search case-insensitively; dotfiles and symlinks never appear (7.855786ms) +✔ tools: the tool set renders text for the model, records details for the journal, and enforces the per-run budget (4.903809ms) +✔ tools: listing and search caps hold (47.173174ms) +✔ tools: credential shapes are caught; ordinary prose and ids are not (2.037206ms) +✔ 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 (27.565853ms) +✔ tools: an unreadable file under the root is skipped by search and refused by read (7.725685ms) +✔ tools: config accepts write: true only as a boolean, and enables the write tools only then (8.025072ms) +✔ tools: every write outside the fence is refused before any byte lands, and no temp file remains (16.110027ms) +✔ tools: write_file leaves the exact bytes, edit_file replaces one exact match, and the set renders the change as uncommitted (3.875058ms) +✔ tools: a target that changed between the check and the rename is refused and the temp file is removed (4.361211ms) +✔ web: config takes an https or loopback-http SearXNG base url and a bounded fetch cap (4.935673ms) +✔ web: address rules refuse every private, loopback, link-local, mapped and multicast form (3.021393ms) +✔ web: web_fetch refuses bad urls, private hosts, rebinding names, non-https redirects, too many hops, error status, non-text bodies, and times out (1100.072164ms) +✔ web: web_fetch returns html as text with the title, follows an https redirect, keeps plain text and json, and cuts at the cap (9.979096ms) +✔ web: html to text drops scripts, styles and comments, decodes entities and keeps block breaks (0.645954ms) +✔ 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 (7.484765ms) +✔ web: the tool set enables the web tools only with a web key, counts them in the budget, and records url, status and hits (5.271081ms) +ℹ tests 173 +ℹ suites 0 +ℹ pass 173 +ℹ fail 0 +ℹ cancelled 0 +ℹ skipped 0 +ℹ todo 0 +ℹ duration_ms 3853.394207 diff --git a/agents/filbert/work/slice1-s3-review/r2-base-suite-auth.txt b/agents/filbert/work/slice1-s3-review/r2-base-suite-auth.txt new file mode 100644 index 00000000..ac228760 --- /dev/null +++ b/agents/filbert/work/slice1-s3-review/r2-base-suite-auth.txt @@ -0,0 +1,17 @@ +OK status with missing harness credential exits 3 and still lists accounts +OK status reports harness credential (read-only) + mosaic accounts +OK api key material never reaches output +OK oauth token material never reaches output +OK unparseable credential file exits 2 +OK symlinked credential file exits 4 +OK env-side credential names reported +OK env var values never reach output +OK accounts without an accounts dir reports none and creates nothing +OK accounts lists files and marks the active one +OK loose account perms flagged in listing +OK agent --auth with missing account file refuses (exit 4) +OK agent --auth with non-0600 account file refuses +OK agent --auth with invalid account name refuses +OK auth.sh without valid config refuses + +selftest: 15 passed, 0 failed diff --git a/agents/filbert/work/slice1-s3-review/r2-base-suite-conductor.txt b/agents/filbert/work/slice1-s3-review/r2-base-suite-conductor.txt new file mode 100644 index 00000000..4313a59a --- /dev/null +++ b/agents/filbert/work/slice1-s3-review/r2-base-suite-conductor.txt @@ -0,0 +1,55 @@ +Note: switching to 'c9ef7ee49f0785287de7ff41130dd4761ad2c7e3'. + +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 'c9ef7ee49f0785287de7ff41130dd4761ad2c7e3'. + +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-s3-review/r2-base-suite-config.txt b/agents/filbert/work/slice1-s3-review/r2-base-suite-config.txt new file mode 100644 index 00000000..76c05ef7 --- /dev/null +++ b/agents/filbert/work/slice1-s3-review/r2-base-suite-config.txt @@ -0,0 +1,26 @@ +OK absent adapter defaults to pi +OK adapter mock validates (exit 0) +OK unsupported adapter exits 2 (exit 2) +OK env exports adapter +OK bootstrap creates default when absent (exit 0) +OK bootstrap wrote config file +OK bootstrap is idempotent on existing config (exit 0) +OK bootstrap did not rewrite existing config +OK validate missing config exits 3 (exit 3) +OK malformed JSON exits 2 (exit 2) +OK unsupported configVersion exits 2 (exit 2) +OK unknown top-level key exits 2 (exit 2) +OK unknown execution key exits 2 (exit 2) +OK unsupported backend exits 2 (exit 2) +OK unsupported environment exits 2 (exit 2) +OK relative dataRoot exits 2 (exit 2) +OK non-canonical dataRoot exits 2 (exit 2) +OK filesystem root dataRoot exits 2 (exit 2) +OK home directory dataRoot exits 2 (exit 2) +OK dataRoot containing config dir exits 2 (exit 2) +OK control character in provider exits 2 (exit 2) +OK symlinked config file exits 2 (exit 2) +OK env exports resolve correctly +OK failed validation modified nothing + +selftest: 24 passed, 0 failed diff --git a/agents/filbert/work/slice1-s3-review/r2-base-suite-discord.txt b/agents/filbert/work/slice1-s3-review/r2-base-suite-discord.txt new file mode 100644 index 00000000..c667a891 --- /dev/null +++ b/agents/filbert/work/slice1-s3-review/r2-base-suite-discord.txt @@ -0,0 +1,68 @@ +toolchain: node v26.8.1 + +OK syntax: packages/discord/src/approvals.mjs +OK syntax: packages/discord/src/authorize.mjs +OK syntax: packages/discord/src/binding.mjs +OK syntax: packages/discord/src/cli.mjs +OK syntax: packages/discord/src/connector.mjs +OK syntax: packages/discord/src/context.mjs +OK syntax: packages/discord/src/engine-pi.mjs +OK syntax: packages/discord/src/errors.mjs +OK syntax: packages/discord/src/gateway.mjs +OK syntax: packages/discord/src/git.mjs +OK syntax: packages/discord/src/journal.mjs +OK syntax: packages/discord/src/rest.mjs +OK syntax: packages/discord/src/setspark.mjs +OK syntax: packages/discord/src/tools.mjs +OK syntax: packages/discord/src/web.mjs +OK syntax: packages/discord/bin/git-credential.mjs +OK syntax: packages/discord/extension/tools.mjs +OK syntax: packages/discord/tests/approvals.test.mjs +OK syntax: packages/discord/tests/authorize.test.mjs +OK syntax: packages/discord/tests/binding.test.mjs +OK syntax: packages/discord/tests/connector.test.mjs +OK syntax: packages/discord/tests/context.test.mjs +OK syntax: packages/discord/tests/engine.test.mjs +OK syntax: packages/discord/tests/fake-pi.mjs +OK syntax: packages/discord/tests/gateway.test.mjs +OK syntax: packages/discord/tests/git.test.mjs +OK syntax: packages/discord/tests/helpers.mjs +OK syntax: packages/discord/tests/journal.test.mjs +OK syntax: packages/discord/tests/recover.test.mjs +OK syntax: packages/discord/tests/rest.test.mjs +OK syntax: packages/discord/tests/setspark.test.mjs +OK syntax: packages/discord/tests/tools.test.mjs +OK syntax: packages/discord/tests/web.test.mjs +OK syntax: packages/discord/fixtures/claim-worker.mjs +OK syntax: packages/discord/fixtures/legacy-owner-worker.mjs +OK syntax: scripts/discord.sh +OK syntax: scripts/discord-service.sh +OK packages/discord declares no dependencies +OK no bot-token-shaped string in packages/discord +OK fixture binding uses placeholder ids only +OK fixture binding validates +OK real pi with the extension exposes exactly list_dir, read_file, search and no built-in tool +OK real pi with a writable root exposes exactly the three reads plus write_file and edit_file, and writes nothing at start +OK real pi with a web key exposes the three reads plus web_fetch and web_search, and no write tool without a writable root +OK real pi with a git root exposes the reads, writes and the four git verbs, commits nothing at start, and never shows the token +OK real pi with protocol vault adds reserve_id to the git verbs +OK real pi with a setspark key exposes the reads and the eight record verbs, no counters, and never shows the key +OK real pi refuses a git key on a read-only root (fail closed) +OK real pi with the pilot flags (--no-tools) exposes no tool at all +OK real pi exits non-zero without MOSAIC_DISCORD_TOOLS: no session, no tools (fail closed) +OK a failing nested test fails the run under a parent runner's NODE_TEST_CONTEXT +OK node --test packages/discord/tests/ (ℹ pass 173) +OK scripts/discord.sh --help exits 0 +OK scripts/discord.sh check without a binding exits 4 +OK scripts/discord.sh recover without a binding exits 4 +OK scripts/discord.sh reload without a binding exits 4 +OK scripts/discord-service.sh without a command exits 4 +OK service unit renders with the repository path, a supervised run as the main process, exit 3 never retried, and reload as SIGHUP +OK service install writes the rendered unit (0644) and leaves no temp file +OK service install a second time reports unchanged +OK systemd-analyze verify accepts the rendered unit +OK service uninstall removes the unit file +OK service install with an unknown flag exits 4 +OK service install with USER unset finishes and names the account for lingering + +discord suite: 64 passed, 0 failed diff --git a/agents/filbert/work/slice1-s3-review/r2-base-suite-extension-package.txt b/agents/filbert/work/slice1-s3-review/r2-base-suite-extension-package.txt new file mode 100644 index 00000000..e0ef2217 --- /dev/null +++ b/agents/filbert/work/slice1-s3-review/r2-base-suite-extension-package.txt @@ -0,0 +1,21 @@ +OK initial ordinary-file install +OK installed tree matches canonical source +OK installed tree has no symlinks +OK check detects installation drift +OK sync refuses to overwrite installation drift +OK check detects an extra destination file +OK check detects an extra destination directory +OK check rejects a destination symlink +OK sync accepts a canonical source update +OK updated installation matches canonical source +scripts/test-extension-package.sh: line 14: 3895996 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-s3-review/r2-base-suite-foundation.txt b/agents/filbert/work/slice1-s3-review/r2-base-suite-foundation.txt new file mode 100644 index 00000000..6338e1e6 --- /dev/null +++ b/agents/filbert/work/slice1-s3-review/r2-base-suite-foundation.txt @@ -0,0 +1,53 @@ +toolchain: node v26.8.1, python 3.12.8, jsonschema 4.26.0 + +OK syntax: scripts/foundation-inspect.mjs +OK syntax: scripts/foundation/strict-json.mjs +OK syntax: scripts/foundation/canonical.mjs +OK syntax: scripts/foundation/resolve.mjs +OK syntax: scripts/foundation/validate-record.mjs +OK syntax: scripts/foundation/fixtures/build-fixtures.mjs +OK syntax: scripts/foundation/canonical.test.mjs +OK syntax: scripts/foundation/cli.test.mjs +OK syntax: scripts/foundation/fixtures.test.mjs +OK syntax: scripts/foundation/resolve.test.mjs +OK syntax: scripts/foundation/strict-json.test.mjs +OK syntax: scripts/foundation/verify-schema.py (ast only; no bytecode written) +OK fixture generator runs +OK checked-in fixtures/bundles equal a fresh generation +OK checked-in fixtures/raw equal a fresh generation +OK checked-in fixtures/index.json equal a fresh generation +OK checked-in demo bundles equal a fresh generation +OK a failing nested test fails the run under a parent runner's NODE_TEST_CONTEXT +OK node --test scripts/foundation/ (ℹ pass 80) +OK differential schema oracle: PASS: differential schema oracle (finite corpus; compatibility evidence, not equivalence proof) + platform witness: strftime('%Y') for year 999 -> '999' (pinned checker refuses years 0001..0999) + node v26.8.1; corpus 1568 records (38 pinned fixtures, 478 unique bundle records, 1052 typeCase/mutation/lexical cases) + schema column: agree-valid 540, agree-invalid 991, DISAGREEMENTS 0; strict-only (parser-bound) cases: 27; unsupported-kind records not schema-assessed by the inspector: 10 + profile column (schema-valid records only): profile-valid 510, profile-invalid 30 + profile refusals asserted: 30 schema-agreed-valid records refused only by the strict typed-string profile (rule profile-pattern-mismatch), 12 declared by name; 73 named probes verified against declared schema/profile columns +OK oracle: zero schema-column disagreements with the pinned checker +OK oracle: strict-only profile refusals are counted and asserted +OK demo: permitted read preview exits 0 (exit 0) +OK demo: permitted file.change preview exits 0 (exit 0) +OK demo: assignment.change proposal is unresolved (exit 3) (exit 3) +OK demo: revoked registration is refused (exit 3) (exit 3) +OK demo: message is not authority (exit 3) (exit 3) +OK usage: no arguments exits 2 (exit 2) +OK io: missing file exits 4 (exit 4) +OK io: directory exits 4 (exit 4) +OK io: symlink exits 4 (O_NOFOLLOW) (exit 4) +OK bound: oversize fixture exits 2 (exit 2) +OK profile: one final LF in a typed selection id is refused before admission (exit 2) (exit 2) +OK profile: two final LFs fail the schema pattern itself (exit 2) (exit 2) +OK profile: escaped newlines in free-form text stay allowed (exit 0) (exit 0) +OK profile refusal is invalid-request/profile-pattern-mismatch with selection and operation withheld, value not echoed +OK text output starts with the disclaimer +OK json output is valid JSON with result allowed and exactly the charter §7 fields +OK json golden matches byte-for-byte +OK sandboxed bundle run (env -i, PATH=/nonexistent) produced the unresolved proposal +OK sandbox inventory (path/type/size/mode/uid/gid/inode/mtime/sha256) unchanged by runs +OK canary never printed (bundle run and credential-file run) +OK a non-bundle JSON file is refused at the shape gate, not read into output +OK no field of the non-bundle file is echoed + +selftest: 44 passed, 0 failed diff --git a/agents/filbert/work/slice1-s3-review/r2-base-suite-queue.txt b/agents/filbert/work/slice1-s3-review/r2-base-suite-queue.txt new file mode 100644 index 00000000..dae7cb23 --- /dev/null +++ b/agents/filbert/work/slice1-s3-review/r2-base-suite-queue.txt @@ -0,0 +1,35 @@ +toolchain: node v26.8.1, git version 2.55.0 + +OK syntax: packages/queue/src/cli.mjs +OK syntax: packages/queue/src/errors.mjs +OK syntax: packages/queue/src/io.mjs +OK syntax: packages/queue/src/lock.mjs +OK syntax: packages/queue/src/queue.mjs +OK syntax: packages/queue/src/review.mjs +OK syntax: packages/queue/src/store.mjs +OK syntax: packages/queue/tests/commit.test.mjs +OK syntax: packages/queue/tests/data.test.mjs +OK syntax: packages/queue/tests/dispatch.test.mjs +OK syntax: packages/queue/tests/helpers.mjs +OK syntax: packages/queue/tests/lock.test.mjs +OK syntax: packages/queue/tests/migration.test.mjs +OK syntax: packages/queue/tests/review.test.mjs +OK syntax: packages/queue/tests/store.test.mjs +OK syntax: packages/queue/tests/write.test.mjs +OK syntax: packages/queue/tests/fixtures/fake-gitea.mjs +OK syntax: packages/queue/tests/fixtures/kill-at.mjs +OK syntax: packages/queue/tests/fixtures/lock-child.mjs +OK syntax: packages/queue/tests/fixtures/mosaic-pre-a2.sh +OK syntax: scripts/queue-commit.sh +OK syntax: scripts/git-hooks/pre-commit +OK syntax: scripts/mosaic +OK queue-commit.sh, the guard and scripts/mosaic are executable +OK packages/queue declares no dependencies +ℹ tests 148 +ℹ pass 148 +ℹ fail 0 +OK node --test packages/queue/tests/ +OK scripts/mosaic queue help +skip queue verify and render --check: this checkout (/home/jwoltje/filbert-scratch/r38b/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-s3-review/r2-base-suite-release.txt b/agents/filbert/work/slice1-s3-review/r2-base-suite-release.txt new file mode 100644 index 00000000..1d6ed050 --- /dev/null +++ b/agents/filbert/work/slice1-s3-review/r2-base-suite-release.txt @@ -0,0 +1,7 @@ +OK valid RELEASE resolves (exit 0) +OK invalid RELEASE exits 1 (exit 1) +OK missing RELEASE exits 1 (exit 1) +OK valid RELEASE leaves image tag consistent with version +skip state-machine cases (docker daemon unavailable) + +selftest: 4 passed, 0 failed diff --git a/agents/filbert/work/slice1-s3-review/r2-base-suite-task.txt b/agents/filbert/work/slice1-s3-review/r2-base-suite-task.txt new file mode 100644 index 00000000..85aa46f0 --- /dev/null +++ b/agents/filbert/work/slice1-s3-review/r2-base-suite-task.txt @@ -0,0 +1,33 @@ +OK valid task validates (exit 0) +OK unknown task key exits 2 (exit 2) +OK unsupported taskVersion exits 2 (exit 2) +OK invalid task id exits 2 (exit 2) +OK empty prompt exits 2 (exit 2) +OK NUL in expectExact exits 2 (exit 2) +OK out-of-range timeout exits 2 (exit 2) +OK missing mission file exits 4 (exit 4) +OK task with valid mission validates (exit 0) +OK invalid mission exits 2 (exit 2) +OK validate missing task exits 4 (exit 4) +OK validation does not modify the task file +OK prune dry-run exits 0 (exit 0) +OK dry-run deleted nothing +OK prune --keep=2 --yes removes oldest (exit 0) +OK kept exactly 2 newest runs +OK newest run kept, oldest pruned +OK append-only receipt written (3 entries) +OK sessions/workspaces untouched by prune +OK prune with invalid keep exits 4 (exit 4) +skip adapter seam cases (docker daemon unavailable) +skip workspace/capability cases (docker daemon unavailable) +skip live task cases (docker unavailable) +OK onboard without name exits 4 (non-interactive) (exit 4) +OK onboard --name renders profile (exit 0) +OK profile written +OK canon structure: required filled, optional placeholdered +OK canon sections present +FAIL user recall run succeeds (exit 1) +FAIL recalled user name (response: ) +OK no agent identity on headless run + +selftest: 26 passed, 2 failed diff --git a/agents/filbert/work/slice1-s3-review/r2-cand-node-bus.txt b/agents/filbert/work/slice1-s3-review/r2-cand-node-bus.txt new file mode 100644 index 00000000..480fc6ae --- /dev/null +++ b/agents/filbert/work/slice1-s3-review/r2-cand-node-bus.txt @@ -0,0 +1,75 @@ +✔ launch identity is stamped, payload identity is refused and stale holder cannot send (243.688933ms) +✔ decision classes route from policy; gated resolution is human-only, choice and target must match (394.370236ms) +✔ claim exclusion, holder release, gated revoke and rerouting to a new holder are atomic (378.259124ms) +✔ launch events require a human CLI capability; generic emit cannot forge authority events (190.030177ms) +✔ within-role decisions close atomically and invalid options or blocking omissions refuse (166.414908ms) +✔ observer capabilities read human inbox but cannot mutate or forge launch identity (196.029388ms) +✔ task action subjects and linked decision trail are complete and ordered (271.039591ms) +✔ launch binding is durable and reconnecting requires the identical trusted record (105.692018ms) +✔ business isolation includes inherited object names and cross-business message references (299.44263ms) +✔ authority never transfers between action, run, target, unresolved or replaced role holder (336.471786ms) +✔ task projection uses schema current view, skipping earlier and equal-start polls (116.7184ms) +✔ revocation permanently bars the old run from reclaiming first, including after broker restart (212.071404ms) +✔ empty message references refuse before storage; refusal-evidence failure stays a typed error (170.936558ms) +✔ both arbiters require human resolution when their cross-role route is themselves (230.085428ms) +✔ S1 adapter takes resolved limits and refs, rejects mismatched instance, never mutates input (2.483051ms) +✔ only validated broker references load; returned data and exceptions cannot expose a known token (8.861539ms) +✔ bad file modes, symlinks, repository/data paths, malformed tokens and missing dates refuse (2.423716ms) +✔ expiry refuses use and env references never become client data (0.639165ms) +✔ S1 parsed service refs work, service mismatch refuses, Gitea rotation due is a warning state (1.507093ms) +✔ opaque tokens shorter than 16 characters refuse before use (0.344056ms) +✔ human proof binds CLI entry, process start and nonce; agents and incomplete ancestry refuse (2.87ms) +✔ process reader gets own kernel identity without exposing environment values (1.745649ms) +✔ EACCES ancestor environments skip only markers; commands and registered launches still refuse (1.199018ms) +✔ real pid 1 remains inspectable when its environment is protected (0.454652ms) +✔ within-role sends cite an open gated launch decision without spending it or naming it in grants (284.772283ms) +✔ missing and foreign-business citations refuse and roll back message and grant (284.754519ms) +✔ cross-role sends still need a matching resolved decision and consume it once (363.897447ms) +✔ broker process binds trusted launches, offers reader capabilities, refuses human mutation, closes cleanly (267.365284ms) +✔ startup token refusal returns safe code without value or partial listening broker (46.571245ms) +✔ loaded fixture token is absent from socket replies and SQLite, including refusal evidence (259.065468ms) +✔ killed broker leaves an explicit stale lock; another process cannot silently reclaim it (239.167291ms) +✔ trusted host registers later launches; socket clients never have a registration verb (198.566643ms) +✔ runtime excludes declared project roots even when host supplies no repoRoots (36.014187ms) +✔ a refused launch binding leaves the broker and existing capabilities alive; bad protocol stops it (191.712893ms) +✔ v3b prototype refusals, views and append-only mutations (1386.640134ms) +✔ gated approval authorizes once, survives store reopen, and fresh approval works (350.424385ms) +✔ another run cannot consume an approval; a failed check leaves it usable (346.173632ms) +✔ two scheduled callers have exactly one grant and one consumed refusal (237.542781ms) +✔ failed commit rolls consumption back; cross-role consumes and within-role stays reusable (353.298622ms) +✔ class drift gated to cross-role refuses before consumption (253.337442ms) +✔ class drift cross-role to gated refuses before consumption (323.574906ms) +✔ class drift gated to within-role refuses before consumption (319.086942ms) +✔ class drift cross-role to within-role refuses before consumption (293.421268ms) +✔ class drift within-role to gated refuses before consumption (200.739612ms) +✔ class drift within-role to cross-role refuses before consumption (190.277177ms) +✔ message.send consumes approval and prevents a later send or authorize (228.418852ms) +✔ role.revoke consumes approval and prevents a later revoke or authorize (248.932208ms) +✔ creates private WAL store and excludes a second writer until explicit close (198.246349ms) +✔ rollback is atomic and schema metadata is checked against trusted DDL, not just itself (220.628917ms) +✔ existing empty database and symlink runtime directory refuse, never initialize over damage (284.709518ms) +✔ crash during a transaction recovers no partial event after explicit fixture-only lock removal (209.202587ms) +✔ writer refuses mixed at/read_at forms atomically, even through trusted SQL helpers (125.001422ms) +✔ async transactions refuse before invoking their function (94.629513ms) +✔ recordTask keeps sync reads and a role write apart (250.930381ms) +✔ read_at must be one canonical UTC format, so the projection compares strings safely (152.088447ms) +✔ a bad entry refuses the whole record (161.177091ms) +✔ taskView reads the projection for one business (203.275656ms) +✔ requestTask hands only a holder and a task verb to the handler, and records refusals (295.117707ms) +✔ the server sends task verbs to the adapter with its own timeout; other verbs stay synchronous (453.779089ms) +✔ without an adapter the server refuses every task verb (305.000273ms) +✔ the runtime refuses an invalid adapter and closes a valid one (278.560511ms) +✔ the process loads the S3 adapter from plain-data trackers (340.686837ms) +✔ socket capability stamps launch identity; shared views use wire, no SQL client (233.452782ms) +✔ two wire claims serialize; a lost reply never automatically retries (265.170068ms) +✔ malformed, oversized and identity-forging envelopes refuse without echoing input (152.168362ms) +✔ client preserves UTF-8 when a response divides a multibyte character (12.521387ms) +✔ committed mutation followed by dropped reply reports unknown and is never retried (229.685316ms) +ℹ tests 67 +ℹ suites 0 +ℹ pass 67 +ℹ fail 0 +ℹ cancelled 0 +ℹ skipped 0 +ℹ todo 0 +ℹ duration_ms 3438.917252 diff --git a/agents/filbert/work/slice1-s3-review/r2-cand-node-business.txt b/agents/filbert/work/slice1-s3-review/r2-cand-node-business.txt new file mode 100644 index 00000000..933d3d7a --- /dev/null +++ b/agents/filbert/work/slice1-s3-review/r2-cand-node-business.txt @@ -0,0 +1,68 @@ +✔ config directory and file path follow MOSAIC_CONFIG (2.284009ms) +✔ the fixture business validates and comes back frozen (6.487939ms) +✔ two instances may share a definition (2.275159ms) +✔ top-level refusals (6.073799ms) +✔ arbiters and projects (9.991129ms) +✔ role instances (3.798752ms) +✔ Vikunja bots (8.303249ms) +✔ a role without Vikunja takes no tracker block (5.495114ms) +✔ credential references match the definition's services (3.63747ms) +✔ launch (8.543695ms) +✔ loadBusiness: file checks (2.248537ms) +✔ loadBusiness: not a regular file (55.746268ms) +✔ loading writes nothing (1.12094ms) +✔ names that are Object.prototype properties don't count as declared (3.05957ms) +✔ the shipped example refuses as written and validates once filled in (0.590772ms) +✔ usage errors exit 4 (363.427862ms) +✔ validate: a good business exits 0 and prints instance digests (85.361898ms) +✔ validate: project files (436.931166ms) +✔ validate: missing files and a broken system config (321.481183ms) +✔ validate: credential reference problems exit 2 and name each one (78.04229ms) +✔ validate: a token file inside the repository is refused (75.149693ms) +✔ validate: role definitions come from MOSAIC_ROLES_DIR (223.179074ms) +✔ resolve: prints one instance's record (252.287269ms) +✔ resolve: refusals (482.205406ms) +✔ parse: exactly one of file or env, plus the service's date (3.241377ms) +✔ check: a good file has no problems (1.062324ms) +✔ check never opens the file: a write-only token passes (0.520883ms) +✔ check: file problems (1.250663ms) +✔ check: token files can't live in the repository or dataRoot, even through a linked directory (1.065013ms) +✔ check: dates and environment references (0.612212ms) +✔ path and load (3.24501ms) +✔ refusals (1.971353ms) +✔ systemVars flattens the validated config (2.380655ms) +✔ precedence: system, business, project, project role, agent (6.614577ms) +✔ limits narrow the definition and never widen it (3.222887ms) +✔ role.launch stays within-role only for the instance the launch block names (6.684685ms) +✔ limits.authority without role.launch leaves the launcher with no launch block (2.69868ms) +✔ limits.authority narrows cross-role actions too (1.634008ms) +✔ classify (1.721356ms) +✔ the record carries what the broker and launcher need (1.541191ms) +✔ digest: key order doesn't matter, any value change does (14.70505ms) +✔ refusals (3.474474ms) +✔ the four shipped version 2 roles load (4.757051ms) +✔ shipped role scopes match addendum B section 2 and the SR runbook (1.42544ms) +✔ shipped authority follows the note's table (0.578519ms) +✔ version 1 files keep loading with no authority (1.213931ms) +✔ the conductor policy isn't a role (0.277292ms) +✔ a missing role file is exit 4, a symbolic link too (0.489879ms) +✔ version 2 refusals (1.712467ms) +✔ authority: closed vocabulary, no gated-only action, no overlap (2.904921ms) +✔ credentials: Gitea scopes (0.959458ms) +✔ credentials: Vikunja scopes are a group-to-verbs map from the grantable list (1.232639ms) +✔ credentials: services (0.60855ms) +✔ contract: a non-empty regular Markdown file beside the role file (0.673426ms) +✔ every key names known layers and a merge rule (1.019171ms) +✔ unknown keys and wrong layers refuse (0.955361ms) +✔ types (2.561672ms) +✔ merge: defaults, then the most specific layer wins (0.358745ms) +✔ merge: limits only narrow, and provenance lists each source (0.490927ms) +✔ merge doesn't change its inputs (0.201055ms) +ℹ tests 60 +ℹ suites 0 +ℹ pass 60 +ℹ fail 0 +ℹ cancelled 0 +ℹ skipped 0 +ℹ todo 0 +ℹ duration_ms 2404.316662 diff --git a/agents/filbert/work/slice1-s3-review/r2-cand-node-discord.txt b/agents/filbert/work/slice1-s3-review/r2-cand-node-discord.txt new file mode 100644 index 00000000..97dbc794 --- /dev/null +++ b/agents/filbert/work/slice1-s3-review/r2-cand-node-discord.txt @@ -0,0 +1,181 @@ +✔ approvals: a request is validated before anything is posted; the rendering shows names and never ids (2.963073ms) +✔ approvals: the ledger is appended and folded into open requests with bind and approval states (3.178894ms) +✔ approvals: a reply approves only when it points at a request, says exactly approve, and comes from a listed approver once (0.720524ms) +✔ approvals: a button approves only on its own request message with the matching custom id (0.530479ms) +✔ approvals flow: a turn that opened a request posts the message with the button, records it, binds it, and both approvers approve (20.512863ms) +✔ approvals flow: a non-approver, a repeat, a wrong custom id and a service refusal each get their fixed line and a drop entry (13.406501ms) +✔ approvals flow: an invalid request from the model, a refused post, and no api client are recorded and post nothing (8.308285ms) +✔ approvals flow: start retries a bind and an approval left as unknown, under their original keys (5.005ms) +✔ authorize: open channel, listed user (2.462329ms) +✔ authorize: wrong guild (0.211188ms) +✔ authorize: no guild (DM) (0.382637ms) +✔ authorize: unlisted channel (0.243141ms) +✔ authorize: unknown channel, no info (0.283841ms) +✔ authorize: thread of listed parent (0.194418ms) +✔ authorize: thread of unlisted parent (0.241093ms) +✔ authorize: text channel that is not a thread and not listed (0.187886ms) +✔ authorize: unlisted user (1.473786ms) +✔ authorize: no author (0.518056ms) +✔ authorize: bot author (listed id, bot flag) (0.245026ms) +✔ authorize: system author (0.327851ms) +✔ authorize: the bot itself (0.104269ms) +✔ authorize: webhook (0.104934ms) +✔ authorize: mention channel without mention (0.751354ms) +✔ authorize: mention channel with bot mention (0.14485ms) +✔ authorize: mention channel with @everyone only (0.257862ms) +✔ authorize: mention channel mentioning someone else (0.100179ms) +✔ authorize: mention channel, content says @bot but mentions empty (0.149361ms) +✔ authorize: private thread under mention channel, mentioned (0.125426ms) +✔ authorize: private thread under mention channel, not mentioned (0.082438ms) +✔ authorize: thread in another guild per channel info (0.08409ms) +✔ authorize: not an object (0.073476ms) +✔ authorize: no id (0.071977ms) +✔ authorize: oversize content is accepted and flagged (0.081707ms) +✔ authorize: exactly the limit is not oversize (0.075284ms) +✔ authorize: a user's channel allowlist drops them outside it, threads count as the parent, others are unaffected (0.513239ms) +✔ authorize: order puts wrong guild before user, and user before channel (no channel lookup for strangers) (0.152837ms) +✔ binding: a complete binding validates and is frozen (3.017009ms) +✔ binding: unknown key, missing field, wrong type refuse with exit 2 (1.851649ms) +✔ binding: empty allowlists refuse (0.544457ms) +✔ binding: a user's channel allowlist must be non-empty, listed and unique; absent means every listed channel (1.390612ms) +✔ reloadDiff: reloadable keys are summarised by id; every fixed key refuses with exit 2 (1.978558ms) +✔ binding: file must be 0600, regular, not a symlink (1.682533ms) +✔ binding: token file mode, symlink, emptiness and shape are checked; token never appears in errors (5.080671ms) +✔ cli: check refuses a non-0600 token file with exit 2 before any network use (142.347456ms) +✔ context files: absolute paths, traversal, symlinks and out-of-repo targets refuse; in-repo files resolve (1.91407ms) +✔ cli: check refuses a missing context file and a missing binding with exit 2; usage is exit 4 (442.993851ms) +✔ cli: reload validates the file first (exit 2), then needs a live owner (exit 1); usage is exit 4 (264.506026ms) +✔ cli: run refuses when STOP is present, before any network use (154.948451ms) +✔ binding: tools is optional, validated strictly, a fixed key for reload, and its roots are resolved against the data root (1.82821ms) +✔ binding: a git key is validated at load and reaches the extension whole, and only on a writable root (1.259171ms) +✔ 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.381395ms) +✔ delivery: refused and unknown outcomes are journaled; a later chunk is not sent after a failure (25.038607ms) +✔ delivery: restart with an unknown entry re-sends the same nonce once and reconciles before accepting traffic (2.602428ms) +✔ delivery: an unknown entry older than the dedupe window is marked refused, not re-sent; a still-unknown one refuses start (1.333856ms) +✔ delivery: repeated unknown reconciliations never refresh the dedupe window; the original intent time decides (1.425041ms) +✔ turn: a failed engine turn posts the fixed line, never model output, and writes a failed record (3.21173ms) +✔ turn: a second message during a turn is held by the engine, both get their own reply and record (35.202423ms) +✔ turn: a thread under a listed channel is answered in the thread; an unknown thread is looked up once (2.823213ms) +✔ drop: an unlisted user gets silence and one drop line; no inbox entry, no REST call, no engine call (1.616033ms) +✔ drop: an oversize message is accepted into the inbox, answered with the fixed line and journaled as a drop (1.054458ms) +✔ restart: an inbox with three ids and a replay of the same three produces zero turns (44.52569ms) +✔ stop: STOP present refuses start; STOP written while running refuses new turns and the current one finishes (32.442936ms) +✔ ceiling: the ceiling plus one is refused and journaled; one fixed line per UTC day; a new day accepts again (7.794603ms) +✔ ceiling: a burst arriving while turns are still running cannot queue past the ceiling (3.651826ms) +✔ ceiling: a turn interrupted by a crash still counts after restart; admissions are durable (1.389594ms) +✔ ceiling: the daily notice survives a same-day restart; one delivery attempt in total, even when the first attempt crashed mid-flight (6.749633ms) +✔ duplicate: the same event delivered twice while the thread lookup is held yields one prompt, one admission and one reply (2.956586ms) +✔ journal: no token-shaped string and no model output on the drop path reaches disk (0.736482ms) +✔ 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.868026ms) +✔ receipt: Discord refusing the reaction leaves the turn intact and records ok false (2.550336ms) +✔ reload: a new user is silent before and answered after; a removed channel goes silent; a lower ceiling applies at once (6.067062ms) +✔ reload: a fixed key refuses with exit 2 and the old binding stays in force (1.645746ms) +✔ tools: with a tools binding the turn record lists every read and its outcome; without one the field is null (4.599939ms) +✔ context: the Discord block names the server, channels and modes, and states the rules from Q15 and Q16 (2.741713ms) +✔ context: with tools the block names the roots, keeps file content as data, and says to state refusals plainly (0.886904ms) +✔ context: a writable root adds the write rules and says a write is real only once Jason commits (2.094096ms) +✔ context: the envelope is one bracketed line then the text; names cannot break the line (1.463935ms) +✔ context: a git root swaps the terminal-commit line for the git verbs, and a vault root adds the id protocol (3.263437ms) +✔ context: assembleContext concatenates files in launcher format and appends the block; sha256 is stable (1.683573ms) +✔ context: splitReply keeps paragraphs together under the limit and splits long ones at lines, spaces, then hard (1.682811ms) +✔ engine: buildPiArgs carries the fixed flags, engine settings, session dir and prompt file (3.566397ms) +✔ engine: with tools, buildPiArgs turns pi's own tools off, loads the extension explicitly and allowlists exactly our three (0.460912ms) +✔ engine: a run with tool turns settles once, on the answer, with every tool call in the result (78.754891ms) +✔ engine: a run that ends on a tool-only turn fails the prompt as empty; a retried run settles on the real end (49.805976ms) +✔ engine: one prompt, one turn, text and usage come back (40.137415ms) +✔ engine: a prompt while streaming is held until pi settles, then sent as its own run, and answered in order (354.984992ms) +✔ engine: a held prompt that times out before pi settles fails on its own and is never sent (248.500124ms) +✔ engine: timeout sends abort and fails only that turn; the process stays (106.191287ms) +✔ engine: tool events from a run that outlived its timeout never land in the next prompt's record (239.999019ms) +✔ engine: a prompt after a turn that timed out before its agent_start waits for pi to settle instead of being refused (150.938225ms) +✔ engine: when pi has not started a timed-out turn by the end of the abort grace, the engine stops pi and fails held prompts (212.826758ms) +✔ engine: a timed-out turn pi starts only after the grace never answers a later prompt (614.581606ms) +✔ engine: late events of a run past its grace, before pi exits, answer nothing and nothing more is sent (early prompt response) (1.54869ms) +✔ engine: late events of a run past its grace, before pi exits, answer nothing and nothing more is sent (late prompt response) (0.639715ms) +✔ engine: a timed-out run pi did start outlives the grace; the next prompt goes out when it ends (431.214694ms) +✔ engine: a malformed JSONL line fails the turn, not the process (43.63606ms) +✔ engine: a turn that ends in error rejects with the error code; process exit fails pending turns (49.40699ms) +✔ gateway: hello -> identify with intents, ready, heartbeat with jitter, ack (2.690703ms) +✔ gateway: missed ack closes the socket and resumes with the last sequence (1.907767ms) +✔ gateway: op 7 reconnect resumes; op 9 non-resumable re-identifies (0.53113ms) +✔ gateway: op 9 resumable resumes (0.526163ms) +✔ gateway: close 4014 is fatal, reports the missing intent, never reconnects (1.061126ms) +✔ gateway: 4004 and 4013 are fatal too; 1006 reconnects with identify when no session (0.548698ms) +✔ gateway: close() is final and unparseable frames are ignored (0.552598ms) +✔ git: config validation is strict, needs write: true, a work tree and a private token file (102.597597ms) +✔ git: the child environment drops every host git config, names one helper, and carries the token path only for origin (51.583927ms) +✔ git: status reports the branch, ahead/behind and changed paths, and refuses off the named branch or mid-merge (113.962396ms) +✔ git: parseStatus reads porcelain v2 including renames and conflicts (0.509335ms) +✔ git: a commit stages exactly the named files, carries the seat author and the requester trailer, and pushes at once (119.775668ms) +✔ git: commit refusals: message, paths, requester, nothing to commit, and an index that already holds other work (122.912009ms) +✔ git: a commit whose push fails is still a commit, says so, and the next commit's push carries both (D6) (147.08931ms) +✔ git: pull is fast-forward only; a diverged origin or dirty local files refuse with nothing merged (263.840565ms) +✔ git: push pushes the named branch only and reports up to date (98.299482ms) +✔ git: no token value or token path ever reaches a git argument list; outputs are masked and capped (111.593045ms) +✔ git: the credential helper answers get over https from a private file and nothing else (294.872892ms) +✔ git: the vault protocol validates before a commit, honours another owner's lock, reserves ids, and locks around writes (960.529764ms) +✔ lock: the claim is exclusive; a second start against a live owner refuses (5.227477ms) +✔ lock: a stale lock (dead owner, reused pid, or record without start) refuses run and is never signaled; only unlock clears it (6.035809ms) +✔ lock: an incomplete claim (directory without owner record) is busy and refuses run; unlock clears it (1.431433ms) +✔ lock: an owner record that exists but cannot be read is invalid: never signaled, never removed, never claimed over (3.005199ms) +✔ lock: legacy upgrade; a live connector holding a {pid, start} record is unknown, unlock refuses and nothing changes; after it exits, unlock clears it (82.870452ms) +✔ lock: a live pid whose record carries a malformed or noncanonical start or boot string is unknown, not a mismatch; nothing signals, removes, or claims over it (414.27476ms) +✔ lock: identity syntax; only canonical unsigned decimal start ticks and lowercase boot uuids are identities (0.458791ms) +✔ lock: a process whose start marker or boot id cannot be read refuses to claim (0.406162ms) +✔ lock: a live pid whose identity cannot be read right now is unknown: never signaled, never removed, never claimed over (0.846589ms) +✔ lock: four processes racing for the same binding; exactly one claims it and the others refuse (53.748375ms) +✔ lock: stale handoff; concurrent starts over a stale lock all refuse, nothing reclaims, one unlock then exactly one live owner (152.58686ms) +✔ lock: four-party schedule; claims landing inside an unlock's gap never survive, one unlock leaves no owner and no residue (79.712479ms) +✔ notices: a kind is recorded per UTC day and found again (0.482002ms) +✔ recover: nothing to do is clean; a lock whose owner is gone or that has no record is cleared and STOP ends up absent (74.28234ms) +✔ recover: an operator STOP refuses with exit 3 and is never removed, whatever the lock says (51.740288ms) +✔ recover: a brake written during the unlock wins; STOP stays with both lines and the start is refused (46.609843ms) +✔ recover: a held binding refuses with exit 3 and writes no STOP: live owner, alive pid without verifiable identity, unreadable record (114.131415ms) +✔ cli: recover exits 0 when ready, 3 behind a brake or a held binding, and run's own STOP refusal is 3 (843.858294ms) +✔ rest: createMessage sends nonce, enforce_nonce, empty allowed_mentions and a soft reply reference (3.413377ms) +✔ rest: 429 waits retry_after and retries; 4xx is refused; 5xx and socket errors are unknown (3.131492ms) +✔ rest: content and nonce limits are enforced locally; typing never throws (0.739157ms) +✔ rest: react PUTs the encoded emoji on the inbound message for @me; 2xx is true, anything else is false and never throws (0.930648ms) +✔ setspark config: a bare https or loopback origin, a private key file, a principal (7.088359ms) +✔ setspark config: reaches the tools config and the binding as a fixed key (3.708867ms) +✔ setspark config: the binding's key survives resolveToolRoots and the engine's JSON hand-off to the extension (1.812564ms) +✔ setspark config: approvers come from the binding's users, never from the binding's setspark key (2.454088ms) +✔ setspark verbs: required_approvers go out as discord ids from names and come back as names (41.083503ms) +✔ setspark verbs: no Discord user id reaches tool text, whatever shape the service returns it in (12.675817ms) +✔ setspark contract: a decision made with names opens a request the connector accepts; names stored by an old record still refuse (12.576069ms) +✔ setspark keys: read per call, one printable token per file, rotation without a restart (4.450085ms) +✔ setspark idempotency keys: principal, turn id, call index; connector keys name a step (0.721963ms) +✔ setspark http core: json in and out, bearer header, idempotency header, fixed user agent, no key anywhere else (3.10125ms) +✔ setspark http core: error bodies become fixed refusals with code and the 409 fields; server text is data, cut (1012.360182ms) +✔ setspark verbs: a setspark key enables the eight verbs and no counters (0.536678ms) +✔ 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.111159ms) +✔ setspark verbs: no turn refuses every write before any request; bad arguments refuse before any request; reads still work (1.584368ms) +✔ setspark verbs: renderRecord caps long output and hides the accepted snapshot (0.24147ms) +✔ setspark api: bind, add_approval (button and reply) and get use integer request ids and the connector's keys (2.660486ms) +✔ tools: config refuses a missing, symlinked, dotted, non-directory or duplicate root and bad limits (4.489669ms) +✔ tools: every escape is refused with a fixed reason and nothing outside the root is read (5.04174ms) +✔ tools: happy paths list, read a window, and search case-insensitively; dotfiles and symlinks never appear (5.854329ms) +✔ tools: the tool set renders text for the model, records details for the journal, and enforces the per-run budget (4.020608ms) +✔ tools: listing and search caps hold (12.583785ms) +✔ tools: credential shapes are caught; ordinary prose and ids are not (1.318736ms) +✔ 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 (12.793439ms) +✔ tools: an unreadable file under the root is skipped by search and refused by read (2.300647ms) +✔ tools: config accepts write: true only as a boolean, and enables the write tools only then (1.501369ms) +✔ tools: every write outside the fence is refused before any byte lands, and no temp file remains (6.402268ms) +✔ tools: write_file leaves the exact bytes, edit_file replaces one exact match, and the set renders the change as uncommitted (4.687671ms) +✔ tools: a target that changed between the check and the rename is refused and the temp file is removed (1.815983ms) +✔ web: config takes an https or loopback-http SearXNG base url and a bounded fetch cap (3.970733ms) +✔ web: address rules refuse every private, loopback, link-local, mapped and multicast form (2.551934ms) +✔ web: web_fetch refuses bad urls, private hosts, rebinding names, non-https redirects, too many hops, error status, non-text bodies, and times out (1033.99121ms) +✔ web: web_fetch returns html as text with the title, follows an https redirect, keeps plain text and json, and cuts at the cap (5.458972ms) +✔ web: html to text drops scripts, styles and comments, decodes entities and keeps block breaks (0.328179ms) +✔ 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.468037ms) +✔ 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.925825ms) +ℹ tests 173 +ℹ suites 0 +ℹ pass 173 +ℹ fail 0 +ℹ cancelled 0 +ℹ skipped 0 +ℹ todo 0 +ℹ duration_ms 2805.660296 diff --git a/agents/filbert/work/slice1-s3-review/r2-cand-node-tasks.txt b/agents/filbert/work/slice1-s3-review/r2-cand-node-tasks.txt new file mode 100644 index 00000000..70f30b8e --- /dev/null +++ b/agents/filbert/work/slice1-s3-review/r2-cand-node-tasks.txt @@ -0,0 +1,59 @@ +✔ the boot config is checked before anything starts (516.214198ms) +✔ a business with no tracker entry refuses task verbs (202.040347ms) +✔ credential.expiring and .expired are recorded once per instance (277.2536ms) +✔ a token file that changes on disk records credential.changed (129.625135ms) +✔ autostart polls, reconciles and retries a startup the tracker was down for (150.836051ms) +✔ a refusal a restart must clear is not retried by the poll (100.523777ms) +✔ a poll that fires while two are queued is dropped (116.03428ms) +✔ close waits for a running verb and refuses one that has not started (286.520729ms) +✔ the bundled Vikunja is the pinned upstream image the runbook names (0.982267ms) +✔ every published port is on 127.0.0.1, and no secret is in the file (0.354809ms) +✔ the fake answers each route with the statuses and shapes Vikunja v2.7.0 sent (872.446139ms) +✔ the recorded task bodies pass the checks S3 applies to every read (0.588013ms) +✔ the client works against the fake over real HTTP with the platform fetch (276.500664ms) +✔ a correct install starts, and the first reconcile records tasks that already exist (558.033859ms) +✔ verbs refuse while a business is starting and after startup refused it (194.991321ms) +✔ startup refuses a token that can do more than its role needs (425.35624ms) +✔ startup refuses an unsupported version and flags an untested one (358.413565ms) +✔ startup refuses a board that the runbook did not install (523.518323ms) +✔ startup refuses a project the sync bot cannot read (102.381308ms) +✔ startup refuses a configured label the pm bot cannot see (164.637086ms) +✔ startup refuses an expired credential and a missing sync credential (324.959769ms) +✔ an unreachable tracker refuses with tracker-unavailable (257.875281ms) +✔ an edit in the UI is recorded once, with the fields that changed (676.606055ms) +✔ a move between open buckets is seen on the board, though updated does not change (231.489796ms) +✔ a person's comment is counted and a bot's is not (290.191335ms) +✔ the hourly reconcile catches a comment through comment_count (253.210118ms) +✔ a task closed in the UI leaves the open view with its done bucket (543.063112ms) +✔ a task that leaves the board is recorded as deleted, moved or out of reach (379.240669ms) +✔ a poll that read before a verb wrote does not overwrite the verb (289.658394ms) +✔ a tracker fault during a tick is reported and the next tick catches up (463.374074ms) +✔ a malformed answer refuses the tick with tracker-shape (203.05616ms) +✔ no token value reaches the database, the log or a refusal (392.288491ms) +✔ the first look at a task counts only comments inside the window (204.484884ms) +✔ task.create needs a recorded human request and a requirement id (685.311567ms) +✔ only labels named in the business file can be written (295.994765ms) +✔ task.schedule sets and clears a due date and relations (307.961545ms) +✔ assign and reassign move the role bots and record task.assigned (395.036157ms) +✔ task.update.assigned is for the assignee and records task.state (428.5151ms) +✔ a wrong expected digest records task.conflict and writes nothing (341.556182ms) +✔ a cross-role verb needs a resolved decision, used once (495.921449ms) +✔ task.close needs a verdict; after it every verb refuses with task-done (429.584203ms) +✔ a lost answer is settled by a re-read and never retried (382.674631ms) +✔ a create whose answer is lost is reported uncertain, and the poll finds the task (195.778392ms) +✔ a task the sync bot cannot read refuses and records nothing (104.964243ms) +✔ verbs and polls for one business run one at a time (188.87669ms) +✔ a due date with milliseconds is written to the second (181.682299ms) +✔ every write landed and the final read failed: the verb succeeds and records what it wrote (126.029473ms) +✔ some writes landed and the final read failed: write-uncertain, and nothing is recorded (97.017427ms) +✔ a create whose final read fails succeeds and records task.created (108.52731ms) +✔ an edit between the last write and the final read shows as external on the next poll (141.955479ms) +✔ task.created is recorded when a later label write fails (95.585552ms) +ℹ tests 51 +ℹ suites 0 +ℹ pass 51 +ℹ fail 0 +ℹ cancelled 0 +ℹ skipped 0 +ℹ todo 0 +ℹ duration_ms 5109.136747 diff --git a/agents/filbert/work/slice1-s3-review/r2-cand-suite-auth.txt b/agents/filbert/work/slice1-s3-review/r2-cand-suite-auth.txt new file mode 100644 index 00000000..ac228760 --- /dev/null +++ b/agents/filbert/work/slice1-s3-review/r2-cand-suite-auth.txt @@ -0,0 +1,17 @@ +OK status with missing harness credential exits 3 and still lists accounts +OK status reports harness credential (read-only) + mosaic accounts +OK api key material never reaches output +OK oauth token material never reaches output +OK unparseable credential file exits 2 +OK symlinked credential file exits 4 +OK env-side credential names reported +OK env var values never reach output +OK accounts without an accounts dir reports none and creates nothing +OK accounts lists files and marks the active one +OK loose account perms flagged in listing +OK agent --auth with missing account file refuses (exit 4) +OK agent --auth with non-0600 account file refuses +OK agent --auth with invalid account name refuses +OK auth.sh without valid config refuses + +selftest: 15 passed, 0 failed diff --git a/agents/filbert/work/slice1-s3-review/r2-cand-suite-conductor.txt b/agents/filbert/work/slice1-s3-review/r2-cand-suite-conductor.txt new file mode 100644 index 00000000..4313a59a --- /dev/null +++ b/agents/filbert/work/slice1-s3-review/r2-cand-suite-conductor.txt @@ -0,0 +1,55 @@ +Note: switching to 'c9ef7ee49f0785287de7ff41130dd4761ad2c7e3'. + +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 'c9ef7ee49f0785287de7ff41130dd4761ad2c7e3'. + +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-s3-review/r2-cand-suite-config.txt b/agents/filbert/work/slice1-s3-review/r2-cand-suite-config.txt new file mode 100644 index 00000000..76c05ef7 --- /dev/null +++ b/agents/filbert/work/slice1-s3-review/r2-cand-suite-config.txt @@ -0,0 +1,26 @@ +OK absent adapter defaults to pi +OK adapter mock validates (exit 0) +OK unsupported adapter exits 2 (exit 2) +OK env exports adapter +OK bootstrap creates default when absent (exit 0) +OK bootstrap wrote config file +OK bootstrap is idempotent on existing config (exit 0) +OK bootstrap did not rewrite existing config +OK validate missing config exits 3 (exit 3) +OK malformed JSON exits 2 (exit 2) +OK unsupported configVersion exits 2 (exit 2) +OK unknown top-level key exits 2 (exit 2) +OK unknown execution key exits 2 (exit 2) +OK unsupported backend exits 2 (exit 2) +OK unsupported environment exits 2 (exit 2) +OK relative dataRoot exits 2 (exit 2) +OK non-canonical dataRoot exits 2 (exit 2) +OK filesystem root dataRoot exits 2 (exit 2) +OK home directory dataRoot exits 2 (exit 2) +OK dataRoot containing config dir exits 2 (exit 2) +OK control character in provider exits 2 (exit 2) +OK symlinked config file exits 2 (exit 2) +OK env exports resolve correctly +OK failed validation modified nothing + +selftest: 24 passed, 0 failed diff --git a/agents/filbert/work/slice1-s3-review/r2-cand-suite-discord.txt b/agents/filbert/work/slice1-s3-review/r2-cand-suite-discord.txt new file mode 100644 index 00000000..c667a891 --- /dev/null +++ b/agents/filbert/work/slice1-s3-review/r2-cand-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-s3-review/r2-cand-suite-extension-package.txt b/agents/filbert/work/slice1-s3-review/r2-cand-suite-extension-package.txt new file mode 100644 index 00000000..efc0706a --- /dev/null +++ b/agents/filbert/work/slice1-s3-review/r2-cand-suite-extension-package.txt @@ -0,0 +1,21 @@ +OK initial ordinary-file install +OK installed tree matches canonical source +OK installed tree has no symlinks +OK check detects installation drift +OK sync refuses to overwrite installation drift +OK check detects an extra destination file +OK check detects an extra destination directory +OK check rejects a destination symlink +OK sync accepts a canonical source update +OK updated installation matches canonical source +scripts/test-extension-package.sh: line 14: 3836966 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-s3-review/r2-cand-suite-foundation.txt b/agents/filbert/work/slice1-s3-review/r2-cand-suite-foundation.txt new file mode 100644 index 00000000..6338e1e6 --- /dev/null +++ b/agents/filbert/work/slice1-s3-review/r2-cand-suite-foundation.txt @@ -0,0 +1,53 @@ +toolchain: node v26.8.1, python 3.12.8, jsonschema 4.26.0 + +OK syntax: scripts/foundation-inspect.mjs +OK syntax: scripts/foundation/strict-json.mjs +OK syntax: scripts/foundation/canonical.mjs +OK syntax: scripts/foundation/resolve.mjs +OK syntax: scripts/foundation/validate-record.mjs +OK syntax: scripts/foundation/fixtures/build-fixtures.mjs +OK syntax: scripts/foundation/canonical.test.mjs +OK syntax: scripts/foundation/cli.test.mjs +OK syntax: scripts/foundation/fixtures.test.mjs +OK syntax: scripts/foundation/resolve.test.mjs +OK syntax: scripts/foundation/strict-json.test.mjs +OK syntax: scripts/foundation/verify-schema.py (ast only; no bytecode written) +OK fixture generator runs +OK checked-in fixtures/bundles equal a fresh generation +OK checked-in fixtures/raw equal a fresh generation +OK checked-in fixtures/index.json equal a fresh generation +OK checked-in demo bundles equal a fresh generation +OK a failing nested test fails the run under a parent runner's NODE_TEST_CONTEXT +OK node --test scripts/foundation/ (ℹ pass 80) +OK differential schema oracle: PASS: differential schema oracle (finite corpus; compatibility evidence, not equivalence proof) + platform witness: strftime('%Y') for year 999 -> '999' (pinned checker refuses years 0001..0999) + node v26.8.1; corpus 1568 records (38 pinned fixtures, 478 unique bundle records, 1052 typeCase/mutation/lexical cases) + schema column: agree-valid 540, agree-invalid 991, DISAGREEMENTS 0; strict-only (parser-bound) cases: 27; unsupported-kind records not schema-assessed by the inspector: 10 + profile column (schema-valid records only): profile-valid 510, profile-invalid 30 + profile refusals asserted: 30 schema-agreed-valid records refused only by the strict typed-string profile (rule profile-pattern-mismatch), 12 declared by name; 73 named probes verified against declared schema/profile columns +OK oracle: zero schema-column disagreements with the pinned checker +OK oracle: strict-only profile refusals are counted and asserted +OK demo: permitted read preview exits 0 (exit 0) +OK demo: permitted file.change preview exits 0 (exit 0) +OK demo: assignment.change proposal is unresolved (exit 3) (exit 3) +OK demo: revoked registration is refused (exit 3) (exit 3) +OK demo: message is not authority (exit 3) (exit 3) +OK usage: no arguments exits 2 (exit 2) +OK io: missing file exits 4 (exit 4) +OK io: directory exits 4 (exit 4) +OK io: symlink exits 4 (O_NOFOLLOW) (exit 4) +OK bound: oversize fixture exits 2 (exit 2) +OK profile: one final LF in a typed selection id is refused before admission (exit 2) (exit 2) +OK profile: two final LFs fail the schema pattern itself (exit 2) (exit 2) +OK profile: escaped newlines in free-form text stay allowed (exit 0) (exit 0) +OK profile refusal is invalid-request/profile-pattern-mismatch with selection and operation withheld, value not echoed +OK text output starts with the disclaimer +OK json output is valid JSON with result allowed and exactly the charter §7 fields +OK json golden matches byte-for-byte +OK sandboxed bundle run (env -i, PATH=/nonexistent) produced the unresolved proposal +OK sandbox inventory (path/type/size/mode/uid/gid/inode/mtime/sha256) unchanged by runs +OK canary never printed (bundle run and credential-file run) +OK a non-bundle JSON file is refused at the shape gate, not read into output +OK no field of the non-bundle file is echoed + +selftest: 44 passed, 0 failed diff --git a/agents/filbert/work/slice1-s3-review/r2-cand-suite-queue.txt b/agents/filbert/work/slice1-s3-review/r2-cand-suite-queue.txt new file mode 100644 index 00000000..e00ad5c1 --- /dev/null +++ b/agents/filbert/work/slice1-s3-review/r2-cand-suite-queue.txt @@ -0,0 +1,35 @@ +toolchain: node v26.8.1, git version 2.55.0 + +OK syntax: packages/queue/src/cli.mjs +OK syntax: packages/queue/src/errors.mjs +OK syntax: packages/queue/src/io.mjs +OK syntax: packages/queue/src/lock.mjs +OK syntax: packages/queue/src/queue.mjs +OK syntax: packages/queue/src/review.mjs +OK syntax: packages/queue/src/store.mjs +OK syntax: packages/queue/tests/commit.test.mjs +OK syntax: packages/queue/tests/data.test.mjs +OK syntax: packages/queue/tests/dispatch.test.mjs +OK syntax: packages/queue/tests/helpers.mjs +OK syntax: packages/queue/tests/lock.test.mjs +OK syntax: packages/queue/tests/migration.test.mjs +OK syntax: packages/queue/tests/review.test.mjs +OK syntax: packages/queue/tests/store.test.mjs +OK syntax: packages/queue/tests/write.test.mjs +OK syntax: packages/queue/tests/fixtures/fake-gitea.mjs +OK syntax: packages/queue/tests/fixtures/kill-at.mjs +OK syntax: packages/queue/tests/fixtures/lock-child.mjs +OK syntax: packages/queue/tests/fixtures/mosaic-pre-a2.sh +OK syntax: scripts/queue-commit.sh +OK syntax: scripts/git-hooks/pre-commit +OK syntax: scripts/mosaic +OK queue-commit.sh, the guard and scripts/mosaic are executable +OK packages/queue declares no dependencies +ℹ tests 148 +ℹ pass 148 +ℹ fail 0 +OK node --test packages/queue/tests/ +OK scripts/mosaic queue help +skip queue verify and render --check: this checkout (/home/jwoltje/filbert-scratch/r38b/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-s3-review/r2-cand-suite-release.txt b/agents/filbert/work/slice1-s3-review/r2-cand-suite-release.txt new file mode 100644 index 00000000..1d6ed050 --- /dev/null +++ b/agents/filbert/work/slice1-s3-review/r2-cand-suite-release.txt @@ -0,0 +1,7 @@ +OK valid RELEASE resolves (exit 0) +OK invalid RELEASE exits 1 (exit 1) +OK missing RELEASE exits 1 (exit 1) +OK valid RELEASE leaves image tag consistent with version +skip state-machine cases (docker daemon unavailable) + +selftest: 4 passed, 0 failed diff --git a/agents/filbert/work/slice1-s3-review/r2-cand-suite-task.txt b/agents/filbert/work/slice1-s3-review/r2-cand-suite-task.txt new file mode 100644 index 00000000..85aa46f0 --- /dev/null +++ b/agents/filbert/work/slice1-s3-review/r2-cand-suite-task.txt @@ -0,0 +1,33 @@ +OK valid task validates (exit 0) +OK unknown task key exits 2 (exit 2) +OK unsupported taskVersion exits 2 (exit 2) +OK invalid task id exits 2 (exit 2) +OK empty prompt exits 2 (exit 2) +OK NUL in expectExact exits 2 (exit 2) +OK out-of-range timeout exits 2 (exit 2) +OK missing mission file exits 4 (exit 4) +OK task with valid mission validates (exit 0) +OK invalid mission exits 2 (exit 2) +OK validate missing task exits 4 (exit 4) +OK validation does not modify the task file +OK prune dry-run exits 0 (exit 0) +OK dry-run deleted nothing +OK prune --keep=2 --yes removes oldest (exit 0) +OK kept exactly 2 newest runs +OK newest run kept, oldest pruned +OK append-only receipt written (3 entries) +OK sessions/workspaces untouched by prune +OK prune with invalid keep exits 4 (exit 4) +skip adapter seam cases (docker daemon unavailable) +skip workspace/capability cases (docker daemon unavailable) +skip live task cases (docker unavailable) +OK onboard without name exits 4 (non-interactive) (exit 4) +OK onboard --name renders profile (exit 0) +OK profile written +OK canon structure: required filled, optional placeholdered +OK canon sections present +FAIL user recall run succeeds (exit 1) +FAIL recalled user name (response: ) +OK no agent identity on headless run + +selftest: 26 passed, 2 failed diff --git a/agents/filbert/work/slice1-s3-review/r2-gate-summary.txt b/agents/filbert/work/slice1-s3-review/r2-gate-summary.txt new file mode 100644 index 00000000..caa46898 --- /dev/null +++ b/agents/filbert/work/slice1-s3-review/r2-gate-summary.txt @@ -0,0 +1,25 @@ +cand node-tasks exit 0 load 5.91 +cand node-bus exit 0 load 5.92 +cand node-business exit 0 load 5.92 +cand node-discord exit 0 load 5.92 +cand suite-auth exit 0 load 5.92 +cand suite-conductor exit 0 load 5.93 +cand suite-config exit 0 load 5.78 +cand suite-discord exit 0 load 5.79 +cand suite-extension-package exit 0 load 5.79 +cand suite-foundation exit 0 load 5.44 +cand suite-queue exit 0 load 5.77 +cand suite-release exit 0 load 5.77 +cand suite-task exit 1 load 5.77 +base node-bus exit 0 load 5.78 +base node-business exit 0 load 5.78 +base node-discord exit 0 load 8.68 +base suite-auth exit 0 load 8.68 +base suite-conductor exit 0 load 8.26 +base suite-config exit 0 load 8.26 +base suite-discord exit 0 load 7.76 +base suite-extension-package exit 0 load 7.46 +base suite-foundation exit 0 load 5.74 +base suite-queue exit 0 load 6.69 +base suite-release exit 0 load 6.69 +base suite-task exit 1 load 6.69 diff --git a/agents/filbert/work/slice1-s3-review/r2-mut-summary.txt b/agents/filbert/work/slice1-s3-review/r2-mut-summary.txt new file mode 100644 index 00000000..5f96fc6d --- /dev/null +++ b/agents/filbert/work/slice1-s3-review/r2-mut-summary.txt @@ -0,0 +1,69 @@ +V1 killed (2) +V2 killed (1) +V3 killed (1) +V4 killed (1) +V5 killed (1) +V6 killed (1) +V7 killed (2) +V8 killed (2) +V9 killed (2) +V10 killed (2) +V11 killed (1) +V12 SURVIVED +V13 killed (1) +V14 killed (1) +V15 killed (1) +V16 killed (1) +V17 killed (1) +V18 killed (1) +V19 SURVIVED +V20 SURVIVED +V21 killed (1) +V22 killed (1) +V23 SURVIVED +V24 SURVIVED +S1 killed (1) +S2 killed (8) +S3 SURVIVED +S4 killed (2) +S5 killed (1) +S6 SURVIVED +S7 SURVIVED +S8 killed (1) +S9 SURVIVED +S10 SURVIVED +S11 killed (1) +U1 killed (1) +U2 killed (3) +U3 killed (1) +U4 killed (2) +U5 SURVIVED +U6 SURVIVED +U7 SURVIVED +U8 killed (1) +A1 killed (1) +A2 killed (1) +A3 killed (2) +A4 killed (1) +A5 killed (1) +A6 killed (1) +K1 SURVIVED +K2 killed (1) +K3 SURVIVED +K4 killed (5) +G1 killed (2) +G2 SURVIVED +B1 killed (1) +B2 killed (1) +B3 killed (1) +B4 killed (1) +B5 killed (1) +B6 killed (1) +B7 killed (1) +B8 SURVIVED +N1 killed (1) +N2 killed (1) +N3 SURVIVED +N4 killed (1) +N5 SURVIVED +N6 killed (1) diff --git a/agents/filbert/work/slice1-s3-review/r2-node24.txt b/agents/filbert/work/slice1-s3-review/r2-node24.txt new file mode 100644 index 00000000..085d9312 --- /dev/null +++ b/agents/filbert/work/slice1-s3-review/r2-node24.txt @@ -0,0 +1,127 @@ +v24.21.0 +✔ launch identity is stamped, payload identity is refused and stale holder cannot send (156.683815ms) +✔ decision classes route from policy; gated resolution is human-only, choice and target must match (294.274172ms) +✔ claim exclusion, holder release, gated revoke and rerouting to a new holder are atomic (285.287143ms) +✔ launch events require a human CLI capability; generic emit cannot forge authority events (201.828825ms) +✔ within-role decisions close atomically and invalid options or blocking omissions refuse (266.798953ms) +✔ observer capabilities read human inbox but cannot mutate or forge launch identity (255.797092ms) +✔ task action subjects and linked decision trail are complete and ordered (255.175739ms) +✔ launch binding is durable and reconnecting requires the identical trusted record (153.48265ms) +✔ business isolation includes inherited object names and cross-business message references (261.614001ms) +✔ authority never transfers between action, run, target, unresolved or replaced role holder (499.890688ms) +✔ task projection uses schema current view, skipping earlier and equal-start polls (220.584023ms) +✔ revocation permanently bars the old run from reclaiming first, including after broker restart (277.668609ms) +✔ empty message references refuse before storage; refusal-evidence failure stays a typed error (190.09813ms) +✔ both arbiters require human resolution when their cross-role route is themselves (274.3981ms) +✔ S1 adapter takes resolved limits and refs, rejects mismatched instance, never mutates input (5.738838ms) +✔ only validated broker references load; returned data and exceptions cannot expose a known token (13.669289ms) +✔ bad file modes, symlinks, repository/data paths, malformed tokens and missing dates refuse (3.074295ms) +✔ expiry refuses use and env references never become client data (0.962253ms) +✔ S1 parsed service refs work, service mismatch refuses, Gitea rotation due is a warning state (2.044467ms) +✔ opaque tokens shorter than 16 characters refuse before use (0.646557ms) +✔ human proof binds CLI entry, process start and nonce; agents and incomplete ancestry refuse (4.189933ms) +✔ process reader gets own kernel identity without exposing environment values (0.819456ms) +✔ EACCES ancestor environments skip only markers; commands and registered launches still refuse (5.21161ms) +✔ real pid 1 remains inspectable when its environment is protected (0.507027ms) +✔ within-role sends cite an open gated launch decision without spending it or naming it in grants (198.358392ms) +✔ missing and foreign-business citations refuse and roll back message and grant (214.848463ms) +✔ cross-role sends still need a matching resolved decision and consume it once (253.418804ms) +✔ broker process binds trusted launches, offers reader capabilities, refuses human mutation, closes cleanly (243.648194ms) +✔ startup token refusal returns safe code without value or partial listening broker (47.012202ms) +✔ loaded fixture token is absent from socket replies and SQLite, including refusal evidence (191.423851ms) +✔ killed broker leaves an explicit stale lock; another process cannot silently reclaim it (189.913075ms) +✔ trusted host registers later launches; socket clients never have a registration verb (179.451682ms) +✔ runtime excludes declared project roots even when host supplies no repoRoots (35.026481ms) +✔ a refused launch binding leaves the broker and existing capabilities alive; bad protocol stops it (209.669565ms) +✔ v3b prototype refusals, views and append-only mutations (1141.755297ms) +✔ gated approval authorizes once, survives store reopen, and fresh approval works (264.541696ms) +✔ another run cannot consume an approval; a failed check leaves it usable (238.367622ms) +✔ two scheduled callers have exactly one grant and one consumed refusal (178.480934ms) +✔ failed commit rolls consumption back; cross-role consumes and within-role stays reusable (378.072645ms) +✔ class drift gated to cross-role refuses before consumption (364.594342ms) +✔ class drift cross-role to gated refuses before consumption (293.318848ms) +✔ class drift gated to within-role refuses before consumption (319.843551ms) +✔ class drift cross-role to within-role refuses before consumption (390.074511ms) +✔ class drift within-role to gated refuses before consumption (302.392116ms) +✔ class drift within-role to cross-role refuses before consumption (266.74969ms) +✔ message.send consumes approval and prevents a later send or authorize (290.653606ms) +✔ role.revoke consumes approval and prevents a later revoke or authorize (287.370151ms) +✔ creates private WAL store and excludes a second writer until explicit close (123.91485ms) +✔ rollback is atomic and schema metadata is checked against trusted DDL, not just itself (214.167415ms) +✔ existing empty database and symlink runtime directory refuse, never initialize over damage (203.524319ms) +✔ crash during a transaction recovers no partial event after explicit fixture-only lock removal (200.118618ms) +✔ writer refuses mixed at/read_at forms atomically, even through trusted SQL helpers (106.76254ms) +✔ async transactions refuse before invoking their function (109.987831ms) +✔ recordTask keeps sync reads and a role write apart (181.34273ms) +✔ read_at must be one canonical UTC format, so the projection compares strings safely (124.834991ms) +✔ a bad entry refuses the whole record (135.312373ms) +✔ taskView reads the projection for one business (131.329473ms) +✔ requestTask hands only a holder and a task verb to the handler, and records refusals (251.724193ms) +✔ the server sends task verbs to the adapter with its own timeout; other verbs stay synchronous (446.554972ms) +✔ without an adapter the server refuses every task verb (281.70093ms) +✔ the runtime refuses an invalid adapter and closes a valid one (295.177841ms) +✔ the process loads the S3 adapter from plain-data trackers (383.983423ms) +✔ socket capability stamps launch identity; shared views use wire, no SQL client (162.358648ms) +✔ two wire claims serialize; a lost reply never automatically retries (203.497931ms) +✔ malformed, oversized and identity-forging envelopes refuse without echoing input (127.470225ms) +✔ client preserves UTF-8 when a response divides a multibyte character (15.892863ms) +✔ committed mutation followed by dropped reply reports unknown and is never retried (152.138594ms) +✔ the boot config is checked before anything starts (114.446203ms) +✔ a business with no tracker entry refuses task verbs (165.570715ms) +✔ credential.expiring and .expired are recorded once per instance (277.869743ms) +✔ a token file that changes on disk records credential.changed (126.831369ms) +✔ autostart polls, reconciles and retries a startup the tracker was down for (142.210614ms) +✔ a refusal a restart must clear is not retried by the poll (113.521601ms) +✔ a poll that fires while two are queued is dropped (207.621166ms) +✔ close waits for a running verb and refuses one that has not started (304.784433ms) +✔ the bundled Vikunja is the pinned upstream image the runbook names (1.562596ms) +✔ every published port is on 127.0.0.1, and no secret is in the file (0.582587ms) +✔ the fake answers each route with the statuses and shapes Vikunja v2.7.0 sent (464.901892ms) +✔ the recorded task bodies pass the checks S3 applies to every read (0.713834ms) +✔ the client works against the fake over real HTTP with the platform fetch (251.982965ms) +✔ a correct install starts, and the first reconcile records tasks that already exist (155.528439ms) +✔ verbs refuse while a business is starting and after startup refused it (172.948478ms) +✔ startup refuses a token that can do more than its role needs (486.365623ms) +✔ startup refuses an unsupported version and flags an untested one (460.393023ms) +✔ startup refuses a board that the runbook did not install (548.890888ms) +✔ startup refuses a project the sync bot cannot read (123.186793ms) +✔ startup refuses a configured label the pm bot cannot see (154.422859ms) +✔ startup refuses an expired credential and a missing sync credential (418.141481ms) +✔ an unreachable tracker refuses with tracker-unavailable (114.527738ms) +✔ an edit in the UI is recorded once, with the fields that changed (241.788096ms) +✔ a move between open buckets is seen on the board, though updated does not change (211.515078ms) +✔ a person's comment is counted and a bot's is not (314.060808ms) +✔ the hourly reconcile catches a comment through comment_count (321.406666ms) +✔ a task closed in the UI leaves the open view with its done bucket (523.66154ms) +✔ a task that leaves the board is recorded as deleted, moved or out of reach (374.270576ms) +✔ a poll that read before a verb wrote does not overwrite the verb (315.101943ms) +✔ a tracker fault during a tick is reported and the next tick catches up (278.236767ms) +✔ a malformed answer refuses the tick with tracker-shape (245.934902ms) +✔ no token value reaches the database, the log or a refusal (387.342385ms) +✔ the first look at a task counts only comments inside the window (221.67746ms) +✔ task.create needs a recorded human request and a requirement id (243.360663ms) +✔ only labels named in the business file can be written (262.314908ms) +✔ task.schedule sets and clears a due date and relations (323.01973ms) +✔ assign and reassign move the role bots and record task.assigned (494.875301ms) +✔ task.update.assigned is for the assignee and records task.state (402.075606ms) +✔ a wrong expected digest records task.conflict and writes nothing (298.001411ms) +✔ a cross-role verb needs a resolved decision, used once (492.684567ms) +✔ task.close needs a verdict; after it every verb refuses with task-done (362.762253ms) +✔ a lost answer is settled by a re-read and never retried (359.022185ms) +✔ a create whose answer is lost is reported uncertain, and the poll finds the task (247.871317ms) +✔ a task the sync bot cannot read refuses and records nothing (96.987524ms) +✔ verbs and polls for one business run one at a time (256.240989ms) +✔ a due date with milliseconds is written to the second (159.84818ms) +✔ every write landed and the final read failed: the verb succeeds and records what it wrote (223.361496ms) +✔ some writes landed and the final read failed: write-uncertain, and nothing is recorded (167.657207ms) +✔ a create whose final read fails succeeds and records task.created (186.75213ms) +✔ an edit between the last write and the final read shows as external on the next poll (126.50278ms) +✔ task.created is recorded when a later label write fails (129.164131ms) +ℹ tests 118 +ℹ suites 0 +ℹ pass 118 +ℹ fail 0 +ℹ cancelled 0 +ℹ skipped 0 +ℹ todo 0 +ℹ duration_ms 5049.292544 diff --git a/agents/filbert/work/slice1-s3-review/r2-probe.txt b/agents/filbert/work/slice1-s3-review/r2-probe.txt new file mode 100644 index 00000000..dc21dd10 --- /dev/null +++ b/agents/filbert/work/slice1-s3-review/r2-probe.txt @@ -0,0 +1,41 @@ +D1 self snapshot due_date self 2026-12-01T09:00:00.000Z returned digest 1c30ad1187e1 +D1 external events after one tick [] +D1 same schedule again sends PATCH 0 +D1 expect=returned digest -> decision-not-found +D2 external events 0 +W1 caller sees ok +W1 coder assigned in tracker true +W1 action.allowed task.assign 1 +W1 task.assigned events 1 self snapshots 2 +W1 poll external events [] +W1 caller retries assign -> task-assigned +H1 human task.assign agent-required +H2 reader task.assign read-only +H3 non-holder coder update not-holder +R1 other task id 2 schedule -> ok relations on task 1 [{"kind":"related","other":2}] +C1 reviewer (no scope.change authority) wrong expect -> task-conflict task.conflict events 1 +C1 reviewer right digest -> decision-required +M1 tick -> tracker-unavailable external 0 missing 0 +T1 next tick -> ok external 0 missing 1 +P1 create -> vikunja:1/2 snapshot self labels [1] due 2026-11-01T10:20:30.000Z digest==returned true task.created 2 +✔ D1 schedule with milliseconds: the poll reports the bot's own due date as an external change (119.739669ms) +✔ D2 whole seconds: no external change (control) (83.77551ms) +✔ W1 write lands, final read fails: refusal, no self record, poll calls it external (90.552022ms) +✔ H1 a human, H2 a reader, and a non-holder cannot call a task verb (86.12839ms) +✔ R1 a relation to another project's task id under this project's ref (81.923787ms) +✔ C1 a conflict event costs no authority; a role without the verb can still write task.conflict (86.879606ms) +✔ M1 one task with a 500 in missing() stalls the tick; T1 the tick after recovers (79.761787ms) +P1 poll external events for the new task [] +P2 caller sees ok state 3 due in tracker 2026-12-02T00:00:00Z schedule events 0 last snapshot self 2026-12-02T00:00:00.000Z +P3 caller sees ok PATCHes sent 0 +✔ P1 create with a label and a relation, final read fails: snapshot matches the poll (104.489902ms) +✔ P2 step settled by re-read, then the final read fails: success (126.271579ms) +✔ P3 nothing to write, final read fails (78.746838ms) +ℹ tests 10 +ℹ suites 0 +ℹ pass 10 +ℹ fail 0 +ℹ cancelled 0 +ℹ skipped 0 +ℹ todo 0 +ℹ duration_ms 1010.940239 diff --git a/agents/filbert/work/slice1-s3-review/r2-vk-create-probe.txt b/agents/filbert/work/slice1-s3-review/r2-vk-create-probe.txt new file mode 100644 index 00000000..0c646022 --- /dev/null +++ b/agents/filbert/work/slice1-s3-review/r2-vk-create-probe.txt @@ -0,0 +1,7 @@ +kanban view 24 default_bucket_id 16 done_bucket_id 18 +plain digest equal true differing [] updated answer 2026-10-09T00:32:29.621Z read 2026-10-09T00:32:29.000Z answer raw "2026-10-09T00:32:29.621574557Z" read raw "2026-10-09T00:32:29Z" + placed in bucket 16 default 16 +full digest equal true differing [] updated answer 2026-10-09T00:32:29.639Z read 2026-10-09T00:32:29.000Z answer raw "2026-10-09T00:32:29.639168224Z" read raw "2026-10-09T00:32:29Z" + placed in bucket 16 default 16 +labels+relation digest equal true differing [] updated answer 2026-10-09T00:32:29.651Z read 2026-10-09T00:32:29.000Z answer raw "2026-10-09T00:32:29.651562633Z" read raw "2026-10-09T00:32:29Z" + placed in bucket 16 default 16 diff --git a/agents/filbert/work/slice1-s3-review/review-r2.md b/agents/filbert/work/slice1-s3-review/review-r2.md new file mode 100644 index 00000000..ef18ac40 --- /dev/null +++ b/agents/filbert/work/slice1-s3-review/review-r2.md @@ -0,0 +1,214 @@ +# Slice 1 S3, row 38, round 2 review (Filbert) + +Issue #1520, request comment 26852, queue rev 186. Sage's request +2026-10-09T00:28:36Z. Packet: `agents/darkwing/work/slice1-s3/` at +`b144a03d`. Candidate: manifest sha256 +`e10e30e3d68c12177afba438ac02a06c208ea0370c7198af1f9d9cf163d637f4`, +28 files, over `c9ef7ee4` with `build-r2.patch` (sha256 +`71ce87e631b04d35591e2df966aae565d5f665ac2a9d5d252322ce1b0d5be69c`). +Round 1: `review-r1.md` here. + +Verdict: **approve.** Both round 1 blockers are fixed and tested, and so +are the three asked-for tests. I checked create()'s new final-read +fallback against the pinned Vikunja image: its snapshot digests the same +as a read, and the task lands in the default bucket. Sage's landing +condition stands: Darkwing's test-release and test-task reruns after the +zai reset. My gate didn't exercise the real model turns (see Suites), so +this approval doesn't cover them. + +The notes below are small. None of them blocks. + +## Method + +- Detached worktrees at `c9ef7ee4` under `~/filbert-scratch/r38b/`. In + the candidate I ran `git apply build-r2.patch` and then `sha256sum -c`: + 28 OK. After the mutant run the tree checks clean against the manifest + again. A third worktree held the round 1 candidate. My own tree diff + between the two candidates shows the same five files as Darkwing's + `r1-to-r2.diff`, all under `packages/tasks`, and the same 335 lines. +- `gate.sh` runs the suites one at a time in both trees and tees each + output. As in round 1, `DOCKER_HOST` points at a socket that doesn't + exist, so no model turns run. +- `probe.test.mjs` holds the round 1 probes, with D1 now expecting the + fix. It adds P1 to P3 for the new fallbacks. +- `vkcreate.mjs` runs against a scratch Vikunja on the pinned image + (`vikunja/vikunja@sha256:e2204a1c…cfc`, v2.7.0) on 127.0.0.1, on the + default bridge, with a throwaway user. It compares create()'s fallback + fields with a read. The container and its data are removed. +- `mutants.sh` runs the 63 round 1 mutants plus N1 to N6, which target + the round 2 code. +- Node 24 run: `node:24` with no network, the tree mounted read-only, and + the host uid. + +## Suites + +| Suite | Candidate | Base | +|---|---|---| +| node tasks (Node 26.8.1) | 51/51 | n/a | +| node tasks + bus (Node 24.21.0) | 118/118 | n/a | +| node bus | 67/67 | 58/58 | +| node business | 60/60 | 60/60 | +| node discord | 173/173 | 173/173 | +| test-auth | 15/15 | 15/15 | +| test-conductor | 17/17 | 17/17 | +| test-config | 24/24 | 24/24 | +| test-discord | 64/64 | 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 | 4/4 | +| test-task | 26 pass, 2 fail | 26 pass, 2 fail | + +Base and candidate fail the same two test-task cases, "user recall run +succeeds" and "recalled user name", as in round 1 and row 39. With no +Docker socket, test-release runs 4 cases and test-task 28, so the real +model turns that hit Darkwing's zai limit never ran here. I hit no 429 +and no address-pool error. Neither suite loads `packages/tasks` or +`packages/bus`, so I don't count Darkwing's 3 and 8 failures against the +candidate. The reruns Sage asked for are still the landing condition. + +## B1: fixed + +`due()` drops the milliseconds before the write, and `FakeVikunja` now +stores due dates to the second and echoes what it was sent, as v2.7.0 +does (`r1-vk-due-probe.txt`). Probe D1, which showed the bug in round 1, +now gives: + +``` +D1 self snapshot due_date self 2026-12-01T09:00:00.000Z +D1 external events after one tick [] +D1 same schedule again sends PATCH 0 +D1 expect=returned digest -> decision-not-found +``` + +The last line passes the `expect` check. It then refuses only because the +probe names a decision that doesn't exist. In round 1 it refused +`task-conflict`. The new test "a due date with milliseconds is written to +the second" covers create, schedule, a repeated schedule, `expect`, and a +PATCH settled by re-read. Mutant N4, which removes the truncation, is +killed. Mutant G1, which survived in round 1, is now killed too. The +README states the one-second precision with an example. + +## B2: fixed + +Probe W1 from round 1 now gives: + +``` +W1 caller sees ok +W1 task.assigned events 1 self snapshots 2 +W1 poll external events [] +W1 caller retries assign -> task-assigned +``` + +I checked the three branches in `handle()`: + +- **Every write landed, read failed.** The verb succeeds and records the + event and a snapshot of `work.expect(before.fields)`. That snapshot has + `etag` null and `updated` taken from the compare-read. + - An older `updated` is the safe direction. + `task_external_changes` requires `p.updated >= ls.updated`, so a later + poll still qualifies. + - The staleness check in `consider()` and `missing()` uses the + snapshot's `at`, not `updated`. +- **Some landed, read failed.** The verb refuses `write-uncertain` and + records nothing. `landed` counts a step that `step()` settled by + re-read. + - Probe P2 shows that: a PATCH that lands but answers 500, settled by + the re-read, then the final read fails. The verb returns ok. + - Mutants N1 (always throw the step's own code) and N2 (no `landed++`) + are both killed. +- **None landed, read failed.** The failed write's own code. A verb with + nothing to write and a failed final read also succeeds, with no PATCH + sent (probe P3). That is correct, because nothing needed writing. + +## create(): same treatment, checked + +When every label and relation write landed and the final read fails, +create() records `task.created` and a snapshot built from the create's +answer plus the labels it wrote, in the default bucket. It then returns +success. Against the pinned image (`r2-vk-create-probe.txt`), for a plain +task, a full one (description, due date, priority) and one with two +labels added out of id order and a relation: + +``` +plain digest equal true differing [] +full digest equal true differing [] +labels+relation digest equal true differing [] + placed in bucket 16 default 16 (each case) +``` + +The fallback fields match a read in every digested field. The task sits +in the view's default bucket, which startup already checks equals +`todo`. In the fake, probe P1 creates with a label and a relation, faults +the final read, and then ticks: no external event, and the snapshot +digest equals the one returned. Mutant N6, which removes the fallback, is +killed. + +## Notes (not blocking) + +1. **The create fallback's `updated` is finer than a read's.** The + create's answer carries nanoseconds (`2026-10-09T00:32:29.621574557Z`), + which `normal()` keeps as `.621Z`. A read gives `00:32:29Z`. + - So the fallback snapshot's `updated` can be up to 999 ms ahead of the + next poll's for the same version. + - `task_external_changes` requires `p.updated >= ls.updated`. A + person's edit in the same wall second as the create would therefore + drop out of that view. + - The `task.changed.external` event is still recorded, because + `consider()` compares digests. + - This needs a failed final read and an edit within the second. Nothing + reads the view yet. Flooring the fallback `updated` to the second + (`sync.mjs` already has `floorSecond`) would close it. +2. **Mutant N3 survives.** It drops the `labels` override in the create + fallback. Darkwing's create test faults the final read on a create + with no labels. My P1 would kill N3; adding `labels: [w.label]` to that + test would too. +3. **Mutant N5 survives.** It throws on any step failure before + recording, so a failed step with a good final read records no + snapshot. Round 1 had the same branch, and no test checks that + snapshot. One test with a refused second step and an assertion on the + last snapshot would hold it. +4. **README, create bullet.** It says the verb succeeds. The fallback + applies only when every label and relation write landed. If one + failed and the final read failed too, `task.created` is recorded with + no snapshot, and the step's refusal is thrown. The code comment says + this; the README bullet doesn't. +5. **The returned `updated`** on the read-failed success path is the + compare-read's, older than the write. The README says so. +6. Round 1 notes 1 to 14 still apply where they weren't fixed. Darkwing + has deferred note 7 (`secretCheck` on the result) to follow-ups. I + agree it stays out of this row. + +## Mutants + +46 of the 63 round 1 mutants are killed. 17 survive, which matches +Darkwing's count. V9, V21, S5 and G1 moved from survived to killed. Every +other result is unchanged from round 1, and the reasons are in +`review-r1.md`. Of the round 2 mutants, four of six are killed. + +| Mutant | Result | +|---|---| +| V9 success snapshot from the final read | killed (T1 test) | +| V21 no `task.created` after a failed step | killed (T2 test) | +| S5 first look counts every comment | killed (T3 test) | +| G1 due date not normalized | killed (B1 test) | +| V12 V19 V20 V23 V24 S3 S6 S7 S9 S10 U5 U6 U7 K1 K3 G2 B8 | survived, as in round 1 | +| N1 partial landing throws the step's code | killed | +| N2 settled steps not counted as landed | killed | +| N3 create fallback without the labels written | **survived** (note 2) | +| N4 due date not truncated | killed | +| N5 a failed step records nothing even when the read worked | **survived** (note 3) | +| N6 no create fallback | killed | + +## Files + +- `probe.test.mjs`, `vkcreate.mjs`, `mutants.sh`, `gate.sh` (round 2 + versions; the round 1 scripts were replaced in place, and `review-r1.md` + records their results) +- Output: + - `r2-probe.txt` + - `r2-vk-create-probe.txt` + - `r2-mut-summary.txt` + - `r2-node24.txt` + - `r2-gate-summary.txt` + - `r2-cand-*.txt` and `r2-base-*.txt` (each suite, teed) diff --git a/agents/filbert/work/slice1-s3-review/vkcreate.mjs b/agents/filbert/work/slice1-s3-review/vkcreate.mjs new file mode 100644 index 00000000..3bc7f457 --- /dev/null +++ b/agents/filbert/work/slice1-s3-review/vkcreate.mjs @@ -0,0 +1,54 @@ +// Filbert, row 38 r2: does create()'s final-read fallback (the create answer plus the labels +// written, in the default bucket) digest the same as a read on Vikunja v2.7.0? Scratch container on +// 127.0.0.1 only. The password and token stay in memory; nothing here prints them. +// Usage: node vkcreate.mjs +import { randomBytes } from 'node:crypto'; +const ORIGIN = process.argv[2]; +if (!/^http:\/\/127\.0\.0\.1:\d+$/.test(ORIGIN)) throw new Error('loopback only'); +const { taskFields, digest } = await import(process.argv[3] + '/packages/tasks/src/digest.mjs'); +const { normal } = await import(process.argv[3] + '/packages/tasks/src/sync.mjs'); +const BASE = ORIGIN + '/api/v2'; +async function api(method, path, body, auth) { + const headers = { 'Content-Type': 'application/json' }; + if (auth) headers.Authorization = `Bearer ${auth}`; + const r = await fetch(BASE + path, { method, headers, body: body === undefined ? undefined : JSON.stringify(body) }); + const t = await r.text(); + let json = null; + try { json = JSON.parse(t); } catch {} + return { status: r.status, json }; +} +const must = (r, l) => { if (r.status >= 300) throw new Error(`${l}: ${r.status} ${JSON.stringify(r.json)}`); return r.json; }; +const user = 'fp' + randomBytes(4).toString('hex'); +const password = randomBytes(18).toString('base64url'); +must(await api('POST', '/register', { username: user, email: user + '@example.invalid', password }), 'register'); +const O = must(await api('POST', '/login', { username: user, password }), 'login').token; +const P = must(await api('POST', '/projects', { title: 'probe' }, O), 'project').id; +const views = must(await api('GET', `/projects/${P}/views`, undefined, O), 'views'); const vl = views.items ?? views; +const kanban = vl.find((v) => v.view_kind === 'kanban'); +console.log('kanban view', kanban.id, 'default_bucket_id', kanban.default_bucket_id, 'done_bucket_id', kanban.done_bucket_id); +const L1 = must(await api('POST', '/labels', { title: 'b' }, O), 'label').id; +const L2 = must(await api('POST', '/labels', { title: 'a' }, O), 'label').id; +const other = must(await api('POST', `/projects/${P}/tasks`, { title: 'other' }, O), 'other').id; +const cases = [ + { title: 'plain' }, + { title: 'full', description: '

d

', due_date: '2026-11-01T10:20:30.000Z', priority: 3 }, + { title: 'labels+relation', due_date: '2026-11-01T10:20:30.000Z', labels: [L1, L2], relation: true }, +]; +for (const c of cases) { + const { labels = [], relation, ...body } = c; + const r = await api('POST', `/projects/${P}/tasks`, body, O); + must(r, 'create'); + for (const l of [...labels].reverse()) must(await api('POST', `/tasks/${r.json.id}/labels`, { label_id: l }, O), 'label'); + if (relation) must(await api('POST', `/tasks/${r.json.id}/relations`, { other_task_id: other, relation_kind: 'related' }, O), 'relation'); + const g = must(await api('GET', `/tasks/${r.json.id}`, undefined, O), 'read'); + const B = kanban.default_bucket_id; + const fb = { ...taskFields(r.json, B), labels: [...labels].sort((a, b) => a - b) }; + const rd = taskFields(g, B); + const diff = Object.keys(rd).filter((k) => JSON.stringify(rd[k]) !== JSON.stringify(fb[k])); + console.log(c.title.padEnd(16), 'digest equal', digest(fb) === digest(rd), 'differing', JSON.stringify(diff), + 'updated answer', normal(r.json.updated), 'read', normal(g.updated), 'answer raw', JSON.stringify(r.json.updated), 'read raw', JSON.stringify(g.updated)); + if (diff.length) console.log(' fallback', JSON.stringify(diff.map((k) => fb[k])), 'read', JSON.stringify(diff.map((k) => rd[k]))); + const bt = must(await api('GET', `/projects/${P}/views/${kanban.id}/buckets/tasks`, undefined, O), 'buckets'); + const where = (bt.items ?? bt).find((b) => (b.tasks ?? []).some((t) => t.id === r.json.id)); + console.log(' placed in bucket', where?.id, 'default', B); +}