docs(slice1): filbert row 38 S3 round 1 review record (changes)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
This commit is contained in:
+10
@@ -0,0 +1,10 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
# test-discord N times, alternating trees; one line per run
|
||||||
|
O=~/filbert-scratch/r38/out
|
||||||
|
for i in 1 2 3 4 5 6; do
|
||||||
|
for P in cand base; do
|
||||||
|
cd ~/filbert-scratch/r38/$P
|
||||||
|
env -u NODE_TEST_CONTEXT timeout 900 bash scripts/test-discord.sh > "$O/loop-$P-$i.txt" 2>&1; rc=$?
|
||||||
|
echo "$P run $i exit $rc load $(cut -d' ' -f1 /proc/loadavg) $(grep -E -i 'passed|failed' "$O/loop-$P-$i.txt" | tail -1)"
|
||||||
|
done
|
||||||
|
done
|
||||||
Executable
+11
@@ -0,0 +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"
|
||||||
|
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)"
|
||||||
|
done
|
||||||
+88
@@ -0,0 +1,88 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
# Row 38 mutants: one perl substitution each, restored after its run.
|
||||||
|
# Usage: mutants.sh <tree> <outdir> [id...]
|
||||||
|
set -u
|
||||||
|
T="$1"; O="$2"; shift 2; ONLY=" $* "; cd "$T"
|
||||||
|
run() {
|
||||||
|
local id="$1" file="$2" expr="$3"
|
||||||
|
if [ "$ONLY" != " " ] && [[ "$ONLY" != *" $id "* ]]; then return; fi
|
||||||
|
cp "$file" "$file.orig"
|
||||||
|
perl -0pi -e "$expr" "$file"
|
||||||
|
if cmp -s "$file" "$file.orig"; then echo "$id NOT-APPLIED $file"; mv "$file.orig" "$file"; return; fi
|
||||||
|
if [ -n "${DRY:-}" ]; then echo "$id applies"; mv "$file.orig" "$file"; return; fi
|
||||||
|
env -u NODE_TEST_CONTEXT timeout 300 node --test 'packages/tasks/tests/*.test.mjs' 'packages/bus/tests/*.test.mjs' > "$O/mut-$id.txt" 2>&1
|
||||||
|
local rc=$? f; f=$(grep -E '^ℹ fail ' "$O/mut-$id.txt" | awk '{print $3}')
|
||||||
|
if [ "${f:-?}" = 0 ] && [ $rc = 0 ]; then echo "$id SURVIVED"; else echo "$id killed (${f:-rc $rc})"; fi
|
||||||
|
mv "$file.orig" "$file"
|
||||||
|
}
|
||||||
|
V=packages/tasks/src/verbs.mjs
|
||||||
|
run V1 $V "s/if \(PM_VERBS\.has\(verb\) && mine\.definition !== 'pm'\) fail\('field-writer'\);//"
|
||||||
|
run V2 $V "s/if \(args\.expect !== undefined && args\.expect !== before\.digest\) \{/if (false) {/"
|
||||||
|
run V3 $V "s/if \(before\.task\.done\) fail\('task-done'\);//"
|
||||||
|
run V4 $V "s/if \(!ctx\.labels\.has\(id\)\) fail\('label-not-allowed'\);//"
|
||||||
|
run V5 $V "s/if \(other\.project !== ctx\.project\) fail\('task-project'\);//"
|
||||||
|
run V6 $V "s/if \(r\.project !== ctx\.project\) fail\('task-project'\);//"
|
||||||
|
run V7 $V "s/if \(after && done\(after\)\) return null;//"
|
||||||
|
run V8 $V "s/if \(o === 'uncertain' && id !== null && done\) \{/if (false) {/"
|
||||||
|
run V9 $V "s/const fields = failure \? after\.fields : work\.expect\(before\.fields\);/const fields = after.fields;/"
|
||||||
|
run V10 $V "s/if \(verb === 'task\.assign' && held\.length\) fail\('task-assigned'\);//"
|
||||||
|
run V11 $V "s/if \(verb === 'task\.reassign' && !held\.length\) fail\('task-unassigned'\);//"
|
||||||
|
run V12 $V "s/for \(const u of held\.filter\(\(u\) => u !== to\.botId\)\)/for (const u of [...bots.keys()].filter((u) => u !== to.botId))/"
|
||||||
|
run V13 $V "s/if \(!fields\.assignees\.includes\(mine\.botId\)\) fail\('not-assigned'\);//"
|
||||||
|
run V14 $V "s/!STATES\.includes\(args\.state\)/![...STATES, 'done'].includes(args.state)/"
|
||||||
|
run V15 $V "s/if \(!\/\^REQ-\[A-Z\]\+-\[1-9\]\[0-9\]\*\\$\/\.test\(plan\.requirement\)\) fail\('invalid-request'\);//"
|
||||||
|
run V16 $V "s/if \(!ctx\.broker\.taskView\(ctx\.business, 'input', plan\.request\)\) fail\('request-not-found'\);//"
|
||||||
|
run V17 $V "s/ctx\.broker\.authorize\(cap, 'task\.create', \{ decision: plan\.decision, target: null \}\);//"
|
||||||
|
run V18 $V "s/ctx\.broker\.authorize\(cap, verb, \{ decision: plan\.decision, target: args\.task_ref \}\);//"
|
||||||
|
run V19 $V "s/for \(const l of labels\.remove\.filter\(\(l\) => fields\.labels\.includes\(l\)\)\)/for (const l of labels.remove)/"
|
||||||
|
run V20 $V "s/Object\.entries\(body\)\.filter\(\(\[k, v\]\) => fields\[k\] !== v\)/Object.entries(body)/"
|
||||||
|
run V21 $V "s/events: \[\{ kind: 'task\.created'/events: failure ? [] : [{ kind: 'task.created'/"
|
||||||
|
run V22 $V "s/comment: note !== undefined,/comment: note ?? false,/"
|
||||||
|
run V23 $V "s/if \(!bucket\) fail\('task-placement'\);//"
|
||||||
|
run V24 $V "s/if \(x\.json\.project_id !== ctx\.project\) fail\('task-moved'\);//"
|
||||||
|
S=packages/tasks/src/sync.mjs
|
||||||
|
run S1 $S "s/const stale = c\?\.source === 'self' && readAt <= c\.at;/const stale = false;/"
|
||||||
|
run S2 $S "s/if \(stale \|\| c\?\.digest === d\) \{/if (stale) {/"
|
||||||
|
run S3 $S "s/export const WINDOW_MS = 60000;/export const WINDOW_MS = 0;/"
|
||||||
|
run S4 $S "s/if \(fresh && !ctx\.bots\.has\(c\.author\.id\)\) n\+\+;/if (fresh) n++;/"
|
||||||
|
run S5 $S "s/const fresh = last === undefined \? Date\.parse\(c\.created\) >= since : c\.id > last;/const fresh = last === undefined ? true : c.id > last;/"
|
||||||
|
run S6 $S "s/if \(c\.source === 'self' && readAt <= c\.at\) continue;//"
|
||||||
|
run S7 $S "s/if \(!t\.done\) continue;//"
|
||||||
|
run S8 $S "s/if \(before !== undefined && count !== before\) comments =/if (false) comments =/"
|
||||||
|
run S9 $S "s/if \(t\.id <= last \|\| t\.project_id !== ctx\.project\) shape\(\);/if (t.id <= last) shape();/"
|
||||||
|
run S10 $S "s/const use = hit && Date\.parse\(hit\.updated\) >= Date\.parse\(task\.updated\) \? hit : task;/const use = task;/"
|
||||||
|
run S11 $S "s/x\.status === 403 \? 'no-access' : 'not-found'/'not-found'/"
|
||||||
|
U=packages/tasks/src/startup.mjs
|
||||||
|
run U1 $U "s/if \(!v \|\| v\[0\] !== 2 \|\| v\[1\] < 4\) refuse\('tracker-version'\);//"
|
||||||
|
run U2 $U "s/if \(probe\.status === 404 \|\| probe\.status === 403 \|\| outcome\(probe\) === 'ok'\) refuse\('scope-too-broad'\);//"
|
||||||
|
run U3 $U "s/if \(p\.status === 404 \|\| p\.status === 403 \|\| outcome\(p\) === 'ok'\) refuse\('scope-too-broad'\);//"
|
||||||
|
run U4 $U "s/if \(r\.definition !== 'pm'\) probes\.push/if (false) probes.push/"
|
||||||
|
run U5 $U "s/if \(mine\.some\(\(s\) => s\.state === 'expired'\)\) refuse\('credential-expired'\);//"
|
||||||
|
run U6 $U "s/ \|\| k\.default_bucket_id !== ids\.todo//"
|
||||||
|
run U7 $U "s/ \|\| kanban\[0\]\.bucket_configuration_mode !== 'manual'//"
|
||||||
|
run U8 $U "s/if \(\[\.\.\.ctx\.labels\.keys\(\)\]\.some\(\(id\) => !ids\.has\(id\)\)\) refuse\('tracker-labels'\);//"
|
||||||
|
A=packages/tasks/src/adapter.mjs
|
||||||
|
run A1 $A "s/else if \(ctx\.state === 'refused' && TRANSIENT\.has\(ctx\.refused\)\)/else if (ctx.state === 'refused')/"
|
||||||
|
run A2 $A "s/if \(ctx\.pending > 1\) return Promise\.resolve\(\);//"
|
||||||
|
run A3 $A "s/if \(ctx\.told\.has\(key\)\) return;//"
|
||||||
|
run A4 $A "s/if \(ctx\.state !== 'ready'\) throw/if (false) throw/"
|
||||||
|
run A5 $A "s/pollSeconds < 10/pollSeconds < 1/"
|
||||||
|
run A6 $A "s/if \(pms\.length !== 1\) throw new BusError\('tracker-config'\);//"
|
||||||
|
K=packages/tasks/src/vikunja.mjs
|
||||||
|
run K1 $K "s/redirect: 'error',/redirect: 'follow',/"
|
||||||
|
run K2 $K "s/!\['', '\/'\]\.includes\(u\.pathname\)/false/"
|
||||||
|
run K3 $K "s/r\.json\?\.code === 4002 \? 'not-found' : 'missing'/'not-found'/"
|
||||||
|
run K4 $K "s/if \(s === 0 \|\| s >= 500\) return 'uncertain';/if (s === 0) return 'uncertain';/"
|
||||||
|
G=packages/tasks/src/digest.mjs
|
||||||
|
run G1 $G "s/: new Date\(due\)\.toISOString\(\),/: due,/"
|
||||||
|
run G2 $G "s/return out\.sort\(\(a, b\) => a - b\);/return out;/"
|
||||||
|
B=packages/bus/src/broker.mjs
|
||||||
|
run B1 $B "s/this\.#store\.transaction\(\(\) => this\.#agent\(s\)\);\n const result = await handler/const result = await handler/"
|
||||||
|
run B2 $B "s/this\.#secretCheck\(args\);\n this\.#store\.transaction/this.#store.transaction/"
|
||||||
|
run B3 $B "s/if \(cap !== null && \(s\.human \|\| s\.reader \|\| s\.business !== business\)\) fail\('agent-required'\);//"
|
||||||
|
run B4 $B "s/const kinds = cap === null \? POLL_KINDS : SELF_KINDS;/const kinds = new Set([...POLL_KINDS, ...SELF_KINDS]);/"
|
||||||
|
run B5 $B "s/if \(!TASK_VERBS\.includes\(request\.verb\)\) fail\('unknown-verb'\);//"
|
||||||
|
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\);//"
|
||||||
@@ -0,0 +1,127 @@
|
|||||||
|
// Filbert, row 38 round 1 probes. 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';
|
||||||
|
const CAND = process.env.CAND;
|
||||||
|
const { world } = await import(CAND + '/packages/tasks/tests/world.mjs');
|
||||||
|
const { FakeVikunja } = await import(CAND + '/packages/tasks/src/fake.mjs');
|
||||||
|
const ready = async (t, edit) => {
|
||||||
|
const w = await world(t, edit);
|
||||||
|
await w.adapter.start('demo');
|
||||||
|
assert.equal(w.adapter.status()[0].state, 'ready');
|
||||||
|
return w;
|
||||||
|
};
|
||||||
|
const create = (w, args = {}) =>
|
||||||
|
w.call(w.caps.pm ?? w.agent('pm'), 'task.create', { title: 'a task', request: w.instruction(), requirement: 'REQ-S-1', ...args });
|
||||||
|
const code = (p) => p.then(() => 'ok', (e) => e.code ?? String(e));
|
||||||
|
// Vikunja v2.7.0 keeps due_date to the second (vk-due-probe.txt, the pinned image). The fake keeps
|
||||||
|
// what it is sent, so this wrapper truncates a written due_date the way the real one does.
|
||||||
|
const seconds = (fake) => async (url, init = {}) => {
|
||||||
|
if (init.body) {
|
||||||
|
const b = JSON.parse(init.body);
|
||||||
|
if (typeof b.due_date === 'string' && b.due_date.includes('.')) {
|
||||||
|
b.due_date = b.due_date.replace(/\.\d+Z$/, 'Z');
|
||||||
|
init = { ...init, body: JSON.stringify(b) };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return fake.fetch(url, init);
|
||||||
|
};
|
||||||
|
|
||||||
|
test('D1 schedule with milliseconds: the poll reports the bot\'s own due date as an external change', async (t) => {
|
||||||
|
const fake = new FakeVikunja();
|
||||||
|
const w = await ready(t, { fake, fetch: seconds(fake) });
|
||||||
|
const { task_ref } = await create(w);
|
||||||
|
const pm = w.caps.pm;
|
||||||
|
const r = await w.call(pm, 'task.schedule', { task_ref, due_date: '2026-12-01T09:00:00.456Z' });
|
||||||
|
const self = w.snapshots(task_ref).at(-1);
|
||||||
|
console.log('D1 self snapshot due_date', self.source, self.fields.due_date, 'returned digest', r.digest.slice(0, 12));
|
||||||
|
await w.adapter.tick('demo');
|
||||||
|
const ext = w.events('task.changed.external');
|
||||||
|
console.log('D1 external events after one tick', JSON.stringify(ext.map((e) => e.body.changed)));
|
||||||
|
const before = w.fake.requests.filter((x) => x.method === 'PATCH').length;
|
||||||
|
await w.call(pm, 'task.schedule', { task_ref, due_date: '2026-12-01T09:00:00.456Z' });
|
||||||
|
const after = w.fake.requests.filter((x) => x.method === 'PATCH').length;
|
||||||
|
console.log('D1 same schedule again sends PATCH', after - before);
|
||||||
|
// 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');
|
||||||
|
});
|
||||||
|
|
||||||
|
test('D2 whole seconds: no external change (control)', async (t) => {
|
||||||
|
const fake = new FakeVikunja();
|
||||||
|
const w = await ready(t, { fake, fetch: seconds(fake) });
|
||||||
|
const { task_ref } = await create(w);
|
||||||
|
await w.call(w.caps.pm, 'task.schedule', { task_ref, due_date: '2026-12-01T09:00:00.000Z' });
|
||||||
|
await w.adapter.tick('demo');
|
||||||
|
console.log('D2 external events', w.events('task.changed.external').length);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('W1 write lands, final read fails: refusal, no self record, poll calls it external', 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' && /\/assignees$/.test(new URL(u).pathname)) {
|
||||||
|
armed = false;
|
||||||
|
fake.fault('GET', /^\/tasks\/1$/, 0);
|
||||||
|
}
|
||||||
|
return res;
|
||||||
|
};
|
||||||
|
const w = await ready(t, { fake, fetch: wrap });
|
||||||
|
const { task_ref } = await create(w);
|
||||||
|
armed = true;
|
||||||
|
const c = await code(w.call(w.caps.pm, 'task.assign', { task_ref, role: 'coder' }));
|
||||||
|
console.log('W1 caller sees', c);
|
||||||
|
console.log('W1 coder assigned in tracker', w.fake.task(1).assignees?.includes?.(w.bots.coder) ?? JSON.stringify(w.fake.task(1)).includes(String(w.bots.coder)));
|
||||||
|
console.log('W1 action.allowed task.assign', w.events('action.allowed').filter((e) => e.body.action === 'task.assign').length);
|
||||||
|
console.log('W1 task.assigned events', w.events('task.assigned').length, 'self snapshots', w.snapshots(task_ref).filter((s) => s.source === 'self').length);
|
||||||
|
await w.adapter.tick('demo');
|
||||||
|
const ext = w.events('task.changed.external');
|
||||||
|
console.log('W1 poll external events', JSON.stringify(ext.map((e) => e.body.changed)));
|
||||||
|
const again = await code(w.call(w.caps.pm, 'task.assign', { task_ref, role: 'coder' }));
|
||||||
|
console.log('W1 caller retries assign ->', again);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('H1 a human, H2 a reader, and a non-holder cannot call a task verb', async (t) => {
|
||||||
|
const w = await ready(t);
|
||||||
|
const { task_ref } = await create(w);
|
||||||
|
console.log('H1 human task.assign', await code(w.call(w.human, 'task.assign', { task_ref, role: 'coder' })));
|
||||||
|
const reader = w.broker.bindReader ? w.broker.bindReader({ business: 'demo' }) : null;
|
||||||
|
if (reader) console.log('H2 reader task.assign', await code(w.call(reader, 'task.assign', { task_ref, role: 'coder' })));
|
||||||
|
const stale = w.broker.bindLaunch({ business: 'demo', role: 'coder', run: 'other-run', harness: 'pi' });
|
||||||
|
console.log('H3 non-holder coder update', await code(w.call(stale, 'task.update.assigned', { task_ref, state: 'blocked' })));
|
||||||
|
});
|
||||||
|
|
||||||
|
test('R1 a relation to another project\'s task id under this project\'s ref', async (t) => {
|
||||||
|
const w = await ready(t);
|
||||||
|
const other = w.fake.project('Other', w.owner);
|
||||||
|
w.fake.share(other, w.bots.pm, 1);
|
||||||
|
const { task_ref } = await create(w);
|
||||||
|
const o = await w.ui('PUT', `/projects/${other}/tasks`, { title: 'other' });
|
||||||
|
const oid = o.json?.id ?? (await w.ui('POST', `/projects/${other}/tasks`, { title: 'other' })).json?.id;
|
||||||
|
const c = await code(w.call(w.caps.pm, 'task.schedule', { task_ref, relations: { add: [{ kind: 'related', task_ref: `vikunja:${w.project}/${oid}` }] } }));
|
||||||
|
console.log('R1 other task id', oid, 'schedule ->', c, 'relations on task 1', JSON.stringify(w.fake.task(1).related ?? w.fake.task(1).relations ?? null));
|
||||||
|
});
|
||||||
|
|
||||||
|
test('C1 a conflict event costs no authority; a role without the verb can still write task.conflict', async (t) => {
|
||||||
|
const w = await ready(t);
|
||||||
|
const { task_ref } = await create(w);
|
||||||
|
const rv = w.agent('reviewer');
|
||||||
|
const c = await code(w.call(rv, 'task.scope.change', { task_ref, title: 'x', expect: '0'.repeat(64) }));
|
||||||
|
console.log('C1 reviewer (no scope.change authority) wrong expect ->', c, 'task.conflict events', w.events('task.conflict').length);
|
||||||
|
const c2 = await code(w.call(rv, 'task.scope.change', { task_ref, title: 'x' }));
|
||||||
|
console.log('C1 reviewer right digest ->', c2);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('M1 one task with a 500 in missing() stalls the tick; T1 the tick after recovers', async (t) => {
|
||||||
|
const w = await ready(t);
|
||||||
|
const a = await create(w);
|
||||||
|
const b = await create(w);
|
||||||
|
await w.ui('DELETE', '/tasks/1');
|
||||||
|
await w.ui('POST', '/tasks/2', { title: 'edited in ui' });
|
||||||
|
w.fake.fault('GET', /^\/tasks\/1$/, 500);
|
||||||
|
console.log('M1 tick ->', await code(w.adapter.tick('demo')), 'external', w.events('task.changed.external').length, 'missing', w.events('task.missing').length);
|
||||||
|
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;
|
||||||
|
});
|
||||||
@@ -0,0 +1,66 @@
|
|||||||
|
✔ launch identity is stamped, payload identity is refused and stale holder cannot send (170.666354ms)
|
||||||
|
✔ decision classes route from policy; gated resolution is human-only, choice and target must match (394.309817ms)
|
||||||
|
✔ claim exclusion, holder release, gated revoke and rerouting to a new holder are atomic (326.492461ms)
|
||||||
|
✔ launch events require a human CLI capability; generic emit cannot forge authority events (172.813274ms)
|
||||||
|
✔ within-role decisions close atomically and invalid options or blocking omissions refuse (189.609061ms)
|
||||||
|
✔ observer capabilities read human inbox but cannot mutate or forge launch identity (176.648402ms)
|
||||||
|
✔ task action subjects and linked decision trail are complete and ordered (172.591988ms)
|
||||||
|
✔ launch binding is durable and reconnecting requires the identical trusted record (80.776489ms)
|
||||||
|
✔ business isolation includes inherited object names and cross-business message references (170.437603ms)
|
||||||
|
✔ authority never transfers between action, run, target, unresolved or replaced role holder (277.298122ms)
|
||||||
|
✔ task projection uses schema current view, skipping earlier and equal-start polls (135.497652ms)
|
||||||
|
✔ revocation permanently bars the old run from reclaiming first, including after broker restart (215.612942ms)
|
||||||
|
✔ empty message references refuse before storage; refusal-evidence failure stays a typed error (142.355196ms)
|
||||||
|
✔ both arbiters require human resolution when their cross-role route is themselves (214.598559ms)
|
||||||
|
✔ S1 adapter takes resolved limits and refs, rejects mismatched instance, never mutates input (1.799032ms)
|
||||||
|
✔ only validated broker references load; returned data and exceptions cannot expose a known token (5.141874ms)
|
||||||
|
✔ bad file modes, symlinks, repository/data paths, malformed tokens and missing dates refuse (4.648869ms)
|
||||||
|
✔ expiry refuses use and env references never become client data (1.154111ms)
|
||||||
|
✔ S1 parsed service refs work, service mismatch refuses, Gitea rotation due is a warning state (1.649405ms)
|
||||||
|
✔ opaque tokens shorter than 16 characters refuse before use (0.377095ms)
|
||||||
|
✔ human proof binds CLI entry, process start and nonce; agents and incomplete ancestry refuse (1.882758ms)
|
||||||
|
✔ process reader gets own kernel identity without exposing environment values (1.477131ms)
|
||||||
|
✔ EACCES ancestor environments skip only markers; commands and registered launches still refuse (0.764004ms)
|
||||||
|
✔ real pid 1 remains inspectable when its environment is protected (0.359202ms)
|
||||||
|
✔ within-role sends cite an open gated launch decision without spending it or naming it in grants (214.863831ms)
|
||||||
|
✔ missing and foreign-business citations refuse and roll back message and grant (294.387469ms)
|
||||||
|
✔ cross-role sends still need a matching resolved decision and consume it once (313.752636ms)
|
||||||
|
✔ broker process binds trusted launches, offers reader capabilities, refuses human mutation, closes cleanly (208.475417ms)
|
||||||
|
✔ startup token refusal returns safe code without value or partial listening broker (38.977665ms)
|
||||||
|
✔ loaded fixture token is absent from socket replies and SQLite, including refusal evidence (313.79539ms)
|
||||||
|
✔ killed broker leaves an explicit stale lock; another process cannot silently reclaim it (178.589953ms)
|
||||||
|
✔ trusted host registers later launches; socket clients never have a registration verb (203.287089ms)
|
||||||
|
✔ runtime excludes declared project roots even when host supplies no repoRoots (41.181855ms)
|
||||||
|
✔ a refused launch binding leaves the broker and existing capabilities alive; bad protocol stops it (188.420883ms)
|
||||||
|
✔ v3b prototype refusals, views and append-only mutations (1252.380782ms)
|
||||||
|
✔ gated approval authorizes once, survives store reopen, and fresh approval works (278.59556ms)
|
||||||
|
✔ another run cannot consume an approval; a failed check leaves it usable (361.323562ms)
|
||||||
|
✔ two scheduled callers have exactly one grant and one consumed refusal (193.098426ms)
|
||||||
|
✔ failed commit rolls consumption back; cross-role consumes and within-role stays reusable (340.740426ms)
|
||||||
|
✔ class drift gated to cross-role refuses before consumption (220.084537ms)
|
||||||
|
✔ class drift cross-role to gated refuses before consumption (200.125295ms)
|
||||||
|
✔ class drift gated to within-role refuses before consumption (195.894515ms)
|
||||||
|
✔ class drift cross-role to within-role refuses before consumption (198.566502ms)
|
||||||
|
✔ class drift within-role to gated refuses before consumption (202.64658ms)
|
||||||
|
✔ class drift within-role to cross-role refuses before consumption (174.286116ms)
|
||||||
|
✔ message.send consumes approval and prevents a later send or authorize (232.285473ms)
|
||||||
|
✔ role.revoke consumes approval and prevents a later revoke or authorize (241.195924ms)
|
||||||
|
✔ creates private WAL store and excludes a second writer until explicit close (135.208071ms)
|
||||||
|
✔ rollback is atomic and schema metadata is checked against trusted DDL, not just itself (184.574622ms)
|
||||||
|
✔ existing empty database and symlink runtime directory refuse, never initialize over damage (319.995065ms)
|
||||||
|
✔ crash during a transaction recovers no partial event after explicit fixture-only lock removal (192.309934ms)
|
||||||
|
✔ writer refuses mixed at/read_at forms atomically, even through trusted SQL helpers (107.289406ms)
|
||||||
|
✔ async transactions refuse before invoking their function (83.925884ms)
|
||||||
|
✔ socket capability stamps launch identity; shared views use wire, no SQL client (163.982077ms)
|
||||||
|
✔ two wire claims serialize; a lost reply never automatically retries (289.817055ms)
|
||||||
|
✔ malformed, oversized and identity-forging envelopes refuse without echoing input (159.647655ms)
|
||||||
|
✔ client preserves UTF-8 when a response divides a multibyte character (11.84129ms)
|
||||||
|
✔ committed mutation followed by dropped reply reports unknown and is never retried (136.498667ms)
|
||||||
|
ℹ tests 58
|
||||||
|
ℹ suites 0
|
||||||
|
ℹ pass 58
|
||||||
|
ℹ fail 0
|
||||||
|
ℹ cancelled 0
|
||||||
|
ℹ skipped 0
|
||||||
|
ℹ todo 0
|
||||||
|
ℹ duration_ms 2940.307585
|
||||||
@@ -0,0 +1,68 @@
|
|||||||
|
✔ config directory and file path follow MOSAIC_CONFIG (1.677217ms)
|
||||||
|
✔ the fixture business validates and comes back frozen (5.902652ms)
|
||||||
|
✔ two instances may share a definition (1.503488ms)
|
||||||
|
✔ top-level refusals (4.55646ms)
|
||||||
|
✔ arbiters and projects (7.840972ms)
|
||||||
|
✔ role instances (3.902984ms)
|
||||||
|
✔ Vikunja bots (8.563122ms)
|
||||||
|
✔ a role without Vikunja takes no tracker block (3.604285ms)
|
||||||
|
✔ credential references match the definition's services (3.929988ms)
|
||||||
|
✔ launch (9.2829ms)
|
||||||
|
✔ loadBusiness: file checks (2.677141ms)
|
||||||
|
✔ loadBusiness: not a regular file (48.983592ms)
|
||||||
|
✔ loading writes nothing (1.140996ms)
|
||||||
|
✔ names that are Object.prototype properties don't count as declared (3.08183ms)
|
||||||
|
✔ the shipped example refuses as written and validates once filled in (2.052159ms)
|
||||||
|
✔ usage errors exit 4 (331.673209ms)
|
||||||
|
✔ validate: a good business exits 0 and prints instance digests (82.726199ms)
|
||||||
|
✔ validate: project files (342.845593ms)
|
||||||
|
✔ validate: missing files and a broken system config (244.824065ms)
|
||||||
|
✔ validate: credential reference problems exit 2 and name each one (70.549043ms)
|
||||||
|
✔ validate: a token file inside the repository is refused (66.896502ms)
|
||||||
|
✔ validate: role definitions come from MOSAIC_ROLES_DIR (191.705086ms)
|
||||||
|
✔ resolve: prints one instance's record (192.831691ms)
|
||||||
|
✔ resolve: refusals (379.214145ms)
|
||||||
|
✔ parse: exactly one of file or env, plus the service's date (3.439387ms)
|
||||||
|
✔ check: a good file has no problems (0.966102ms)
|
||||||
|
✔ check never opens the file: a write-only token passes (0.435671ms)
|
||||||
|
✔ check: file problems (1.074103ms)
|
||||||
|
✔ check: token files can't live in the repository or dataRoot, even through a linked directory (1.371196ms)
|
||||||
|
✔ check: dates and environment references (0.591328ms)
|
||||||
|
✔ path and load (3.19644ms)
|
||||||
|
✔ refusals (1.809179ms)
|
||||||
|
✔ systemVars flattens the validated config (2.859102ms)
|
||||||
|
✔ precedence: system, business, project, project role, agent (7.383328ms)
|
||||||
|
✔ limits narrow the definition and never widen it (2.963535ms)
|
||||||
|
✔ role.launch stays within-role only for the instance the launch block names (7.248448ms)
|
||||||
|
✔ limits.authority without role.launch leaves the launcher with no launch block (2.048194ms)
|
||||||
|
✔ limits.authority narrows cross-role actions too (1.364395ms)
|
||||||
|
✔ classify (1.994315ms)
|
||||||
|
✔ the record carries what the broker and launcher need (1.638653ms)
|
||||||
|
✔ digest: key order doesn't matter, any value change does (7.595632ms)
|
||||||
|
✔ refusals (2.550827ms)
|
||||||
|
✔ the four shipped version 2 roles load (5.160114ms)
|
||||||
|
✔ shipped role scopes match addendum B section 2 and the SR runbook (1.705104ms)
|
||||||
|
✔ shipped authority follows the note's table (0.769018ms)
|
||||||
|
✔ version 1 files keep loading with no authority (1.313158ms)
|
||||||
|
✔ the conductor policy isn't a role (0.267415ms)
|
||||||
|
✔ a missing role file is exit 4, a symbolic link too (0.471421ms)
|
||||||
|
✔ version 2 refusals (1.901447ms)
|
||||||
|
✔ authority: closed vocabulary, no gated-only action, no overlap (2.640565ms)
|
||||||
|
✔ credentials: Gitea scopes (1.328386ms)
|
||||||
|
✔ credentials: Vikunja scopes are a group-to-verbs map from the grantable list (1.716566ms)
|
||||||
|
✔ credentials: services (0.850438ms)
|
||||||
|
✔ contract: a non-empty regular Markdown file beside the role file (1.048375ms)
|
||||||
|
✔ every key names known layers and a merge rule (1.006546ms)
|
||||||
|
✔ unknown keys and wrong layers refuse (0.841714ms)
|
||||||
|
✔ types (2.251209ms)
|
||||||
|
✔ merge: defaults, then the most specific layer wins (0.323416ms)
|
||||||
|
✔ merge: limits only narrow, and provenance lists each source (0.382534ms)
|
||||||
|
✔ merge doesn't change its inputs (0.147981ms)
|
||||||
|
ℹ tests 60
|
||||||
|
ℹ suites 0
|
||||||
|
ℹ pass 60
|
||||||
|
ℹ fail 0
|
||||||
|
ℹ cancelled 0
|
||||||
|
ℹ skipped 0
|
||||||
|
ℹ todo 0
|
||||||
|
ℹ duration_ms 1983.453222
|
||||||
@@ -0,0 +1,181 @@
|
|||||||
|
✔ approvals: a request is validated before anything is posted; the rendering shows names and never ids (3.418894ms)
|
||||||
|
✔ approvals: the ledger is appended and folded into open requests with bind and approval states (1.588731ms)
|
||||||
|
✔ approvals: a reply approves only when it points at a request, says exactly approve, and comes from a listed approver once (0.618165ms)
|
||||||
|
✔ approvals: a button approves only on its own request message with the matching custom id (0.623462ms)
|
||||||
|
✔ approvals flow: a turn that opened a request posts the message with the button, records it, binds it, and both approvers approve (17.335972ms)
|
||||||
|
✔ approvals flow: a non-approver, a repeat, a wrong custom id and a service refusal each get their fixed line and a drop entry (6.246381ms)
|
||||||
|
✔ approvals flow: an invalid request from the model, a refused post, and no api client are recorded and post nothing (7.052023ms)
|
||||||
|
✔ approvals flow: start retries a bind and an approval left as unknown, under their original keys (1.918919ms)
|
||||||
|
✔ authorize: open channel, listed user (2.031648ms)
|
||||||
|
✔ authorize: wrong guild (0.226127ms)
|
||||||
|
✔ authorize: no guild (DM) (0.319121ms)
|
||||||
|
✔ authorize: unlisted channel (0.19857ms)
|
||||||
|
✔ authorize: unknown channel, no info (0.284117ms)
|
||||||
|
✔ authorize: thread of listed parent (0.237745ms)
|
||||||
|
✔ authorize: thread of unlisted parent (0.180907ms)
|
||||||
|
✔ authorize: text channel that is not a thread and not listed (0.160063ms)
|
||||||
|
✔ authorize: unlisted user (0.622063ms)
|
||||||
|
✔ authorize: no author (0.353953ms)
|
||||||
|
✔ authorize: bot author (listed id, bot flag) (0.162479ms)
|
||||||
|
✔ authorize: system author (0.128842ms)
|
||||||
|
✔ authorize: the bot itself (0.120558ms)
|
||||||
|
✔ authorize: webhook (0.108836ms)
|
||||||
|
✔ authorize: mention channel without mention (0.142014ms)
|
||||||
|
✔ authorize: mention channel with bot mention (0.150943ms)
|
||||||
|
✔ authorize: mention channel with @everyone only (0.121938ms)
|
||||||
|
✔ authorize: mention channel mentioning someone else (0.133257ms)
|
||||||
|
✔ authorize: mention channel, content says @bot but mentions empty (0.11526ms)
|
||||||
|
✔ authorize: private thread under mention channel, mentioned (0.102522ms)
|
||||||
|
✔ authorize: private thread under mention channel, not mentioned (0.080882ms)
|
||||||
|
✔ authorize: thread in another guild per channel info (0.083277ms)
|
||||||
|
✔ authorize: not an object (0.073412ms)
|
||||||
|
✔ authorize: no id (0.076617ms)
|
||||||
|
✔ authorize: oversize content is accepted and flagged (0.091642ms)
|
||||||
|
✔ authorize: exactly the limit is not oversize (0.084257ms)
|
||||||
|
✔ authorize: a user's channel allowlist drops them outside it, threads count as the parent, others are unaffected (0.50229ms)
|
||||||
|
✔ authorize: order puts wrong guild before user, and user before channel (no channel lookup for strangers) (0.1667ms)
|
||||||
|
✔ binding: a complete binding validates and is frozen (2.924003ms)
|
||||||
|
✔ binding: unknown key, missing field, wrong type refuse with exit 2 (1.649073ms)
|
||||||
|
✔ binding: empty allowlists refuse (0.479675ms)
|
||||||
|
✔ binding: a user's channel allowlist must be non-empty, listed and unique; absent means every listed channel (1.391585ms)
|
||||||
|
✔ reloadDiff: reloadable keys are summarised by id; every fixed key refuses with exit 2 (1.932551ms)
|
||||||
|
✔ binding: file must be 0600, regular, not a symlink (1.593592ms)
|
||||||
|
✔ binding: token file mode, symlink, emptiness and shape are checked; token never appears in errors (1.528543ms)
|
||||||
|
✔ cli: check refuses a non-0600 token file with exit 2 before any network use (100.295916ms)
|
||||||
|
✔ context files: absolute paths, traversal, symlinks and out-of-repo targets refuse; in-repo files resolve (1.788682ms)
|
||||||
|
✔ cli: check refuses a missing context file and a missing binding with exit 2; usage is exit 4 (346.454643ms)
|
||||||
|
✔ cli: reload validates the file first (exit 2), then needs a live owner (exit 1); usage is exit 4 (314.837468ms)
|
||||||
|
✔ cli: run refuses when STOP is present, before any network use (188.683147ms)
|
||||||
|
✔ binding: tools is optional, validated strictly, a fixed key for reload, and its roots are resolved against the data root (2.029331ms)
|
||||||
|
✔ binding: a git key is validated at load and reaches the extension whole, and only on a writable root (1.398272ms)
|
||||||
|
✔ delivery: an accepted message is in the inbox before the turn, the reply is chunked with one nonce per chunk, and the turn record is write-once (19.519938ms)
|
||||||
|
✔ delivery: refused and unknown outcomes are journaled; a later chunk is not sent after a failure (17.158362ms)
|
||||||
|
✔ delivery: restart with an unknown entry re-sends the same nonce once and reconciles before accepting traffic (1.637157ms)
|
||||||
|
✔ delivery: an unknown entry older than the dedupe window is marked refused, not re-sent; a still-unknown one refuses start (1.148981ms)
|
||||||
|
✔ delivery: repeated unknown reconciliations never refresh the dedupe window; the original intent time decides (1.700674ms)
|
||||||
|
✔ turn: a failed engine turn posts the fixed line, never model output, and writes a failed record (2.641398ms)
|
||||||
|
✔ turn: a second message during a turn is held by the engine, both get their own reply and record (33.672289ms)
|
||||||
|
✔ turn: a thread under a listed channel is answered in the thread; an unknown thread is looked up once (3.954467ms)
|
||||||
|
✔ drop: an unlisted user gets silence and one drop line; no inbox entry, no REST call, no engine call (1.081753ms)
|
||||||
|
✔ drop: an oversize message is accepted into the inbox, answered with the fixed line and journaled as a drop (1.06264ms)
|
||||||
|
✔ restart: an inbox with three ids and a replay of the same three produces zero turns (42.327019ms)
|
||||||
|
✔ stop: STOP present refuses start; STOP written while running refuses new turns and the current one finishes (32.205697ms)
|
||||||
|
✔ ceiling: the ceiling plus one is refused and journaled; one fixed line per UTC day; a new day accepts again (4.929637ms)
|
||||||
|
✔ ceiling: a burst arriving while turns are still running cannot queue past the ceiling (4.317779ms)
|
||||||
|
✔ ceiling: a turn interrupted by a crash still counts after restart; admissions are durable (2.435616ms)
|
||||||
|
✔ ceiling: the daily notice survives a same-day restart; one delivery attempt in total, even when the first attempt crashed mid-flight (4.321712ms)
|
||||||
|
✔ duplicate: the same event delivered twice while the thread lookup is held yields one prompt, one admission and one reply (2.389081ms)
|
||||||
|
✔ journal: no token-shaped string and no model output on the drop path reaches disk (0.500832ms)
|
||||||
|
✔ 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.200807ms)
|
||||||
|
✔ receipt: Discord refusing the reaction leaves the turn intact and records ok false (2.081265ms)
|
||||||
|
✔ reload: a new user is silent before and answered after; a removed channel goes silent; a lower ceiling applies at once (4.068548ms)
|
||||||
|
✔ reload: a fixed key refuses with exit 2 and the old binding stays in force (1.916453ms)
|
||||||
|
✔ tools: with a tools binding the turn record lists every read and its outcome; without one the field is null (2.639723ms)
|
||||||
|
✔ context: the Discord block names the server, channels and modes, and states the rules from Q15 and Q16 (2.595703ms)
|
||||||
|
✔ context: with tools the block names the roots, keeps file content as data, and says to state refusals plainly (0.849243ms)
|
||||||
|
✔ context: a writable root adds the write rules and says a write is real only once Jason commits (1.961036ms)
|
||||||
|
✔ context: the envelope is one bracketed line then the text; names cannot break the line (1.208237ms)
|
||||||
|
✔ context: a git root swaps the terminal-commit line for the git verbs, and a vault root adds the id protocol (1.664833ms)
|
||||||
|
✔ context: assembleContext concatenates files in launcher format and appends the block; sha256 is stable (1.561992ms)
|
||||||
|
✔ context: splitReply keeps paragraphs together under the limit and splits long ones at lines, spaces, then hard (0.93942ms)
|
||||||
|
✔ engine: buildPiArgs carries the fixed flags, engine settings, session dir and prompt file (2.452078ms)
|
||||||
|
✔ engine: with tools, buildPiArgs turns pi's own tools off, loads the extension explicitly and allowlists exactly our three (0.466431ms)
|
||||||
|
✔ engine: a run with tool turns settles once, on the answer, with every tool call in the result (65.336443ms)
|
||||||
|
✔ engine: a run that ends on a tool-only turn fails the prompt as empty; a retried run settles on the real end (43.333216ms)
|
||||||
|
✔ engine: one prompt, one turn, text and usage come back (32.809925ms)
|
||||||
|
✔ engine: a prompt while streaming is held until pi settles, then sent as its own run, and answered in order (345.303315ms)
|
||||||
|
✔ engine: a held prompt that times out before pi settles fails on its own and is never sent (258.728293ms)
|
||||||
|
✔ engine: timeout sends abort and fails only that turn; the process stays (107.82729ms)
|
||||||
|
✔ engine: tool events from a run that outlived its timeout never land in the next prompt's record (233.212359ms)
|
||||||
|
✔ engine: a prompt after a turn that timed out before its agent_start waits for pi to settle instead of being refused (236.625806ms)
|
||||||
|
✔ 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.990261ms)
|
||||||
|
✔ engine: a timed-out turn pi starts only after the grace never answers a later prompt (615.020738ms)
|
||||||
|
✔ engine: late events of a run past its grace, before pi exits, answer nothing and nothing more is sent (early prompt response) (1.333777ms)
|
||||||
|
✔ engine: late events of a run past its grace, before pi exits, answer nothing and nothing more is sent (late prompt response) (0.562625ms)
|
||||||
|
✔ engine: a timed-out run pi did start outlives the grace; the next prompt goes out when it ends (434.738195ms)
|
||||||
|
✔ engine: a malformed JSONL line fails the turn, not the process (35.971937ms)
|
||||||
|
✔ engine: a turn that ends in error rejects with the error code; process exit fails pending turns (45.679922ms)
|
||||||
|
✔ gateway: hello -> identify with intents, ready, heartbeat with jitter, ack (2.744922ms)
|
||||||
|
✔ gateway: missed ack closes the socket and resumes with the last sequence (1.979739ms)
|
||||||
|
✔ gateway: op 7 reconnect resumes; op 9 non-resumable re-identifies (0.653605ms)
|
||||||
|
✔ gateway: op 9 resumable resumes (0.400788ms)
|
||||||
|
✔ gateway: close 4014 is fatal, reports the missing intent, never reconnects (1.117461ms)
|
||||||
|
✔ gateway: 4004 and 4013 are fatal too; 1006 reconnects with identify when no session (0.603109ms)
|
||||||
|
✔ gateway: close() is final and unparseable frames are ignored (0.567106ms)
|
||||||
|
✔ git: config validation is strict, needs write: true, a work tree and a private token file (59.384948ms)
|
||||||
|
✔ git: the child environment drops every host git config, names one helper, and carries the token path only for origin (48.242564ms)
|
||||||
|
✔ git: status reports the branch, ahead/behind and changed paths, and refuses off the named branch or mid-merge (75.265611ms)
|
||||||
|
✔ git: parseStatus reads porcelain v2 including renames and conflicts (0.313089ms)
|
||||||
|
✔ git: a commit stages exactly the named files, carries the seat author and the requester trailer, and pushes at once (85.272333ms)
|
||||||
|
✔ git: commit refusals: message, paths, requester, nothing to commit, and an index that already holds other work (106.664655ms)
|
||||||
|
✔ git: a commit whose push fails is still a commit, says so, and the next commit's push carries both (D6) (149.671844ms)
|
||||||
|
✔ git: pull is fast-forward only; a diverged origin or dirty local files refuse with nothing merged (311.425907ms)
|
||||||
|
✔ git: push pushes the named branch only and reports up to date (89.938216ms)
|
||||||
|
✔ git: no token value or token path ever reaches a git argument list; outputs are masked and capped (309.840381ms)
|
||||||
|
✔ git: the credential helper answers get over https from a private file and nothing else (195.178171ms)
|
||||||
|
✔ git: the vault protocol validates before a commit, honours another owner's lock, reserves ids, and locks around writes (773.401801ms)
|
||||||
|
✔ lock: the claim is exclusive; a second start against a live owner refuses (5.474497ms)
|
||||||
|
✔ lock: a stale lock (dead owner, reused pid, or record without start) refuses run and is never signaled; only unlock clears it (5.906454ms)
|
||||||
|
✔ lock: an incomplete claim (directory without owner record) is busy and refuses run; unlock clears it (1.112494ms)
|
||||||
|
✔ lock: an owner record that exists but cannot be read is invalid: never signaled, never removed, never claimed over (2.928528ms)
|
||||||
|
✔ lock: legacy upgrade; a live connector holding a {pid, start} record is unknown, unlock refuses and nothing changes; after it exits, unlock clears it (48.806464ms)
|
||||||
|
✔ 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 (348.074291ms)
|
||||||
|
✔ lock: identity syntax; only canonical unsigned decimal start ticks and lowercase boot uuids are identities (0.444254ms)
|
||||||
|
✔ lock: a process whose start marker or boot id cannot be read refuses to claim (0.510921ms)
|
||||||
|
✔ lock: a live pid whose identity cannot be read right now is unknown: never signaled, never removed, never claimed over (0.99163ms)
|
||||||
|
✔ lock: four processes racing for the same binding; exactly one claims it and the others refuse (49.540814ms)
|
||||||
|
✔ lock: stale handoff; concurrent starts over a stale lock all refuse, nothing reclaims, one unlock then exactly one live owner (202.813118ms)
|
||||||
|
✔ lock: four-party schedule; claims landing inside an unlock's gap never survive, one unlock leaves no owner and no residue (94.536429ms)
|
||||||
|
✔ notices: a kind is recorded per UTC day and found again (0.825686ms)
|
||||||
|
✔ recover: nothing to do is clean; a lock whose owner is gone or that has no record is cleared and STOP ends up absent (45.811652ms)
|
||||||
|
✔ recover: an operator STOP refuses with exit 3 and is never removed, whatever the lock says (40.545538ms)
|
||||||
|
✔ recover: a brake written during the unlock wins; STOP stays with both lines and the start is refused (33.256936ms)
|
||||||
|
✔ recover: a held binding refuses with exit 3 and writes no STOP: live owner, alive pid without verifiable identity, unreadable record (70.902362ms)
|
||||||
|
✔ cli: recover exits 0 when ready, 3 behind a brake or a held binding, and run's own STOP refusal is 3 (899.293434ms)
|
||||||
|
✔ rest: createMessage sends nonce, enforce_nonce, empty allowed_mentions and a soft reply reference (2.614602ms)
|
||||||
|
✔ rest: 429 waits retry_after and retries; 4xx is refused; 5xx and socket errors are unknown (3.505177ms)
|
||||||
|
✔ rest: content and nonce limits are enforced locally; typing never throws (0.713597ms)
|
||||||
|
✔ rest: react PUTs the encoded emoji on the inbound message for @me; 2xx is true, anything else is false and never throws (0.892346ms)
|
||||||
|
✔ setspark config: a bare https or loopback origin, a private key file, a principal (4.89809ms)
|
||||||
|
✔ setspark config: reaches the tools config and the binding as a fixed key (2.453786ms)
|
||||||
|
✔ setspark config: the binding's key survives resolveToolRoots and the engine's JSON hand-off to the extension (1.532545ms)
|
||||||
|
✔ setspark config: approvers come from the binding's users, never from the binding's setspark key (2.010859ms)
|
||||||
|
✔ setspark verbs: required_approvers go out as discord ids from names and come back as names (23.831945ms)
|
||||||
|
✔ setspark verbs: no Discord user id reaches tool text, whatever shape the service returns it in (10.622549ms)
|
||||||
|
✔ setspark contract: a decision made with names opens a request the connector accepts; names stored by an old record still refuse (7.461498ms)
|
||||||
|
✔ setspark keys: read per call, one printable token per file, rotation without a restart (4.054818ms)
|
||||||
|
✔ setspark idempotency keys: principal, turn id, call index; connector keys name a step (0.684339ms)
|
||||||
|
✔ setspark http core: json in and out, bearer header, idempotency header, fixed user agent, no key anywhere else (3.091037ms)
|
||||||
|
✔ setspark http core: error bodies become fixed refusals with code and the 409 fields; server text is data, cut (1011.066228ms)
|
||||||
|
✔ setspark verbs: a setspark key enables the eight verbs and no counters (0.448277ms)
|
||||||
|
✔ setspark verbs: writes carry the turn's key and the asserted requester, reads carry no key, and the api key never appears in text or details (6.46754ms)
|
||||||
|
✔ setspark verbs: no turn refuses every write before any request; bad arguments refuse before any request; reads still work (1.260637ms)
|
||||||
|
✔ setspark verbs: renderRecord caps long output and hides the accepted snapshot (0.200483ms)
|
||||||
|
✔ setspark api: bind, add_approval (button and reply) and get use integer request ids and the connector's keys (2.627885ms)
|
||||||
|
✔ tools: config refuses a missing, symlinked, dotted, non-directory or duplicate root and bad limits (4.113577ms)
|
||||||
|
✔ tools: every escape is refused with a fixed reason and nothing outside the root is read (3.767466ms)
|
||||||
|
✔ tools: happy paths list, read a window, and search case-insensitively; dotfiles and symlinks never appear (4.681541ms)
|
||||||
|
✔ tools: the tool set renders text for the model, records details for the journal, and enforces the per-run budget (3.58591ms)
|
||||||
|
✔ tools: listing and search caps hold (8.819304ms)
|
||||||
|
✔ tools: credential shapes are caught; ordinary prose and ids are not (0.682381ms)
|
||||||
|
✔ tools: the read uses the checked file itself; a symlink, a swapped file, a FIFO, a grown file or a hard link at read time is refused (4.867644ms)
|
||||||
|
✔ tools: an unreadable file under the root is skipped by search and refused by read (1.84471ms)
|
||||||
|
✔ tools: config accepts write: true only as a boolean, and enables the write tools only then (1.31416ms)
|
||||||
|
✔ tools: every write outside the fence is refused before any byte lands, and no temp file remains (6.058828ms)
|
||||||
|
✔ tools: write_file leaves the exact bytes, edit_file replaces one exact match, and the set renders the change as uncommitted (3.681604ms)
|
||||||
|
✔ tools: a target that changed between the check and the rename is refused and the temp file is removed (1.914307ms)
|
||||||
|
✔ web: config takes an https or loopback-http SearXNG base url and a bounded fetch cap (3.653701ms)
|
||||||
|
✔ web: address rules refuse every private, loopback, link-local, mapped and multicast form (2.416595ms)
|
||||||
|
✔ web: web_fetch refuses bad urls, private hosts, rebinding names, non-https redirects, too many hops, error status, non-text bodies, and times out (1024.36458ms)
|
||||||
|
✔ 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.18434ms)
|
||||||
|
✔ web: html to text drops scripts, styles and comments, decodes entities and keeps block breaks (0.302912ms)
|
||||||
|
✔ 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.61827ms)
|
||||||
|
✔ 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.911409ms)
|
||||||
|
ℹ tests 173
|
||||||
|
ℹ suites 0
|
||||||
|
ℹ pass 173
|
||||||
|
ℹ fail 0
|
||||||
|
ℹ cancelled 0
|
||||||
|
ℹ skipped 0
|
||||||
|
ℹ todo 0
|
||||||
|
ℹ duration_ms 2797.884101
|
||||||
@@ -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
|
||||||
@@ -0,0 +1,55 @@
|
|||||||
|
Note: switching to '81339889dc04dbc1a581285ce5eb17182f82fa35'.
|
||||||
|
|
||||||
|
You are in 'detached HEAD' state. You can look around, make experimental
|
||||||
|
changes and commit them, and you can discard any commits you make in this
|
||||||
|
state without impacting any branches by switching back to a branch.
|
||||||
|
|
||||||
|
If you want to create a new branch to retain commits you create, you may
|
||||||
|
do so (now or later) by using -c with the switch command. Example:
|
||||||
|
|
||||||
|
git switch -c <new-branch-name>
|
||||||
|
|
||||||
|
Or undo this operation with:
|
||||||
|
|
||||||
|
git switch -
|
||||||
|
|
||||||
|
Turn off this advice by setting config variable advice.detachedHead to false
|
||||||
|
|
||||||
|
Not currently on any branch.
|
||||||
|
nothing to commit, working tree clean
|
||||||
|
Note: switching to '81339889dc04dbc1a581285ce5eb17182f82fa35'.
|
||||||
|
|
||||||
|
You are in 'detached HEAD' state. You can look around, make experimental
|
||||||
|
changes and commit them, and you can discard any commits you make in this
|
||||||
|
state without impacting any branches by switching back to a branch.
|
||||||
|
|
||||||
|
If you want to create a new branch to retain commits you create, you may
|
||||||
|
do so (now or later) by using -c with the switch command. Example:
|
||||||
|
|
||||||
|
git switch -c <new-branch-name>
|
||||||
|
|
||||||
|
Or undo this operation with:
|
||||||
|
|
||||||
|
git switch -
|
||||||
|
|
||||||
|
Turn off this advice by setting config variable advice.detachedHead to false
|
||||||
|
|
||||||
|
OK dry-run: allowed change, exit 0, nothing committed (exit 0)
|
||||||
|
OK dry-run committed nothing
|
||||||
|
OK apply: allowed change exits 0 (exit 0)
|
||||||
|
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
|
||||||
@@ -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
|
||||||
@@ -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
|
||||||
@@ -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: 3084193 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
|
||||||
@@ -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
|
||||||
@@ -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/r38/base) is not the queue's canonical root (/mnt/storage/src/mosaic-stack)
|
||||||
|
|
||||||
|
queue suite: 27 passed, 0 failed
|
||||||
@@ -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
|
||||||
@@ -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
|
||||||
@@ -0,0 +1,75 @@
|
|||||||
|
✔ launch identity is stamped, payload identity is refused and stale holder cannot send (471.547229ms)
|
||||||
|
✔ decision classes route from policy; gated resolution is human-only, choice and target must match (733.045778ms)
|
||||||
|
✔ claim exclusion, holder release, gated revoke and rerouting to a new holder are atomic (817.63762ms)
|
||||||
|
✔ launch events require a human CLI capability; generic emit cannot forge authority events (544.096203ms)
|
||||||
|
✔ within-role decisions close atomically and invalid options or blocking omissions refuse (275.787698ms)
|
||||||
|
✔ observer capabilities read human inbox but cannot mutate or forge launch identity (414.555953ms)
|
||||||
|
✔ task action subjects and linked decision trail are complete and ordered (259.738635ms)
|
||||||
|
✔ launch binding is durable and reconnecting requires the identical trusted record (138.155439ms)
|
||||||
|
✔ business isolation includes inherited object names and cross-business message references (377.182062ms)
|
||||||
|
✔ authority never transfers between action, run, target, unresolved or replaced role holder (466.743753ms)
|
||||||
|
✔ task projection uses schema current view, skipping earlier and equal-start polls (164.623985ms)
|
||||||
|
✔ revocation permanently bars the old run from reclaiming first, including after broker restart (270.897265ms)
|
||||||
|
✔ empty message references refuse before storage; refusal-evidence failure stays a typed error (151.43438ms)
|
||||||
|
✔ both arbiters require human resolution when their cross-role route is themselves (352.18389ms)
|
||||||
|
✔ S1 adapter takes resolved limits and refs, rejects mismatched instance, never mutates input (2.976649ms)
|
||||||
|
✔ only validated broker references load; returned data and exceptions cannot expose a known token (8.241292ms)
|
||||||
|
✔ bad file modes, symlinks, repository/data paths, malformed tokens and missing dates refuse (23.598088ms)
|
||||||
|
✔ expiry refuses use and env references never become client data (0.852207ms)
|
||||||
|
✔ S1 parsed service refs work, service mismatch refuses, Gitea rotation due is a warning state (14.258057ms)
|
||||||
|
✔ opaque tokens shorter than 16 characters refuse before use (0.563348ms)
|
||||||
|
✔ human proof binds CLI entry, process start and nonce; agents and incomplete ancestry refuse (3.322083ms)
|
||||||
|
✔ process reader gets own kernel identity without exposing environment values (0.779249ms)
|
||||||
|
✔ EACCES ancestor environments skip only markers; commands and registered launches still refuse (0.99166ms)
|
||||||
|
✔ real pid 1 remains inspectable when its environment is protected (0.431097ms)
|
||||||
|
✔ within-role sends cite an open gated launch decision without spending it or naming it in grants (625.261781ms)
|
||||||
|
✔ missing and foreign-business citations refuse and roll back message and grant (526.665127ms)
|
||||||
|
✔ cross-role sends still need a matching resolved decision and consume it once (785.94268ms)
|
||||||
|
✔ broker process binds trusted launches, offers reader capabilities, refuses human mutation, closes cleanly (566.900742ms)
|
||||||
|
✔ startup token refusal returns safe code without value or partial listening broker (37.977207ms)
|
||||||
|
✔ loaded fixture token is absent from socket replies and SQLite, including refusal evidence (512.083957ms)
|
||||||
|
✔ killed broker leaves an explicit stale lock; another process cannot silently reclaim it (391.009659ms)
|
||||||
|
✔ trusted host registers later launches; socket clients never have a registration verb (489.439761ms)
|
||||||
|
✔ runtime excludes declared project roots even when host supplies no repoRoots (50.332523ms)
|
||||||
|
✔ a refused launch binding leaves the broker and existing capabilities alive; bad protocol stops it (458.65248ms)
|
||||||
|
✔ v3b prototype refusals, views and append-only mutations (3027.693359ms)
|
||||||
|
✔ gated approval authorizes once, survives store reopen, and fresh approval works (801.314456ms)
|
||||||
|
✔ another run cannot consume an approval; a failed check leaves it usable (552.446362ms)
|
||||||
|
✔ two scheduled callers have exactly one grant and one consumed refusal (577.506855ms)
|
||||||
|
✔ failed commit rolls consumption back; cross-role consumes and within-role stays reusable (778.294325ms)
|
||||||
|
✔ class drift gated to cross-role refuses before consumption (492.388873ms)
|
||||||
|
✔ class drift cross-role to gated refuses before consumption (341.554402ms)
|
||||||
|
✔ class drift gated to within-role refuses before consumption (321.611629ms)
|
||||||
|
✔ class drift cross-role to within-role refuses before consumption (461.857957ms)
|
||||||
|
✔ class drift within-role to gated refuses before consumption (236.339347ms)
|
||||||
|
✔ class drift within-role to cross-role refuses before consumption (221.814828ms)
|
||||||
|
✔ message.send consumes approval and prevents a later send or authorize (266.636791ms)
|
||||||
|
✔ role.revoke consumes approval and prevents a later revoke or authorize (363.866982ms)
|
||||||
|
✔ creates private WAL store and excludes a second writer until explicit close (370.56028ms)
|
||||||
|
✔ rollback is atomic and schema metadata is checked against trusted DDL, not just itself (549.337749ms)
|
||||||
|
✔ existing empty database and symlink runtime directory refuse, never initialize over damage (479.158951ms)
|
||||||
|
✔ crash during a transaction recovers no partial event after explicit fixture-only lock removal (525.03365ms)
|
||||||
|
✔ writer refuses mixed at/read_at forms atomically, even through trusted SQL helpers (224.932479ms)
|
||||||
|
✔ async transactions refuse before invoking their function (293.732314ms)
|
||||||
|
✔ recordTask keeps sync reads and a role write apart (554.015402ms)
|
||||||
|
✔ read_at must be one canonical UTC format, so the projection compares strings safely (337.370583ms)
|
||||||
|
✔ a bad entry refuses the whole record (241.177012ms)
|
||||||
|
✔ taskView reads the projection for one business (362.460922ms)
|
||||||
|
✔ requestTask hands only a holder and a task verb to the handler, and records refusals (663.00606ms)
|
||||||
|
✔ the server sends task verbs to the adapter with its own timeout; other verbs stay synchronous (643.740836ms)
|
||||||
|
✔ without an adapter the server refuses every task verb (470.629571ms)
|
||||||
|
✔ the runtime refuses an invalid adapter and closes a valid one (303.935045ms)
|
||||||
|
✔ the process loads the S3 adapter from plain-data trackers (431.135409ms)
|
||||||
|
✔ socket capability stamps launch identity; shared views use wire, no SQL client (467.416217ms)
|
||||||
|
✔ two wire claims serialize; a lost reply never automatically retries (600.561111ms)
|
||||||
|
✔ malformed, oversized and identity-forging envelopes refuse without echoing input (435.089767ms)
|
||||||
|
✔ client preserves UTF-8 when a response divides a multibyte character (26.983694ms)
|
||||||
|
✔ committed mutation followed by dropped reply reports unknown and is never retried (459.747712ms)
|
||||||
|
ℹ tests 67
|
||||||
|
ℹ suites 0
|
||||||
|
ℹ pass 67
|
||||||
|
ℹ fail 0
|
||||||
|
ℹ cancelled 0
|
||||||
|
ℹ skipped 0
|
||||||
|
ℹ todo 0
|
||||||
|
ℹ duration_ms 5522.78836
|
||||||
@@ -0,0 +1,68 @@
|
|||||||
|
✔ config directory and file path follow MOSAIC_CONFIG (2.436728ms)
|
||||||
|
✔ the fixture business validates and comes back frozen (6.201817ms)
|
||||||
|
✔ two instances may share a definition (1.857395ms)
|
||||||
|
✔ top-level refusals (5.708503ms)
|
||||||
|
✔ arbiters and projects (7.767594ms)
|
||||||
|
✔ role instances (4.920971ms)
|
||||||
|
✔ Vikunja bots (10.749022ms)
|
||||||
|
✔ a role without Vikunja takes no tracker block (3.840473ms)
|
||||||
|
✔ credential references match the definition's services (4.737713ms)
|
||||||
|
✔ launch (36.354181ms)
|
||||||
|
✔ loadBusiness: file checks (2.063508ms)
|
||||||
|
✔ loadBusiness: not a regular file (48.13006ms)
|
||||||
|
✔ loading writes nothing (2.149806ms)
|
||||||
|
✔ names that are Object.prototype properties don't count as declared (3.460969ms)
|
||||||
|
✔ the shipped example refuses as written and validates once filled in (0.789241ms)
|
||||||
|
✔ usage errors exit 4 (356.250676ms)
|
||||||
|
✔ validate: a good business exits 0 and prints instance digests (90.351744ms)
|
||||||
|
✔ validate: project files (417.493057ms)
|
||||||
|
✔ validate: missing files and a broken system config (313.020378ms)
|
||||||
|
✔ validate: credential reference problems exit 2 and name each one (81.429118ms)
|
||||||
|
✔ validate: a token file inside the repository is refused (72.689626ms)
|
||||||
|
✔ validate: role definitions come from MOSAIC_ROLES_DIR (238.489911ms)
|
||||||
|
✔ resolve: prints one instance's record (264.584218ms)
|
||||||
|
✔ resolve: refusals (439.125302ms)
|
||||||
|
✔ parse: exactly one of file or env, plus the service's date (4.038881ms)
|
||||||
|
✔ check: a good file has no problems (1.10644ms)
|
||||||
|
✔ check never opens the file: a write-only token passes (0.524742ms)
|
||||||
|
✔ check: file problems (1.263781ms)
|
||||||
|
✔ check: token files can't live in the repository or dataRoot, even through a linked directory (1.132029ms)
|
||||||
|
✔ check: dates and environment references (0.618031ms)
|
||||||
|
✔ path and load (3.889809ms)
|
||||||
|
✔ refusals (2.306381ms)
|
||||||
|
✔ systemVars flattens the validated config (2.869624ms)
|
||||||
|
✔ precedence: system, business, project, project role, agent (7.69108ms)
|
||||||
|
✔ limits narrow the definition and never widen it (3.508464ms)
|
||||||
|
✔ role.launch stays within-role only for the instance the launch block names (8.518617ms)
|
||||||
|
✔ limits.authority without role.launch leaves the launcher with no launch block (3.504665ms)
|
||||||
|
✔ limits.authority narrows cross-role actions too (1.590315ms)
|
||||||
|
✔ classify (1.446605ms)
|
||||||
|
✔ the record carries what the broker and launcher need (1.564856ms)
|
||||||
|
✔ digest: key order doesn't matter, any value change does (8.566485ms)
|
||||||
|
✔ refusals (2.519541ms)
|
||||||
|
✔ the four shipped version 2 roles load (4.307522ms)
|
||||||
|
✔ shipped role scopes match addendum B section 2 and the SR runbook (1.569662ms)
|
||||||
|
✔ shipped authority follows the note's table (0.81245ms)
|
||||||
|
✔ version 1 files keep loading with no authority (1.46652ms)
|
||||||
|
✔ the conductor policy isn't a role (0.284116ms)
|
||||||
|
✔ a missing role file is exit 4, a symbolic link too (0.452278ms)
|
||||||
|
✔ version 2 refusals (1.746533ms)
|
||||||
|
✔ authority: closed vocabulary, no gated-only action, no overlap (4.097353ms)
|
||||||
|
✔ credentials: Gitea scopes (1.370127ms)
|
||||||
|
✔ credentials: Vikunja scopes are a group-to-verbs map from the grantable list (1.856877ms)
|
||||||
|
✔ credentials: services (0.859758ms)
|
||||||
|
✔ contract: a non-empty regular Markdown file beside the role file (1.135142ms)
|
||||||
|
✔ every key names known layers and a merge rule (1.419722ms)
|
||||||
|
✔ unknown keys and wrong layers refuse (1.115598ms)
|
||||||
|
✔ types (2.832948ms)
|
||||||
|
✔ merge: defaults, then the most specific layer wins (0.443132ms)
|
||||||
|
✔ merge: limits only narrow, and provenance lists each source (0.565765ms)
|
||||||
|
✔ merge doesn't change its inputs (0.238685ms)
|
||||||
|
ℹ tests 60
|
||||||
|
ℹ suites 0
|
||||||
|
ℹ pass 60
|
||||||
|
ℹ fail 0
|
||||||
|
ℹ cancelled 0
|
||||||
|
ℹ skipped 0
|
||||||
|
ℹ todo 0
|
||||||
|
ℹ duration_ms 2372.564836
|
||||||
@@ -0,0 +1,181 @@
|
|||||||
|
✔ approvals: a request is validated before anything is posted; the rendering shows names and never ids (3.480764ms)
|
||||||
|
✔ approvals: the ledger is appended and folded into open requests with bind and approval states (1.541087ms)
|
||||||
|
✔ approvals: a reply approves only when it points at a request, says exactly approve, and comes from a listed approver once (0.711458ms)
|
||||||
|
✔ approvals: a button approves only on its own request message with the matching custom id (0.595933ms)
|
||||||
|
✔ approvals flow: a turn that opened a request posts the message with the button, records it, binds it, and both approvers approve (22.374426ms)
|
||||||
|
✔ approvals flow: a non-approver, a repeat, a wrong custom id and a service refusal each get their fixed line and a drop entry (6.906617ms)
|
||||||
|
✔ approvals flow: an invalid request from the model, a refused post, and no api client are recorded and post nothing (8.27948ms)
|
||||||
|
✔ approvals flow: start retries a bind and an approval left as unknown, under their original keys (2.485432ms)
|
||||||
|
✔ authorize: open channel, listed user (6.51225ms)
|
||||||
|
✔ authorize: wrong guild (0.297552ms)
|
||||||
|
✔ authorize: no guild (DM) (0.205469ms)
|
||||||
|
✔ authorize: unlisted channel (0.230876ms)
|
||||||
|
✔ authorize: unknown channel, no info (0.204578ms)
|
||||||
|
✔ authorize: thread of listed parent (0.301381ms)
|
||||||
|
✔ authorize: thread of unlisted parent (0.176456ms)
|
||||||
|
✔ authorize: text channel that is not a thread and not listed (0.335058ms)
|
||||||
|
✔ authorize: unlisted user (0.293678ms)
|
||||||
|
✔ authorize: no author (0.330784ms)
|
||||||
|
✔ authorize: bot author (listed id, bot flag) (0.177333ms)
|
||||||
|
✔ authorize: system author (0.144332ms)
|
||||||
|
✔ authorize: the bot itself (0.109214ms)
|
||||||
|
✔ authorize: webhook (0.242841ms)
|
||||||
|
✔ authorize: mention channel without mention (0.264761ms)
|
||||||
|
✔ authorize: mention channel with bot mention (0.33973ms)
|
||||||
|
✔ authorize: mention channel with @everyone only (0.123802ms)
|
||||||
|
✔ authorize: mention channel mentioning someone else (0.1257ms)
|
||||||
|
✔ authorize: mention channel, content says @bot but mentions empty (0.092813ms)
|
||||||
|
✔ authorize: private thread under mention channel, mentioned (0.102816ms)
|
||||||
|
✔ authorize: private thread under mention channel, not mentioned (0.086821ms)
|
||||||
|
✔ authorize: thread in another guild per channel info (0.086589ms)
|
||||||
|
✔ authorize: not an object (0.086123ms)
|
||||||
|
✔ authorize: no id (0.078108ms)
|
||||||
|
✔ authorize: oversize content is accepted and flagged (0.098587ms)
|
||||||
|
✔ authorize: exactly the limit is not oversize (0.074195ms)
|
||||||
|
✔ authorize: a user's channel allowlist drops them outside it, threads count as the parent, others are unaffected (0.628043ms)
|
||||||
|
✔ authorize: order puts wrong guild before user, and user before channel (no channel lookup for strangers) (0.181371ms)
|
||||||
|
✔ binding: a complete binding validates and is frozen (2.992748ms)
|
||||||
|
✔ binding: unknown key, missing field, wrong type refuse with exit 2 (1.683601ms)
|
||||||
|
✔ binding: empty allowlists refuse (0.526949ms)
|
||||||
|
✔ binding: a user's channel allowlist must be non-empty, listed and unique; absent means every listed channel (1.471574ms)
|
||||||
|
✔ reloadDiff: reloadable keys are summarised by id; every fixed key refuses with exit 2 (2.089237ms)
|
||||||
|
✔ binding: file must be 0600, regular, not a symlink (1.73017ms)
|
||||||
|
✔ binding: token file mode, symlink, emptiness and shape are checked; token never appears in errors (2.676312ms)
|
||||||
|
✔ cli: check refuses a non-0600 token file with exit 2 before any network use (114.93971ms)
|
||||||
|
✔ context files: absolute paths, traversal, symlinks and out-of-repo targets refuse; in-repo files resolve (1.826622ms)
|
||||||
|
✔ cli: check refuses a missing context file and a missing binding with exit 2; usage is exit 4 (461.102192ms)
|
||||||
|
✔ cli: reload validates the file first (exit 2), then needs a live owner (exit 1); usage is exit 4 (322.754775ms)
|
||||||
|
✔ cli: run refuses when STOP is present, before any network use (172.807047ms)
|
||||||
|
✔ binding: tools is optional, validated strictly, a fixed key for reload, and its roots are resolved against the data root (2.638986ms)
|
||||||
|
✔ binding: a git key is validated at load and reaches the extension whole, and only on a writable root (1.421558ms)
|
||||||
|
✔ delivery: an accepted message is in the inbox before the turn, the reply is chunked with one nonce per chunk, and the turn record is write-once (19.526806ms)
|
||||||
|
✔ delivery: refused and unknown outcomes are journaled; a later chunk is not sent after a failure (18.969037ms)
|
||||||
|
✔ delivery: restart with an unknown entry re-sends the same nonce once and reconciles before accepting traffic (1.71201ms)
|
||||||
|
✔ delivery: an unknown entry older than the dedupe window is marked refused, not re-sent; a still-unknown one refuses start (1.853004ms)
|
||||||
|
✔ delivery: repeated unknown reconciliations never refresh the dedupe window; the original intent time decides (1.464536ms)
|
||||||
|
✔ turn: a failed engine turn posts the fixed line, never model output, and writes a failed record (2.854002ms)
|
||||||
|
✔ turn: a second message during a turn is held by the engine, both get their own reply and record (32.940901ms)
|
||||||
|
✔ turn: a thread under a listed channel is answered in the thread; an unknown thread is looked up once (4.353978ms)
|
||||||
|
✔ drop: an unlisted user gets silence and one drop line; no inbox entry, no REST call, no engine call (1.116086ms)
|
||||||
|
✔ drop: an oversize message is accepted into the inbox, answered with the fixed line and journaled as a drop (1.424358ms)
|
||||||
|
✔ restart: an inbox with three ids and a replay of the same three produces zero turns (55.049777ms)
|
||||||
|
✔ stop: STOP present refuses start; STOP written while running refuses new turns and the current one finishes (55.447292ms)
|
||||||
|
✔ ceiling: the ceiling plus one is refused and journaled; one fixed line per UTC day; a new day accepts again (6.19769ms)
|
||||||
|
✔ ceiling: a burst arriving while turns are still running cannot queue past the ceiling (4.825ms)
|
||||||
|
✔ ceiling: a turn interrupted by a crash still counts after restart; admissions are durable (8.586396ms)
|
||||||
|
✔ ceiling: the daily notice survives a same-day restart; one delivery attempt in total, even when the first attempt crashed mid-flight (5.199484ms)
|
||||||
|
✔ duplicate: the same event delivered twice while the thread lookup is held yields one prompt, one admission and one reply (1.56209ms)
|
||||||
|
✔ journal: no token-shaped string and no model output on the drop path reaches disk (0.502115ms)
|
||||||
|
✔ 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 (16.074197ms)
|
||||||
|
✔ receipt: Discord refusing the reaction leaves the turn intact and records ok false (2.85958ms)
|
||||||
|
✔ reload: a new user is silent before and answered after; a removed channel goes silent; a lower ceiling applies at once (14.031778ms)
|
||||||
|
✔ reload: a fixed key refuses with exit 2 and the old binding stays in force (1.244568ms)
|
||||||
|
✔ tools: with a tools binding the turn record lists every read and its outcome; without one the field is null (5.459517ms)
|
||||||
|
✔ context: the Discord block names the server, channels and modes, and states the rules from Q15 and Q16 (2.925655ms)
|
||||||
|
✔ context: with tools the block names the roots, keeps file content as data, and says to state refusals plainly (0.967581ms)
|
||||||
|
✔ context: a writable root adds the write rules and says a write is real only once Jason commits (2.103691ms)
|
||||||
|
✔ context: the envelope is one bracketed line then the text; names cannot break the line (1.351026ms)
|
||||||
|
✔ context: a git root swaps the terminal-commit line for the git verbs, and a vault root adds the id protocol (1.711993ms)
|
||||||
|
✔ context: assembleContext concatenates files in launcher format and appends the block; sha256 is stable (1.393249ms)
|
||||||
|
✔ context: splitReply keeps paragraphs together under the limit and splits long ones at lines, spaces, then hard (0.877144ms)
|
||||||
|
✔ engine: buildPiArgs carries the fixed flags, engine settings, session dir and prompt file (2.936517ms)
|
||||||
|
✔ engine: with tools, buildPiArgs turns pi's own tools off, loads the extension explicitly and allowlists exactly our three (0.455526ms)
|
||||||
|
✔ engine: a run with tool turns settles once, on the answer, with every tool call in the result (64.765574ms)
|
||||||
|
✔ engine: a run that ends on a tool-only turn fails the prompt as empty; a retried run settles on the real end (49.685532ms)
|
||||||
|
✔ engine: one prompt, one turn, text and usage come back (35.864312ms)
|
||||||
|
✔ engine: a prompt while streaming is held until pi settles, then sent as its own run, and answered in order (345.49191ms)
|
||||||
|
✔ engine: a held prompt that times out before pi settles fails on its own and is never sent (247.674929ms)
|
||||||
|
✔ engine: timeout sends abort and fails only that turn; the process stays (108.424311ms)
|
||||||
|
✔ engine: tool events from a run that outlived its timeout never land in the next prompt's record (238.122183ms)
|
||||||
|
✔ engine: a prompt after a turn that timed out before its agent_start waits for pi to settle instead of being refused (137.250131ms)
|
||||||
|
✔ 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.132484ms)
|
||||||
|
✔ engine: a timed-out turn pi starts only after the grace never answers a later prompt (614.965805ms)
|
||||||
|
✔ engine: late events of a run past its grace, before pi exits, answer nothing and nothing more is sent (early prompt response) (2.205658ms)
|
||||||
|
✔ engine: late events of a run past its grace, before pi exits, answer nothing and nothing more is sent (late prompt response) (0.827887ms)
|
||||||
|
✔ engine: a timed-out run pi did start outlives the grace; the next prompt goes out when it ends (457.610581ms)
|
||||||
|
✔ engine: a malformed JSONL line fails the turn, not the process (39.396201ms)
|
||||||
|
✔ engine: a turn that ends in error rejects with the error code; process exit fails pending turns (60.505692ms)
|
||||||
|
✔ gateway: hello -> identify with intents, ready, heartbeat with jitter, ack (2.927521ms)
|
||||||
|
✔ gateway: missed ack closes the socket and resumes with the last sequence (2.155367ms)
|
||||||
|
✔ gateway: op 7 reconnect resumes; op 9 non-resumable re-identifies (0.560726ms)
|
||||||
|
✔ gateway: op 9 resumable resumes (0.409772ms)
|
||||||
|
✔ gateway: close 4014 is fatal, reports the missing intent, never reconnects (1.157215ms)
|
||||||
|
✔ gateway: 4004 and 4013 are fatal too; 1006 reconnects with identify when no session (1.540657ms)
|
||||||
|
✔ gateway: close() is final and unparseable frames are ignored (0.537333ms)
|
||||||
|
✔ git: config validation is strict, needs write: true, a work tree and a private token file (73.818329ms)
|
||||||
|
✔ git: the child environment drops every host git config, names one helper, and carries the token path only for origin (98.143916ms)
|
||||||
|
✔ git: status reports the branch, ahead/behind and changed paths, and refuses off the named branch or mid-merge (176.969902ms)
|
||||||
|
✔ git: parseStatus reads porcelain v2 including renames and conflicts (0.49728ms)
|
||||||
|
✔ git: a commit stages exactly the named files, carries the seat author and the requester trailer, and pushes at once (149.954763ms)
|
||||||
|
✔ git: commit refusals: message, paths, requester, nothing to commit, and an index that already holds other work (130.929256ms)
|
||||||
|
✔ git: a commit whose push fails is still a commit, says so, and the next commit's push carries both (D6) (196.771443ms)
|
||||||
|
✔ git: pull is fast-forward only; a diverged origin or dirty local files refuse with nothing merged (271.260154ms)
|
||||||
|
✔ git: push pushes the named branch only and reports up to date (79.694918ms)
|
||||||
|
✔ git: no token value or token path ever reaches a git argument list; outputs are masked and capped (121.953966ms)
|
||||||
|
✔ git: the credential helper answers get over https from a private file and nothing else (283.865308ms)
|
||||||
|
✔ git: the vault protocol validates before a commit, honours another owner's lock, reserves ids, and locks around writes (1163.004697ms)
|
||||||
|
✔ lock: the claim is exclusive; a second start against a live owner refuses (5.264936ms)
|
||||||
|
✔ lock: a stale lock (dead owner, reused pid, or record without start) refuses run and is never signaled; only unlock clears it (6.446188ms)
|
||||||
|
✔ lock: an incomplete claim (directory without owner record) is busy and refuses run; unlock clears it (1.157467ms)
|
||||||
|
✔ lock: an owner record that exists but cannot be read is invalid: never signaled, never removed, never claimed over (3.339088ms)
|
||||||
|
✔ lock: legacy upgrade; a live connector holding a {pid, start} record is unknown, unlock refuses and nothing changes; after it exits, unlock clears it (62.903558ms)
|
||||||
|
✔ 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 (493.72036ms)
|
||||||
|
✔ lock: identity syntax; only canonical unsigned decimal start ticks and lowercase boot uuids are identities (0.575706ms)
|
||||||
|
✔ lock: a process whose start marker or boot id cannot be read refuses to claim (0.68514ms)
|
||||||
|
✔ lock: a live pid whose identity cannot be read right now is unknown: never signaled, never removed, never claimed over (1.345038ms)
|
||||||
|
✔ lock: four processes racing for the same binding; exactly one claims it and the others refuse (66.990636ms)
|
||||||
|
✔ lock: stale handoff; concurrent starts over a stale lock all refuse, nothing reclaims, one unlock then exactly one live owner (184.173985ms)
|
||||||
|
✔ lock: four-party schedule; claims landing inside an unlock's gap never survive, one unlock leaves no owner and no residue (96.37225ms)
|
||||||
|
✔ notices: a kind is recorded per UTC day and found again (0.75007ms)
|
||||||
|
✔ recover: nothing to do is clean; a lock whose owner is gone or that has no record is cleared and STOP ends up absent (53.513948ms)
|
||||||
|
✔ recover: an operator STOP refuses with exit 3 and is never removed, whatever the lock says (64.479595ms)
|
||||||
|
✔ recover: a brake written during the unlock wins; STOP stays with both lines and the start is refused (42.92945ms)
|
||||||
|
✔ recover: a held binding refuses with exit 3 and writes no STOP: live owner, alive pid without verifiable identity, unreadable record (92.490733ms)
|
||||||
|
✔ cli: recover exits 0 when ready, 3 behind a brake or a held binding, and run's own STOP refusal is 3 (961.760799ms)
|
||||||
|
✔ rest: createMessage sends nonce, enforce_nonce, empty allowed_mentions and a soft reply reference (2.929643ms)
|
||||||
|
✔ rest: 429 waits retry_after and retries; 4xx is refused; 5xx and socket errors are unknown (7.035962ms)
|
||||||
|
✔ rest: content and nonce limits are enforced locally; typing never throws (0.873221ms)
|
||||||
|
✔ rest: react PUTs the encoded emoji on the inbound message for @me; 2xx is true, anything else is false and never throws (1.007832ms)
|
||||||
|
✔ setspark config: a bare https or loopback origin, a private key file, a principal (5.950569ms)
|
||||||
|
✔ setspark config: reaches the tools config and the binding as a fixed key (2.777123ms)
|
||||||
|
✔ setspark config: the binding's key survives resolveToolRoots and the engine's JSON hand-off to the extension (1.390977ms)
|
||||||
|
✔ setspark config: approvers come from the binding's users, never from the binding's setspark key (1.783678ms)
|
||||||
|
✔ setspark verbs: required_approvers go out as discord ids from names and come back as names (23.653954ms)
|
||||||
|
✔ setspark verbs: no Discord user id reaches tool text, whatever shape the service returns it in (8.696194ms)
|
||||||
|
✔ setspark contract: a decision made with names opens a request the connector accepts; names stored by an old record still refuse (7.92764ms)
|
||||||
|
✔ setspark keys: read per call, one printable token per file, rotation without a restart (6.202747ms)
|
||||||
|
✔ setspark idempotency keys: principal, turn id, call index; connector keys name a step (0.747711ms)
|
||||||
|
✔ setspark http core: json in and out, bearer header, idempotency header, fixed user agent, no key anywhere else (4.356668ms)
|
||||||
|
✔ setspark http core: error bodies become fixed refusals with code and the 409 fields; server text is data, cut (1010.652943ms)
|
||||||
|
✔ setspark verbs: a setspark key enables the eight verbs and no counters (0.510425ms)
|
||||||
|
✔ setspark verbs: writes carry the turn's key and the asserted requester, reads carry no key, and the api key never appears in text or details (7.797788ms)
|
||||||
|
✔ setspark verbs: no turn refuses every write before any request; bad arguments refuse before any request; reads still work (2.258763ms)
|
||||||
|
✔ setspark verbs: renderRecord caps long output and hides the accepted snapshot (0.313282ms)
|
||||||
|
✔ setspark api: bind, add_approval (button and reply) and get use integer request ids and the connector's keys (2.870164ms)
|
||||||
|
✔ tools: config refuses a missing, symlinked, dotted, non-directory or duplicate root and bad limits (4.397213ms)
|
||||||
|
✔ tools: every escape is refused with a fixed reason and nothing outside the root is read (4.347595ms)
|
||||||
|
✔ tools: happy paths list, read a window, and search case-insensitively; dotfiles and symlinks never appear (6.177907ms)
|
||||||
|
✔ tools: the tool set renders text for the model, records details for the journal, and enforces the per-run budget (5.426716ms)
|
||||||
|
✔ tools: listing and search caps hold (11.131462ms)
|
||||||
|
✔ tools: credential shapes are caught; ordinary prose and ids are not (0.902566ms)
|
||||||
|
✔ tools: the read uses the checked file itself; a symlink, a swapped file, a FIFO, a grown file or a hard link at read time is refused (5.404534ms)
|
||||||
|
✔ tools: an unreadable file under the root is skipped by search and refused by read (1.198411ms)
|
||||||
|
✔ tools: config accepts write: true only as a boolean, and enables the write tools only then (1.126936ms)
|
||||||
|
✔ tools: every write outside the fence is refused before any byte lands, and no temp file remains (5.438851ms)
|
||||||
|
✔ tools: write_file leaves the exact bytes, edit_file replaces one exact match, and the set renders the change as uncommitted (3.292824ms)
|
||||||
|
✔ tools: a target that changed between the check and the rename is refused and the temp file is removed (1.541194ms)
|
||||||
|
✔ web: config takes an https or loopback-http SearXNG base url and a bounded fetch cap (4.836275ms)
|
||||||
|
✔ web: address rules refuse every private, loopback, link-local, mapped and multicast form (3.049629ms)
|
||||||
|
✔ web: web_fetch refuses bad urls, private hosts, rebinding names, non-https redirects, too many hops, error status, non-text bodies, and times out (1031.735887ms)
|
||||||
|
✔ web: web_fetch returns html as text with the title, follows an https redirect, keeps plain text and json, and cuts at the cap (6.075427ms)
|
||||||
|
✔ web: html to text drops scripts, styles and comments, decodes entities and keeps block breaks (0.346365ms)
|
||||||
|
✔ 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 (4.050214ms)
|
||||||
|
✔ web: the tool set enables the web tools only with a web key, counts them in the budget, and records url, status and hits (4.080956ms)
|
||||||
|
ℹ tests 173
|
||||||
|
ℹ suites 0
|
||||||
|
ℹ pass 173
|
||||||
|
ℹ fail 0
|
||||||
|
ℹ cancelled 0
|
||||||
|
ℹ skipped 0
|
||||||
|
ℹ todo 0
|
||||||
|
ℹ duration_ms 2923.476924
|
||||||
@@ -0,0 +1,52 @@
|
|||||||
|
✔ the boot config is checked before anything starts (161.063999ms)
|
||||||
|
✔ a business with no tracker entry refuses task verbs (211.70173ms)
|
||||||
|
✔ credential.expiring and .expired are recorded once per instance (350.793246ms)
|
||||||
|
✔ a token file that changes on disk records credential.changed (182.517472ms)
|
||||||
|
✔ autostart polls, reconciles and retries a startup the tracker was down for (317.396259ms)
|
||||||
|
✔ a refusal a restart must clear is not retried by the poll (241.907605ms)
|
||||||
|
✔ a poll that fires while two are queued is dropped (399.44531ms)
|
||||||
|
✔ close waits for a running verb and refuses one that has not started (454.205665ms)
|
||||||
|
✔ the bundled Vikunja is the pinned upstream image the runbook names (1.367579ms)
|
||||||
|
✔ every published port is on 127.0.0.1, and no secret is in the file (0.834128ms)
|
||||||
|
✔ the fake answers each route with the statuses and shapes Vikunja v2.7.0 sent (626.670516ms)
|
||||||
|
✔ the recorded task bodies pass the checks S3 applies to every read (0.950159ms)
|
||||||
|
✔ the client works against the fake over real HTTP with the platform fetch (460.499823ms)
|
||||||
|
✔ a correct install starts, and the first reconcile records tasks that already exist (233.469659ms)
|
||||||
|
✔ verbs refuse while a business is starting and after startup refused it (220.139194ms)
|
||||||
|
✔ startup refuses a token that can do more than its role needs (819.392746ms)
|
||||||
|
✔ startup refuses an unsupported version and flags an untested one (856.087044ms)
|
||||||
|
✔ startup refuses a board that the runbook did not install (1635.977404ms)
|
||||||
|
✔ startup refuses a project the sync bot cannot read (243.429992ms)
|
||||||
|
✔ startup refuses a configured label the pm bot cannot see (330.85812ms)
|
||||||
|
✔ startup refuses an expired credential and a missing sync credential (617.657746ms)
|
||||||
|
✔ an unreachable tracker refuses with tracker-unavailable (201.959624ms)
|
||||||
|
✔ an edit in the UI is recorded once, with the fields that changed (367.49559ms)
|
||||||
|
✔ a move between open buckets is seen on the board, though updated does not change (284.242627ms)
|
||||||
|
✔ a person's comment is counted and a bot's is not (568.453137ms)
|
||||||
|
✔ the hourly reconcile catches a comment through comment_count (528.968054ms)
|
||||||
|
✔ a task closed in the UI leaves the open view with its done bucket (1105.177493ms)
|
||||||
|
✔ a task that leaves the board is recorded as deleted, moved or out of reach (1020.21309ms)
|
||||||
|
✔ a poll that read before a verb wrote does not overwrite the verb (577.431971ms)
|
||||||
|
✔ a tracker fault during a tick is reported and the next tick catches up (510.876063ms)
|
||||||
|
✔ a malformed answer refuses the tick with tracker-shape (372.367911ms)
|
||||||
|
✔ no token value reaches the database, the log or a refusal (800.594196ms)
|
||||||
|
✔ task.create needs a recorded human request and a requirement id (357.667046ms)
|
||||||
|
✔ only labels named in the business file can be written (390.793593ms)
|
||||||
|
✔ task.schedule sets and clears a due date and relations (705.826762ms)
|
||||||
|
✔ assign and reassign move the role bots and record task.assigned (1176.844108ms)
|
||||||
|
✔ task.update.assigned is for the assignee and records task.state (1113.060134ms)
|
||||||
|
✔ a wrong expected digest records task.conflict and writes nothing (659.208242ms)
|
||||||
|
✔ a cross-role verb needs a resolved decision, used once (751.178559ms)
|
||||||
|
✔ task.close needs a verdict; after it every verb refuses with task-done (819.563754ms)
|
||||||
|
✔ a lost answer is settled by a re-read and never retried (607.993573ms)
|
||||||
|
✔ a create whose answer is lost is reported uncertain, and the poll finds the task (620.810585ms)
|
||||||
|
✔ a task the sync bot cannot read refuses and records nothing (435.367416ms)
|
||||||
|
✔ verbs and polls for one business run one at a time (437.173167ms)
|
||||||
|
ℹ tests 44
|
||||||
|
ℹ suites 0
|
||||||
|
ℹ pass 44
|
||||||
|
ℹ fail 0
|
||||||
|
ℹ cancelled 0
|
||||||
|
ℹ skipped 0
|
||||||
|
ℹ todo 0
|
||||||
|
ℹ duration_ms 8218.260045
|
||||||
@@ -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
|
||||||
@@ -0,0 +1,55 @@
|
|||||||
|
Note: switching to '81339889dc04dbc1a581285ce5eb17182f82fa35'.
|
||||||
|
|
||||||
|
You are in 'detached HEAD' state. You can look around, make experimental
|
||||||
|
changes and commit them, and you can discard any commits you make in this
|
||||||
|
state without impacting any branches by switching back to a branch.
|
||||||
|
|
||||||
|
If you want to create a new branch to retain commits you create, you may
|
||||||
|
do so (now or later) by using -c with the switch command. Example:
|
||||||
|
|
||||||
|
git switch -c <new-branch-name>
|
||||||
|
|
||||||
|
Or undo this operation with:
|
||||||
|
|
||||||
|
git switch -
|
||||||
|
|
||||||
|
Turn off this advice by setting config variable advice.detachedHead to false
|
||||||
|
|
||||||
|
Not currently on any branch.
|
||||||
|
nothing to commit, working tree clean
|
||||||
|
Note: switching to '81339889dc04dbc1a581285ce5eb17182f82fa35'.
|
||||||
|
|
||||||
|
You are in 'detached HEAD' state. You can look around, make experimental
|
||||||
|
changes and commit them, and you can discard any commits you make in this
|
||||||
|
state without impacting any branches by switching back to a branch.
|
||||||
|
|
||||||
|
If you want to create a new branch to retain commits you create, you may
|
||||||
|
do so (now or later) by using -c with the switch command. Example:
|
||||||
|
|
||||||
|
git switch -c <new-branch-name>
|
||||||
|
|
||||||
|
Or undo this operation with:
|
||||||
|
|
||||||
|
git switch -
|
||||||
|
|
||||||
|
Turn off this advice by setting config variable advice.detachedHead to false
|
||||||
|
|
||||||
|
OK dry-run: allowed change, exit 0, nothing committed (exit 0)
|
||||||
|
OK dry-run committed nothing
|
||||||
|
OK apply: allowed change exits 0 (exit 0)
|
||||||
|
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
|
||||||
@@ -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
|
||||||
@@ -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
|
||||||
@@ -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: 3027311 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
|
||||||
@@ -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
|
||||||
@@ -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/r38/cand) is not the queue's canonical root (/mnt/storage/src/mosaic-stack)
|
||||||
|
|
||||||
|
queue suite: 27 passed, 0 failed
|
||||||
@@ -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
|
||||||
@@ -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
|
||||||
@@ -0,0 +1,37 @@
|
|||||||
|
cand node-tasks exit 0 load 10.87
|
||||||
|
cand node-bus exit 0 load 11.28
|
||||||
|
cand node-business exit 0 load 11.28
|
||||||
|
cand node-discord exit 0 load 11.90
|
||||||
|
cand suite-auth exit 0 load 11.90
|
||||||
|
cand suite-conductor exit 0 load 11.58
|
||||||
|
cand suite-config exit 0 load 10.82
|
||||||
|
cand suite-discord exit 0 load 10.99
|
||||||
|
cand suite-extension-package exit 0 load 10.91
|
||||||
|
cand suite-foundation exit 0 load 8.48
|
||||||
|
cand suite-queue exit 0 load 10.77
|
||||||
|
cand suite-release exit 0 load 10.77
|
||||||
|
cand suite-task exit 1 load 10.07
|
||||||
|
base node-bus exit 0 load 10.07
|
||||||
|
base node-business exit 0 load 9.42
|
||||||
|
base node-discord exit 0 load 9.42
|
||||||
|
base suite-auth exit 0 load 8.91
|
||||||
|
base suite-conductor exit 0 load 9.08
|
||||||
|
base suite-config exit 0 load 9.08
|
||||||
|
base suite-discord exit 0 load 8.28
|
||||||
|
base suite-extension-package exit 0 load 8.28
|
||||||
|
base suite-foundation exit 0 load 8.76
|
||||||
|
base suite-queue exit 0 load 8.01
|
||||||
|
base suite-release exit 0 load 8.01
|
||||||
|
base suite-task exit 1 load 7.45
|
||||||
|
cand run 1 exit 0 load 7.25 discord suite: 64 passed, 0 failed
|
||||||
|
base run 1 exit 0 load 6.83 discord suite: 64 passed, 0 failed
|
||||||
|
cand run 2 exit 0 load 6.80 discord suite: 64 passed, 0 failed
|
||||||
|
base run 2 exit 0 load 6.97 discord suite: 64 passed, 0 failed
|
||||||
|
cand run 3 exit 0 load 6.37 discord suite: 64 passed, 0 failed
|
||||||
|
base run 3 exit 0 load 6.32 discord suite: 64 passed, 0 failed
|
||||||
|
cand run 4 exit 0 load 6.30 discord suite: 64 passed, 0 failed
|
||||||
|
base run 4 exit 0 load 5.86 discord suite: 64 passed, 0 failed
|
||||||
|
cand run 5 exit 0 load 5.79 discord suite: 64 passed, 0 failed
|
||||||
|
base run 5 exit 0 load 5.41 discord suite: 64 passed, 0 failed
|
||||||
|
cand run 6 exit 0 load 7.14 discord suite: 64 passed, 0 failed
|
||||||
|
base run 6 exit 0 load 6.59 discord suite: 64 passed, 0 failed
|
||||||
@@ -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
|
||||||
@@ -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
|
||||||
@@ -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
|
||||||
@@ -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
|
||||||
@@ -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
|
||||||
@@ -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
|
||||||
@@ -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
|
||||||
@@ -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
|
||||||
@@ -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
|
||||||
@@ -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
|
||||||
@@ -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
|
||||||
@@ -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
|
||||||
@@ -0,0 +1,64 @@
|
|||||||
|
V1 killed (2)
|
||||||
|
V2 killed (1)
|
||||||
|
V3 killed (1)
|
||||||
|
V4 killed (1)
|
||||||
|
V5 killed (1)
|
||||||
|
V6 killed (1)
|
||||||
|
V7 killed (1)
|
||||||
|
V8 killed (1)
|
||||||
|
V9 SURVIVED
|
||||||
|
V10 killed (1)
|
||||||
|
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 SURVIVED
|
||||||
|
V22 killed (1)
|
||||||
|
V23 SURVIVED
|
||||||
|
V24 SURVIVED
|
||||||
|
S1 killed (1)
|
||||||
|
S2 killed (5)
|
||||||
|
S3 SURVIVED
|
||||||
|
S4 killed (2)
|
||||||
|
S5 SURVIVED
|
||||||
|
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 (3)
|
||||||
|
G1 SURVIVED
|
||||||
|
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
|
||||||
|
manifest-ok
|
||||||
@@ -0,0 +1,120 @@
|
|||||||
|
v24.21.0
|
||||||
|
✔ launch identity is stamped, payload identity is refused and stale holder cannot send (177.995558ms)
|
||||||
|
✔ decision classes route from policy; gated resolution is human-only, choice and target must match (226.379303ms)
|
||||||
|
✔ claim exclusion, holder release, gated revoke and rerouting to a new holder are atomic (264.656306ms)
|
||||||
|
✔ launch events require a human CLI capability; generic emit cannot forge authority events (173.936466ms)
|
||||||
|
✔ within-role decisions close atomically and invalid options or blocking omissions refuse (155.77841ms)
|
||||||
|
✔ observer capabilities read human inbox but cannot mutate or forge launch identity (167.571228ms)
|
||||||
|
✔ task action subjects and linked decision trail are complete and ordered (180.969194ms)
|
||||||
|
✔ launch binding is durable and reconnecting requires the identical trusted record (117.652825ms)
|
||||||
|
✔ business isolation includes inherited object names and cross-business message references (182.988785ms)
|
||||||
|
✔ authority never transfers between action, run, target, unresolved or replaced role holder (236.202894ms)
|
||||||
|
✔ task projection uses schema current view, skipping earlier and equal-start polls (129.943441ms)
|
||||||
|
✔ revocation permanently bars the old run from reclaiming first, including after broker restart (180.080272ms)
|
||||||
|
✔ empty message references refuse before storage; refusal-evidence failure stays a typed error (108.282332ms)
|
||||||
|
✔ both arbiters require human resolution when their cross-role route is themselves (191.103379ms)
|
||||||
|
✔ S1 adapter takes resolved limits and refs, rejects mismatched instance, never mutates input (2.901125ms)
|
||||||
|
✔ only validated broker references load; returned data and exceptions cannot expose a known token (24.21701ms)
|
||||||
|
✔ bad file modes, symlinks, repository/data paths, malformed tokens and missing dates refuse (11.61988ms)
|
||||||
|
✔ expiry refuses use and env references never become client data (0.869794ms)
|
||||||
|
✔ S1 parsed service refs work, service mismatch refuses, Gitea rotation due is a warning state (1.627046ms)
|
||||||
|
✔ opaque tokens shorter than 16 characters refuse before use (0.342368ms)
|
||||||
|
✔ human proof binds CLI entry, process start and nonce; agents and incomplete ancestry refuse (4.057288ms)
|
||||||
|
✔ process reader gets own kernel identity without exposing environment values (1.932984ms)
|
||||||
|
✔ EACCES ancestor environments skip only markers; commands and registered launches still refuse (1.454891ms)
|
||||||
|
✔ real pid 1 remains inspectable when its environment is protected (0.53668ms)
|
||||||
|
✔ within-role sends cite an open gated launch decision without spending it or naming it in grants (210.931132ms)
|
||||||
|
✔ missing and foreign-business citations refuse and roll back message and grant (165.573886ms)
|
||||||
|
✔ cross-role sends still need a matching resolved decision and consume it once (243.861362ms)
|
||||||
|
✔ broker process binds trusted launches, offers reader capabilities, refuses human mutation, closes cleanly (230.652875ms)
|
||||||
|
✔ startup token refusal returns safe code without value or partial listening broker (39.139736ms)
|
||||||
|
✔ loaded fixture token is absent from socket replies and SQLite, including refusal evidence (183.304182ms)
|
||||||
|
✔ killed broker leaves an explicit stale lock; another process cannot silently reclaim it (174.404037ms)
|
||||||
|
✔ trusted host registers later launches; socket clients never have a registration verb (194.739448ms)
|
||||||
|
✔ runtime excludes declared project roots even when host supplies no repoRoots (38.165033ms)
|
||||||
|
✔ a refused launch binding leaves the broker and existing capabilities alive; bad protocol stops it (157.757851ms)
|
||||||
|
✔ v3b prototype refusals, views and append-only mutations (999.7647ms)
|
||||||
|
✔ gated approval authorizes once, survives store reopen, and fresh approval works (255.746449ms)
|
||||||
|
✔ another run cannot consume an approval; a failed check leaves it usable (201.630399ms)
|
||||||
|
✔ two scheduled callers have exactly one grant and one consumed refusal (157.451638ms)
|
||||||
|
✔ failed commit rolls consumption back; cross-role consumes and within-role stays reusable (312.538007ms)
|
||||||
|
✔ class drift gated to cross-role refuses before consumption (205.950574ms)
|
||||||
|
✔ class drift cross-role to gated refuses before consumption (221.072715ms)
|
||||||
|
✔ class drift gated to within-role refuses before consumption (198.148507ms)
|
||||||
|
✔ class drift cross-role to within-role refuses before consumption (214.761828ms)
|
||||||
|
✔ class drift within-role to gated refuses before consumption (183.546474ms)
|
||||||
|
✔ class drift within-role to cross-role refuses before consumption (177.062436ms)
|
||||||
|
✔ message.send consumes approval and prevents a later send or authorize (169.702317ms)
|
||||||
|
✔ role.revoke consumes approval and prevents a later revoke or authorize (210.23678ms)
|
||||||
|
✔ creates private WAL store and excludes a second writer until explicit close (135.028493ms)
|
||||||
|
✔ rollback is atomic and schema metadata is checked against trusted DDL, not just itself (157.928989ms)
|
||||||
|
✔ existing empty database and symlink runtime directory refuse, never initialize over damage (172.711454ms)
|
||||||
|
✔ crash during a transaction recovers no partial event after explicit fixture-only lock removal (166.363463ms)
|
||||||
|
✔ writer refuses mixed at/read_at forms atomically, even through trusted SQL helpers (107.218464ms)
|
||||||
|
✔ async transactions refuse before invoking their function (107.062057ms)
|
||||||
|
✔ recordTask keeps sync reads and a role write apart (197.403236ms)
|
||||||
|
✔ read_at must be one canonical UTC format, so the projection compares strings safely (89.063123ms)
|
||||||
|
✔ a bad entry refuses the whole record (110.50118ms)
|
||||||
|
✔ taskView reads the projection for one business (124.982524ms)
|
||||||
|
✔ requestTask hands only a holder and a task verb to the handler, and records refusals (240.14733ms)
|
||||||
|
✔ the server sends task verbs to the adapter with its own timeout; other verbs stay synchronous (403.174034ms)
|
||||||
|
✔ without an adapter the server refuses every task verb (194.58278ms)
|
||||||
|
✔ the runtime refuses an invalid adapter and closes a valid one (210.094013ms)
|
||||||
|
✔ the process loads the S3 adapter from plain-data trackers (251.572161ms)
|
||||||
|
✔ socket capability stamps launch identity; shared views use wire, no SQL client (161.598888ms)
|
||||||
|
✔ two wire claims serialize; a lost reply never automatically retries (161.348123ms)
|
||||||
|
✔ malformed, oversized and identity-forging envelopes refuse without echoing input (110.578016ms)
|
||||||
|
✔ client preserves UTF-8 when a response divides a multibyte character (13.803031ms)
|
||||||
|
✔ committed mutation followed by dropped reply reports unknown and is never retried (140.825499ms)
|
||||||
|
✔ the boot config is checked before anything starts (131.635722ms)
|
||||||
|
✔ a business with no tracker entry refuses task verbs (130.438873ms)
|
||||||
|
✔ credential.expiring and .expired are recorded once per instance (243.626085ms)
|
||||||
|
✔ a token file that changes on disk records credential.changed (115.477902ms)
|
||||||
|
✔ autostart polls, reconciles and retries a startup the tracker was down for (142.618869ms)
|
||||||
|
✔ a refusal a restart must clear is not retried by the poll (101.884484ms)
|
||||||
|
✔ a poll that fires while two are queued is dropped (103.399807ms)
|
||||||
|
✔ close waits for a running verb and refuses one that has not started (202.47576ms)
|
||||||
|
✔ the bundled Vikunja is the pinned upstream image the runbook names (1.622513ms)
|
||||||
|
✔ every published port is on 127.0.0.1, and no secret is in the file (0.584512ms)
|
||||||
|
✔ the fake answers each route with the statuses and shapes Vikunja v2.7.0 sent (419.775759ms)
|
||||||
|
✔ the recorded task bodies pass the checks S3 applies to every read (1.236979ms)
|
||||||
|
✔ the client works against the fake over real HTTP with the platform fetch (228.634561ms)
|
||||||
|
✔ a correct install starts, and the first reconcile records tasks that already exist (159.589711ms)
|
||||||
|
✔ verbs refuse while a business is starting and after startup refused it (136.700055ms)
|
||||||
|
✔ startup refuses a token that can do more than its role needs (435.328987ms)
|
||||||
|
✔ startup refuses an unsupported version and flags an untested one (299.577898ms)
|
||||||
|
✔ startup refuses a board that the runbook did not install (360.356472ms)
|
||||||
|
✔ startup refuses a project the sync bot cannot read (103.639411ms)
|
||||||
|
✔ startup refuses a configured label the pm bot cannot see (86.468568ms)
|
||||||
|
✔ startup refuses an expired credential and a missing sync credential (197.947641ms)
|
||||||
|
✔ an unreachable tracker refuses with tracker-unavailable (89.357586ms)
|
||||||
|
✔ an edit in the UI is recorded once, with the fields that changed (236.98378ms)
|
||||||
|
✔ a move between open buckets is seen on the board, though updated does not change (200.469822ms)
|
||||||
|
✔ a person's comment is counted and a bot's is not (273.225862ms)
|
||||||
|
✔ the hourly reconcile catches a comment through comment_count (221.516299ms)
|
||||||
|
✔ a task closed in the UI leaves the open view with its done bucket (396.680361ms)
|
||||||
|
✔ a task that leaves the board is recorded as deleted, moved or out of reach (263.024955ms)
|
||||||
|
✔ a poll that read before a verb wrote does not overwrite the verb (166.731917ms)
|
||||||
|
✔ a tracker fault during a tick is reported and the next tick catches up (168.501175ms)
|
||||||
|
✔ a malformed answer refuses the tick with tracker-shape (125.934132ms)
|
||||||
|
✔ no token value reaches the database, the log or a refusal (249.734475ms)
|
||||||
|
✔ task.create needs a recorded human request and a requirement id (217.071845ms)
|
||||||
|
✔ only labels named in the business file can be written (254.896184ms)
|
||||||
|
✔ task.schedule sets and clears a due date and relations (282.467596ms)
|
||||||
|
✔ assign and reassign move the role bots and record task.assigned (305.563947ms)
|
||||||
|
✔ task.update.assigned is for the assignee and records task.state (312.128897ms)
|
||||||
|
✔ a wrong expected digest records task.conflict and writes nothing (193.094654ms)
|
||||||
|
✔ a cross-role verb needs a resolved decision, used once (240.495473ms)
|
||||||
|
✔ task.close needs a verdict; after it every verb refuses with task-done (232.734889ms)
|
||||||
|
✔ a lost answer is settled by a re-read and never retried (230.812095ms)
|
||||||
|
✔ a create whose answer is lost is reported uncertain, and the poll finds the task (188.427104ms)
|
||||||
|
✔ a task the sync bot cannot read refuses and records nothing (138.146555ms)
|
||||||
|
✔ verbs and polls for one business run one at a time (283.40276ms)
|
||||||
|
ℹ tests 111
|
||||||
|
ℹ suites 0
|
||||||
|
ℹ pass 111
|
||||||
|
ℹ fail 0
|
||||||
|
ℹ cancelled 0
|
||||||
|
ℹ skipped 0
|
||||||
|
ℹ todo 0
|
||||||
|
ℹ duration_ms 3072.92625
|
||||||
@@ -0,0 +1,34 @@
|
|||||||
|
D1 self snapshot due_date self 2026-12-01T09:00:00.456Z returned digest e5a760bab0c0
|
||||||
|
D1 external events after one tick [["due_date"]]
|
||||||
|
D1 same schedule again sends PATCH 1
|
||||||
|
D1 expect=returned digest -> task-conflict
|
||||||
|
D2 external events 0
|
||||||
|
W1 caller sees tracker-unavailable
|
||||||
|
W1 coder assigned in tracker true
|
||||||
|
W1 action.allowed task.assign 1
|
||||||
|
W1 task.assigned events 0 self snapshots 1
|
||||||
|
W1 poll external events [["assignees"]]
|
||||||
|
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
|
||||||
|
✔ D1 schedule with milliseconds: the poll reports the bot's own due date as an external change (153.643079ms)
|
||||||
|
✔ D2 whole seconds: no external change (control) (153.767528ms)
|
||||||
|
✔ W1 write lands, final read fails: refusal, no self record, poll calls it external (141.822904ms)
|
||||||
|
✔ H1 a human, H2 a reader, and a non-holder cannot call a task verb (129.310616ms)
|
||||||
|
✔ R1 a relation to another project's task id under this project's ref (156.710932ms)
|
||||||
|
✔ C1 a conflict event costs no authority; a role without the verb can still write task.conflict (192.008667ms)
|
||||||
|
✔ M1 one task with a 500 in missing() stalls the tick; T1 the tick after recovers (156.768599ms)
|
||||||
|
ℹ tests 7
|
||||||
|
ℹ suites 0
|
||||||
|
ℹ pass 7
|
||||||
|
ℹ fail 0
|
||||||
|
ℹ cancelled 0
|
||||||
|
ℹ skipped 0
|
||||||
|
ℹ todo 0
|
||||||
|
ℹ duration_ms 1180.631533
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
create due .123Z (answer) 201 "2026-11-01T10:20:30.123Z"
|
||||||
|
read after create 200 "2026-11-01T10:20:30Z"
|
||||||
|
patch due .456Z (answer) 200 "2026-12-01T00:00:00.456Z"
|
||||||
|
read after patch 200 "2026-12-01T00:00:00Z"
|
||||||
|
patch due .000Z (answer) 200 "2026-12-01T00:00:00Z"
|
||||||
|
read after .000Z 200 "2026-12-01T00:00:00Z"
|
||||||
|
list due_date 200 ["2026-12-01T00:00:00Z"]
|
||||||
@@ -0,0 +1,242 @@
|
|||||||
|
# Slice 1 S3, row 38, round 1 review (Filbert)
|
||||||
|
|
||||||
|
Issue #1520, request comment 26844, packet pointer 26845, queue rev 179.
|
||||||
|
Packet: `agents/darkwing/work/slice1-s3/` at `4ac133dd`. Candidate: manifest
|
||||||
|
sha256 `be8dbd8ca03ca7264666ba171bd7bffbaaa08c6b265d60e7d587f1bc53897daf`,
|
||||||
|
28 files, over `81339889` with `build.patch` (sha256
|
||||||
|
`eb225e5305f50811354604ee45a6e0a7489d9a76543615b2caaba102c13d5700`).
|
||||||
|
Rehearsal log `live-rehearsal.txt` (sha256
|
||||||
|
`2a79eda46af5bb20b3aa0f5e5de959320b310a299b1c157c2712df9da74c012f`).
|
||||||
|
Brief: `docs/plans/2026-10-04_slice-1.md`, S3.
|
||||||
|
|
||||||
|
Verdict: **changes.** There are two blockers. B1: a due date with
|
||||||
|
milliseconds makes the bot report its own write as a person's edit, and
|
||||||
|
breaks the digest it hands back. B2: when a write lands and the final read
|
||||||
|
fails, the caller gets a read refusal, nothing is recorded, and the poll
|
||||||
|
later reports the bot's write as external. Both fixes are small. I also
|
||||||
|
ask for three tests in round 2. Everything else is a note.
|
||||||
|
|
||||||
|
The gate's live-run item can't be met this round: the estate run waits on
|
||||||
|
T236's base URL. The scratch rehearsal log is clean (below).
|
||||||
|
|
||||||
|
## Method
|
||||||
|
|
||||||
|
- Detached worktrees at `81339889` under `~/filbert-scratch/r38/`, one for
|
||||||
|
the base and one for the candidate. In the candidate I ran `git apply
|
||||||
|
build.patch` and then `sha256sum -c`: 28 OK. After the mutant run the
|
||||||
|
tree checks clean against the manifest again.
|
||||||
|
- `gate.sh` runs the suites one at a time in both trees and tees each
|
||||||
|
output. `discord-loop.sh` runs test-discord six more times in each tree.
|
||||||
|
- `vkprobe.mjs` runs against a scratch Vikunja on the pinned image
|
||||||
|
(`vikunja/vikunja@sha256:e2204a1c…cfc`, v2.7.0) on 127.0.0.1, with my own
|
||||||
|
throwaway user. It checks how due dates come back.
|
||||||
|
- `probe.test.mjs` runs against the candidate's `world.mjs` and
|
||||||
|
`FakeVikunja` (D1, D2, W1, H1 to H3, R1, C1, M1).
|
||||||
|
- `mutants.sh` applies 63 single perl substitutions to `packages/tasks/src`,
|
||||||
|
`packages/bus/src/broker.mjs` and `packages/bus/src/server.mjs`. For each
|
||||||
|
one it runs the tasks and bus node tests, then restores the file.
|
||||||
|
- Node 24 run: `node:24` with no network, the tree mounted read-only, and
|
||||||
|
the host uid.
|
||||||
|
- `docker compose config` on `deploy/vikunja/compose.yaml`, with and
|
||||||
|
without its variables.
|
||||||
|
- A scan of `live-rehearsal.txt` for bearer headers, `token` values and
|
||||||
|
64-hex strings.
|
||||||
|
|
||||||
|
## Suites
|
||||||
|
|
||||||
|
| Suite | Candidate | Base |
|
||||||
|
|---|---|---|
|
||||||
|
| node tasks (Node 26.8.1) | 44/44 | n/a |
|
||||||
|
| node tasks + bus (Node 24.21.0) | 111/111 | 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, and 6/6 more clean runs | 64/64, and 6/6 more |
|
||||||
|
| 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". These are the same two failures as in
|
||||||
|
row 39.
|
||||||
|
|
||||||
|
On test-discord, Darkwing asked me to judge it. I saw no flake in seven
|
||||||
|
runs per tree, at load 5 to 12. The patch doesn't touch the discord
|
||||||
|
package. I count it green.
|
||||||
|
|
||||||
|
## B1 (blocking): a due date with milliseconds reads back as a person's edit
|
||||||
|
|
||||||
|
`due()` accepts any canonical ISO time with milliseconds. Vikunja v2.7.0
|
||||||
|
stores due dates to the second. On the pinned image (`r1-vk-due-probe.txt`):
|
||||||
|
|
||||||
|
```
|
||||||
|
patch due .456Z (answer) 200 "2026-12-01T00:00:00.456Z"
|
||||||
|
read after patch 200 "2026-12-01T00:00:00Z"
|
||||||
|
```
|
||||||
|
|
||||||
|
`task.schedule` with `2026-12-01T09:00:00.456Z` records a self snapshot
|
||||||
|
with `.456Z` (`work.expect`). The next poll reads `.000Z` after
|
||||||
|
`digest.mjs` normalizes it, and records `task.changed.external` with
|
||||||
|
`changed: ["due_date"]`. The brief keeps that event "for a person's edit".
|
||||||
|
Probe D1 shows this with the fake truncating due dates the way Vikunja
|
||||||
|
does. D2 is the whole-second control, with no external event. D1 also
|
||||||
|
shows:
|
||||||
|
|
||||||
|
- The digest returned to the pm no longer matches, so its next verb with
|
||||||
|
`expect` set to that digest refuses `task-conflict`.
|
||||||
|
- Scheduling the same due date again sends another PATCH, because
|
||||||
|
`.456Z` never equals the stored `.000Z`.
|
||||||
|
- By inspection, an uncertain PATCH's re-read checks
|
||||||
|
`a.fields.due_date === iso`. That never matches, so a landed write
|
||||||
|
reports `write-uncertain`.
|
||||||
|
|
||||||
|
Agents write millisecond ISO times by default (`new Date().toISOString()`),
|
||||||
|
so this case is ordinary, not an edge. `task.create` is unaffected,
|
||||||
|
because it records what it reads back.
|
||||||
|
|
||||||
|
Fix: refuse a due date whose milliseconds aren't `.000`, or truncate to the
|
||||||
|
second in `due()` before it is used. Also have `FakeVikunja` store due dates
|
||||||
|
to the second, so a test can show the fix.
|
||||||
|
|
||||||
|
## B2 (blocking): a landed write with a failed final read
|
||||||
|
|
||||||
|
In `handle`, a final `read(id)` that throws after every step succeeded
|
||||||
|
rethrows the read's refusal. Probe W1 faults the GET after the assignee
|
||||||
|
POST:
|
||||||
|
|
||||||
|
```
|
||||||
|
W1 caller sees tracker-unavailable
|
||||||
|
W1 coder assigned in tracker true
|
||||||
|
W1 action.allowed task.assign 1
|
||||||
|
W1 task.assigned events 0 self snapshots 1 (the 1 is from create)
|
||||||
|
W1 poll external events [["assignees"]]
|
||||||
|
W1 caller retries assign -> task-assigned
|
||||||
|
```
|
||||||
|
|
||||||
|
- The write landed and the authority was spent.
|
||||||
|
- The caller is told `tracker-unavailable`, which the README documents as
|
||||||
|
a read failure, so a retry looks safe. It then refuses `task-assigned`.
|
||||||
|
- No `task.assigned` event and no self snapshot are written. The next
|
||||||
|
poll records the bot's own assign as a person's edit.
|
||||||
|
|
||||||
|
The same happens when some steps landed, a later step failed, and the
|
||||||
|
final read also fails: the step's refusal is thrown and nothing is
|
||||||
|
recorded.
|
||||||
|
|
||||||
|
Fix: when the final read fails, still record what is known. On success
|
||||||
|
that means the event and a snapshot of `work.expect(before.fields)`.
|
||||||
|
`before.updated` is the best `updated` available, or the column can be
|
||||||
|
null. Then refuse with `write-uncertain`, or return. If the steps
|
||||||
|
partly landed, at least refuse `write-uncertain` rather than a read
|
||||||
|
refusal. Add a test that faults the final GET.
|
||||||
|
|
||||||
|
## Asked for in round 2 (not blocking on their own)
|
||||||
|
|
||||||
|
- **T1, a test for the success snapshot.** Mutant V9 records the final
|
||||||
|
read's fields instead of `work.expect(before.fields)`, and survives. The
|
||||||
|
code comment states why: so a concurrent edit still shows as external
|
||||||
|
on the next poll. One test with a UI edit between the last step and the
|
||||||
|
final read would hold it.
|
||||||
|
- **T2, a test for `task.created` after a failed label write.** Mutant V21
|
||||||
|
drops the event when a later step failed, and survives. The comment says
|
||||||
|
the event is recorded anyway because the task exists.
|
||||||
|
- **T3, a test for the comment window on the first look.** Mutant S5
|
||||||
|
counts every old comment on the first look at a task, and survives. After
|
||||||
|
a restart, that replays every comment ever made as new.
|
||||||
|
|
||||||
|
## Notes (not blocking)
|
||||||
|
|
||||||
|
1. **An uncertain create** gives `write-uncertain` and records nothing.
|
||||||
|
This is documented and tested. The poll later reports the task as
|
||||||
|
external with `previous: null`.
|
||||||
|
2. **One bad task in `missing()` fails the whole tick.** Any status other
|
||||||
|
than 200, 404/4002 or 403 throws. Probe M1 shows a 500 on one task
|
||||||
|
gives `tracker-unavailable`, and nothing from that tick is recorded. The
|
||||||
|
next tick recovers (T1). A shape failure on one task would repeat every
|
||||||
|
tick, so one odd task could stop the poll until someone fixes it.
|
||||||
|
3. **A relation's target is checked by its ref only.** Probe R1 relates
|
||||||
|
task 1 to task 2, which lives in another project, through
|
||||||
|
`vikunja:<this project>/2`. The verb succeeds and the relation is made.
|
||||||
|
That needs the pm bot shared on the other project, so this records the
|
||||||
|
boundary only.
|
||||||
|
4. **`task.conflict` costs no authority.** Probe C1: a reviewer, with no
|
||||||
|
`task.scope.change` authority, writes a `task.conflict` event by passing
|
||||||
|
a wrong `expect`. With the right digest it refuses `decision-required`.
|
||||||
|
A role can add conflict events to any task in its business without
|
||||||
|
holding the verb.
|
||||||
|
5. **No size cap on response bodies** in `client()`. The tracker is
|
||||||
|
127.0.0.1 and owned, so this is low risk.
|
||||||
|
6. **The first reconcile** records every existing task as external with
|
||||||
|
`previous: null`. This is intended and tested.
|
||||||
|
7. **`secretCheck` on the result** runs after the write. A secret-shaped
|
||||||
|
value in a task title would report a completed write as a refusal. It
|
||||||
|
fails closed, but it has B2's shape.
|
||||||
|
8. **Socket timeout.** A verb queued behind a long tick can pass the 60 s
|
||||||
|
socket timeout. The caller gets outcome-unknown, which is honest.
|
||||||
|
9. **The fake keeps millisecond due dates.** Vikunja doesn't. B1's fix
|
||||||
|
should close this.
|
||||||
|
10. **startup:** a 401 on the views or buckets list maps to
|
||||||
|
`tracker-unavailable`, not `tracker-unauthorized`.
|
||||||
|
11. **A worker token can write pm fields.** The token scope (`tasks
|
||||||
|
update`) covers them, and only the adapter's field-writer check guards.
|
||||||
|
This is the addendum's design, recorded here as the boundary.
|
||||||
|
12. **Rehearsal log.** 34 lines, every step as expected. No bearer header,
|
||||||
|
token value or 64-hex string. `run.mjs` also refuses to write the log
|
||||||
|
if a token appears in it.
|
||||||
|
13. **Compose.** `docker compose config` with the variables set gives the
|
||||||
|
pinned digest, `host_ip: 127.0.0.1` and `user: uid:gid`. Without them
|
||||||
|
it refuses.
|
||||||
|
14. **Darkwing's follow-ups** (the S1 `baseUrl` path, the close verdict
|
||||||
|
check, coder reassign and the comment bound): I agree they stay out of
|
||||||
|
this row.
|
||||||
|
|
||||||
|
## Mutants
|
||||||
|
|
||||||
|
42 of 63 killed. 21 survived. None of the survivors is a defect by itself,
|
||||||
|
but several sit on paths the code comments call out.
|
||||||
|
|
||||||
|
| Mutant | Result |
|
||||||
|
|---|---|
|
||||||
|
| V1 to V8, V10, V11, V13 to V18, V22 | killed |
|
||||||
|
| S1, S2, S4, S8, S11 | killed |
|
||||||
|
| U1 to U4, U8 | killed |
|
||||||
|
| A1 to A6 | killed |
|
||||||
|
| K2, K4 | killed |
|
||||||
|
| B1 to B7 | killed |
|
||||||
|
| V9 success snapshot from the final read | **survived** (T1) |
|
||||||
|
| V12 unassign every bot, not only those held | survived; test gap, the fake answers 204 for an absent assignee, as Vikunja does |
|
||||||
|
| V19 remove labels not on the task | survived; test gap, the fake models the 403 but no test removes an absent label |
|
||||||
|
| V20 scope PATCH sends unchanged fields | survived; near-equivalent, the PATCH carries values already there |
|
||||||
|
| V21 no `task.created` after a failed step | **survived** (T2) |
|
||||||
|
| V23 open task not on the board passes | survived; test gap, no test reads a task mid-move |
|
||||||
|
| V24 `read` drops the project check | survived; near-equivalent, a moved task is off this board, so V23's check still refuses it |
|
||||||
|
| S3 cursor window 0 | survived; test gap, the fake has no bump lag |
|
||||||
|
| S5 first look counts every comment | **survived** (T3) |
|
||||||
|
| S6 `missing()` drops the newer-self skip | survived; test gap, needs a verb racing a tick |
|
||||||
|
| S7 open cursor hit off the board treated as done | survived; test gap, needs a move racing the board read |
|
||||||
|
| S9 `walk` drops the project check | survived; test gap, the fake only lists the project's tasks |
|
||||||
|
| S10 board copy always used over the cursor copy | survived; near-equivalent with the fake, which serves one state to both reads |
|
||||||
|
| U5 expired credential passes startup | survived; test gap. The comment says Vikunja accepts a past expiry, so this check is the only guard. A test is short |
|
||||||
|
| U6 default bucket not checked | survived; test gap |
|
||||||
|
| U7 bucket mode not checked | survived; test gap |
|
||||||
|
| K1 `redirect: 'follow'` | survived; test gap, the fake never redirects |
|
||||||
|
| K3 a 404 without code 4002 counts as not-found | survived; test gap |
|
||||||
|
| G1 due date not normalized | survived; this is B1's fidelity gap, the fake never returns a whole-second due date |
|
||||||
|
| G2 labels not sorted | survived; test gap, the fake returns labels in the order added, and no test adds them out of id order |
|
||||||
|
| B8 transient human cap kept after a task verb | survived; test gap |
|
||||||
|
|
||||||
|
## Files
|
||||||
|
|
||||||
|
- `probe.test.mjs`, `vkprobe.mjs`, `mutants.sh`, `gate.sh`, `discord-loop.sh`
|
||||||
|
- Output:
|
||||||
|
- `r1-probe.txt`
|
||||||
|
- `r1-vk-due-probe.txt`
|
||||||
|
- `r1-mut-summary.txt`
|
||||||
|
- `r1-node24.txt`
|
||||||
|
- `r1-gate-summary.txt`
|
||||||
|
- `r1-cand-*.txt` and `r1-base-*.txt` (each suite, teed)
|
||||||
@@ -0,0 +1,31 @@
|
|||||||
|
// Filbert, row 38 r1: does Vikunja v2.7.0 keep milliseconds in due_date? Scratch container on
|
||||||
|
// 127.0.0.1 only. The password and token stay in memory; nothing here prints them.
|
||||||
|
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 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 password = randomBytes(18).toString('base64url');
|
||||||
|
must(await api('POST', '/register', { username: 'filbert-probe', email: '[email protected]', password }), 'register');
|
||||||
|
const O = must(await api('POST', '/login', { username: 'filbert-probe', password }), 'login').token;
|
||||||
|
const P = must(await api('POST', '/projects', { title: 'probe' }, O), 'project').id;
|
||||||
|
const show = (label, r) => console.log(label.padEnd(34), r.status, JSON.stringify(r.json?.due_date ?? r.json));
|
||||||
|
const c = await api('POST', `/projects/${P}/tasks`, { title: 'D', due_date: '2026-11-01T10:20:30.123Z' }, O);
|
||||||
|
show('create due .123Z (answer)', c);
|
||||||
|
const id = c.json.id;
|
||||||
|
show('read after create', await api('GET', `/tasks/${id}`, undefined, O));
|
||||||
|
show('patch due .456Z (answer)', await api('PATCH', `/tasks/${id}`, { due_date: '2026-12-01T00:00:00.456Z' }, O));
|
||||||
|
show('read after patch', await api('GET', `/tasks/${id}`, undefined, O));
|
||||||
|
show('patch due .000Z (answer)', await api('PATCH', `/tasks/${id}`, { due_date: '2026-12-01T00:00:00.000Z' }, O));
|
||||||
|
show('read after .000Z', await api('GET', `/tasks/${id}`, undefined, O));
|
||||||
|
const l = await api('GET', `/projects/${P}/tasks`, undefined, O);
|
||||||
|
console.log('list due_date'.padEnd(34), l.status, JSON.stringify(l.json?.items?.map((t) => t.due_date)));
|
||||||
Reference in New Issue
Block a user