work: Rocko row 114 round 1 packet (#1608), candidate manifest 68c60a799146

Co-Authored-By: Claude Opus 5.5 <[email protected]>
This commit is contained in:
2026-10-14 15:39:45 -05:00
co-authored by Claude Opus 5.5
parent 19c6ab6793
commit a94a943c92
39 changed files with 1845 additions and 0 deletions
+116
View File
@@ -0,0 +1,116 @@
# Row 114 — Discord connector unit on a pinned copy (#1608)
Rocko, GPT-6.1-Sol. Reviewer Filbert. Base 72c9ac36c, after row108's
landing (base.txt). Detached /mnt/storage/src/mosaic-stack-rocko-114.
Started rev1185; Sage assigned sage-0228 and granted slot sage-0229.
No candidate code commit, push or live Discord action.
## Implementation and choices
Service render/install --copy selects a full 40-character lowercase id
under the shared XDG_DATA_HOME/mosaic-stack/copies location or --copies.
A missing CLI or pi refuses exit1 before unit-directory/temp creation.
Explicit empty id is distinct from an omitted flag and also refuses.
Copy directory/node paths use the same plain path alphabet as the other
service scripts. Without --copy, renderer produces the original checkout
bytes; install names the checkout and recommends --copy.
The template is deliberately untouched. Rendering first substitutes the
same original README/PATH/checkout placeholders, then changes only
WorkingDirectory and ExecStart when --copy is given. ExecStart calls Node
on the copy's CLI directly, --supervised --repo copy. Copies have neither
the discord.sh wrapper nor the systemd template, and require neither at
runtime. Every other rendered line, including docs, restart/brake/stop,
PATH and NoNewPrivileges, remains byte-identical. Test compares all such
lines; out/checkout-render-before/after.txt also compare exactly.
Status reads actual WorkingDirectory from the user manager, prints its tree
and copy id or checkout path, including effective drop-ins. It does not
infer the tree from an install flag. Tests use a stub manager and isolated
HOME/config paths; no real unit/binding/token is read by this build.
CLI accepts --offline only for check. It executes the original prepare,
readToken and local reporting paths, returns before constructing REST and
gateway, and ends with an explicit no-network line. The existing local
checks include binding, context against --repo, tool roots, pi/token file
and journal/STOP reporting. It retains the normal check's diagnostic STOP
behavior: reports presence, does not clear STOP/create a journal/claim a
run or launch pi. Online check and the run body are unchanged. Run with
--offline refuses usage before reading config.
## Evidence and correction
New focused tests initially refused the unrecognized flags/missing status.
After implementation, the positive offline test's overly plain synthetic
token failed the existing token-format check. Changed only fixture content
to the existing helper's non-live dummy value; no validation weakening.
Five focused tests pass (out/green-final.txt). The offline fixture traps
fetch/WebSocket, marks attempted network calls, and proves offline passes
without attempts while normal check still reaches REST and is trapped.
Bad0644 token mode, missing context under selected repo and run flag misuse
are pinned. Copy tests pin identical legacy bytes, unchanged other lines,
empty/short/missing id, missing CLI/pi without any unit, and effective cwd.
Shell cases also cover temporary --dir --no-reload installs, all required
refusals, status and wrapper offline/run misuse.
The three prescribed mutants run independently in own process groups;
timeouts kill the whole group, and source restores in finally. They
reintroduce checkout ExecStart, remove pi existence refusal and make offline
call getMe. Final gate/count/mutation and process/network receipts follow.
DOCKER_HOST=unix:///nonexistent.sock; Docker-dependent cases explicitly skip.
## Protected live plan — unexecuted
This patch touches packages/discord; Sage must notify Mos before and after
landing, and every intervening Discord landing stays flagged while the
live connector runs the checkout. Live switching waits for the SetSpark
freeze to lift and Mos's separate window. Sage prepares a pushed copy;
Mos's operator runs Node on that copy's cli.mjs check binding --repo copy
--offline (the copy has no discord.sh), preserves the prior unit, installs
--copy and reloads/restarts only on Mos's go. Binding context.files resolve
inside the copy, so uncopied files refuse. Rollback restores the saved unit,
daemon-reloads/restarts on Mos's go and keeps both copies. No live check,
REST call, unit install/enable/reload/restart or private binding/token access
performed; fixtures only. stack-copy.sh/check/COPY_PATHS remain untouched;
connector pin is independent. Decision137 doc-only review fixes ship in a
separate packet after landing.
## Final gate
| Suite | Passed | Failure / coverage |
| --- | ---: | --- |
| Discord node | 183 | 0 failed, skipped or cancelled |
| test-auth.sh | 15 | 0 |
| test-conductor.sh | 17 | 0 |
| test-config.sh | 24 | 0 |
| test-discord.sh | 78 | 0; includes Discord183 |
| test-extension-package.sh | 18 | 0 |
| test-foundation.sh | 44 | 0; includes foundation node80 |
| test-land-work.sh | 84 | 0 |
| test-queue.sh | 28 | 0; includes queue node156; canonical-root verify/render skipped |
| test-release.sh | 4 | 0; Docker state machine skipped |
| test-task.sh | 26 | 0; four Docker blocks skipped |
Gate ran20:30:20-20:31:48 UTC, all11 steps exit0. Dead Docker socket for
all commands; no Docker/provider coverage claimed. Task skips adapter/
mission/retry/skills/roles/seat/policy/user injection, workspace/capability,
live task/fork and live user recall blocks. No observed recurrence of the
reported gate faults. Source hashes and diff/patch checks pass after mutants.
| Mutant | Exit | Result |
| --- | ---: | --- |
| checkout-execstart | 1 | killed |
| missing-pi-accepted | 1 | killed |
| offline-calls-getMe | 1 | killed |
3/3 prescribed mutants killed, no timeout. All runners exited; process
sweep exit0 busy[]. Docker inspect explicitly reported the named network
mosaic-stack-rocko-114_default not found. No removal needed. Slot returned
20:32:57 UTC; no subsequent tests. Final manifest7/7 verified:
68c60a79914692f3f6d4923f7bcff41de109ccca33bf6fba687fb88efa24bcac.
Claimant in-review move rev1186, round1; expected missing-token request
failure rev1187, command exit1 (out/queue-in-review.txt). Sage requests
as jarvis, Filbert reviews. No protected live step executed.
Own logs-only commit: 618d71ebbb48d2a36512f84ca1c29624289a4423. No candidate source commit.
+1
View File
@@ -0,0 +1 @@
72c9ac36c33ebb43bf0585bc3ae797969f5ae112
+392
View File
@@ -0,0 +1,392 @@
diff --git a/docs/TOOLS.md b/docs/TOOLS.md
index 20be532e9..fc38cefab 100644
--- a/docs/TOOLS.md
+++ b/docs/TOOLS.md
@@ -469,7 +469,19 @@ user entry may carry `channels`, an allowlist of listed channel ids.
`scripts/discord-service.sh install` renders and writes the systemd user
unit `[email protected]` (one instance per binding, restart on
failure, exit 3 never retried, SIGTERM on `systemctl --user stop`, SIGHUP on
-`systemctl --user reload`). An optional `tools` key in the binding declares
+`systemctl --user reload`). With
+`install --copy ID [--copies DIR]`, a full id selects an existing shared
+copy holding the Discord CLI and pi; ExecStart runs Node on that CLI with
+--repo and WorkingDirectory in the copy. Without --copy the checkout unit
+is unchanged. Status names its effective cwd/copy id, including drop-ins.
+`check --offline` validates local binding/context/tool roots/pi/token mode
+and reports journal/STOP without REST or gateway calls; run refuses this
+flag. For a Mos-approved re-pin after the SetSpark freeze: make the copy,
+run `node <copy>/packages/discord/src/cli.mjs check <binding> --repo <copy> --offline`,
+save the installed unit, install --copy, then restart on Mos's go. Context
+files resolve inside the copy and missing files refuse. Rollback restores
+the saved unit and daemon-reloads/restarts on Mos's go; keep both copies.
+The connector's pin is independent of stack-copy.sh check. An optional `tools` key in the binding declares
file roots; pi then runs with its own tools off and the package's
extension providing `list_dir`, `read_file` and `search` confined to those
roots, plus `write_file` and `edit_file` for roots marked `"write": true`
diff --git a/packages/discord/README.md b/packages/discord/README.md
index 8f235b556..29b9fc7cc 100644
--- a/packages/discord/README.md
+++ b/packages/discord/README.md
@@ -17,7 +17,7 @@ CHAT-03 conversation controller exists.
## Commands
```
-scripts/discord.sh check <binding>
+scripts/discord.sh check <binding> [--offline]
scripts/discord.sh run <binding> [--supervised]
scripts/discord.sh stop <binding>
scripts/discord.sh unlock <binding>
@@ -34,6 +34,9 @@ scripts/discord-service.sh render | install | uninstall | status <binding>
every listed channel over REST; opens one gateway connection, waits for
READY, closes it. Nothing is sent to a channel. Close code 4014 is reported
as the message-content intent not being granted in the developer portal.
+ `check --offline` stops after local binding/context/tool roots/pi/token-mode
+ validation and journal/STOP reporting. It makes no REST or gateway call
+ and says so in its last line. `run --offline` is a usage error.
- `run` refuses when `STOP` exists or an unresolved delivery cannot be
reconciled. Otherwise it starts pi, connects, and serves turns until
SIGTERM, SIGINT or `stop`. Run it under the service unit below, or by hand
@@ -121,6 +124,25 @@ at runtime, as before. Surviving logout and reboot needs
`uninstall` refuses while an instance is active. The log goes to journald:
`run` writes no message text to stderr, only ids, counts and state.
+Pinned install: `scripts/discord-service.sh install --copy ID [--copies DIR]`
+uses a full 40-character copy id under the shared mosaic-stack/copies
+location. It requires the copy's Discord CLI and node_modules/.bin/pi,
+runs Node directly on that CLI with --repo and WorkingDirectory set to
+the copy, and retains the unit's restart, brake and stop behavior. Without
+--copy the unit still runs the checkout; install explicitly recommends
+pinning. Status reads effective WorkingDirectory, including drop-ins, and
+prints the tree and copy id or checkout path.
+
+Every live step requires Mos's go after the SetSpark freeze lifts. Re-pin:
+make the new pushed copy with stack-copy.sh; run
+`node <copy>/packages/discord/src/cli.mjs check <binding> --repo <copy> --offline`;
+preserve the old installed unit file; install --copy ID; reload/restart on
+Mos's go. The copy has no scripts/discord.sh, so use its CLI directly.
+Binding context.files resolve against the copy: a file not copied refuses
+at start/check. Rollback restores the saved unit file, daemon-reloads and
+restarts only on Mos's go; retain both copies. This connector pin is
+independent of the bus/Console/CLI pin and stack-copy.sh check.
+
## The binding
Deployment policy for one seat on one server. It carries Discord ids of real
diff --git a/packages/discord/src/cli.mjs b/packages/discord/src/cli.mjs
index 045f4918e..ca8c8bdde 100755
--- a/packages/discord/src/cli.mjs
+++ b/packages/discord/src/cli.mjs
@@ -1,6 +1,6 @@
#!/usr/bin/env node
// Usage:
-// mosaic-discord check <binding> [--config PATH] [--repo PATH]
+// mosaic-discord check <binding> [--config PATH] [--repo PATH] [--offline]
// mosaic-discord run <binding> [--config PATH] [--repo PATH] [--supervised]
// mosaic-discord stop <binding> [--config PATH]
// mosaic-discord unlock <binding> [--config PATH]
@@ -61,7 +61,7 @@ import { createConnector } from "./connector.mjs";
import { ensureJournal, requestStop, stopRequested, readPid, stopTarget, writePid, clearPid, unlock, recover, appendReload, BRAKE_EXIT } from "./journal.mjs";
const USAGE = [
- "usage: mosaic-discord check <binding> [--config PATH] [--repo PATH]",
+ "usage: mosaic-discord check <binding> [--config PATH] [--repo PATH] [--offline]",
" mosaic-discord run <binding> [--config PATH] [--repo PATH] [--supervised]",
" mosaic-discord stop <binding> [--config PATH]",
" mosaic-discord unlock <binding> [--config PATH]",
@@ -70,7 +70,7 @@ const USAGE = [
].join("\n");
function parse(argv) {
- const opts = { command: null, binding: null, config: defaultConfigPath(), repo: process.cwd(), supervised: false };
+ const opts = { command: null, binding: null, config: defaultConfigPath(), repo: process.cwd(), supervised: false, offline: false };
for (let i = 0; i < argv.length; i++) {
const a = argv[i];
if (a === "--config" || a === "--repo") {
@@ -80,6 +80,8 @@ function parse(argv) {
opts.command = "help";
} else if (a === "--supervised") {
opts.supervised = true;
+ } else if (a === "--offline") {
+ opts.offline = true;
} else if (a.startsWith("--")) throw new DiscordError(`unknown argument: ${a}\n${USAGE}`, 4);
else if (opts.command === null) opts.command = a;
else if (opts.binding === null) opts.binding = a;
@@ -89,6 +91,7 @@ function parse(argv) {
if (!["check", "run", "stop", "unlock", "recover", "reload"].includes(opts.command)) throw new DiscordError(USAGE, 4);
if (opts.binding === null) throw new DiscordError(`${opts.command} needs a binding name\n${USAGE}`, 4);
if (opts.supervised && opts.command !== "run") throw new DiscordError(`--supervised applies to run only\n${USAGE}`, 4);
+ if (opts.offline && opts.command !== "check") throw new DiscordError(`--offline applies to check only\n${USAGE}`, 4);
return opts;
}
@@ -123,6 +126,11 @@ async function check(opts) {
say(toolRoots ? `tools: ${enabledToolNames(toolRoots).join(",")}; roots ${toolRoots.roots.map((r) => `${r.name}=${r.path}${r.write ? " (writable)" : ""}`).join(" ")}, ${toolRoots.maxCallsPerTurn} calls/message, ${toolRoots.maxFileBytes} bytes/file${toolRoots.web ? `, web via ${toolRoots.web.searxng} (${toolRoots.web.maxFetchBytes} bytes/page)` : ""}${toolRoots.setspark ? `, setspark via ${toolRoots.setspark.baseUrl} as ${toolRoots.setspark.principal}` : ""}` : "tools: none");
say(`token file mode 0600 ok; STOP ${stopRequested(journalDir) ? "PRESENT" : "absent"}`);
+ if (opts.offline) {
+ say("offline check passed; no REST or gateway calls were made");
+ return;
+ }
+
const rest = createRest({ token, log: warn });
const me = await rest.getMe();
if (me.id !== binding.botUserId) throw new DiscordError(`token belongs to bot ${me.id}, binding says ${binding.botUserId}`);
diff --git a/packages/discord/tests/copy-service.test.mjs b/packages/discord/tests/copy-service.test.mjs
new file mode 100644
index 000000000..c2445701f
--- /dev/null
+++ b/packages/discord/tests/copy-service.test.mjs
@@ -0,0 +1,26 @@
+import test from 'node:test';
+import assert from 'node:assert/strict';
+import {spawnSync} from 'node:child_process';
+import {readFileSync,writeFileSync,mkdirSync,unlinkSync,chmodSync,existsSync} from 'node:fs';
+import {join,dirname} from 'node:path';
+import {fileURLToPath} from 'node:url';
+import {makeRoot} from './helpers.mjs';
+const ROOT=fileURLToPath(new URL('../../../',import.meta.url)).replace(/\/$/,'');
+const SCRIPT=join(ROOT,'scripts/discord-service.sh');
+function setup(){const root=makeRoot(),copies=join(root,'copies'),id='a'.repeat(40),copy=join(copies,id),units=join(root,'units');mkdirSync(join(copy,'packages/discord/src'),{recursive:true});mkdirSync(join(copy,'node_modules/.bin'),{recursive:true});writeFileSync(join(copy,'packages/discord/src/cli.mjs'),'// fixture');writeFileSync(join(copy,'node_modules/.bin/pi'),'#!/bin/sh\nexit 0\n',{mode:0o755});const env={PATH:process.env.PATH,HOME:root,XDG_CONFIG_HOME:join(root,'config')};const run=args=>spawnSync(SCRIPT,args,{env,encoding:'utf8',timeout:5000});return {root,copies,id,copy,units,run,env};}
+test('pinned Discord render/install and missing prerequisites; checkout bytes unchanged',()=>{
+ const s=setup(),args=['--copy',s.id,'--copies',s.copies],r=s.run(['render',...args]);assert.equal(r.status,0,r.stderr);
+ assert.ok(r.stdout.includes(`WorkingDirectory=${s.copy}\n`));assert.ok(r.stdout.includes(`ExecStart=${process.execPath} ${s.copy}/packages/discord/src/cli.mjs run %i --supervised --repo ${s.copy}\n`));
+ for(const line of r.stdout.split('\n').filter(l=>/^(ExecStart|WorkingDirectory)=/.test(l)))assert.ok(!line.includes(ROOT),line);
+ const legacy=s.run(['render']);assert.equal(legacy.status,0,legacy.stderr);
+ const nodeDir=dirname(process.execPath),originalPath='/usr/local/bin:/usr/bin:/bin',path=originalPath.split(':').includes(nodeDir)?originalPath:nodeDir+':'+originalPath;
+ const expected=readFileSync(join(ROOT,'packages/discord/systemd/[email protected]'),'utf8').replaceAll('@REPO@',ROOT).replaceAll('@PATH@',path);assert.equal(legacy.stdout,expected);
+ assert.equal(r.stdout.split('\n').filter(l=>!/^(ExecStart|WorkingDirectory)=/.test(l)).join('\n'),legacy.stdout.split('\n').filter(l=>!/^(ExecStart|WorkingDirectory)=/.test(l)).join('\n'));
+ const installed=s.run(['install',...args,'--dir',s.units,'--no-reload']);assert.equal(installed.status,0,installed.stderr);assert.equal(readFileSync(join(s.units,'[email protected]'),'utf8'),r.stdout);
+ for(const id of ['','short','b'.repeat(40)])assert.equal(s.run(['install','--copy',id,'--copies',s.copies,'--dir',join(s.root,'missing'),'--no-reload']).status,1);
+ assert.ok(!existsSync(join(s.root,'missing/[email protected]')));
+ const cli=join(s.copy,'packages/discord/src/cli.mjs');unlinkSync(cli);assert.equal(s.run(['install',...args,'--dir',join(s.root,'missing-cli'),'--no-reload']).status,1);assert.ok(!existsSync(join(s.root,'missing-cli/[email protected]')));writeFileSync(cli,'// fixture');unlinkSync(join(s.copy,'node_modules/.bin/pi'));assert.equal(s.run(['install',...args,'--dir',join(s.root,'missing-pi'),'--no-reload']).status,1);assert.ok(!existsSync(join(s.root,'missing-pi/[email protected]')));
+});
+test('Discord status reports effective WorkingDirectory from a fake user manager',()=>{
+ const s=setup(),bin=join(s.root,'bin');mkdirSync(bin);const script=join(bin,'systemctl');writeFileSync(script,`#!/bin/sh\ncase "$*" in *show*)echo 'WorkingDirectory=${s.copy}';; *)echo inactive;;esac\n`);chmodSync(script,0o755);s.env.PATH=bin+':'+s.env.PATH;const r=s.run(['status','fixture']);assert.equal(r.status,0,r.stderr);assert.ok(r.stdout.includes(`copy: ${s.id}`));assert.ok(r.stdout.includes(`tree: ${s.copy}`));
+});
diff --git a/packages/discord/tests/offline-check.test.mjs b/packages/discord/tests/offline-check.test.mjs
new file mode 100644
index 000000000..ae069322b
--- /dev/null
+++ b/packages/discord/tests/offline-check.test.mjs
@@ -0,0 +1,26 @@
+import test from 'node:test';
+import assert from 'node:assert/strict';
+import {spawnSync} from 'node:child_process';
+import {writeFileSync,chmodSync,unlinkSync,existsSync} from 'node:fs';
+import {join} from 'node:path';
+import {fileURLToPath} from 'node:url';
+import {makeRoot,makeRepo,makeDeployment} from './helpers.mjs';
+const CLI=fileURLToPath(new URL('../src/cli.mjs',import.meta.url));
+function setup(){
+ const root=makeRoot(),repo=makeRepo(root),d=makeDeployment(root,{}, {tokenContent:'MTAw.abcdefghijklmnopqrstuvwxyz0123456789'}),calls=join(root,'network-called'),preload=join(root,'no-network.mjs');
+ writeFileSync(preload,`import {writeFileSync} from 'node:fs';const refuse=()=>{writeFileSync(${JSON.stringify(calls)},'called');throw Error('fixture refuses network')};globalThis.fetch=refuse;globalThis.WebSocket=class{constructor(){refuse()}};`);
+ const run=(command='check',args=[])=>spawnSync(process.execPath,['--import',preload,CLI,command,d.raw.name,'--config',d.config,'--repo',repo,...args],{encoding:'utf8',timeout:5000});
+ return {root,repo,d,calls,run};
+}
+test('check --offline validates local fixture without REST or gateway calls',()=>{
+ const s=setup(),r=s.run('check',['--offline']);assert.equal(r.status,0,r.stderr);assert.match(r.stdout,/token file mode 0600 ok; STOP absent/);assert.match(r.stdout,/offline check passed; no REST or gateway calls were made\n$/);assert.ok(!existsSync(s.calls));
+ // Online check still reaches REST: the trap fails it immediately.
+ const online=s.run();assert.equal(online.status,1);assert.ok(existsSync(s.calls));assert.match(online.stderr,/fixture refuses network/);
+});
+test('check --offline refuses token mode and missing context under the selected repo',()=>{
+ const s=setup();chmodSync(s.d.tokenFile,0o644);const bad=s.run('check',['--offline']);assert.equal(bad.status,2,bad.stderr);assert.match(bad.stderr,/0600/);assert.ok(!existsSync(s.calls));
+ chmodSync(s.d.tokenFile,0o600);unlinkSync(join(s.repo,'contracts/CONSTITUTION.md'));const context=s.run('check',['--offline']);assert.equal(context.status,2,context.stderr);assert.ok(!existsSync(s.calls));
+});
+test('run --offline is a usage refusal before reading config or networking',()=>{
+ const s=setup();unlinkSync(s.d.config);const r=s.run('run',['--offline']);assert.equal(r.status,4,r.stderr);assert.match(r.stderr,/--offline/);assert.ok(!existsSync(s.calls));
+});
diff --git a/scripts/discord-service.sh b/scripts/discord-service.sh
index fd2da1de1..ea726e08e 100755
--- a/scripts/discord-service.sh
+++ b/scripts/discord-service.sh
@@ -6,8 +6,8 @@
# instance per binding (mosaic-discord@<binding>). Nothing here reads the
# binding or the token. See packages/discord/README.md, "Service unit".
#
-# render print the rendered unit on stdout
-# install [--dir DIR] [--no-reload]
+# render [--copy ID [--copies DIR]] print the rendered unit on stdout
+# install [--copy ID [--copies DIR]] [--dir DIR] [--no-reload]
# write the unit (write to a temp file, then
# rename) and run `systemctl --user daemon-reload`
# uninstall [--dir DIR] [--no-reload]
@@ -19,20 +19,44 @@ set -euo pipefail
REPO="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
TEMPLATE="$REPO/packages/discord/systemd/[email protected]"
UNIT_NAME="[email protected]"
-USAGE="usage: scripts/discord-service.sh render | install [--dir DIR] [--no-reload] | uninstall [--dir DIR] [--no-reload] | status <binding>"
+USAGE="usage: scripts/discord-service.sh render [--copy ID [--copies DIR]] | install [--copy ID [--copies DIR]] [--dir DIR] [--no-reload] | uninstall [--dir DIR] [--no-reload] | status <binding>"
die() { echo "discord-service: $*" >&2; exit 1; }
usage() { echo "$USAGE" >&2; exit 4; }
+COPY_ID=""
+COPY_SET=0
+COPIES="${XDG_DATA_HOME:-$HOME/.local/share}/mosaic-stack/copies"
+copy_root() {
+ [[ "$COPY_ID" =~ ^[0-9a-f]{40}$ ]] || die '--copy must be a full 40-character commit id'
+ case "$COPIES" in /*) ;; *) die 'copies directory must be absolute' ;; esac
+ case "$COPIES" in *[!A-Za-z0-9/._+-]*) die 'copies directory contains a character the unit cannot carry' ;; esac
+ local root="$COPIES/$COPY_ID"
+ [ -f "$root/packages/discord/src/cli.mjs" ] || die "copy lacks packages/discord/src/cli.mjs: $root"
+ [ -f "$root/node_modules/.bin/pi" ] || die "copy lacks node_modules/.bin/pi: $root"
+ echo "$root"
+}
+
render() {
[ -f "$TEMPLATE" ] || die "template missing: $TEMPLATE"
+ local root=""
+ [ "$COPY_SET" = 0 ] || root="$(copy_root)" || return 1
local node_dir
node_dir="$(dirname "$(command -v node || true)")"
[ -n "$node_dir" ] && [ "$node_dir" != "." ] || die "node not found on PATH"
local path="/usr/local/bin:/usr/bin:/bin"
case ":$path:" in *":$node_dir:"*) ;; *) path="$node_dir:$path" ;; esac
case "$REPO" in *@*|*'|'*) die "repository path contains a character the template cannot carry: $REPO" ;; esac
- sed -e "s|@REPO@|$REPO|g" -e "s|@PATH@|$path|g" "$TEMPLATE"
+ local unit
+ unit="$(sed -e "s|@REPO@|$REPO|g" -e "s|@PATH@|$path|g" "$TEMPLATE")" || return 1
+ if [ -n "$root" ]; then
+ local node
+ node="$(command -v node)"
+ case "$node" in *[!A-Za-z0-9/._+-]*) die 'node path contains a character the unit cannot carry' ;; esac
+ sed -e "s|^WorkingDirectory=.*$|WorkingDirectory=$root|" -e "s|^ExecStart=.*$|ExecStart=$node $root/packages/discord/src/cli.mjs run %i --supervised --repo $root|" <<< "$unit"
+ else
+ printf '%s\n' "$unit"
+ fi
}
# Parse --dir and --no-reload for install and uninstall.
@@ -41,8 +65,10 @@ RELOAD=1
parse_flags() {
while [ $# -gt 0 ]; do
case "$1" in
- --dir) [ $# -ge 2 ] || usage; UNIT_DIR="$2"; shift 2 ;;
- --no-reload) RELOAD=0; shift ;;
+ --copy) [ "$cmd" != uninstall ] && [ $# -ge 2 ] || usage; COPY_ID="$2"; COPY_SET=1; shift 2 ;;
+ --copies) [ "$cmd" != uninstall ] && [ $# -ge 2 ] || usage; COPIES="$2"; shift 2 ;;
+ --dir) [ "$cmd" != render ] && [ $# -ge 2 ] || usage; UNIT_DIR="$2"; shift 2 ;;
+ --no-reload) [ "$cmd" != render ] || usage; RELOAD=0; shift ;;
*) usage ;;
esac
done
@@ -50,10 +76,12 @@ parse_flags() {
install_unit() {
parse_flags "$@"
+ local rendered
+ rendered="$(render)" || exit 1
mkdir -p "$UNIT_DIR"
local target="$UNIT_DIR/$UNIT_NAME" tmp
tmp="$(mktemp "$UNIT_DIR/.$UNIT_NAME.XXXXXX")"
- render > "$tmp"
+ printf '%s\n' "$rendered" > "$tmp"
chmod 0644 "$tmp"
if [ -f "$target" ] && cmp -s "$tmp" "$target"; then
rm -f "$tmp"
@@ -66,6 +94,11 @@ install_unit() {
systemctl --user daemon-reload || die "daemon-reload failed"
echo "daemon-reload done"
fi
+ if [ -n "$COPY_ID" ]; then
+ echo "copy: $COPY_ID"
+ else
+ echo "checkout: $REPO (use --copy ID to pin the connector)"
+ fi
cat <<MSG
next, for one binding:
systemctl --user enable --now mosaic-discord@<binding> start now and at login
@@ -106,6 +139,17 @@ status_unit() {
echo " enabled: $(systemctl --user is-enabled "$unit" 2>&1 || true)"
echo " active: $(systemctl --user is-active "$unit" 2>&1 || true)"
systemctl --user show "$unit" -p MainPID -p NRestarts -p ExecMainStartTimestamp -p Result 2>/dev/null | sed 's/^/ /'
+ local working id
+ working="$(systemctl --user show "$unit" -p WorkingDirectory --value 2>/dev/null)" || working=""
+ # Accept property-form output too, for systemctl fixtures and older wrappers.
+ working="${working#WorkingDirectory=}"
+ echo " tree: ${working:-absent (WorkingDirectory unavailable)}"
+ id="${working##*/}"
+ if [[ "$id" =~ ^[0-9a-f]{40}$ ]] && [ -f "$working/packages/discord/src/cli.mjs" ]; then
+ echo " copy: $id"
+ elif [ -n "$working" ]; then
+ echo " checkout: $working"
+ fi
local config="${XDG_CONFIG_HOME:-$HOME/.config}/mosaic-dev/config.json"
local data_root
data_root="$(node -e 'const c=JSON.parse(require("fs").readFileSync(process.argv[1],"utf8")); process.stdout.write(String(c.dataRoot||""))' "$config" 2>/dev/null || true)"
@@ -130,7 +174,7 @@ status_unit() {
[ $# -ge 1 ] || usage
cmd="$1"; shift
case "$cmd" in
- render) [ $# -eq 0 ] || usage; render ;;
+ render) parse_flags "$@"; render ;;
install) install_unit "$@" ;;
uninstall) uninstall_unit "$@" ;;
status) status_unit "$@" ;;
diff --git a/scripts/test-discord.sh b/scripts/test-discord.sh
index 9ec4ddda5..109dfdfa5 100755
--- a/scripts/test-discord.sh
+++ b/scripts/test-discord.sh
@@ -209,6 +209,62 @@ env -u USER scripts/discord-service.sh install --dir "$SANDBOX/units-nouser" --n
&& grep -qF "enable-linger $(id -un)" "$SANDBOX/install.nouser"
check "service install with USER unset finishes and names the account for lingering" $?
+# --- pinned copy service, fixture paths only ---
+COPY_ID="aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
+COPIES="$SANDBOX/copies"
+COPY="$COPIES/$COPY_ID"
+mkdir -p "$COPY/packages/discord/src" "$COPY/node_modules/.bin"
+printf '// fixture\n' > "$COPY/packages/discord/src/cli.mjs"
+printf '#!/bin/sh\nexit 0\n' > "$COPY/node_modules/.bin/pi"
+chmod 755 "$COPY/node_modules/.bin/pi"
+scripts/discord-service.sh render --copy "$COPY_ID" --copies "$COPIES" >"$SANDBOX/unit.copy" 2>&1 \
+ && grep -qxF "WorkingDirectory=$COPY" "$SANDBOX/unit.copy" \
+ && grep -qxF "ExecStart=$(command -v node) $COPY/packages/discord/src/cli.mjs run %i --supervised --repo $COPY" "$SANDBOX/unit.copy" \
+ && ! sed -n '/^WorkingDirectory=/p;/^ExecStart=/p' "$SANDBOX/unit.copy" | grep -qF "$REPO_DIR"
+check "pinned render runs copy CLI with copy cwd/repo and no checkout executable" $?
+scripts/discord-service.sh install --copy "$COPY_ID" --copies "$COPIES" --dir "$SANDBOX/pinned-units" --no-reload >"$SANDBOX/pinned-install" 2>&1 \
+ && cmp -s "$SANDBOX/pinned-units/[email protected]" "$SANDBOX/unit.copy"
+check "pinned install writes the rendered unit in a temporary unit directory" $?
+for kind in short missing cli pi; do
+ id="$COPY_ID"
+ case "$kind" in short) id=short;; missing) id=bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb;; cli) rm "$COPY/packages/discord/src/cli.mjs";; pi) printf '// fixture\n' > "$COPY/packages/discord/src/cli.mjs";rm "$COPY/node_modules/.bin/pi";; esac
+ scripts/discord-service.sh install --copy "$id" --copies "$COPIES" --dir "$SANDBOX/refused-$kind" --no-reload >"$SANDBOX/refused-$kind.log" 2>&1
+ rc=$?
+ [ "$rc" -eq 1 ] && [ ! -e "$SANDBOX/refused-$kind/[email protected]" ]
+ check "pinned install refuses $kind prerequisite and writes no unit" $?
+done
+printf '#!/bin/sh\nexit 0\n' > "$COPY/node_modules/.bin/pi"
+mkdir -p "$SANDBOX/fake-bin"
+cat >"$SANDBOX/fake-bin/systemctl" <<EOF_STATUS
+#!/bin/sh
+case "\$*" in *show*) echo 'WorkingDirectory=$COPY';; *)echo inactive;; esac
+EOF_STATUS
+chmod 755 "$SANDBOX/fake-bin/systemctl"
+HOME="$SANDBOX/home" XDG_CONFIG_HOME="$SANDBOX/config-home" PATH="$SANDBOX/fake-bin:$PATH" scripts/discord-service.sh status fixture >"$SANDBOX/status.copy" 2>&1 \
+ && grep -qF "copy: $COPY_ID" "$SANDBOX/status.copy" \
+ && grep -qF "tree: $COPY" "$SANDBOX/status.copy"
+check "status names the copy from fake systemctl effective cwd" $?
+grep -qF "checkout: $REPO_DIR" "$SANDBOX/install.1" && grep -qF -- '--copy' "$SANDBOX/install.1"
+check "checkout install explicitly recommends --copy" $?
+
+# Offline wrapper entrypoint. Only a synthetic token/binding/config are used.
+node - "$SANDBOX" <<'EOF_OFFLINE'
+const fs=require('fs'),p=require('path'),root=process.argv[2];
+const data=p.join(root,'offline-data');fs.mkdirSync(p.join(data,'discord'),{recursive:true});
+const b=JSON.parse(fs.readFileSync('packages/discord/fixtures/binding.example.json','utf8'));
+b.name='offline-fixture';b.tokenFile=p.join(root,'offline-token');b.context.files=['packages/discord/README.md'];delete b.tools;
+fs.writeFileSync(b.tokenFile,'MTAw.abcdefghijklmnopqrstuvwxyz0123456789\n',{mode:0o600});
+fs.writeFileSync(p.join(data,'discord',b.name+'.json'),JSON.stringify(b),{mode:0o600});
+fs.writeFileSync(p.join(root,'offline-config'),JSON.stringify({dataRoot:data}));
+fs.writeFileSync(p.join(root,'offline-trap.mjs'),`globalThis.fetch=()=>{throw Error('network forbidden in offline fixture')};globalThis.WebSocket=class{constructor(){throw Error('gateway forbidden in offline fixture')}};`);
+EOF_OFFLINE
+NODE_OPTIONS="--import=$SANDBOX/offline-trap.mjs" scripts/discord.sh check offline-fixture --config "$SANDBOX/offline-config" --offline >"$SANDBOX/offline.out" 2>&1 \
+ && grep -qF 'offline check passed; no REST or gateway calls were made' "$SANDBOX/offline.out"
+check "wrapper check --offline passes local fixture and makes no network call" $?
+NODE_OPTIONS="--import=$SANDBOX/offline-trap.mjs" scripts/discord.sh run offline-fixture --config "$SANDBOX/offline-config" --offline >"$SANDBOX/offline-run.out" 2>&1
+[ $? -eq 4 ]
+check "wrapper run --offline refuses usage" $?
+
echo
echo "discord suite: $PASS passed, $FAIL failed"
[ "$FAIL" -eq 0 ]
@@ -0,0 +1,7 @@
1cd5abf22a24b985e262a7d0baed7c5bf66e7a3cbafb47c06dd51eace4afe9f6 docs/TOOLS.md
5949914eb943139d18cac183755319e96830ea92be3b33f3bd056536467655d2 packages/discord/README.md
8f18ee5bb7e0c2a049670caf20438cc35c44bcd5333943e9084f0abb6c9e2234 packages/discord/src/cli.mjs
75a84d470cc5cb77f0a9252f8828dfbe29645958f423884846a0ac39d9e51ced packages/discord/tests/copy-service.test.mjs
c56f4f93e66bc979f0e95e52c2ab2a09b149191071de09b8d739b0a0592df8fe packages/discord/tests/offline-check.test.mjs
bc290fcf852930f398b44689f0731d00412c475781b3eae51c0a01beaf5d1102 scripts/discord-service.sh
f5b42da59265a3195fa5c87053dbc3dcdcec9ecc9d5468cbadeb683346e62e34 scripts/test-discord.sh
+7
View File
@@ -0,0 +1,7 @@
docs/TOOLS.md
packages/discord/README.md
packages/discord/src/cli.mjs
packages/discord/tests/copy-service.test.mjs
packages/discord/tests/offline-check.test.mjs
scripts/discord-service.sh
scripts/test-discord.sh
+27
View File
@@ -0,0 +1,27 @@
#!/usr/bin/env python3
"""Run shell suites from a detached worktree, capture every exit."""
import pathlib, subprocess, json, datetime, sys, os
root=pathlib.Path.cwd()
if root == pathlib.Path('/mnt/storage/src/mosaic-stack'):
raise SystemExit('gate refuses canonical checkout')
if subprocess.check_output(['git','branch','--show-current'],text=True).strip():
raise SystemExit('gate requires detached HEAD')
if os.environ.get('DOCKER_HOST') != 'unix:///nonexistent.sock':
raise SystemExit('decision 127 requires DOCKER_HOST=unix:///nonexistent.sock')
out=pathlib.Path(sys.argv[1]); out.mkdir(parents=True,exist_ok=True)
results=[]
commands=[]
for package in [root/'packages/discord']:
tests=sorted(package.glob('tests/**/*.test.mjs'))
if tests: commands.append(('node-'+package.name,['node','--test',*[str(t.relative_to(root)) for t in tests]],600))
for suite in sorted(root.glob('scripts/test-*.sh')):
commands.append((suite.stem,['bash',str(suite)],600))
for name,command,timeout in commands:
began=datetime.datetime.now(datetime.timezone.utc).isoformat()
with (out/(name+'.txt')).open('w') as log:
p=subprocess.run(command,cwd=root,stdout=log,stderr=subprocess.STDOUT,timeout=timeout)
row={'suite':name,'exit':p.returncode,'began':began,'ended':datetime.datetime.now(datetime.timezone.utc).isoformat()}
results.append(row)
(out/'gate-results.json').write_text(json.dumps(results,indent=2)+'\n')
print(json.dumps(row),flush=True)
sys.exit(any(r['exit'] for r in results))
+22
View File
@@ -0,0 +1,22 @@
import pathlib,subprocess,sys,json,os,signal
root=pathlib.Path.cwd();out=pathlib.Path(sys.argv[1]);out.mkdir(parents=True,exist_ok=True)
assert root != pathlib.Path('/mnt/storage/src/mosaic-stack')
assert os.environ.get('DOCKER_HOST')=='unix:///nonexistent.sock'
muts=[
('checkout-execstart','scripts/discord-service.sh','ExecStart=$node $root/packages/discord/src/cli.mjs run %i --supervised --repo $root','ExecStart=$REPO/scripts/discord.sh run %i --supervised','pinned Discord'),
('missing-pi-accepted','scripts/discord-service.sh','[ -f "$root/node_modules/.bin/pi" ] || die',': || die','pinned Discord'),
('offline-calls-getMe','packages/discord/src/cli.mjs','if (opts.offline) {','if (false) {','check --offline validates'),
]
results=[]
for name,file,old,new,pattern in muts:
p=root/file;original=p.read_text();assert original.count(old)==1,(name,original.count(old))
try:
p.write_text(original.replace(old,new,1))
with (out/(name+'.txt')).open('w') as log:
child=subprocess.Popen(['node','--test','--test-name-pattern='+pattern,'packages/discord/tests/copy-service.test.mjs' if name!='offline-calls-getMe' else 'packages/discord/tests/offline-check.test.mjs'],stdout=log,stderr=subprocess.STDOUT,start_new_session=True)
try:code=child.wait(timeout=60);status='killed' if code else 'survived'
except subprocess.TimeoutExpired:os.killpg(child.pid,signal.SIGKILL);child.wait();code=124;status='timeout'
row={'name':name,'exit':code,'status':status};results.append(row);print(json.dumps(row),flush=True)
(out/'summary.json').write_text(json.dumps(results,indent=2)+'\n')
finally:p.write_text(original)
sys.exit(any(r['status']!='killed' for r in results))
@@ -0,0 +1,39 @@
# Mosaic Discord connector, one instance per binding: mosaic-discord@<binding>.
# Rendered by scripts/discord-service.sh from packages/discord/systemd/;
# /mnt/storage/src/mosaic-stack-rocko-114 and /usr/local/bin:/usr/bin:/bin are filled in at install time. Edit the template and
# reinstall; do not edit the installed copy.
[Unit]
Description=Mosaic Discord connector (%i)
Documentation=file:///mnt/storage/src/mosaic-stack-rocko-114/packages/discord/README.md
After=network-online.target
Wants=network-online.target
StartLimitIntervalSec=600
StartLimitBurst=5
[Service]
Type=simple
WorkingDirectory=/mnt/storage/src/mosaic-stack-rocko-114
Environment=PATH=/usr/local/bin:/usr/bin:/bin
# A supervised run first clears a lock left by a crash or a reboot, and
# refuses with exit 3 while STOP is present or the binding is held. Exit 3
# is never retried: the brake belongs to the operator. This happens inside
# the main process on purpose: systemd honours RestartPreventExitStatus only
# for the main process, so a refusing ExecStartPre would loop.
ExecStart=/mnt/storage/src/mosaic-stack-rocko-114/scripts/discord.sh run %i --supervised
# `systemctl --user reload` re-reads the binding in place (channels, users,
# limits); the connector refuses and keeps the old binding when the file is
# invalid or a fixed key changed. See reloads.jsonl under the journal.
ExecReload=/bin/kill -HUP $MAINPID
Restart=on-failure
RestartSec=15
RestartPreventExitStatus=3
# `systemctl --user stop` sends SIGTERM; the connector finishes the turn in
# flight, then exits 0. The stop timeout covers the largest turn timeout a
# binding may set (3600 s) plus margin; a normal stop takes seconds.
KillSignal=SIGTERM
KillMode=mixed
TimeoutStopSec=3700
NoNewPrivileges=yes
[Install]
WantedBy=default.target
@@ -0,0 +1,39 @@
# Mosaic Discord connector, one instance per binding: mosaic-discord@<binding>.
# Rendered by scripts/discord-service.sh from packages/discord/systemd/;
# /mnt/storage/src/mosaic-stack-rocko-114 and /usr/local/bin:/usr/bin:/bin are filled in at install time. Edit the template and
# reinstall; do not edit the installed copy.
[Unit]
Description=Mosaic Discord connector (%i)
Documentation=file:///mnt/storage/src/mosaic-stack-rocko-114/packages/discord/README.md
After=network-online.target
Wants=network-online.target
StartLimitIntervalSec=600
StartLimitBurst=5
[Service]
Type=simple
WorkingDirectory=/mnt/storage/src/mosaic-stack-rocko-114
Environment=PATH=/usr/local/bin:/usr/bin:/bin
# A supervised run first clears a lock left by a crash or a reboot, and
# refuses with exit 3 while STOP is present or the binding is held. Exit 3
# is never retried: the brake belongs to the operator. This happens inside
# the main process on purpose: systemd honours RestartPreventExitStatus only
# for the main process, so a refusing ExecStartPre would loop.
ExecStart=/mnt/storage/src/mosaic-stack-rocko-114/scripts/discord.sh run %i --supervised
# `systemctl --user reload` re-reads the binding in place (channels, users,
# limits); the connector refuses and keeps the old binding when the file is
# invalid or a fixed key changed. See reloads.jsonl under the journal.
ExecReload=/bin/kill -HUP $MAINPID
Restart=on-failure
RestartSec=15
RestartPreventExitStatus=3
# `systemctl --user stop` sends SIGTERM; the connector finishes the turn in
# flight, then exits 0. The stop timeout covers the largest turn timeout a
# binding may set (3600 s) plus margin; a normal stop takes seconds.
KillSignal=SIGTERM
KillMode=mixed
TimeoutStopSec=3700
NoNewPrivileges=yes
[Install]
WantedBy=default.target
@@ -0,0 +1 @@
7/7 hashes match; patch equals final detached diff; detached base matches; checkout before/after renders match; canonical patch applies and diff clean; own logs-only commit 618d71ebbb48d2a36512f84ca1c29624289a4423; protected live actions unexecuted.
@@ -0,0 +1,68 @@
[
{
"suite": "node-discord",
"exit": 0,
"began": "2026-10-14T20:30:20.823230+00:00",
"ended": "2026-10-14T20:30:23.493776+00:00"
},
{
"suite": "test-auth",
"exit": 0,
"began": "2026-10-14T20:30:23.494015+00:00",
"ended": "2026-10-14T20:30:24.109265+00:00"
},
{
"suite": "test-conductor",
"exit": 0,
"began": "2026-10-14T20:30:24.109530+00:00",
"ended": "2026-10-14T20:30:30.287430+00:00"
},
{
"suite": "test-config",
"exit": 0,
"began": "2026-10-14T20:30:30.287694+00:00",
"ended": "2026-10-14T20:30:31.955466+00:00"
},
{
"suite": "test-discord",
"exit": 0,
"began": "2026-10-14T20:30:31.955716+00:00",
"ended": "2026-10-14T20:30:38.736178+00:00"
},
{
"suite": "test-extension-package",
"exit": 0,
"began": "2026-10-14T20:30:38.736435+00:00",
"ended": "2026-10-14T20:30:40.052900+00:00"
},
{
"suite": "test-foundation",
"exit": 0,
"began": "2026-10-14T20:30:40.053145+00:00",
"ended": "2026-10-14T20:31:17.005136+00:00"
},
{
"suite": "test-land-work",
"exit": 0,
"began": "2026-10-14T20:31:17.005438+00:00",
"ended": "2026-10-14T20:31:25.637399+00:00"
},
{
"suite": "test-queue",
"exit": 0,
"began": "2026-10-14T20:31:25.637691+00:00",
"ended": "2026-10-14T20:31:45.992880+00:00"
},
{
"suite": "test-release",
"exit": 0,
"began": "2026-10-14T20:31:45.993134+00:00",
"ended": "2026-10-14T20:31:46.157295+00:00"
},
{
"suite": "test-task",
"exit": 0,
"began": "2026-10-14T20:31:46.157584+00:00",
"ended": "2026-10-14T20:31:48.126073+00:00"
}
]
@@ -0,0 +1,191 @@
✔ approvals: a request is validated before anything is posted; the rendering shows names and never ids (7.7375ms)
✔ approvals: the ledger is appended and folded into open requests with bind and approval states (1.728755ms)
✔ approvals: a reply approves only when it points at a request, says exactly approve, and comes from a listed approver once (0.660941ms)
✔ approvals: a button approves only on its own request message with the matching custom id (0.480185ms)
✔ approvals flow: a turn that opened a request posts the message with the button, records it, binds it, and both approvers approve (17.296632ms)
✔ 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.588519ms)
✔ approvals flow: an invalid request from the model, a refused post, and no api client are recorded and post nothing (7.386651ms)
✔ approvals flow: start retries a bind and an approval left as unknown, under their original keys (2.280338ms)
✔ authorize: open channel, listed user (1.82861ms)
✔ authorize: wrong guild (0.184147ms)
✔ authorize: no guild (DM) (0.172822ms)
✔ authorize: unlisted channel (0.174402ms)
✔ authorize: unknown channel, no info (0.177221ms)
✔ authorize: thread of listed parent (0.21491ms)
✔ authorize: thread of unlisted parent (0.154106ms)
✔ authorize: text channel that is not a thread and not listed (0.137261ms)
✔ authorize: unlisted user (0.987234ms)
✔ authorize: no author (0.258428ms)
✔ authorize: bot author (listed id, bot flag) (0.130292ms)
✔ authorize: system author (0.116261ms)
✔ authorize: the bot itself (0.09737ms)
✔ authorize: webhook (0.098117ms)
✔ authorize: mention channel without mention (0.168159ms)
✔ authorize: mention channel with bot mention (0.157334ms)
✔ authorize: mention channel with @everyone only (0.126031ms)
✔ authorize: mention channel mentioning someone else (0.079199ms)
✔ authorize: mention channel, content says @bot but mentions empty (0.211148ms)
✔ authorize: private thread under mention channel, mentioned (0.096633ms)
✔ authorize: private thread under mention channel, not mentioned (0.075681ms)
✔ authorize: thread in another guild per channel info (0.096333ms)
✔ authorize: not an object (0.069499ms)
✔ authorize: no id (0.101974ms)
✔ authorize: oversize content is accepted and flagged (0.072529ms)
✔ authorize: exactly the limit is not oversize (0.062169ms)
✔ authorize: a user's channel allowlist drops them outside it, threads count as the parent, others are unaffected (0.572655ms)
✔ authorize: order puts wrong guild before user, and user before channel (no channel lookup for strangers) (0.220375ms)
✔ binding: a complete binding validates and is frozen (2.592724ms)
✔ binding: unknown key, missing field, wrong type refuse with exit 2 (1.277499ms)
✔ binding: empty allowlists refuse (0.484324ms)
✔ binding: a user's channel allowlist must be non-empty, listed and unique; absent means every listed channel (1.35884ms)
✔ reloadDiff: reloadable keys are summarised by id; every fixed key refuses with exit 2 (1.544901ms)
✔ binding: dmRecipient is optional, a snowflake, one of the listed users, and a fixed key (2.632685ms)
✔ binding: file must be 0600, regular, not a symlink (1.696578ms)
✔ binding: token file mode, symlink, emptiness and shape are checked; token never appears in errors (1.650015ms)
✔ cli: check refuses a non-0600 token file with exit 2 before any network use (110.716888ms)
✔ context files: absolute paths, traversal, symlinks and out-of-repo targets refuse; in-repo files resolve (2.727265ms)
✔ cli: check refuses a missing context file and a missing binding with exit 2; usage is exit 4 (311.22442ms)
✔ cli: reload validates the file first (exit 2), then needs a live owner (exit 1); usage is exit 4 (188.64441ms)
✔ cli: run refuses when STOP is present, before any network use (127.115842ms)
✔ binding: tools is optional, validated strictly, a fixed key for reload, and its roots are resolved against the data root (2.407766ms)
✔ binding: a git key is validated at load and reaches the extension whole, and only on a writable root (1.414096ms)
✔ 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 (20.411015ms)
✔ delivery: refused and unknown outcomes are journaled; a later chunk is not sent after a failure (16.46198ms)
✔ delivery: restart with an unknown entry re-sends the same nonce once and reconciles before accepting traffic (1.651165ms)
✔ delivery: an unknown entry older than the dedupe window is marked refused, not re-sent; a still-unknown one refuses start (1.257068ms)
✔ delivery: repeated unknown reconciliations never refresh the dedupe window; the original intent time decides (1.394563ms)
✔ turn: a failed engine turn posts the fixed line, never model output, and writes a failed record (3.649376ms)
✔ turn: a second message during a turn is held by the engine, both get their own reply and record (33.223894ms)
✔ turn: a thread under a listed channel is answered in the thread; an unknown thread is looked up once (3.349565ms)
✔ drop: an unlisted user gets silence and one drop line; no inbox entry, no REST call, no engine call (1.081032ms)
✔ drop: an oversize message is accepted into the inbox, answered with the fixed line and journaled as a drop (1.035414ms)
✔ restart: an inbox with three ids and a replay of the same three produces zero turns (42.067365ms)
✔ stop: STOP present refuses start; STOP written while running refuses new turns and the current one finishes (32.137564ms)
✔ ceiling: the ceiling plus one is refused and journaled; one fixed line per UTC day; a new day accepts again (6.021324ms)
✔ ceiling: a burst arriving while turns are still running cannot queue past the ceiling (4.161459ms)
✔ ceiling: a turn interrupted by a crash still counts after restart; admissions are durable (2.513163ms)
✔ ceiling: the daily notice survives a same-day restart; one delivery attempt in total, even when the first attempt crashed mid-flight (4.309453ms)
✔ duplicate: the same event delivered twice while the thread lookup is held yields one prompt, one admission and one reply (2.219847ms)
✔ journal: no token-shaped string and no model output on the drop path reaches disk (0.495667ms)
✔ receipt: an admitted message gets one eyes reaction on the inbound message; drops and refusals get none; a failed reaction is recorded and does not fail the turn (2.66673ms)
✔ receipt: Discord refusing the reaction leaves the turn intact and records ok false (2.170439ms)
✔ reload: a new user is silent before and answered after; a removed channel goes silent; a lower ceiling applies at once (3.946203ms)
✔ reload: a fixed key refuses with exit 2 and the old binding stays in force (1.991666ms)
✔ tools: with a tools binding the turn record lists every read and its outcome; without one the field is null (2.529012ms)
✔ context: the Discord block names the server, channels and modes, and states the rules from Q15 and Q16 (2.626272ms)
✔ context: with tools the block names the roots, keeps file content as data, and says to state refusals plainly (0.755232ms)
✔ context: a writable root adds the write rules and says a write is real only once Jason commits (1.828371ms)
✔ context: the envelope is one bracketed line then the text; names cannot break the line (1.09758ms)
✔ context: a git root swaps the terminal-commit line for the git verbs, and a vault root adds the id protocol (2.720755ms)
✔ context: assembleContext concatenates files in launcher format and appends the block; sha256 is stable (1.18ms)
✔ context: splitReply keeps paragraphs together under the limit and splits long ones at lines, spaces, then hard (0.640898ms)
✔ pinned Discord render/install and missing prerequisites; checkout bytes unchanged (90.714262ms)
✔ Discord status reports effective WorkingDirectory from a fake user manager (49.6019ms)
✔ engine: buildPiArgs carries the fixed flags, engine settings, session dir and prompt file (3.009851ms)
✔ engine: with tools, buildPiArgs turns pi's own tools off, loads the extension explicitly and allowlists exactly our three (0.442298ms)
✔ engine: a run with tool turns settles once, on the answer, with every tool call in the result (61.214272ms)
✔ engine: a run that ends on a tool-only turn fails the prompt as empty; a retried run settles on the real end (41.922629ms)
✔ engine: one prompt, one turn, text and usage come back (42.573884ms)
✔ engine: a prompt while streaming is held until pi settles, then sent as its own run, and answered in order (343.382019ms)
✔ engine: a held prompt that times out before pi settles fails on its own and is never sent (230.35888ms)
✔ engine: timeout sends abort and fails only that turn; the process stays (103.261162ms)
✔ engine: tool events from a run that outlived its timeout never land in the next prompt's record (231.288342ms)
✔ engine: a prompt after a turn that timed out before its agent_start waits for pi to settle instead of being refused (126.827973ms)
✔ 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.661556ms)
✔ engine: a timed-out turn pi starts only after the grace never answers a later prompt (615.487598ms)
✔ engine: late events of a run past its grace, before pi exits, answer nothing and nothing more is sent (early prompt response) (1.370293ms)
✔ engine: late events of a run past its grace, before pi exits, answer nothing and nothing more is sent (late prompt response) (0.537854ms)
✔ engine: a timed-out run pi did start outlives the grace; the next prompt goes out when it ends (427.933175ms)
✔ engine: a malformed JSONL line fails the turn, not the process (22.736136ms)
✔ engine: a turn that ends in error rejects with the error code; process exit fails pending turns (46.413276ms)
✔ gateway: hello -> identify with intents, ready, heartbeat with jitter, ack (2.711402ms)
✔ gateway: missed ack closes the socket and resumes with the last sequence (1.939565ms)
✔ gateway: op 7 reconnect resumes; op 9 non-resumable re-identifies (0.591266ms)
✔ gateway: op 9 resumable resumes (0.3611ms)
✔ gateway: close 4014 is fatal, reports the missing intent, never reconnects (0.954906ms)
✔ gateway: 4004 and 4013 are fatal too; 1006 reconnects with identify when no session (0.572683ms)
✔ gateway: close() is final and unparseable frames are ignored (0.49881ms)
✔ git: config validation is strict, needs write: true, a work tree and a private token file (68.576099ms)
✔ git: the child environment drops every host git config, names one helper, and carries the token path only for origin (45.56167ms)
✔ git: status reports the branch, ahead/behind and changed paths, and refuses off the named branch or mid-merge (73.765148ms)
✔ git: parseStatus reads porcelain v2 including renames and conflicts (0.28583ms)
✔ git: a commit stages exactly the named files, carries the seat author and the requester trailer, and pushes at once (73.301209ms)
✔ git: commit refusals: message, paths, requester, nothing to commit, and an index that already holds other work (87.53822ms)
✔ git: a commit whose push fails is still a commit, says so, and the next commit's push carries both (D6) (82.429664ms)
✔ git: pull is fast-forward only; a diverged origin or dirty local files refuse with nothing merged (187.748855ms)
✔ git: push pushes the named branch only and reports up to date (68.54912ms)
✔ git: no token value or token path ever reaches a git argument list; outputs are masked and capped (77.62595ms)
✔ git: the credential helper answers get over https from a private file and nothing else (192.45159ms)
✔ git: the vault protocol validates before a commit, honours another owner's lock, reserves ids, and locks around writes (350.996448ms)
✔ lock: the claim is exclusive; a second start against a live owner refuses (4.442589ms)
✔ lock: a stale lock (dead owner, reused pid, or record without start) refuses run and is never signaled; only unlock clears it (80.14209ms)
✔ lock: an incomplete claim (directory without owner record) is busy and refuses run; unlock clears it (0.879317ms)
✔ lock: an owner record that exists but cannot be read is invalid: never signaled, never removed, never claimed over (1.878001ms)
✔ lock: legacy upgrade; a live connector holding a {pid, start} record is unknown, unlock refuses and nothing changes; after it exits, unlock clears it (51.730431ms)
✔ 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 (305.241917ms)
✔ lock: identity syntax; only canonical unsigned decimal start ticks and lowercase boot uuids are identities (0.436451ms)
✔ lock: a process whose start marker or boot id cannot be read refuses to claim (22.13892ms)
✔ lock: a live pid whose identity cannot be read right now is unknown: never signaled, never removed, never claimed over (0.941645ms)
✔ lock: four processes racing for the same binding; exactly one claims it and the others refuse (40.69254ms)
✔ lock: stale handoff; concurrent starts over a stale lock all refuse, nothing reclaims, one unlock then exactly one live owner (131.741059ms)
✔ lock: four-party schedule; claims landing inside an unlock's gap never survive, one unlock leaves no owner and no residue (80.649113ms)
✔ notices: a kind is recorded per UTC day and found again (0.40637ms)
✔ notify: the DM channel opens once, every send carries the nonce, and only the message id comes back (5.113329ms)
✔ notify: refusals and unknowns surface as RestOutcome without ids; a refused send reopens the channel next time (2.061449ms)
✔ notify: no dmRecipient, a non-0600 binding or token, or a missing binding refuse before any network use (1.419553ms)
✔ check --offline validates local fixture without REST or gateway calls (164.936804ms)
✔ check --offline refuses token mode and missing context under the selected repo (115.255403ms)
✔ run --offline is a usage refusal before reading config or networking (64.658492ms)
✔ recover: nothing to do is clean; a lock whose owner is gone or that has no record is cleared and STOP ends up absent (50.07082ms)
✔ recover: an operator STOP refuses with exit 3 and is never removed, whatever the lock says (36.622472ms)
✔ recover: a brake written during the unlock wins; STOP stays with both lines and the start is refused (31.735013ms)
✔ recover: a held binding refuses with exit 3 and writes no STOP: live owner, alive pid without verifiable identity, unreadable record (68.023788ms)
✔ cli: recover exits 0 when ready, 3 behind a brake or a held binding, and run's own STOP refusal is 3 (615.329128ms)
✔ rest: createMessage sends nonce, enforce_nonce, empty allowed_mentions and a soft reply reference (2.266712ms)
✔ rest: 429 waits retry_after and retries; 4xx is refused; 5xx and socket errors are unknown (3.491416ms)
✔ rest: content and nonce limits are enforced locally; typing never throws (0.631621ms)
✔ rest: react PUTs the encoded emoji on the inbound message for @me; 2xx is true, anything else is false and never throws (0.842643ms)
✔ rest: createDm posts the recipient and resolves the channel id; 4xx refused, 5xx unknown, bad id never sent (0.817126ms)
✔ setspark config: a bare https or loopback origin, a private key file, a principal (4.827514ms)
✔ setspark config: reaches the tools config and the binding as a fixed key (2.052688ms)
✔ setspark config: the binding's key survives resolveToolRoots and the engine's JSON hand-off to the extension (1.176934ms)
✔ setspark config: approvers come from the binding's users, never from the binding's setspark key (1.479781ms)
✔ setspark verbs: required_approvers go out as discord ids from names and come back as names (23.175566ms)
✔ setspark verbs: no Discord user id reaches tool text, whatever shape the service returns it in (9.630839ms)
✔ setspark contract: a decision made with names opens a request the connector accepts; names stored by an old record still refuse (7.136501ms)
✔ setspark keys: read per call, one printable token per file, rotation without a restart (3.791747ms)
✔ setspark idempotency keys: principal, turn id, call index; connector keys name a step (0.668929ms)
✔ setspark http core: json in and out, bearer header, idempotency header, fixed user agent, no key anywhere else (3.01183ms)
✔ setspark http core: error bodies become fixed refusals with code and the 409 fields; server text is data, cut (1008.960502ms)
✔ setspark verbs: a setspark key enables the eight verbs and no counters (0.476089ms)
✔ 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.187784ms)
✔ setspark verbs: no turn refuses every write before any request; bad arguments refuse before any request; reads still work (1.233657ms)
✔ setspark verbs: renderRecord caps long output and hides the accepted snapshot (0.199212ms)
✔ setspark api: bind, add_approval (button and reply) and get use integer request ids and the connector's keys (2.273972ms)
✔ tools: config refuses a missing, symlinked, dotted, non-directory or duplicate root and bad limits (3.935152ms)
✔ tools: every escape is refused with a fixed reason and nothing outside the root is read (3.904793ms)
✔ tools: happy paths list, read a window, and search case-insensitively; dotfiles and symlinks never appear (5.228771ms)
✔ tools: the tool set renders text for the model, records details for the journal, and enforces the per-run budget (3.559234ms)
✔ tools: listing and search caps hold (7.52236ms)
✔ tools: credential shapes are caught; ordinary prose and ids are not (0.58763ms)
✔ 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 (3.978095ms)
✔ tools: an unreadable file under the root is skipped by search and refused by read (0.943191ms)
✔ tools: config accepts write: true only as a boolean, and enables the write tools only then (0.875055ms)
✔ tools: every write outside the fence is refused before any byte lands, and no temp file remains (4.810605ms)
✔ tools: write_file leaves the exact bytes, edit_file replaces one exact match, and the set renders the change as uncommitted (2.382867ms)
✔ tools: a target that changed between the check and the rename is refused and the temp file is removed (1.31175ms)
✔ web: config takes an https or loopback-http SearXNG base url and a bounded fetch cap (3.186139ms)
✔ web: address rules refuse every private, loopback, link-local, mapped and multicast form (1.989368ms)
✔ web: web_fetch refuses bad urls, private hosts, rebinding names, non-https redirects, too many hops, error status, non-text bodies, and times out (1022.146076ms)
✔ web: web_fetch returns html as text with the title, follows an https redirect, keeps plain text and json, and cuts at the cap (4.200609ms)
✔ web: html to text drops scripts, styles and comments, decodes entities and keeps block breaks (0.223248ms)
✔ web: web_search asks the instance for json, returns at most ten clean results, and refuses a bad query, a down instance or an unusable answer (2.564412ms)
✔ 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.333835ms)
ℹ tests 183
ℹ suites 0
ℹ pass 183
ℹ fail 0
ℹ cancelled 0
ℹ skipped 0
ℹ todo 0
ℹ duration_ms 2631.617248
@@ -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,23 @@
On branch refactor
Your branch is up to date with 'origin/refactor'.
nothing to commit, working tree clean
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,82 @@
toolchain: node v26.8.1
OK syntax: packages/discord/src/approvals.mjs
OK syntax: packages/discord/src/authorize.mjs
OK syntax: packages/discord/src/binding.mjs
OK syntax: packages/discord/src/cli.mjs
OK syntax: packages/discord/src/connector.mjs
OK syntax: packages/discord/src/context.mjs
OK syntax: packages/discord/src/engine-pi.mjs
OK syntax: packages/discord/src/errors.mjs
OK syntax: packages/discord/src/gateway.mjs
OK syntax: packages/discord/src/git.mjs
OK syntax: packages/discord/src/journal.mjs
OK syntax: packages/discord/src/notify.mjs
OK syntax: packages/discord/src/rest.mjs
OK syntax: packages/discord/src/setspark.mjs
OK syntax: packages/discord/src/tools.mjs
OK syntax: packages/discord/src/web.mjs
OK syntax: packages/discord/bin/git-credential.mjs
OK syntax: packages/discord/extension/tools.mjs
OK syntax: packages/discord/tests/approvals.test.mjs
OK syntax: packages/discord/tests/authorize.test.mjs
OK syntax: packages/discord/tests/binding.test.mjs
OK syntax: packages/discord/tests/connector.test.mjs
OK syntax: packages/discord/tests/context.test.mjs
OK syntax: packages/discord/tests/copy-service.test.mjs
OK syntax: packages/discord/tests/engine.test.mjs
OK syntax: packages/discord/tests/fake-pi.mjs
OK syntax: packages/discord/tests/gateway.test.mjs
OK syntax: packages/discord/tests/git.test.mjs
OK syntax: packages/discord/tests/helpers.mjs
OK syntax: packages/discord/tests/journal.test.mjs
OK syntax: packages/discord/tests/notify.test.mjs
OK syntax: packages/discord/tests/offline-check.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 183)
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
OK pinned render runs copy CLI with copy cwd/repo and no checkout executable
OK pinned install writes the rendered unit in a temporary unit directory
OK pinned install refuses short prerequisite and writes no unit
OK pinned install refuses missing prerequisite and writes no unit
OK pinned install refuses cli prerequisite and writes no unit
OK pinned install refuses pi prerequisite and writes no unit
OK status names the copy from fake systemctl effective cwd
OK checkout install explicitly recommends --copy
OK wrapper check --offline passes local fixture and makes no network call
OK wrapper run --offline refuses usage
discord suite: 78 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
/mnt/storage/src/mosaic-stack-rocko-114/scripts/test-extension-package.sh: line 14: 1903040 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,86 @@
OK args: exit 2, target-branch
OK args: exit 2, target-branch
OK args: exit 4
OK args: exit 4
OK args: exit 4
OK --base next refuses before the config is read (got 2)
OK a missing system config exits 3 (got 3)
OK nobus: exit 3
OK schema-table: exit 2, bus-schema
OK schema-meta: exit 2, bus-schema
OK openBus opens the database read-only
OK other-business: exit 2, decision-not-found
OK no-raise: exit 2, decision-not-found
OK not-a-merge: exit 2, not-a-merge
OK open: exit 2, not-resolved
OK withdrawn: exit 2, not-resolved
OK by-agent: exit 2, not-human
OK via-webui: exit 2, not-human
OK no-answer: exit 2, not-human
OK answer-role: exit 2, not-human
OK answer-run: exit 2, not-human
OK answer-choice: exit 2, not-human
OK two-answers: exit 2, not-human
OK chose-no: exit 2, not-merge-option
OK two-refs: exit 2, work-ref
OK bad-ref: exit 2, work-ref
OK task-ref-disagrees: exit 2, work-ref
OK raise-task-disagrees: exit 2, work-ref
OK target-main: exit 2, target-branch
OK superseded-question: exit 2, superseded
OK superseded-task: exit 2, superseded
OK superseded-by: exit 2, superseded
OK no-verdict: exit 2, no-verdict
OK changes-later: exit 2, not-approved
OK short-commit: exit 2, not-approved
OK wrong-task-body: exit 2, not-approved
OK args: exit 2, decision-not-found
OK earlier-merge: exit 0
OK ref-moved: exit 2, ref-moved
OK root-top: exit 2, root-file
OK root-newdir: exit 2, root-file
OK root-src: exit 2, root-file
OK unrelated: exit 2, base-conflict
OK empty: exit 2, base-conflict
OK an empty reviewed diff says so
OK dirty-changed: exit 2, checkout-dirty
OK dirty-staged: exit 2, checkout-dirty
OK dirty-untracked: exit 2, checkout-dirty
OK conflict: exit 2, base-conflict
OK a refusal after the worktree is added leaves no worktree or scratch
OK cleanup looks for the worktree's Docker network
OK dirty-during: exit 2, checkout-dirty
OK a reviewed path dirtied during the gate moves no ref
OK drift: exit 2, patch-drift
OK patch-drift leaves no worktree or scratch
OK gate-pkg: exit 2, gate-failed
OK gate-failed names the failing step's log, and it exists
OK gate-failed leaves no worktree or scratch
OK gate-script: exit 2, gate-failed
OK a failing scripts/test-*.sh fails the gate
OK a failed gate moves no ref
OK nomods: exit 4
OK a target without node_modules leaves no worktree or scratch
OK moved: exit 2, base-moved
OK base-moved keeps the base where it moved to
OK dry: exit 0
OK --dry-run writes no ref
OK --dry-run prints the commit with --subject and --attribution
OK --dry-run runs the gate
OK --dry-run leaves no worktree or scratch
OK land: exit 0
OK the landing is one commit on the old base
OK its diff is the reviewed diff by patch-id
OK its tree is the dry run's
OK the message has the option's subject, the citations and the attribution last
OK it prints the line for the PM
OK the checked-out base has the reviewed paths and keeps the unrelated edit
OK only refs/heads/refactor exists in the target
OK the work ref is unchanged
OK the bus database is unchanged
OK the gate ran each package suite but queue, and each scripts/test-*.sh
OK the landing leaves no worktree or scratch
OK the target's node_modules is still there, with its contents
OK the worktree's Docker network is removed
---
land-work selftests: 84 passed, 0 failed
@@ -0,0 +1,36 @@
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/gate-owner.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 156
ℹ pass 156
ℹ fail 0
OK node --test packages/queue/tests/
OK scripts/mosaic queue help
skip queue verify and render --check: this checkout (/mnt/storage/src/mosaic-stack-rocko-114) is not the queue's canonical root (/mnt/storage/src/mosaic-stack)
queue suite: 28 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,32 @@
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
skip live recall pair: user recall run, recalled user name (docker daemon unavailable)
OK no agent identity on headless run
selftest: 26 passed, 0 failed
@@ -0,0 +1,13 @@
✔ pinned Discord render/install and missing prerequisites; checkout bytes unchanged (35.745472ms)
✔ Discord status reports effective WorkingDirectory from a fake user manager (28.458045ms)
✔ check --offline validates local fixture without REST or gateway calls (107.822599ms)
✔ check --offline refuses token mode and missing context under the selected repo (104.910772ms)
✔ run --offline is a usage refusal before reading config or networking (51.506878ms)
ℹ tests 5
ℹ suites 0
ℹ pass 5
ℹ fail 0
ℹ cancelled 0
ℹ skipped 0
ℹ todo 0
ℹ duration_ms 335.898598
@@ -0,0 +1,35 @@
✔ pinned Discord render/install and missing prerequisites; checkout bytes unchanged (33.955405ms)
✔ Discord status reports effective WorkingDirectory from a fake user manager (28.150537ms)
✖ check --offline validates local fixture without REST or gateway calls (55.152759ms)
✔ check --offline refuses token mode and missing context under the selected repo (107.793918ms)
✔ run --offline is a usage refusal before reading config or networking (53.575105ms)
ℹ tests 5
ℹ suites 0
ℹ pass 4
ℹ fail 1
ℹ cancelled 0
ℹ skipped 0
ℹ todo 0
ℹ duration_ms 286.142494
✖ failing tests:
test at packages/discord/tests/offline-check.test.mjs:15:1
✖ check --offline validates local fixture without REST or gateway calls (55.152759ms)
AssertionError [ERR_ASSERTION]: discord: token file does not hold a bot token: /mnt/storage/scratch/tmp/mosaic-discord-VWXFQT/secrets/bot.token
2 !== 0
at TestContext.<anonymous> (file:///mnt/storage/src/mosaic-stack-rocko-114/packages/discord/tests/offline-check.test.mjs:16:56)
at Test.runInAsyncScope (node:async_hooks:226:14)
at Test.run (node:internal/test_runner/test:1402:25)
at Test.start (node:internal/test_runner/test:1262:17)
at startSubtestAfterBootstrap (node:internal/test_runner/harness:387:17) {
generatedMessage: false,
code: 'ERR_ASSERTION',
actual: 2,
expected: 0,
operator: 'strictEqual',
diff: 'simple'
}
@@ -0,0 +1,30 @@
✖ pinned Discord render/install and missing prerequisites; checkout bytes unchanged (11.395833ms)
ℹ tests 1
ℹ suites 0
ℹ pass 0
ℹ fail 1
ℹ cancelled 0
ℹ skipped 0
ℹ todo 0
ℹ duration_ms 75.471822
✖ failing tests:
test at packages/discord/tests/copy-service.test.mjs:11:1
✖ pinned Discord render/install and missing prerequisites; checkout bytes unchanged (11.395833ms)
AssertionError [ERR_ASSERTION]: The expression evaluated to a falsy value:
assert.ok(r.stdout.includes(`ExecStart=${process.execPath} ${s.copy}/packages/discord/src/cli.mjs run %i --supervised --repo ${s.copy}\n`))
at TestContext.<anonymous> (file:///mnt/storage/src/mosaic-stack-rocko-114/packages/discord/tests/copy-service.test.mjs:13:70)
at Test.runInAsyncScope (node:async_hooks:226:14)
at Test.run (node:internal/test_runner/test:1402:25)
at Test.start (node:internal/test_runner/test:1262:17)
at startSubtestAfterBootstrap (node:internal/test_runner/harness:387:17) {
generatedMessage: true,
code: 'ERR_ASSERTION',
actual: false,
expected: true,
operator: '==',
diff: 'simple'
}
@@ -0,0 +1,30 @@
✖ pinned Discord render/install and missing prerequisites; checkout bytes unchanged (43.477288ms)
ℹ tests 1
ℹ suites 0
ℹ pass 0
ℹ fail 1
ℹ cancelled 0
ℹ skipped 0
ℹ todo 0
ℹ duration_ms 109.274458
✖ failing tests:
test at packages/discord/tests/copy-service.test.mjs:11:1
✖ pinned Discord render/install and missing prerequisites; checkout bytes unchanged (43.477288ms)
AssertionError [ERR_ASSERTION]: Expected values to be strictly equal:
0 !== 1
at TestContext.<anonymous> (file:///mnt/storage/src/mosaic-stack-rocko-114/packages/discord/tests/copy-service.test.mjs:22:333)
at Test.runInAsyncScope (node:async_hooks:226:14)
at Test.run (node:internal/test_runner/test:1402:25)
at Test.start (node:internal/test_runner/test:1262:17)
at startSubtestAfterBootstrap (node:internal/test_runner/harness:387:17) {
generatedMessage: true,
code: 'ERR_ASSERTION',
actual: 0,
expected: 1,
operator: 'strictEqual',
diff: 'simple'
}
@@ -0,0 +1,31 @@
✖ check --offline validates local fixture without REST or gateway calls (54.337228ms)
ℹ tests 1
ℹ suites 0
ℹ pass 0
ℹ fail 1
ℹ cancelled 0
ℹ skipped 0
ℹ todo 0
ℹ duration_ms 120.395496
✖ failing tests:
test at packages/discord/tests/offline-check.test.mjs:15:1
✖ check --offline validates local fixture without REST or gateway calls (54.337228ms)
AssertionError [ERR_ASSERTION]: discord: GET /users/@me: fixture refuses network
1 !== 0
at TestContext.<anonymous> (file:///mnt/storage/src/mosaic-stack-rocko-114/packages/discord/tests/offline-check.test.mjs:16:56)
at Test.runInAsyncScope (node:async_hooks:226:14)
at Test.run (node:internal/test_runner/test:1402:25)
at Test.start (node:internal/test_runner/test:1262:17)
at startSubtestAfterBootstrap (node:internal/test_runner/harness:387:17) {
generatedMessage: false,
code: 'ERR_ASSERTION',
actual: 1,
expected: 0,
operator: 'strictEqual',
diff: 'simple'
}
@@ -0,0 +1,17 @@
[
{
"name": "checkout-execstart",
"exit": 1,
"status": "killed"
},
{
"name": "missing-pi-accepted",
"exit": 1,
"status": "killed"
},
{
"name": "offline-calls-getMe",
"exit": 1,
"status": "killed"
}
]
@@ -0,0 +1 @@
Named network absent or Docker unavailable; dead-socket gate created none.
@@ -0,0 +1,2 @@
[]
Error response from daemon: network mosaic-stack-rocko-114_default not found
@@ -0,0 +1 @@
busy: []
@@ -0,0 +1,3 @@
request rocko-114-review-1 not sent: MOSAIC_GITEA_CREDENTIAL_FILE is not set; a request posts only with rocko's own token file
ok rocko-114-review-1 rev 1186 row 114 in-progress→in-review round 1 on #1608; request rocko-114-review-1 requesting
ok rocko-114-review-1.outcome rev 1187 row 114 round 1 request rocko-114-review-1 failed
@@ -0,0 +1,67 @@
✖ check --offline validates local fixture without REST or gateway calls (53.152825ms)
✖ check --offline refuses token mode and missing context under the selected repo (50.950038ms)
✔ run --offline is a usage refusal before reading config or networking (49.833721ms)
ℹ tests 3
ℹ suites 0
ℹ pass 1
ℹ fail 2
ℹ cancelled 0
ℹ skipped 0
ℹ todo 0
ℹ duration_ms 224.637027
✖ failing tests:
test at packages/discord/tests/offline-check.test.mjs:15:1
✖ check --offline validates local fixture without REST or gateway calls (53.152825ms)
AssertionError [ERR_ASSERTION]: discord: unknown argument: --offline
usage: mosaic-discord check <binding> [--config PATH] [--repo PATH]
mosaic-discord run <binding> [--config PATH] [--repo PATH] [--supervised]
mosaic-discord stop <binding> [--config PATH]
mosaic-discord unlock <binding> [--config PATH]
mosaic-discord recover <binding> [--config PATH]
mosaic-discord reload <binding> [--config PATH]
4 !== 0
at TestContext.<anonymous> (file:///mnt/storage/src/mosaic-stack-rocko-114/packages/discord/tests/offline-check.test.mjs:16:56)
at Test.runInAsyncScope (node:async_hooks:226:14)
at Test.run (node:internal/test_runner/test:1402:25)
at Test.start (node:internal/test_runner/test:1262:17)
at startSubtestAfterBootstrap (node:internal/test_runner/harness:387:17) {
generatedMessage: false,
code: 'ERR_ASSERTION',
actual: 4,
expected: 0,
operator: 'strictEqual',
diff: 'simple'
}
test at packages/discord/tests/offline-check.test.mjs:20:1
✖ check --offline refuses token mode and missing context under the selected repo (50.950038ms)
AssertionError [ERR_ASSERTION]: discord: unknown argument: --offline
usage: mosaic-discord check <binding> [--config PATH] [--repo PATH]
mosaic-discord run <binding> [--config PATH] [--repo PATH] [--supervised]
mosaic-discord stop <binding> [--config PATH]
mosaic-discord unlock <binding> [--config PATH]
mosaic-discord recover <binding> [--config PATH]
mosaic-discord reload <binding> [--config PATH]
4 !== 2
at TestContext.<anonymous> (file:///mnt/storage/src/mosaic-stack-rocko-114/packages/discord/tests/offline-check.test.mjs:21:95)
at Test.runInAsyncScope (node:async_hooks:226:14)
at Test.run (node:internal/test_runner/test:1402:25)
at Test.processPendingSubtests (node:internal/test_runner/test:974:18)
at Test.postRun (node:internal/test_runner/test:1542:19)
at Test.run (node:internal/test_runner/test:1467:12)
at async startSubtestAfterBootstrap (node:internal/test_runner/harness:387:3) {
generatedMessage: false,
code: 'ERR_ASSERTION',
actual: 4,
expected: 2,
operator: 'strictEqual',
diff: 'simple'
}
@@ -0,0 +1,53 @@
✖ pinned Discord render/install and missing prerequisites; checkout bytes unchanged (5.455806ms)
✖ Discord status reports effective WorkingDirectory from a fake user manager (30.187919ms)
ℹ tests 2
ℹ suites 0
ℹ pass 0
ℹ fail 2
ℹ cancelled 0
ℹ skipped 0
ℹ todo 0
ℹ duration_ms 100.729282
✖ failing tests:
test at packages/discord/tests/copy-service.test.mjs:11:1
✖ pinned Discord render/install and missing prerequisites; checkout bytes unchanged (5.455806ms)
AssertionError [ERR_ASSERTION]: usage: scripts/discord-service.sh render | install [--dir DIR] [--no-reload] | uninstall [--dir DIR] [--no-reload] | status <binding>
4 !== 0
at TestContext.<anonymous> (file:///mnt/storage/src/mosaic-stack-rocko-114/packages/discord/tests/copy-service.test.mjs:12:94)
at Test.runInAsyncScope (node:async_hooks:226:14)
at Test.run (node:internal/test_runner/test:1402:25)
at Test.start (node:internal/test_runner/test:1262:17)
at startSubtestAfterBootstrap (node:internal/test_runner/harness:387:17) {
generatedMessage: false,
code: 'ERR_ASSERTION',
actual: 4,
expected: 0,
operator: 'strictEqual',
diff: 'simple'
}
test at packages/discord/tests/copy-service.test.mjs:23:1
✖ Discord status reports effective WorkingDirectory from a fake user manager (30.187919ms)
AssertionError [ERR_ASSERTION]: The expression evaluated to a falsy value:
assert.ok(r.stdout.includes(`copy: ${s.id}`))
at TestContext.<anonymous> (file:///mnt/storage/src/mosaic-stack-rocko-114/packages/discord/tests/copy-service.test.mjs:24:337)
at Test.runInAsyncScope (node:async_hooks:226:14)
at Test.run (node:internal/test_runner/test:1402:25)
at Test.processPendingSubtests (node:internal/test_runner/test:974:18)
at Test.postRun (node:internal/test_runner/test:1542:19)
at Test.run (node:internal/test_runner/test:1467:12)
at async startSubtestAfterBootstrap (node:internal/test_runner/harness:387:3) {
generatedMessage: true,
code: 'ERR_ASSERTION',
actual: false,
expected: true,
operator: '==',
diff: 'simple'
}
@@ -0,0 +1,126 @@
[
{
"suite": "node-discord",
"exit": 0,
"began": "2026-10-14T20:30:20.823230+00:00",
"ended": "2026-10-14T20:30:23.493776+00:00",
"summaries": [
"\u2714 turn: a failed engine turn posts the fixed line, never model output, and writes a failed record (3.649376ms)",
"\u2714 receipt: an admitted message gets one eyes reaction on the inbound message; drops and refusals get none; a failed reaction is recorded and does not fail the turn (2.66673ms)",
"\u2139 tests 183",
"\u2139 pass 183",
"\u2139 fail 0",
"\u2139 cancelled 0",
"\u2139 skipped 0"
]
},
{
"suite": "test-auth",
"exit": 0,
"began": "2026-10-14T20:30:23.494015+00:00",
"ended": "2026-10-14T20:30:24.109265+00:00",
"summaries": [
"selftest: 15 passed, 0 failed"
]
},
{
"suite": "test-conductor",
"exit": 0,
"began": "2026-10-14T20:30:24.109530+00:00",
"ended": "2026-10-14T20:30:30.287430+00:00",
"summaries": [
"OK failed run refused (exit 1)",
"OK failed run: target untouched",
"selftest: 17 passed, 0 failed"
]
},
{
"suite": "test-config",
"exit": 0,
"began": "2026-10-14T20:30:30.287694+00:00",
"ended": "2026-10-14T20:30:31.955466+00:00",
"summaries": [
"OK failed validation modified nothing",
"selftest: 24 passed, 0 failed"
]
},
{
"suite": "test-discord",
"exit": 0,
"began": "2026-10-14T20:30:31.955716+00:00",
"ended": "2026-10-14T20:30:38.736178+00:00",
"summaries": [
"OK node --test packages/discord/tests/ (\u2139 pass 183)",
"discord suite: 78 passed, 0 failed"
]
},
{
"suite": "test-extension-package",
"exit": 0,
"began": "2026-10-14T20:30:38.736435+00:00",
"ended": "2026-10-14T20:30:40.052900+00:00",
"summaries": [
"extension package selftest: 18 passed, 0 failed"
]
},
{
"suite": "test-foundation",
"exit": 0,
"began": "2026-10-14T20:30:40.053145+00:00",
"ended": "2026-10-14T20:31:17.005136+00:00",
"summaries": [
"OK node --test scripts/foundation/ (\u2139 pass 80)",
"selftest: 44 passed, 0 failed"
]
},
{
"suite": "test-land-work",
"exit": 0,
"began": "2026-10-14T20:31:17.005438+00:00",
"ended": "2026-10-14T20:31:25.637399+00:00",
"summaries": [
"OK gate-pkg: exit 2, gate-failed",
"OK gate-failed names the failing step's log, and it exists",
"OK gate-failed leaves no worktree or scratch",
"OK gate-script: exit 2, gate-failed",
"OK a failed gate moves no ref",
"land-work selftests: 84 passed, 0 failed"
]
},
{
"suite": "test-queue",
"exit": 0,
"began": "2026-10-14T20:31:25.637691+00:00",
"ended": "2026-10-14T20:31:45.992880+00:00",
"summaries": [
"\u2139 tests 156",
"\u2139 pass 156",
"\u2139 fail 0",
"skip queue verify and render --check: this checkout (/mnt/storage/src/mosaic-stack-rocko-114) is not the queue's canonical root (/mnt/storage/src/mosaic-stack)",
"queue suite: 28 passed, 0 failed"
]
},
{
"suite": "test-release",
"exit": 0,
"began": "2026-10-14T20:31:45.993134+00:00",
"ended": "2026-10-14T20:31:46.157295+00:00",
"summaries": [
"skip state-machine cases (docker daemon unavailable)",
"selftest: 4 passed, 0 failed"
]
},
{
"suite": "test-task",
"exit": 0,
"began": "2026-10-14T20:31:46.157584+00:00",
"ended": "2026-10-14T20:31:48.126073+00:00",
"summaries": [
"skip adapter seam cases (docker daemon unavailable)",
"skip workspace/capability cases (docker daemon unavailable)",
"skip live task cases (docker unavailable)",
"skip live recall pair: user recall run, recalled user name (docker daemon unavailable)",
"selftest: 26 passed, 0 failed"
]
}
]
@@ -0,0 +1,82 @@
toolchain: node v26.8.1
OK syntax: packages/discord/src/approvals.mjs
OK syntax: packages/discord/src/authorize.mjs
OK syntax: packages/discord/src/binding.mjs
OK syntax: packages/discord/src/cli.mjs
OK syntax: packages/discord/src/connector.mjs
OK syntax: packages/discord/src/context.mjs
OK syntax: packages/discord/src/engine-pi.mjs
OK syntax: packages/discord/src/errors.mjs
OK syntax: packages/discord/src/gateway.mjs
OK syntax: packages/discord/src/git.mjs
OK syntax: packages/discord/src/journal.mjs
OK syntax: packages/discord/src/notify.mjs
OK syntax: packages/discord/src/rest.mjs
OK syntax: packages/discord/src/setspark.mjs
OK syntax: packages/discord/src/tools.mjs
OK syntax: packages/discord/src/web.mjs
OK syntax: packages/discord/bin/git-credential.mjs
OK syntax: packages/discord/extension/tools.mjs
OK syntax: packages/discord/tests/approvals.test.mjs
OK syntax: packages/discord/tests/authorize.test.mjs
OK syntax: packages/discord/tests/binding.test.mjs
OK syntax: packages/discord/tests/connector.test.mjs
OK syntax: packages/discord/tests/context.test.mjs
OK syntax: packages/discord/tests/copy-service.test.mjs
OK syntax: packages/discord/tests/engine.test.mjs
OK syntax: packages/discord/tests/fake-pi.mjs
OK syntax: packages/discord/tests/gateway.test.mjs
OK syntax: packages/discord/tests/git.test.mjs
OK syntax: packages/discord/tests/helpers.mjs
OK syntax: packages/discord/tests/journal.test.mjs
OK syntax: packages/discord/tests/notify.test.mjs
OK syntax: packages/discord/tests/offline-check.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 183)
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
OK pinned render runs copy CLI with copy cwd/repo and no checkout executable
OK pinned install writes the rendered unit in a temporary unit directory
OK pinned install refuses short prerequisite and writes no unit
OK pinned install refuses missing prerequisite and writes no unit
OK pinned install refuses cli prerequisite and writes no unit
OK pinned install refuses pi prerequisite and writes no unit
OK status names the copy from fake systemctl effective cwd
OK checkout install explicitly recommends --copy
OK wrapper check --offline passes local fixture and makes no network call
OK wrapper run --offline refuses usage
discord suite: 78 passed, 0 failed
@@ -0,0 +1,38 @@
850f50e1672e1667b501e2f293f352b0fbf69d6124f7059f2d11e03735d7648a BUILD.md
8c897b19b571ad5b0ea8ee3a28b0d9d339bb98126dd3ce5f3193e7e89a655f8f base.txt
07d20b63826e25d5273175c298e00402810272e8d03d43748bf15b40de008dd2 build.patch
68c60a79914692f3f6d4923f7bcff41de109ccca33bf6fba687fb88efa24bcac candidate-manifest.sha256
80e206246f3e8b496f6c4b9744f50ccdb78a978178f12f3a60b70ca91a989064 files.txt
8ad99be6e5418fbb45f3d2de9bc55dbda878f75fbddeec19b9a41557543b18af gate.py
987ecd603283b2bed71f6703b6be4a4f6672e30b96b764602c3f13f945986b90 mutants.py
088e0684151cd1d0b9d50577d1c481a805739e7084b58a61a241ab5c430be503 out/checkout-render-after.txt
088e0684151cd1d0b9d50577d1c481a805739e7084b58a61a241ab5c430be503 out/checkout-render-before.txt
8793c861ff5ca1ff86e7250d20a5e5406c579d106573421bd0a9509a5a00e502 out/final-verification.txt
2ddefa97647c42b7fff1e9e1cfc0323e45a2994f4f505832b215b0ef8d1955dc out/full/gate-results.json
666aaa26657e42f419872f00aa6232664a264ee681f207e25d05d4ab682db347 out/full/node-discord.txt
f91fc95a173339137f981e24fbf630acfde957e32c3cd71e28304532083c6a77 out/full/test-auth.txt
e1206366f07f3d9029530dcdcb55c0282d3f052adc60fc7fd51642d5d4ec3600 out/full/test-conductor.txt
52d211444489ea729dafc3c9745d4474d9dfd50f41055cbad792424197f04073 out/full/test-config.txt
87d2e24ce0be7586e31ea1f2c9ce5618b3127515b3c3cd3893f2a0ed899d5822 out/full/test-discord.txt
7f08b2b9deb0549a13bd205f00284e3c42838ff7ede6c07d503b3466447152d6 out/full/test-extension-package.txt
83dfaa21c4e4ad941088efab3f69e87ebd2f6bd3cc72ede938e21dc06da8a34f out/full/test-foundation.txt
8e2254aa3e1bb9b205ab72c68273d447c0328c9f156ccba2a46f3a0476468b58 out/full/test-land-work.txt
3faa89e75a914ce8d41980c71c54bdbe78ef1af764108088d3cae90a907e3730 out/full/test-queue.txt
6183e6b9b05edb497e92ffd09a19bba49c6f656c750ecb2acc283021113d1a98 out/full/test-release.txt
5dc16a91c36d6ff437e5fbb66764ece758e412f32f74b5a6d0c9cb9cad332f59 out/full/test-task.txt
e6403893a1796778506f4979ae52349c183e846bd9e8ea89049dcd8a1997c862 out/green-final.txt
d36abefd090b0e48ff62f4b12f32255367372cf1d59fcb1d6b26dcc72faf354e out/green.txt
c5c3085c3cb096d3c8499907e780e818352387507869718c05e8e8466075cf0b out/mutants/checkout-execstart.txt
8e8bb9e2af5da4a7905e5dc29bd299d546ef806039662ef8c662283d254dec98 out/mutants/missing-pi-accepted.txt
d92b1ba65404e7fd0b0e59980ca6f4f2deb3e348cdf96276494b078467e83651 out/mutants/offline-calls-getMe.txt
bee2f510b309a3af98d43f63e34384968065d7bdea5a786670bf3605a72eba47 out/mutants/summary.json
48dbb5dcccd5415a1e1b39326081598f543198471420cb3f174ab9d19bb8b0b5 out/network-clear.txt
0c50414f09a179e749639c37f45c13f746432213c0c8052660eda09158094303 out/network-inspect.txt
7b1c894bf5ac4017fc38d5d07b725f71fa4f0edd11ff68028ed53b8e6a2263a9 out/process-clear.txt
44267c81ad178dfd389b4d6deb3dc80176ad9fe828e7b6cce82c37e441beb52d out/queue-in-review.txt
5f17c71b2638de539f996a7d00290516d39fe5a24734ae08b55ddad01bfd02d8 out/red-offline.txt
b45024c5f50fc490553c1784c8975964cd2a240f5aa7e53f57a2924d80b12785 out/red-service.txt
fcfbc35c0c256e7da6019994bdb4ee319f4379dcb5a27b6a2e8e68d818ba45a7 out/suite-counts.json
87d2e24ce0be7586e31ea1f2c9ce5618b3127515b3c3cd3893f2a0ed899d5822 out/test-discord-first.txt
b3dd66ba58d6a2c78156c2a766ab51b37a8b378847de76311a715d104e076865 process-sweep.py
b0b8a95469e77280442d4733be76e0ed84ffd52f32b11c59780b968205685b06 verify.sh
@@ -0,0 +1,13 @@
"""Inspect only processes under this row's worktree; no unrelated argv/env."""
from pathlib import Path
import os,sys
root='/mnt/storage/src/mosaic-stack-rocko-114';busy=[]
for p in Path('/proc').iterdir():
if not p.name.isdigit():continue
try:
cwd=os.readlink(p/'cwd')
if cwd==root or cwd.startswith(root+'/'):
busy.append((int(p.name),(p/'comm').read_text().strip(),cwd))
except OSError:pass
print('busy:',busy)
sys.exit(bool(busy))
+10
View File
@@ -0,0 +1,10 @@
#!/usr/bin/env bash
set -euo pipefail
packet=/mnt/storage/src/mosaic-stack/agents/rocko/work/queue-114/r1
[ -z "$(git branch --show-current)" ] || { echo 'detached worktree required' >&2;exit 2; }
export DOCKER_HOST=unix:///nonexistent.sock
sha256sum -c "$packet/candidate-manifest.sha256"
git diff --check
python "$packet/gate.py" "${1:?pass absolute gate output directory}"
python "$packet/mutants.py" "${2:?pass separate absolute mutant output directory}"
sha256sum -c "$packet/candidate-manifest.sha256"