gander: record row 196 rebase gate stop and row 252 design

This commit is contained in:
jason.woltje committed 2026-10-21 13:55:41 -05:00
1 parent 64539c96bb
commit d218e14bd9
34 files changed
+2699

No files matched your search

+1
View File
@@ -18,3 +18,4 @@ sage-1425: complete static self-review before slot, document refused caller outc
Static review complete: peer/helper lifecycle, runtime, verifier walk, tester socket, Console spawn, Unix client, release/recovery/preflight and fixtures inspected. Found release path preflight missing before stop: long-path fixture RED -> GREEN with preflight; unsafe parent also refuses before any stop. release-path-{red,green}.txt. M29 prepared. Restored unchanged legacy tester-port-invalid wording. Prepared batch now M1-M29, still unexecuted. No further implementation changes planned before Sage's go.
sage-1464 (2026-10-20T20:40Z): renewed bus/WebUI/CLI and M1-M29 slot is fifth, after rows 192, 201, 204 and 200. Continue holding until Sage's go. Fleet notice #426(a), mos-1086, live 20:32:55Z: no branch deletion; any rebase must use a new branch name; leave pr-merge.sh -d off. No rebase or deletion is planned for this row.
sage-2051 / mos-1193 (2026-10-21T18:45Z) explicitly orders committing and fast-forward pushing the current row196 build before its deferred full verification, then rebasing onto a36523713 on new gander/row196-r2. This supersedes the earlier no-product-commit-before-suite gate for this preservation commit only. Full acceptance remains pending. No tests until Sage says clear. Subsequent batch order: M1-M29, bus, WebUI, CLI, every whole scripts/test-*.sh; TMPDIR must be created with mktemp under /mnt/storage/scratch/tmp, never /tmp. Preserve the old branch. Row252 design may proceed while waiting; no row252 code before Webby approves.
sage-2058 clear: clean detached 64539c96b worktree, short scratch TMPDIR, busy && before starts, pipefail and tee, mutation restoration hash and Git-status checks. M1-M22 killed; M23 rejected by broad runtime SyntaxError classification despite passing node --check, 3 pass/2 fail in killer. Stopped, restored clean, slot returned gander-0031 18:51:50Z. No suites started. sage-2063 calls stop 1; driver fix separately committed, no rerun before next clear. r2/REBASE.md records all ten conflict resolutions. Row252 design drafted, no code.
+55
View File
@@ -0,0 +1,55 @@
# Row 196 r2 rebase and gate record
Preservation: caf5c9b71b7ffe3e1a195c50a96439cb1d94bb0f, fast-forward pushed
on gander/row196 per sage-2051 / mos-1193. Stat against 4d26003f7:
93 files changed, 9940 insertions(+), 178 deletions(-).
Rebased candidate: 64539c96bb9eb34ff0cce6ab2e04410041a2a31e, on a36523713,
pushed as new gander/row196-r2. Old branch preserved. No force push or deletion.
range-diff.txt and patch-id-{before,after}.txt hold the exact comparison.
Stable patch ids differ: 294d45c6c8494e83e4276a8c554fcbe85674f3d0 before;
fb96c71db3715d7ea766b278377c4f215ba1537b after.
## Merge reasoning for the ten conflicted files
| File | Resolution and reason |
|---|---|
| BUILD-LOG.md | Retain upstream append-only phases followed by row196 entries. No historical entry removed or rewritten. |
| docs/TOOLS.md | Combine socket/legacy tester flags with upstream --sso and --no-send usage. Keep new operator modes. |
| packages/bus/src/runtime.mjs | Keep upstream host executor imports, run-directory sweep, grant reconciliation and host API. Add interpreter receipt and peer reader before listen, shared verifier and per-request binds. No second verifier. |
| packages/bus/src/server.mjs | Keep upstream HOST_VERBS/HOSTS_VERBS and host argument. Merge user proof and peer checks, with idempotent transient-cap release on host async completion too, avoiding duplicate disconnect. |
| packages/cli/tests/stack-release.test.mjs | Keep upstream extracted fixture import and all new upstream tests, append the whole row196 case outside upstream's closing block. Move row196 optional long data-root parameter into stack-release-fixture.mjs, where mk now lives. |
| packages/webui/src/cli.mjs | Retain SSO parsing, startup, summary and shutdown; replace only tester TCP with private Unix transport. Preserve testerNoSignIn when SSO is on. |
| packages/webui/src/serve.mjs | Retain SSO session/Host checks and signer. Add socket-only tester transport and fixed logical origin. Tester refuses authorization, session/login cookies and provider headers; it never uses the SSO signer. |
| packages/webui/tests/proof-cli.test.mjs | Retain upstream lane imports while moving the tester assertion to Unix HTTP. Close the fixture's socket before spawning the same-root CLI. No product relay. |
| scripts/stack-release.sh | Retain external-unit protocol, instance flags, SSO transformation, no-send and grant/config checks. Run socket migration on BASE_CONSOLE after sso_base. Preserve interpreter gate after external start-floor calculation. New socket flag participates in proof/SSO insertion and rollback carry, avoiding ordering and TCP-fallback regressions. External callers cannot use the new render-changing flag. |
| scripts/webui-service.sh | Retain instance selection, PORT_SET and SSO flag/status handling; add socket support checks and mode conflict. Status regex recognizes either legacy tester port or socket after send. |
Follow-through outside the conflict blocks: migrate the new SSO tester fixture to
Unix HTTP while retaining every sign-in refusal; adapt M15 to BASE_CONSOLE; retain
approved DESIGN.md/PLAN.md, absent on the new base. No tests before Sage's clear.
The two reviewed row164 file hashes remain unchanged by this rebase.
## First r2 slot, sage-2058
Clean detached worktree pinned to 64539c96b, linked node_modules. paths.txt
records it and the short mktemp TMPDIR under /mnt/storage/scratch/tmp. Product
tracked files stayed clean; node_modules is the expected untracked symlink.
The external driver creates an isolated Git snapshot for mutations and checks
both the original file hash and empty Git status after every restoration.
Every test start uses busy.py gated with &&, pipefail and tee. busy.py retains
canonical busy detections and excludes only its own process ancestors, which
would otherwise match the driver's command text. No other test process is
whitelisted.
M1-M22 killed. M23 syntax preflight passed; the whole interpreter killer file
reported 3 pass/2 fail. The old driver rejected its runtime JSON SyntaxError as
invalid, so stopped before M24. M23 also made the FIFO child hit its own one
second timeout. No golden package or shell suite started. All 23 restorations
passed hash and clean-status checks. batch.log and mutants/results.json retain
the original classification, not retrospectively edited. Slot handed back in
gander-0031 at 2026-10-21T18:51:50Z. Sage calls this stop 1 (sage-2063).
Next run requires Sage's next clear, after Morgana's row243 singles. M1-M22
stand under sage-2063, pending the separate driver correction. Rerun M23-M29,
then whole bus/WebUI/CLI suites and all ten whole scripts/test-*.sh. A second
stop goes to Sage before any rerun; a third goes to Mos. No acceptance claim.
+23
View File
@@ -0,0 +1,23 @@
M1 KILLED
M2 KILLED
M3 KILLED
M4 KILLED
M5 KILLED
M6 KILLED
M7 KILLED
M8 KILLED
M9 KILLED
M10 KILLED
M11 KILLED
M12 KILLED
M13 KILLED
M14 KILLED
M15 KILLED
M16 KILLED
M17 KILLED
M18 KILLED
M19 KILLED
M20 KILLED
M21 KILLED
M22 KILLED
M23 SURVIVED/INVALID
+15
View File
@@ -0,0 +1,15 @@
import os,subprocess,sys
ancestors=set();pid=os.getpid()
while pid>1 and pid not in ancestors:
ancestors.add(pid)
try:pid=int(open('/proc/%s/stat'%pid).read().rsplit(')',1)[1].split()[1])
except (OSError,ValueError):break
r=subprocess.run(['bash','/mnt/storage/scratch/sage-busy-now.sh'],capture_output=True,text=True)
other=[]
for line in r.stdout.splitlines():
if line.startswith('BUSY '):
try:pid=int(line.split()[1].rstrip(':'))
except ValueError:other.append(line);continue
if pid not in ancestors:other.append(line)
if other:print('\n'.join(other));sys.exit(1)
print('CLEAR (current driver ancestors excluded)')
@@ -0,0 +1,66 @@
// Authorized batch only. Mutate isolated scratch copies, never the candidate or installed tree.
import {mkdtempSync,cpSync,readFileSync,writeFileSync,rmSync,symlinkSync,existsSync,mkdirSync} from 'node:fs';
import {tmpdir} from 'node:os';import {join,resolve} from 'node:path';
import {spawnSync,execFileSync} from 'node:child_process';import {createHash} from 'node:crypto';
import {fileURLToPath} from 'node:url';
const root='/mnt/storage/scratch/tmp/g196-tree-tn651z';
const out='/home/jwoltje/.t3/worktrees/mosaic-stack/gander-row196/agents/gander/work/queue-196/r2/mutants';mkdirSync(out,{recursive:true});
const scratch=mkdtempSync(join(tmpdir(),'g196-mut-'));
const p='packages/bus/src/principal.mjs',h='packages/bus/src/human.mjs',w='packages/webui/src/tester-socket.mjs',s='packages/bus/src/server.mjs',peer='packages/bus/src/peercred.mjs';
const principal='packages/bus/tests/principal.test.mjs',socket='packages/webui/tests/tester-socket.test.mjs',transport='packages/bus/tests/peer-transport.test.mjs',interpreter='packages/bus/tests/interpreter.test.mjs';
const release=['--test-name-pattern=row196 explicit socket','packages/cli/tests/stack-release.test.mjs'];
const specs=[
['M1', 'packages/webui/src/serve.mjs','tester uses TCP',socket, text=>text.replace('if (tester) return bindTesterSocket(server, testerSocket);',"if (tester) return new Promise(r=>server.listen(0,'127.0.0.1',()=>r(server)));" )],
['M2',w,'socket mode widened',socket,t=>t.replace('chmodSync(temporary, 0o600)','chmodSync(temporary, 0o666)')],
['M3','packages/webui/src/serve.mjs','sign-in accepted on tester',socket,t=>t.replace("if (tester && (Object.hasOwn(req.headers, 'authorization')", "if (false && (Object.hasOwn(req.headers, 'authorization')")],
['M4',p,'copied PID accepted',principal,t=>t.replace('peer.pid !== proof.pid','false')],
['M5',peer,'helper parse error ignored','packages/bus/tests/peercred.test.mjs',t=>t.replace('} catch { finish(true); }','} catch { finish(false, {pid:2,uid:0,gid:0}); }')],
['M6',p,'foreign uid accepted',principal,t=>t.replace('peer.uid !== process.getuid()','false')],
['M7',w,'unsafe parent accepted',socket,t=>t.replace('(dir.mode & 0o077)','false')],
['M8',h,'agent environment walk skipped',principal,t=>t.replace('current.env !== null && MARKERS.some','false && MARKERS.some')],
['M9',h,'leaf identity not reread',principal,t=>t.replace('if (again.startTime !== proof.startTime || again.ppid !== p.ppid)','if (false)')],
['M10',p,'Console direct-parent check removed',principal,t=>t.replace("if (parent.uid !== peer.uid || parent.argv[1] !== resolve(paths.console) || parent.argv[2] !== 'serve')",'if (false)')],
['M11',p,'Console basename accepted',principal,t=>t.replace("import { resolve }", "import { resolve, basename }").replace('parent.argv[1] !== resolve(paths.console)','basename(parent.argv[1] ?? "") !== basename(paths.console)')],
['M12',h,'unknown proof identity accepted',principal,t=>t.replace("Object.keys(proof).some((k) => !['pid', 'startTime', 'nonce', 'business'].includes(k))",'false')],
['M13',s,'transient cap leaked',transport,t=>t.replace('broker.disconnect(boundCap)','void(boundCap)')],
['M14',p,'tester walk used for user',principal,t=>t.replace("verifyHuman(checked, { cliPath: paths[kind], launches, readProcess,\n nonceKey: kind === 'user' ? 'MOSAIC_BUS_USER_NONCE' : 'MOSAIC_BUS_CLI_NONCE' })",'verifyTester(checked, {cliPath:paths[kind],launches,readProcess,readNonce:()=>checked.nonce})')],
['M15','scripts/stack-release.sh','TCP retained during conversion',release,t=>t.replace('RUN_CONSOLE=("${without_tester[@]}" --tester-socket)','RUN_CONSOLE=("${BASE_CONSOLE[@]}" --tester-socket)')],
['M16','scripts/stack-release.sh','TCP reopened on downgrade',release,t=>t.replace('RUN_CONSOLE=("${without_tester[@]}") DROP_TESTER="--tester-socket','RUN_CONSOLE=("${without_tester[@]}" --tester-port 7333) DROP_TESTER="--tester-socket')],
['M17','packages/bus/src/broker.mjs','tester switch ignored','packages/bus/tests/tester.test.mjs',t=>t.replace("if (this.#business(record.business).vars?.['bus.tester'] !== true)",'if (false)')],
['M18',p,'user kind becomes owner',principal,t=>t.replace("return { kind: 'user', business, id: proof.user }","return { kind: 'owner', business, id: proof.user }")],
['M19','packages/cli/src/tester-http.mjs','selected instance root ignored',socket,t=>t.replace('socketPath: testerSocketPath(dataRoot)','socketPath: testerSocketPath(dataRoot + "/wrong-instance")')],
['M20',peer,'helper gets another pending socket','packages/bus/tests/peercred.test.mjs',t=>t.replace('export function createPeerReader','function originalPeerReader')+`\nexport function createPeerReader(options){const base=originalPeerReader(options),queue=[];let scheduled=false;return{read(socket){return new Promise((resolve,reject)=>{queue.push({socket,resolve,reject});if(!scheduled){scheduled=true;queueMicrotask(()=>{scheduled=false;const jobs=queue.splice(0);jobs.forEach((job,i)=>base.read(jobs[(i+1)%jobs.length].socket).then(job.resolve,job.reject));});}});},close(){base.close();}};}\n`],
['M21','packages/bus/src/interpreter.mjs','user-owned interpreter accepted',interpreter,t=>t.replace('s.uid !== 0 || (s.mode & 0o022)','false || (s.mode & 0o022)')],
['M22',s,'cap requests invoke helper',transport,t=>t.replace(' if (kind) {',' if (r.cap) await peers.read(socket);\n if (kind) {')],
['M23','packages/bus/src/interpreter.mjs','restart retains stale identity',interpreter,t=>t.replace('export function recordInterpreterStart(directory, record) {','export function recordInterpreterStart(directory, record) {\n try { readFileSync(join(directory,"peercred-starts.jsonl")); return; } catch {}')],
['M24',w,'listener binds final path directly',socket,t=>t.replace("const temporary = join(parent, '.' + randomBytes(4).toString('hex') + '.s');",'const temporary = path;')],
['M25','packages/bus/src/interpreter.mjs','FIFO receipt blocks before type refusal',['--test-name-pattern=non-regular startup receipt',interpreter],t=>t.replaceAll(' | constants.O_NONBLOCK','')],
['M26',peer,'missing helper accepted at startup',['--test-name-pattern=missing or non-regular peer helper','packages/bus/tests/peercred.test.mjs'],t=>t.replace('if (!s.isFile() || s.isSymbolicLink()) throw Error();','if (false) throw Error();').replace('catch { throw refused(); }','catch {}')],
['M27',peer,'shutdown returns before helpers are reaped',['--test-name-pattern=shutdown waits','packages/bus/tests/peercred.test.mjs'],t=>t.replace(" await Promise.all([...children].map(child => new Promise(resolve => {\n child.once('close', resolve);\n child.kill('SIGKILL');\n })));"," for (const child of children) child.kill('SIGKILL');")],
['M28','scripts/stack-release.sh','proof pair placed after tester socket flag',release,t=>t.replace('--send | --tester-port | --tester-socket) break','--send | --tester-port) break')],
['M29','scripts/stack-release.sh','unsafe tester path reaches service stop',release,t=>t.split('\n').filter(line=>!line.includes('|| check_refuse tester-socket-path')).join('\n')],
];
const hash=x=>createHash('sha256').update(x).digest('hex');
const results=[];
try {
for(const name of ['packages','scripts','adapters','roles','contracts','RELEASE','package.json','package-lock.json','.gitignore'])if(existsSync(join(root,name)))cpSync(join(root,name),join(scratch,name),{recursive:true,filter:src=>!src.includes('/node_modules/')&&!src.endsWith('/node_modules')});
symlinkSync(join(root,'node_modules'),join(scratch,'node_modules'));
for(const args of [['init','-q'],['add','.'],['-c','user.name=fixture','-c','user.email=fixture@invalid','-c','commit.gpgsign=false','-c','core.hooksPath=/dev/null','commit','-qm','candidate snapshot']])execFileSync('git',args,{cwd:scratch});
for(const [id,file,description,test,mutate] of specs) {
const at=join(scratch,file),original=readFileSync(at,'utf8'),before=hash(original),changed=mutate(original);
if(changed===original)throw Error(id+' did not change bytes');
writeFileSync(at,changed);
const syntax=spawnSync(file.endsWith('.sh')?'bash':process.execPath,[file.endsWith('.sh')?'-n':'--check',at],{encoding:'utf8'});
if(syntax.status!==0)throw Error(id+' invalid mutation: '+syntax.stderr);
const args=['--test',...(Array.isArray(test)?test:[test])];
const run=spawnSync('bash',['-c','set -o pipefail; python3 "$G196_BUSY" && "$G196_NODE" "$@" 2>&1 | tee "$G196_LOG"','--',...args],{cwd:scratch,env:{...process.env,G196_NODE:process.execPath,G196_LOG:join(out,id+'.txt')},encoding:'utf8',timeout:60000,maxBuffer:2*1024*1024});
const log=(run.stdout??'')+(run.stderr??'');writeFileSync(join(out,id+'.txt'),log);
writeFileSync(at,original);const clean=execFileSync('git',['status','--porcelain'],{cwd:scratch,encoding:'utf8'});if(clean.trim())throw Error(id+' dirty restored tree: '+clean);if(hash(readFileSync(at))!==before)throw Error(id+' restore hash failed');
const failed=/[ℹ#]\s*(?:fail|tests failed)\s+[1-9]/.test(log) || /✖/.test(log);
const killed=run.status!==0&&failed&&!/SyntaxError|ERR_MODULE_NOT_FOUND/.test(log);
const result={id,description,test,args,status:run.status,signal:run.signal,killed,restoredSha256:before};results.push(result);
writeFileSync(join(out,'results.json'),JSON.stringify(results,null,2)+'\n');
console.log(id+' '+(killed?'KILLED':'SURVIVED/INVALID'));
if(!killed) {process.exitCode=1;break;}
}
} finally {rmSync(scratch,{recursive:true,force:true});}
@@ -0,0 +1,54 @@
CLEAR (current driver ancestors excluded)
✖ private tester HTTP serves on 0600 socket in 0700 parent and fixed origin (49.694568ms)
✖ private tester refuses symlink, unsafe parent and long path without repairs (1.885024ms)
✔ tester cleanup must leave a replacement inode untouched (2.384441ms)
ℹ tests 3
ℹ suites 0
ℹ pass 1
ℹ fail 2
ℹ cancelled 0
ℹ skipped 0
ℹ todo 0
ℹ duration_ms 121.753018
✖ failing tests:
test at packages/webui/tests/tester-socket.test.mjs:13:1
✖ private tester HTTP serves on 0600 socket in 0700 parent and fixed origin (49.694568ms)
AssertionError [ERR_ASSERTION]: Expected values to be strictly equal:
+ actual - expected
+ {
+ address: '127.0.0.1',
+ family: 'IPv4',
+ port: 41395
+ }
- '/mnt/storage/scratch/tmp/gJ3j/g196-w-L7UCok/console/tester.sock'
at TestContext.<anonymous> (file:///mnt/storage/scratch/tmp/gJ3j/g196-mut-crpRhv/packages/webui/tests/tester-socket.test.mjs:16:9)
at process.processTicksAndRejections (node:internal/process/task_queues:104:5)
at async Test.run (node:internal/test_runner/test:1409:7)
at async startSubtestAfterBootstrap (node:internal/test_runner/harness:387:3) {
generatedMessage: true,
code: 'ERR_ASSERTION',
actual: { address: '127.0.0.1', family: 'IPv4', port: 41395 },
expected: '/mnt/storage/scratch/tmp/gJ3j/g196-w-L7UCok/console/tester.sock',
operator: 'strictEqual',
diff: 'simple'
}
test at packages/webui/tests/tester-socket.test.mjs:26:1
✖ private tester refuses symlink, unsafe parent and long path without repairs (1.885024ms)
AssertionError [ERR_ASSERTION]: unsafe tester bind must refuse
at refused (file:///mnt/storage/scratch/tmp/gJ3j/g196-mut-crpRhv/packages/webui/tests/tester-socket.test.mjs:11:9)
at process.processTicksAndRejections (node:internal/process/task_queues:104:5)
at async TestContext.<anonymous> (file:///mnt/storage/scratch/tmp/gJ3j/g196-mut-crpRhv/packages/webui/tests/tester-socket.test.mjs:29:2)
at async Test.run (node:internal/test_runner/test:1409:7)
at async Test.processPendingSubtests (node:internal/test_runner/test:974:7) {
generatedMessage: false,
code: 'ERR_ASSERTION',
actual: false,
expected: true,
operator: '==',
diff: 'simple'
}
@@ -0,0 +1,33 @@
CLEAR (current driver ancestors excluded)
✔ principal binds kernel peer PID and uid, maps owner, and never takes identity or role from body (1.647432ms)
✖ user requires its own nonce and full walk plus immediate same-tree Console parent (0.71001ms)
✔ tester deliberately admits agent parents but rejects managed runs and another nonce (0.285297ms)
✔ human leaf identity is reread after walking the parents (0.167787ms)
ℹ tests 4
ℹ suites 0
ℹ pass 3
ℹ fail 1
ℹ cancelled 0
ℹ skipped 0
ℹ todo 0
ℹ duration_ms 59.125304
✖ failing tests:
test at packages/bus/tests/principal.test.mjs:22:1
✖ user requires its own nonce and full walk plus immediate same-tree Console parent (0.71001ms)
AssertionError [ERR_ASSERTION]: Missing expected exception.
at TestContext.<anonymous> (file:///mnt/storage/scratch/tmp/gJ3j/g196-mut-crpRhv/packages/bus/tests/principal.test.mjs:27:10)
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: undefined,
expected: /human-required/,
operator: 'throws',
diff: 'simple'
}
@@ -0,0 +1,33 @@
CLEAR (current driver ancestors excluded)
✔ principal binds kernel peer PID and uid, maps owner, and never takes identity or role from body (1.685528ms)
✖ user requires its own nonce and full walk plus immediate same-tree Console parent (0.680266ms)
✔ tester deliberately admits agent parents but rejects managed runs and another nonce (0.286586ms)
✔ human leaf identity is reread after walking the parents (0.150943ms)
ℹ tests 4
ℹ suites 0
ℹ pass 3
ℹ fail 1
ℹ cancelled 0
ℹ skipped 0
ℹ todo 0
ℹ duration_ms 51.004525
✖ failing tests:
test at packages/bus/tests/principal.test.mjs:22:1
✖ user requires its own nonce and full walk plus immediate same-tree Console parent (0.680266ms)
AssertionError [ERR_ASSERTION]: Missing expected exception.
at TestContext.<anonymous> (file:///mnt/storage/scratch/tmp/gJ3j/g196-mut-crpRhv/packages/bus/tests/principal.test.mjs:27:10)
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: undefined,
expected: /human-required/,
operator: 'throws',
diff: 'simple'
}
@@ -0,0 +1,31 @@
CLEAR (current driver ancestors excluded)
✖ principal binds kernel peer PID and uid, maps owner, and never takes identity or role from body (1.927254ms)
✔ user requires its own nonce and full walk plus immediate same-tree Console parent (0.491561ms)
✔ tester deliberately admits agent parents but rejects managed runs and another nonce (0.278262ms)
✔ human leaf identity is reread after walking the parents (0.159989ms)
ℹ tests 4
ℹ suites 0
ℹ pass 3
ℹ fail 1
ℹ cancelled 0
ℹ skipped 0
ℹ todo 0
ℹ duration_ms 53.494457
✖ failing tests:
test at packages/bus/tests/principal.test.mjs:15:1
✖ principal binds kernel peer PID and uid, maps owner, and never takes identity or role from body (1.927254ms)
AssertionError [ERR_ASSERTION]: Missing expected exception.
at TestContext.<anonymous> (file:///mnt/storage/scratch/tmp/gJ3j/g196-mut-crpRhv/packages/bus/tests/principal.test.mjs:19:9)
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: undefined,
expected: /human-required/,
operator: 'throws',
diff: 'simple'
}
@@ -0,0 +1,55 @@
CLEAR (current driver ancestors excluded)
✖ proof receives actual peer, cap traffic never invokes helper and transient cap disconnects (9.052406ms)
✔ proof helper error and disconnect during verification never dispatch or bind (1.997863ms)
✔ Client receives explicit proof refusal codes without dispatch, not outcome-unknown (43.098804ms)
✖ asynchronous proof calls release their transient session on success, failure and timeout (2.35303ms)
ℹ tests 4
ℹ suites 0
ℹ pass 2
ℹ fail 2
ℹ cancelled 0
ℹ skipped 0
ℹ todo 0
ℹ duration_ms 114.087067
✖ failing tests:
test at packages/bus/tests/peer-transport.test.mjs:11:1
✖ proof receives actual peer, cap traffic never invokes helper and transient cap disconnects (9.052406ms)
AssertionError [ERR_ASSERTION]: The expression evaluated to a falsy value:
assert.ok(disconnected.includes('transient'))
at TestContext.<anonymous> (file:///mnt/storage/scratch/tmp/gJ3j/g196-mut-crpRhv/packages/bus/tests/peer-transport.test.mjs:18:52)
at process.processTicksAndRejections (node:internal/process/task_queues:104:5)
at async Test.run (node:internal/test_runner/test:1409:7)
at async startSubtestAfterBootstrap (node:internal/test_runner/harness:387:3) {
generatedMessage: true,
code: 'ERR_ASSERTION',
actual: false,
expected: true,
operator: '==',
diff: 'simple'
}
test at packages/bus/tests/peer-transport.test.mjs:46:1
✖ asynchronous proof calls release their transient session on success, failure and timeout (2.35303ms)
AssertionError [ERR_ASSERTION]: Expected values to be strictly deep-equal:
+ actual - expected
+ []
- [
- 'success'
- ]
at TestContext.<anonymous> (file:///mnt/storage/scratch/tmp/gJ3j/g196-mut-crpRhv/packages/bus/tests/peer-transport.test.mjs:56:11)
at process.processTicksAndRejections (node:internal/process/task_queues:104:5)
at async Test.run (node:internal/test_runner/test:1409:7)
at async Test.processPendingSubtests (node:internal/test_runner/test:974:7) {
generatedMessage: true,
code: 'ERR_ASSERTION',
actual: [],
expected: [ 'success' ],
operator: 'deepStrictEqual',
diff: 'simple'
}
@@ -0,0 +1,93 @@
CLEAR (current driver ancestors excluded)
✖ principal binds kernel peer PID and uid, maps owner, and never takes identity or role from body (2.090667ms)
✖ user requires its own nonce and full walk plus immediate same-tree Console parent (0.460285ms)
✔ tester deliberately admits agent parents but rejects managed runs and another nonce (0.249211ms)
✖ human leaf identity is reread after walking the parents (0.26504ms)
ℹ tests 4
ℹ suites 0
ℹ pass 1
ℹ fail 3
ℹ cancelled 0
ℹ skipped 0
ℹ todo 0
ℹ duration_ms 62.713304
✖ failing tests:
test at packages/bus/tests/principal.test.mjs:15:1
✖ principal binds kernel peer PID and uid, maps owner, and never takes identity or role from body (2.090667ms)
AssertionError [ERR_ASSERTION]: The input did not match the regular expression /human-required/. Input:
'Error: tester-required'
at TestContext.<anonymous> (file:///mnt/storage/scratch/tmp/gJ3j/g196-mut-crpRhv/packages/bus/tests/principal.test.mjs:19:9)
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: Error: tester-required
at refuse (file:///mnt/storage/scratch/tmp/gJ3j/g196-mut-crpRhv/packages/bus/src/tester.mjs:13:9)
at verifyTester (file:///mnt/storage/scratch/tmp/gJ3j/g196-mut-crpRhv/packages/bus/src/tester.mjs:40:5)
at verifyPrincipal (file:///mnt/storage/scratch/tmp/gJ3j/g196-mut-crpRhv/packages/bus/src/principal.mjs:31:24)
at file:///mnt/storage/scratch/tmp/gJ3j/g196-mut-crpRhv/packages/bus/tests/principal.test.mjs:19:20
at getActual (node:assert:580:5)
at strict.throws (node:assert:728:24)
at TestContext.<anonymous> (file:///mnt/storage/scratch/tmp/gJ3j/g196-mut-crpRhv/packages/bus/tests/principal.test.mjs:19:9)
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),
expected: /human-required/,
operator: 'throws',
diff: 'simple'
}
test at packages/bus/tests/principal.test.mjs:22:1
✖ user requires its own nonce and full walk plus immediate same-tree Console parent (0.460285ms)
AssertionError [ERR_ASSERTION]: Missing expected exception.
at TestContext.<anonymous> (file:///mnt/storage/scratch/tmp/gJ3j/g196-mut-crpRhv/packages/bus/tests/principal.test.mjs:27:10)
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: undefined,
expected: /human-required/,
operator: 'throws',
diff: 'simple'
}
test at packages/bus/tests/principal.test.mjs:44:1
✖ human leaf identity is reread after walking the parents (0.26504ms)
AssertionError [ERR_ASSERTION]: The input did not match the regular expression /human-required/. Input:
'Error: tester-required'
at TestContext.<anonymous> (file:///mnt/storage/scratch/tmp/gJ3j/g196-mut-crpRhv/packages/bus/tests/principal.test.mjs:47:9)
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 Test.processPendingSubtests (node:internal/test_runner/test:974:7) {
generatedMessage: true,
code: 'ERR_ASSERTION',
actual: Error: tester-required
at refuse (file:///mnt/storage/scratch/tmp/gJ3j/g196-mut-crpRhv/packages/bus/src/tester.mjs:13:9)
at verifyTester (file:///mnt/storage/scratch/tmp/gJ3j/g196-mut-crpRhv/packages/bus/src/tester.mjs:70:5)
at verifyPrincipal (file:///mnt/storage/scratch/tmp/gJ3j/g196-mut-crpRhv/packages/bus/src/principal.mjs:31:24)
at file:///mnt/storage/scratch/tmp/gJ3j/g196-mut-crpRhv/packages/bus/tests/principal.test.mjs:47:20
at getActual (node:assert:580:5)
at strict.throws (node:assert:728:24)
at TestContext.<anonymous> (file:///mnt/storage/scratch/tmp/gJ3j/g196-mut-crpRhv/packages/bus/tests/principal.test.mjs:47:9)
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),
expected: /human-required/,
operator: 'throws',
diff: 'simple'
}
@@ -0,0 +1,40 @@
CLEAR (current driver ancestors excluded)
✖ row196 explicit socket conversion refuses inherited TCP before stop and rollback disables socket (3916.205226ms)
ℹ tests 1
ℹ suites 0
ℹ pass 0
ℹ fail 1
ℹ cancelled 0
ℹ skipped 0
ℹ todo 0
ℹ duration_ms 4304.679862
✖ failing tests:
test at packages/cli/tests/stack-release.test.mjs:2277:1
✖ row196 explicit socket conversion refuses inherited TCP before stop and rollback disables socket (3916.205226ms)
AssertionError [ERR_ASSERTION]: stack-release: interpreter: {"path":"/usr/bin/python3","sha256":"fixture"}
stack-release: captured the rollback set: /mnt/storage/scratch/tmp/gJ3j/sr-hy3kBG/data/state/stack-rollback/20261021T185103Z-1263a5e3af50
stack-release: drop --tester-port 7333 (replaced by private Unix socket); the target Console does not support it
stack-release: stopped both units
stack-release: stopped store schema: 3g
stack-release: backup: /mnt/storage/scratch/tmp/gJ3j/sr-hy3kBG/data/backups/stack/20261021T185103Z-1263a5e3af50
stack-release: rolling back: install-console
stack-release: stopped both units
stack-release: put back the unit files and the wrapper from /mnt/storage/scratch/tmp/gJ3j/sr-hy3kBG/data/state/stack-rollback/20261021T185103Z-1263a5e3af50
stack-release: gate passed on 901e0b11e9113fbb981db30e6fbe83635540d1f3
stack-release: install-console; rolled back to 901e0b11e9113fbb981db30e6fbe83635540d1f3, which passed the gate
1 !== 0
at TestContext.<anonymous> (file:///mnt/storage/scratch/tmp/gJ3j/g196-mut-crpRhv/packages/cli/tests/stack-release.test.mjs:2290:78)
at async Test.run (node:internal/test_runner/test:1409:7)
at async startSubtestAfterBootstrap (node:internal/test_runner/harness:387:3) {
generatedMessage: false,
code: 'ERR_ASSERTION',
actual: 1,
expected: 0,
operator: 'strictEqual',
diff: 'simple'
}
@@ -0,0 +1,69 @@
CLEAR (current driver ancestors excluded)
✖ row196 explicit socket conversion refuses inherited TCP before stop and rollback disables socket (5111.05339ms)
ℹ tests 1
ℹ suites 0
ℹ pass 0
ℹ fail 1
ℹ cancelled 0
ℹ skipped 0
ℹ todo 0
ℹ duration_ms 5664.207303
✖ failing tests:
test at packages/cli/tests/stack-release.test.mjs:2277:1
✖ row196 explicit socket conversion refuses inherited TCP before stop and rollback disables socket (5111.05339ms)
AssertionError [ERR_ASSERTION]: The input was expected to not match the regular expression /--tester-(socket|port)/. Input:
'# Mosaic Console: `node packages/webui/src/cli.mjs serve`, one instance.\n' +
'# Rendered by scripts/webui-service.sh from packages/webui/systemd/; /mnt/storage/scratch/tmp/gJ3j/stack-release-bKqva6/copies/0d30b8dfbb41b6ca567206d291451b1f5b7d13bd,\n' +
'# /usr/local/bin:/usr/bin:/bin, /usr/bin/node and the serve flags below are filled in at install time.\n' +
'# /mnt/storage/scratch/tmp/gJ3j/stack-release-bKqva6/copies/0d30b8dfbb41b6ca567206d291451b1f5b7d13bd is the read-only copy `install --copy ID` names, and the flags\n' +
'# pass the canonical checkout as --checkout for the queue read (row 77).\n' +
'# Row 178: optional --proof-header NAME --proof-file PATH checks proxy proof;\n' +
'# the value is read once by serve and never carried in this unit.\n' +
'# Edit the template and reinstall; do not edit the installed unit.\n' +
'#\n' +
'# The Console reads the bus through the human transport, so whoever starts\n' +
"# this unit holds the human's capability. Jason starts and restarts it\n" +
'# (`systemctl --user enable --now mosaic-webui`), and so may Mos under lead\n' +
'# decision 102, which accepts that sends then enter the trail as Jason. Any\n' +
'# other agent starting it works around the human proof (lead decisions 62\n' +
'# and 94). The unit sets no MOSAIC_*, CLAUDE*, CODEX* or PI_* variable.\n' +
'[Unit]\n' +
'Description=Mosaic Console\n' +
'Documentation=file:///mnt/storage/scratch/tmp/gJ3j/stack-release-bKqva6/copies/0d30b8dfbb41b6ca567206d291451b1f5b7d13bd/packages/webui/README.md\n' +
'StartLimitIntervalSec=600\n' +
'StartLimitBurst=5\n' +
'\n' +
'[Service]\n' +
'Type=simple\n' +
'WorkingDirectory=/mnt/storage/scratch/tmp/gJ3j/stack-release-bKqva6/copies/0d30b8dfbb41b6ca567206d291451b1f5b7d13bd\n' +
'Environment=PATH=/usr/local/bin:/usr/bin:/bin\n' +
'# The server binds 127.0.0.1 only. Usage, config and listen refusals\n' +
'# (a port already in use included) exit 2 and are not retried; fix the\n' +
'# flags or free the port, then start it again.\n' +
'ExecStart=/usr/bin/node /mnt/storage/scratch/tmp/gJ3j/stack-release-bKqva6/copies/0d30b8dfbb41b6ca567206d291451b1f5b7d13bd/packages/webui/src/cli.mjs serve --checkout /mnt/storage/scratch/tmp/gJ3j/stack-release-bKqva6/repo --port 41915 --business acme --public-host console.example --auth-header x-user --allow-user jason --proof-header x-console-proof --proof-file /mnt/storage/scratch/tmp/gJ3j/sr-K2QFsd/proof.187 --tester-port 7333\n' +
'Restart=on-failure\n' +
'RestartSec=15\n' +
'RestartPreventExitStatus=2\n' +
'# `systemctl --user stop` sends SIGTERM; the server stops accepting and\n' +
'# closes. Open connections that outlast TimeoutStopSec are killed.\n' +
'KillSignal=SIGTERM\n' +
'TimeoutStopSec=30\n' +
'NoNewPrivileges=yes\n' +
'\n' +
'[Install]\n' +
'WantedBy=default.target\n'
at TestContext.<anonymous> (file:///mnt/storage/scratch/tmp/gJ3j/g196-mut-crpRhv/packages/cli/tests/stack-release.test.mjs:2292:77)
at process.processTicksAndRejections (node:internal/process/task_queues:104:5)
at async Test.run (node:internal/test_runner/test:1409:7)
at async startSubtestAfterBootstrap (node:internal/test_runner/harness:387:3) {
generatedMessage: true,
code: 'ERR_ASSERTION',
actual: "# Mosaic Console: `node packages/webui/src/cli.mjs serve`, one instance.\n# Rendered by scripts/webui-service.sh from packages/webui/systemd/; /mnt/storage/scratch/tmp/gJ3j/stack-release-bKqva6/copies/0d30b8dfbb41b6ca567206d291451b1f5b7d13bd,\n# /usr/local/bin:/usr/bin:/bin, /usr/bin/node and the serve flags below are filled in at install time.\n# /mnt/storage/scratch/tmp/gJ3j/stack-release-bKqva6/copies/0d30b8dfbb41b6ca567206d291451b1f5b7d13bd is the read-only copy `install --copy ID` names, and the flags\n# pass the canonical checkout as --checkout for the queue read (row 77).\n# Row 178: optional --proof-header NAME --proof-file PATH checks proxy proof;\n# the value is read once by serve and never carried in this unit.\n# Edit the template and reinstall; do not edit the installed unit.\n#\n# The Console reads the bus through the human transport, so whoever starts\n# this unit holds the human's capability. Jason starts and restarts it\n# (`systemctl --user enable --now mosaic-webui`), and so may Mos under lead\n# decision 102, which accepts that sends then enter the trail as Jason. Any\n# other agent starting it works around the human proof (lead decisions 62\n# and 94). The unit sets no MOSAIC_*, CLAUDE*, CODEX* or PI_* variable.\n[Unit]\nDescription=Mosaic Console\nDocumentation=file:///mnt/storage/scratch/tmp/gJ3j/stack-release-bKqva6/copies/0d30b8dfbb41b6ca567206d291451b1f5b7d13bd/packages/webui/README.md\nStartLimitIntervalSec=600\nStartLimitBurst=5\n\n[Service]\nType=simple\nWorkingDirectory=/mnt/storage/scratch/tmp/gJ3j/stack-release-bKqva6/copies/0d30b8dfbb41b6ca567206d291451b1f5b7d13bd\nEnvironment=PATH=/usr/local/bin:/usr/bin:/bin\n# The server binds 127.0.0.1 only. Usage, config and listen refusals\n# (a port already in use included) exit 2 and are not retried; fix the\n# flags or free the port, then start it again.\nExecStart=/usr/bin/node /mnt/storage/scratch/tmp/gJ3j/stack-release-bKqva6/copies/0d30b8dfbb41b6ca567206d291451b1f5b7d13bd/packages/webui/src/cli.mjs serve --checkout /mnt/storage/scratch/tmp/gJ3j/stack-release-bKqva6/repo --port 41915 --business acme --public-host console.example --auth-header x-user --allow-user jason --proof-header x-console-proof --proof-file /mnt/storage/scratch/tmp/gJ3j/sr-K2QFsd/proof.187 --tester-port 7333\nRestart=on-failure\nRestartSec=15\nRestartPreventExitStatus=2\n# `systemctl --user stop` sends SIGTERM; the server stops accepting and\n# closes. Open connections that outlast TimeoutStopSec are killed.\nKillSignal=SIGTERM\nTimeoutStopSec=30\nNoNewPrivileges=yes\n\n[Install]\nWantedBy=default.target\n",
expected: /--tester-(socket|port)/,
operator: 'doesNotMatch',
diff: 'simple'
}
@@ -0,0 +1,67 @@
CLEAR (current driver ancestors excluded)
✖ the tester binds only when the business sets bus.tester to exactly true (17.53619ms)
✔ a business may not name a role tester, so the tester address is never a role (17.82968ms)
✔ every verb outside the tester list refuses tester-refused and is recorded against the tester (36.71909ms)
✔ a tester message is from tester, needs no prefix, and writes no human.input or authority (14.532304ms)
✔ a tester message to a role no run holds refuses tester-target-not-running and stores nothing (10.145587ms)
✔ a tester message is refused while the run is only launched, and routes once it claims (12.444532ms)
✔ a human message to a role no run holds still waits and tells launch.by (10.928459ms)
✔ a role answers a tester only in reply to a tester message, and the answer never reaches the human (20.898774ms)
✔ the tester reads the human views a reader reads (24.490999ms)
✔ tester proof binds its own CLI, start time and nonce, refuses a managed run and allows an agent (1.344138ms)
✔ neither CLI's nonce proves the other: each proof is bound to its own CLI and nonce variable (0.406093ms)
✔ testerNonce reads only the tester's variable and gives null on any read error (0.198658ms)
✔ an envelope carries exactly one of cap, human and tester; no tester authenticator refuses (118.437146ms)
✖ tester-cli.mjs binds through the runtime when the switch is on, and refuses off or under a run marker (1234.579269ms)
ℹ tests 14
ℹ suites 0
ℹ pass 12
ℹ fail 2
ℹ cancelled 0
ℹ skipped 0
ℹ todo 0
ℹ duration_ms 1628.145277
✖ failing tests:
test at packages/bus/tests/tester.test.mjs:44:1
✖ the tester binds only when the business sets bus.tester to exactly true (17.53619ms)
AssertionError [ERR_ASSERTION]: Missing expected exception: null
at TestContext.<anonymous> (file:///mnt/storage/scratch/tmp/gJ3j/g196-mut-crpRhv/packages/bus/tests/tester.test.mjs:47:12)
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: undefined,
expected: { code: 'tester-off' },
operator: 'throws',
diff: 'simple'
}
test at packages/bus/tests/tester.test.mjs:427:1
✖ tester-cli.mjs binds through the runtime when the switch is on, and refuses off or under a run marker (1234.579269ms)
AssertionError [ERR_ASSERTION]: Expected values to be strictly deep-equal:
+ actual - expected
{
+ err: '',
+ out: '[]',
+ status: 0
- err: 'tester-off',
- out: '',
- status: 2
}
at TestContext.<anonymous> (file:///mnt/storage/scratch/tmp/gJ3j/g196-mut-crpRhv/packages/bus/tests/tester.test.mjs:448:10)
at process.processTicksAndRejections (node:internal/process/task_queues:104:5)
at async Test.run (node:internal/test_runner/test:1409:7)
at async Test.processPendingSubtests (node:internal/test_runner/test:974:7) {
generatedMessage: true,
code: 'ERR_ASSERTION',
actual: { status: 0, out: '[]', err: '' },
expected: { status: 2, out: '', err: 'tester-off' },
operator: 'deepStrictEqual',
diff: 'simple'
}
@@ -0,0 +1,42 @@
CLEAR (current driver ancestors excluded)
✔ principal binds kernel peer PID and uid, maps owner, and never takes identity or role from body (1.996624ms)
✖ user requires its own nonce and full walk plus immediate same-tree Console parent (1.266948ms)
✔ tester deliberately admits agent parents but rejects managed runs and another nonce (0.350185ms)
✔ human leaf identity is reread after walking the parents (0.203698ms)
ℹ tests 4
ℹ suites 0
ℹ pass 3
ℹ fail 1
ℹ cancelled 0
ℹ skipped 0
ℹ todo 0
ℹ duration_ms 76.258279
✖ failing tests:
test at packages/bus/tests/principal.test.mjs:22:1
✖ user requires its own nonce and full walk plus immediate same-tree Console parent (1.266948ms)
AssertionError [ERR_ASSERTION]: Expected values to be strictly deep-equal:
+ actual - expected
{
business: 'demo',
id: 'owner',
+ kind: 'owner'
- kind: 'user'
}
at TestContext.<anonymous> (file:///mnt/storage/scratch/tmp/gJ3j/g196-mut-crpRhv/packages/bus/tests/principal.test.mjs:24:9)
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: { kind: 'owner', business: 'demo', id: 'owner' },
expected: { kind: 'user', business: 'demo', id: 'owner' },
operator: 'deepStrictEqual',
diff: 'simple'
}
@@ -0,0 +1,24 @@
CLEAR (current driver ancestors excluded)
✖ private tester HTTP serves on 0600 socket in 0700 parent and fixed origin (56.676653ms)
✔ private tester refuses symlink, unsafe parent and long path without repairs (1.634686ms)
✔ tester cleanup must leave a replacement inode untouched (1.97195ms)
ℹ tests 3
ℹ suites 0
ℹ pass 2
ℹ fail 1
ℹ cancelled 0
ℹ skipped 0
ℹ todo 0
ℹ duration_ms 131.19279
✖ failing tests:
test at packages/webui/tests/tester-socket.test.mjs:13:1
✖ private tester HTTP serves on 0600 socket in 0700 parent and fixed origin (56.676653ms)
Error: connect ENOENT /mnt/storage/scratch/tmp/gJ3j/g196-w-6wAD6F/wrong-instance/console/tester.sock
at PipeConnectWrap.afterConnect [as oncomplete] (node:net:2021:16) {
errno: -2,
code: 'ENOENT',
syscall: 'connect',
address: '/mnt/storage/scratch/tmp/gJ3j/g196-w-6wAD6F/wrong-instance/console/tester.sock'
}
@@ -0,0 +1,32 @@
CLEAR (current driver ancestors excluded)
✖ private tester HTTP serves on 0600 socket in 0700 parent and fixed origin (40.421617ms)
✔ private tester refuses symlink, unsafe parent and long path without repairs (0.920109ms)
✖ tester cleanup must leave a replacement inode untouched (0.939648ms)
ℹ tests 3
ℹ suites 0
ℹ pass 1
ℹ fail 2
ℹ cancelled 0
ℹ skipped 0
ℹ todo 0
ℹ duration_ms 102.876749
✖ failing tests:
test at packages/webui/tests/tester-socket.test.mjs:13:1
✖ private tester HTTP serves on 0600 socket in 0700 parent and fixed origin (40.421617ms)
Error: tester-socket-unsafe
at bindTesterSocket (file:///mnt/storage/scratch/tmp/gJ3j/g196-mut-crpRhv/packages/webui/src/tester-socket.mjs:29:102)
at process.processTicksAndRejections (node:internal/process/task_queues:104:5)
at async TestContext.<anonymous> (file:///mnt/storage/scratch/tmp/gJ3j/g196-mut-crpRhv/packages/webui/tests/tester-socket.test.mjs:15:12)
at async Test.run (node:internal/test_runner/test:1409:7)
at async startSubtestAfterBootstrap (node:internal/test_runner/harness:387:3)
test at packages/webui/tests/tester-socket.test.mjs:36:1
✖ tester cleanup must leave a replacement inode untouched (0.939648ms)
Error: tester-socket-unsafe
at bindTesterSocket (file:///mnt/storage/scratch/tmp/gJ3j/g196-mut-crpRhv/packages/webui/src/tester-socket.mjs:29:102)
at process.processTicksAndRejections (node:internal/process/task_queues:104:5)
at async TestContext.<anonymous> (file:///mnt/storage/scratch/tmp/gJ3j/g196-mut-crpRhv/packages/webui/tests/tester-socket.test.mjs:42:15)
at async Test.run (node:internal/test_runner/test:1409:7)
at async Test.processPendingSubtests (node:internal/test_runner/test:974:7)
@@ -0,0 +1,53 @@
CLEAR (current driver ancestors excluded)
✖ T2 eight proof callers and a racing copier bind the exact accepted socket, which survives helper exit (292.257839ms)
✔ helper output and failures fail closed including unsafe gid (2.680068ms)
✔ helper timeout, capacity and shutdown refuse without leaving children (25.751457ms)
✔ missing or non-regular peer helper refuses at construction before opening the bus (0.36369ms)
✖ shutdown waits for killed helper close before returning (0.370388ms)
ℹ tests 5
ℹ suites 0
ℹ pass 3
ℹ fail 2
ℹ cancelled 0
ℹ skipped 0
ℹ todo 0
ℹ duration_ms 401.085642
✖ failing tests:
test at packages/bus/tests/peercred.test.mjs:15:1
✖ T2 eight proof callers and a racing copier bind the exact accepted socket, which survives helper exit (292.257839ms)
AssertionError [ERR_ASSERTION]: Expected values to be strictly equal:
false !== true
at file:///mnt/storage/scratch/tmp/gJ3j/g196-mut-crpRhv/packages/bus/tests/peercred.test.mjs:56:12
at Array.forEach (<anonymous>)
at TestContext.<anonymous> (file:///mnt/storage/scratch/tmp/gJ3j/g196-mut-crpRhv/packages/bus/tests/peercred.test.mjs:52:11)
at process.processTicksAndRejections (node:internal/process/task_queues:104:5)
at async Test.run (node:internal/test_runner/test:1409:7)
at async startSubtestAfterBootstrap (node:internal/test_runner/harness:387:3) {
generatedMessage: true,
code: 'ERR_ASSERTION',
actual: false,
expected: true,
operator: 'strictEqual',
diff: 'simple'
}
test at packages/bus/tests/peercred.test.mjs:98:1
✖ shutdown waits for killed helper close before returning (0.370388ms)
AssertionError [ERR_ASSERTION]: shutdown must await the helper close event
0 !== 1
at TestContext.<anonymous> (file:///mnt/storage/scratch/tmp/gJ3j/g196-mut-crpRhv/packages/bus/tests/peercred.test.mjs:106:9)
at async Test.run (node:internal/test_runner/test:1409:7)
at async Test.processPendingSubtests (node:internal/test_runner/test:974:7) {
generatedMessage: false,
code: 'ERR_ASSERTION',
actual: 0,
expected: 1,
operator: 'strictEqual',
diff: 'simple'
}
@@ -0,0 +1,32 @@
CLEAR (current driver ancestors excluded)
✖ system Python is a resolved root-owned ELF, never a shim or home interpreter (2.489468ms)
✔ real system interpreter is available and safely inspected (22.481127ms)
✔ restart receipt appends actual replacement identity and refuses unsafe files (9.851443ms)
✔ activation gate compares fresh startup receipt to the current interpreter identity (1.482676ms)
✔ non-regular startup receipt refuses promptly without waiting on a FIFO peer (33.784543ms)
ℹ tests 5
ℹ suites 0
ℹ pass 4
ℹ fail 1
ℹ cancelled 0
ℹ skipped 0
ℹ todo 0
ℹ duration_ms 128.30192
✖ failing tests:
test at packages/bus/tests/interpreter.test.mjs:15:1
✖ system Python is a resolved root-owned ELF, never a shim or home interpreter (2.489468ms)
AssertionError [ERR_ASSERTION]: Missing expected exception.
at TestContext.<anonymous> (file:///mnt/storage/scratch/tmp/gJ3j/g196-mut-crpRhv/packages/bus/tests/interpreter.test.mjs:31:31)
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: undefined,
expected: /interpreter-unsafe/,
operator: 'throws',
diff: 'simple'
}
@@ -0,0 +1,33 @@
CLEAR (current driver ancestors excluded)
✖ proof receives actual peer, cap traffic never invokes helper and transient cap disconnects (7.03352ms)
✔ proof helper error and disconnect during verification never dispatch or bind (1.790604ms)
✔ Client receives explicit proof refusal codes without dispatch, not outcome-unknown (42.381026ms)
✔ asynchronous proof calls release their transient session on success, failure and timeout (28.479209ms)
ℹ tests 4
ℹ suites 0
ℹ pass 3
ℹ fail 1
ℹ cancelled 0
ℹ skipped 0
ℹ todo 0
ℹ duration_ms 135.242751
✖ failing tests:
test at packages/bus/tests/peer-transport.test.mjs:11:1
✖ proof receives actual peer, cap traffic never invokes helper and transient cap disconnects (7.03352ms)
AssertionError [ERR_ASSERTION]: Expected values to be strictly equal:
2 !== 1
at TestContext.<anonymous> (file:///mnt/storage/scratch/tmp/gJ3j/g196-mut-crpRhv/packages/bus/tests/peer-transport.test.mjs:20:9)
at process.processTicksAndRejections (node:internal/process/task_queues:104:5)
at async Test.run (node:internal/test_runner/test:1409:7)
at async startSubtestAfterBootstrap (node:internal/test_runner/harness:387:3) {
generatedMessage: true,
code: 'ERR_ASSERTION',
actual: 2,
expected: 1,
operator: 'strictEqual',
diff: 'simple'
}
@@ -0,0 +1,45 @@
CLEAR (current driver ancestors excluded)
✔ system Python is a resolved root-owned ELF, never a shim or home interpreter (2.259714ms)
✔ real system interpreter is available and safely inspected (20.668185ms)
✖ restart receipt appends actual replacement identity and refuses unsafe files (2.992321ms)
✔ activation gate compares fresh startup receipt to the current interpreter identity (1.363356ms)
✖ non-regular startup receipt refuses promptly without waiting on a FIFO peer (1005.398641ms)
ℹ tests 5
ℹ suites 0
ℹ pass 3
ℹ fail 2
ℹ cancelled 0
ℹ skipped 0
ℹ todo 0
ℹ duration_ms 1086.382437
✖ failing tests:
test at packages/bus/tests/interpreter.test.mjs:38:1
✖ restart receipt appends actual replacement identity and refuses unsafe files (2.992321ms)
SyntaxError: "undefined" is not valid JSON
at JSON.parse (<anonymous>)
at TestContext.<anonymous> (file:///mnt/storage/scratch/tmp/gJ3j/g196-mut-crpRhv/packages/bus/tests/interpreter.test.mjs:49:48)
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 Test.processPendingSubtests (node:internal/test_runner/test:974:7)
test at packages/bus/tests/interpreter.test.mjs:69:1
✖ non-regular startup receipt refuses promptly without waiting on a FIFO peer (1005.398641ms)
AssertionError [ERR_ASSERTION]: unsafe FIFO must refuse without blocking startup
null !== 0
at TestContext.<anonymous> (file:///mnt/storage/scratch/tmp/gJ3j/g196-mut-crpRhv/packages/bus/tests/interpreter.test.mjs:75:9)
at async Test.run (node:internal/test_runner/test:1409:7)
at async Test.processPendingSubtests (node:internal/test_runner/test:974:7) {
generatedMessage: false,
code: 'ERR_ASSERTION',
actual: null,
expected: 0,
operator: 'strictEqual',
diff: 'simple'
}
@@ -0,0 +1,32 @@
CLEAR (current driver ancestors excluded)
✖ private tester HTTP serves on 0600 socket in 0700 parent and fixed origin (59.338551ms)
✔ private tester refuses symlink, unsafe parent and long path without repairs (1.388654ms)
✔ tester cleanup must leave a replacement inode untouched (1.72767ms)
ℹ tests 3
ℹ suites 0
ℹ pass 2
ℹ fail 1
ℹ cancelled 0
ℹ skipped 0
ℹ todo 0
ℹ duration_ms 128.502051
✖ failing tests:
test at packages/webui/tests/tester-socket.test.mjs:13:1
✖ private tester HTTP serves on 0600 socket in 0700 parent and fixed origin (59.338551ms)
AssertionError [ERR_ASSERTION]: Expected values to be strictly equal:
200 !== 403
at TestContext.<anonymous> (file:///mnt/storage/scratch/tmp/gJ3j/g196-mut-crpRhv/packages/webui/tests/tester-socket.test.mjs:21:10)
at process.processTicksAndRejections (node:internal/process/task_queues:104:5)
at async Test.run (node:internal/test_runner/test:1409:7)
at async startSubtestAfterBootstrap (node:internal/test_runner/harness:387:3) {
generatedMessage: true,
code: 'ERR_ASSERTION',
actual: 200,
expected: 403,
operator: 'strictEqual',
diff: 'simple'
}
@@ -0,0 +1,31 @@
CLEAR (current driver ancestors excluded)
✖ principal binds kernel peer PID and uid, maps owner, and never takes identity or role from body (1.691534ms)
✔ user requires its own nonce and full walk plus immediate same-tree Console parent (0.594758ms)
✔ tester deliberately admits agent parents but rejects managed runs and another nonce (0.274537ms)
✔ human leaf identity is reread after walking the parents (0.160418ms)
ℹ tests 4
ℹ suites 0
ℹ pass 3
ℹ fail 1
ℹ cancelled 0
ℹ skipped 0
ℹ todo 0
ℹ duration_ms 52.34153
✖ failing tests:
test at packages/bus/tests/principal.test.mjs:15:1
✖ principal binds kernel peer PID and uid, maps owner, and never takes identity or role from body (1.691534ms)
AssertionError [ERR_ASSERTION]: Missing expected exception.
at TestContext.<anonymous> (file:///mnt/storage/scratch/tmp/gJ3j/g196-mut-crpRhv/packages/bus/tests/principal.test.mjs:18:77)
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: undefined,
expected: /human-required/,
operator: 'throws',
diff: 'simple'
}
@@ -0,0 +1,35 @@
CLEAR (current driver ancestors excluded)
✔ T2 eight proof callers and a racing copier bind the exact accepted socket, which survives helper exit (252.611828ms)
✖ helper output and failures fail closed including unsafe gid (1.307507ms)
✔ helper timeout, capacity and shutdown refuse without leaving children (26.237379ms)
✔ missing or non-regular peer helper refuses at construction before opening the bus (0.252246ms)
✖ shutdown waits for killed helper close before returning (4.713775ms)
ℹ tests 5
ℹ suites 0
ℹ pass 3
ℹ fail 2
ℹ cancelled 0
ℹ skipped 0
ℹ todo 0
ℹ duration_ms 376.324408
✖ failing tests:
test at packages/bus/tests/peercred.test.mjs:74:1
✖ helper output and failures fail closed including unsafe gid (1.307507ms)
AssertionError [ERR_ASSERTION]: Missing expected rejection.
at process.processTicksAndRejections (node:internal/process/task_queues:104:5)
at async TestContext.<anonymous> (file:///mnt/storage/scratch/tmp/gJ3j/g196-mut-crpRhv/packages/bus/tests/peercred.test.mjs:77:5)
at async Test.run (node:internal/test_runner/test:1409:7)
at async Test.processPendingSubtests (node:internal/test_runner/test:974:7) {
generatedMessage: false,
code: 'ERR_ASSERTION',
actual: undefined,
expected: /peercred-required/,
operator: 'rejects',
diff: 'simple'
}
test at packages/bus/tests/peercred.test.mjs:98:1
✖ shutdown waits for killed helper close before returning (4.713775ms)
[AssertionError [ERR_ASSERTION]: Missing expected rejection.] { generatedMessage: false, code: 'ERR_ASSERTION', actual: undefined, expected: /peercred-required/, operator: 'rejects', diff: 'simple' }
@@ -0,0 +1,31 @@
CLEAR (current driver ancestors excluded)
✖ principal binds kernel peer PID and uid, maps owner, and never takes identity or role from body (1.939462ms)
✔ user requires its own nonce and full walk plus immediate same-tree Console parent (0.501573ms)
✔ tester deliberately admits agent parents but rejects managed runs and another nonce (0.285393ms)
✔ human leaf identity is reread after walking the parents (0.163302ms)
ℹ tests 4
ℹ suites 0
ℹ pass 3
ℹ fail 1
ℹ cancelled 0
ℹ skipped 0
ℹ todo 0
ℹ duration_ms 56.13375
✖ failing tests:
test at packages/bus/tests/principal.test.mjs:15:1
✖ principal binds kernel peer PID and uid, maps owner, and never takes identity or role from body (1.939462ms)
AssertionError [ERR_ASSERTION]: Missing expected exception.
at TestContext.<anonymous> (file:///mnt/storage/scratch/tmp/gJ3j/g196-mut-crpRhv/packages/bus/tests/principal.test.mjs:18:77)
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: undefined,
expected: /human-required/,
operator: 'throws',
diff: 'simple'
}
@@ -0,0 +1,30 @@
CLEAR (current driver ancestors excluded)
✔ private tester HTTP serves on 0600 socket in 0700 parent and fixed origin (87.784551ms)
✖ private tester refuses symlink, unsafe parent and long path without repairs (2.258513ms)
✔ tester cleanup must leave a replacement inode untouched (2.916815ms)
ℹ tests 3
ℹ suites 0
ℹ pass 2
ℹ fail 1
ℹ cancelled 0
ℹ skipped 0
ℹ todo 0
ℹ duration_ms 190.892552
✖ failing tests:
test at packages/webui/tests/tester-socket.test.mjs:26:1
✖ private tester refuses symlink, unsafe parent and long path without repairs (2.258513ms)
AssertionError [ERR_ASSERTION]: unsafe tester bind must refuse
at refused (file:///mnt/storage/scratch/tmp/gJ3j/g196-mut-crpRhv/packages/webui/tests/tester-socket.test.mjs:11:9)
at process.processTicksAndRejections (node:internal/process/task_queues:104:5)
at async TestContext.<anonymous> (file:///mnt/storage/scratch/tmp/gJ3j/g196-mut-crpRhv/packages/webui/tests/tester-socket.test.mjs:29:2)
at async Test.run (node:internal/test_runner/test:1409:7)
at async Test.processPendingSubtests (node:internal/test_runner/test:974:7) {
generatedMessage: false,
code: 'ERR_ASSERTION',
actual: false,
expected: true,
operator: '==',
diff: 'simple'
}
@@ -0,0 +1,33 @@
CLEAR (current driver ancestors excluded)
✔ principal binds kernel peer PID and uid, maps owner, and never takes identity or role from body (1.760997ms)
✖ user requires its own nonce and full walk plus immediate same-tree Console parent (0.759143ms)
✔ tester deliberately admits agent parents but rejects managed runs and another nonce (0.301518ms)
✔ human leaf identity is reread after walking the parents (0.176796ms)
ℹ tests 4
ℹ suites 0
ℹ pass 3
ℹ fail 1
ℹ cancelled 0
ℹ skipped 0
ℹ todo 0
ℹ duration_ms 63.261715
✖ failing tests:
test at packages/bus/tests/principal.test.mjs:22:1
✖ user requires its own nonce and full walk plus immediate same-tree Console parent (0.759143ms)
AssertionError [ERR_ASSERTION]: Missing expected exception.
at TestContext.<anonymous> (file:///mnt/storage/scratch/tmp/gJ3j/g196-mut-crpRhv/packages/bus/tests/principal.test.mjs:27:10)
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: undefined,
expected: /human-required/,
operator: 'throws',
diff: 'simple'
}
@@ -0,0 +1,33 @@
CLEAR (current driver ancestors excluded)
✔ principal binds kernel peer PID and uid, maps owner, and never takes identity or role from body (1.588487ms)
✔ user requires its own nonce and full walk plus immediate same-tree Console parent (0.483775ms)
✔ tester deliberately admits agent parents but rejects managed runs and another nonce (0.301471ms)
✖ human leaf identity is reread after walking the parents (0.520477ms)
ℹ tests 4
ℹ suites 0
ℹ pass 3
ℹ fail 1
ℹ cancelled 0
ℹ skipped 0
ℹ todo 0
ℹ duration_ms 58.566309
✖ failing tests:
test at packages/bus/tests/principal.test.mjs:44:1
✖ human leaf identity is reread after walking the parents (0.520477ms)
AssertionError [ERR_ASSERTION]: Missing expected exception.
at TestContext.<anonymous> (file:///mnt/storage/scratch/tmp/gJ3j/g196-mut-crpRhv/packages/bus/tests/principal.test.mjs:47:9)
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 Test.processPendingSubtests (node:internal/test_runner/test:974:7) {
generatedMessage: false,
code: 'ERR_ASSERTION',
actual: undefined,
expected: /human-required/,
operator: 'throws',
diff: 'simple'
}
@@ -0,0 +1,309 @@
[
{
"id": "M1",
"description": "tester uses TCP",
"test": "packages/webui/tests/tester-socket.test.mjs",
"args": [
"--test",
"packages/webui/tests/tester-socket.test.mjs"
],
"status": 1,
"signal": null,
"killed": true,
"restoredSha256": "da624f188c69e016ff82e1d1dcae1c579e68a57f0f25d773bb5d8bca56b80ee1"
},
{
"id": "M2",
"description": "socket mode widened",
"test": "packages/webui/tests/tester-socket.test.mjs",
"args": [
"--test",
"packages/webui/tests/tester-socket.test.mjs"
],
"status": 1,
"signal": null,
"killed": true,
"restoredSha256": "1c1fe0c795adac97f28502564a75f26d5666d9be5ae51169f7dd5bb7741fad16"
},
{
"id": "M3",
"description": "sign-in accepted on tester",
"test": "packages/webui/tests/tester-socket.test.mjs",
"args": [
"--test",
"packages/webui/tests/tester-socket.test.mjs"
],
"status": 1,
"signal": null,
"killed": true,
"restoredSha256": "da624f188c69e016ff82e1d1dcae1c579e68a57f0f25d773bb5d8bca56b80ee1"
},
{
"id": "M4",
"description": "copied PID accepted",
"test": "packages/bus/tests/principal.test.mjs",
"args": [
"--test",
"packages/bus/tests/principal.test.mjs"
],
"status": 1,
"signal": null,
"killed": true,
"restoredSha256": "32ef39d80b523bdd586c29620d7a752c309c8e1aa70befc1414125468f3411dd"
},
{
"id": "M5",
"description": "helper parse error ignored",
"test": "packages/bus/tests/peercred.test.mjs",
"args": [
"--test",
"packages/bus/tests/peercred.test.mjs"
],
"status": 1,
"signal": null,
"killed": true,
"restoredSha256": "29c88655e8b9aed8db604573c9213fd9f3c40ef35ff04f4eda8fbe8f1b8a32d7"
},
{
"id": "M6",
"description": "foreign uid accepted",
"test": "packages/bus/tests/principal.test.mjs",
"args": [
"--test",
"packages/bus/tests/principal.test.mjs"
],
"status": 1,
"signal": null,
"killed": true,
"restoredSha256": "32ef39d80b523bdd586c29620d7a752c309c8e1aa70befc1414125468f3411dd"
},
{
"id": "M7",
"description": "unsafe parent accepted",
"test": "packages/webui/tests/tester-socket.test.mjs",
"args": [
"--test",
"packages/webui/tests/tester-socket.test.mjs"
],
"status": 1,
"signal": null,
"killed": true,
"restoredSha256": "1c1fe0c795adac97f28502564a75f26d5666d9be5ae51169f7dd5bb7741fad16"
},
{
"id": "M8",
"description": "agent environment walk skipped",
"test": "packages/bus/tests/principal.test.mjs",
"args": [
"--test",
"packages/bus/tests/principal.test.mjs"
],
"status": 1,
"signal": null,
"killed": true,
"restoredSha256": "0c6d96abe4607cba0b02b0638a2fc217ff8135bac9a47761a0ef015fbe9d3079"
},
{
"id": "M9",
"description": "leaf identity not reread",
"test": "packages/bus/tests/principal.test.mjs",
"args": [
"--test",
"packages/bus/tests/principal.test.mjs"
],
"status": 1,
"signal": null,
"killed": true,
"restoredSha256": "0c6d96abe4607cba0b02b0638a2fc217ff8135bac9a47761a0ef015fbe9d3079"
},
{
"id": "M10",
"description": "Console direct-parent check removed",
"test": "packages/bus/tests/principal.test.mjs",
"args": [
"--test",
"packages/bus/tests/principal.test.mjs"
],
"status": 1,
"signal": null,
"killed": true,
"restoredSha256": "32ef39d80b523bdd586c29620d7a752c309c8e1aa70befc1414125468f3411dd"
},
{
"id": "M11",
"description": "Console basename accepted",
"test": "packages/bus/tests/principal.test.mjs",
"args": [
"--test",
"packages/bus/tests/principal.test.mjs"
],
"status": 1,
"signal": null,
"killed": true,
"restoredSha256": "32ef39d80b523bdd586c29620d7a752c309c8e1aa70befc1414125468f3411dd"
},
{
"id": "M12",
"description": "unknown proof identity accepted",
"test": "packages/bus/tests/principal.test.mjs",
"args": [
"--test",
"packages/bus/tests/principal.test.mjs"
],
"status": 1,
"signal": null,
"killed": true,
"restoredSha256": "0c6d96abe4607cba0b02b0638a2fc217ff8135bac9a47761a0ef015fbe9d3079"
},
{
"id": "M13",
"description": "transient cap leaked",
"test": "packages/bus/tests/peer-transport.test.mjs",
"args": [
"--test",
"packages/bus/tests/peer-transport.test.mjs"
],
"status": 1,
"signal": null,
"killed": true,
"restoredSha256": "830faa9a76150b59189cde395ec8deeb768e4b0d45ed262a57bb6fc6e459abb4"
},
{
"id": "M14",
"description": "tester walk used for user",
"test": "packages/bus/tests/principal.test.mjs",
"args": [
"--test",
"packages/bus/tests/principal.test.mjs"
],
"status": 1,
"signal": null,
"killed": true,
"restoredSha256": "32ef39d80b523bdd586c29620d7a752c309c8e1aa70befc1414125468f3411dd"
},
{
"id": "M15",
"description": "TCP retained during conversion",
"test": [
"--test-name-pattern=row196 explicit socket",
"packages/cli/tests/stack-release.test.mjs"
],
"args": [
"--test",
"--test-name-pattern=row196 explicit socket",
"packages/cli/tests/stack-release.test.mjs"
],
"status": 1,
"signal": null,
"killed": true,
"restoredSha256": "b916a2793311d21e3ce7117e9b5e0768e6e33c841fa3aa39fb7ffc9c5974acd2"
},
{
"id": "M16",
"description": "TCP reopened on downgrade",
"test": [
"--test-name-pattern=row196 explicit socket",
"packages/cli/tests/stack-release.test.mjs"
],
"args": [
"--test",
"--test-name-pattern=row196 explicit socket",
"packages/cli/tests/stack-release.test.mjs"
],
"status": 1,
"signal": null,
"killed": true,
"restoredSha256": "b916a2793311d21e3ce7117e9b5e0768e6e33c841fa3aa39fb7ffc9c5974acd2"
},
{
"id": "M17",
"description": "tester switch ignored",
"test": "packages/bus/tests/tester.test.mjs",
"args": [
"--test",
"packages/bus/tests/tester.test.mjs"
],
"status": 1,
"signal": null,
"killed": true,
"restoredSha256": "e6f33bac95a40c9d52d67f297eec361cd0f30da5d3c28057468ee5d56c3ae7fc"
},
{
"id": "M18",
"description": "user kind becomes owner",
"test": "packages/bus/tests/principal.test.mjs",
"args": [
"--test",
"packages/bus/tests/principal.test.mjs"
],
"status": 1,
"signal": null,
"killed": true,
"restoredSha256": "32ef39d80b523bdd586c29620d7a752c309c8e1aa70befc1414125468f3411dd"
},
{
"id": "M19",
"description": "selected instance root ignored",
"test": "packages/webui/tests/tester-socket.test.mjs",
"args": [
"--test",
"packages/webui/tests/tester-socket.test.mjs"
],
"status": 1,
"signal": null,
"killed": true,
"restoredSha256": "4bd7bdf0898844684ebf9444b110d2c5b6fa874b2681183e54f1f62f97823192"
},
{
"id": "M20",
"description": "helper gets another pending socket",
"test": "packages/bus/tests/peercred.test.mjs",
"args": [
"--test",
"packages/bus/tests/peercred.test.mjs"
],
"status": 1,
"signal": null,
"killed": true,
"restoredSha256": "29c88655e8b9aed8db604573c9213fd9f3c40ef35ff04f4eda8fbe8f1b8a32d7"
},
{
"id": "M21",
"description": "user-owned interpreter accepted",
"test": "packages/bus/tests/interpreter.test.mjs",
"args": [
"--test",
"packages/bus/tests/interpreter.test.mjs"
],
"status": 1,
"signal": null,
"killed": true,
"restoredSha256": "d5d98a9133a1b449acc48143fd559d890a470c6ea210b30b9b70e491e6cd855a"
},
{
"id": "M22",
"description": "cap requests invoke helper",
"test": "packages/bus/tests/peer-transport.test.mjs",
"args": [
"--test",
"packages/bus/tests/peer-transport.test.mjs"
],
"status": 1,
"signal": null,
"killed": true,
"restoredSha256": "830faa9a76150b59189cde395ec8deeb768e4b0d45ed262a57bb6fc6e459abb4"
},
{
"id": "M23",
"description": "restart retains stale identity",
"test": "packages/bus/tests/interpreter.test.mjs",
"args": [
"--test",
"packages/bus/tests/interpreter.test.mjs"
],
"status": 1,
"signal": null,
"killed": false,
"restoredSha256": "d5d98a9133a1b449acc48143fd559d890a470c6ea210b30b9b70e491e6cd855a"
}
]
@@ -0,0 +1 @@
fb96c71db3715d7ea766b278377c4f215ba1537b 0000000000000000000000000000000000000000
@@ -0,0 +1 @@
294d45c6c8494e83e4276a8c554fcbe85674f3d0 0000000000000000000000000000000000000000
@@ -0,0 +1,2 @@
/mnt/storage/scratch/tmp/g196-tree-tn651z
/mnt/storage/scratch/tmp/gJ3j
File diff suppressed because it is too large. Load diff
+229
View File
@@ -0,0 +1,229 @@
# Row 252: Console inbox Approve and Deny
Revision 1, 2026-10-21. Gander. Reviewer Webby. Gate owner Sage.
Vikunja #435 (J2), Gitea #1753. Design only, not approved for implementation.
Brief: 640aa03f2:docs/plans/2026-10-21_console-inbox-approve.md.
Design base: row 196 rebased build 64539c96b on a36523713. Row 196 verification
and Duckworth's manifest review remain pending. Row 252 code must wait for Webby
approval, the row 196 landing, and the PRD amendment described below.
## 1. Decisions that get buttons
The first cut covers grant requests only. Q188 and #435 ask for that case;
turning every decision into a Console write would widen this security slice.
Other decisions keep today's option text and copyable `mosaic decide ID KEY`.
Eligibility is determined by the bus from the original authoritative raise:
- The decision belongs to the selected business, is open and routed to human.
- Its action is credential.mint and the broker's original decision.raise event
has a validated grant binding. A question or option containing the word
grant, an action supplied by the browser, or a copied authorization object
does not qualify.
- There are exactly two distinct current option keys. The broker's recorded
approvalChoice names one. Approve uses that key; Deny uses the other key.
Both controls display the real option text. No guessed `yes` or `no` key.
- Missing binding, ambiguous options, a superseded/closed decision, a technical
or role-routed decision, and non-grant authorization requests get no buttons.
Existing terminal handling remains available where it is available today.
The bus adds a read-only console-resolution description to the decision view:
eligibility, the exact two keys and labels, a safe grant summary, and a digest
of decision id, business, options, approvalChoice and original grant binding.
It contains no token or key material. This description helps the UI; the final
write recomputes every check from authoritative state in its transaction.
Approving records consent and starts the existing grant workflow. It does not
claim the external enrollment or credential operation has already succeeded.
Deny resolves with the non-approval key and starts no grant executor.
## 2. Identity and the one bus verifier
The resolving identity is (provider id, issuer, sub) from the request's live
row 192 OIDC session. Session.name is display text, never authority. Headers,
POST fields and a shared secret cannot select the human. ID or access tokens,
session cookies and client secrets never become bus credentials or audit text.
Extend row 196 verifyPrincipal rather than create a second verifier. Add a
Console-owner variant of its human proof with a reviewed same-tree
console-cli.mjs path and a separate MOSAIC_BUS_CONSOLE_NONCE namespace. It must:
1. Match the accepted Unix connection's SO_PEERCRED pid and instance uid.
2. Verify the fixed Console wrapper path and initial spawn nonce, using the
existing human walk and all marker, agent binary and launch-record refusals.
3. Require the exact same-tree webui cli.mjs serve as direct parent, and reread
child/parent identity as row 196 does for Console users. No CLI respawn.
4. Bind business and the provider/issuer/sub tuple to initial per-spawn metadata.
The Console constructs these only from the session verified for this request.
The wrapper refuses body overrides. The bus compares proof metadata to that
initial environment and to trusted current console.sso owner subjects.
5. Resolve to businesses[business].human only when the tuple is an allowed
owner subject in this instance's checked console.sso configuration. Unknown
subjects, changed mapping, missing/invalid configuration and user principals
refuse. A terminal owner still works if Console configuration is unavailable.
The runtime receives the checked owner-subject map through its trusted startup
API, never through a socket verb. Provider id alone is insufficient: issuer and
sub must match. A configuration change requires the normal reviewed re-pin and
restart; logout, idle/absolute expiry and provider refusal use live Q175 checks
on each HTTP request. No Console principal or cap is cached between requests.
Each preview and final write spawns a fresh wrapper with a fresh nonce.
The verifier returns owner provenance including via=console and the subject
only after those checks. A separate trusted binder issues an internal transient
Console cap restricted to the preview and Console resolution operations below.
Do not weaken today's bindHuman or general #human gate to accept via=console
for every owner operation. CLI, user, tester, reader, launch and seat caps cannot
use the Console resolution operation, and a Console cap cannot invoke other
owner writes. The cap never leaves the broker and ends with the connection.
The proof trusts the verified Console's mapping, as row 196 explicitly permits.
The bus does not independently revalidate an ID token. A hostile process on the
instance uid can inspect memory or interfere with same-uid state; this is not a
wall against that process. Separate uids narrow cross-instance reach only.
The row 196 provenance statement remains unchanged:
The Console provenance option trusts verified code and process metadata, not an independent
bus-side OIDC exchange. That trade must be approved, not hidden behind the phrase zero trust.
## 3. Write path and evidence
A signed-in human opens an eligible decision and chooses Approve or Deny.
POST /api/decisions/ID/confirmation asks for a confirmation view. After all
HTTP guards, a fresh Console proof calls a read-only decision.preview.console
bus operation. The broker returns the current eligibility and digest. The
Console creates a random 256-bit confirmation token held only in server memory
as a hash. It binds the token to this live session, business, decision, exact
choice and digest, expiring after 120 seconds. Bound the store to 1024 entries;
refuse a new confirmation at capacity, rather than evict a live confirmation.
No bus write, grant, seen event or resolution occurs at this step.
The confirmation view shows the decision id, question, selected real option
text, grant method/target and consequences. The human must click a second,
explicit `Confirm approve` or `Confirm deny`. Cancel sends no resolution.
No modal or notification opening resolves a decision. Render all content as
text with the existing escaping rules, never as trusted markup.
POST /api/decisions/ID/resolve takes only choice and confirmation. The Console
rechecks the live session and HTTP guards, matches and consumes that session's
confirmation once before awaiting the bus, then spawns a fresh wrapper. The
restricted operation decision.resolve.console carries id, choice and the
expected digest. The browser cannot submit via, human, provider, issuer, sub,
grant fields, a principal or a role. Those fields are refused, not ignored.
The broker rechecks restricted Console identity, business, authoritative grant
eligibility, current option and digest, and open status in the same transaction
that appends the resolution. This operation shares the existing decision.resolve
transaction and notification behavior rather than duplicate that machinery.
Only its explicit provenance/eligibility gates differ. Existing CLI decide and
role resolution keep their current authority and evidence.
Evidence is append-only. decision_events.by stays the configured business human
and decision_events.via becomes console. The accompanying human.input event in
the same transaction records decision, choice, op=resolved, via=console and the
subject object {provider, iss, sub}, with a reference to the decision event's
sequence. Trail output shows this provenance. Do not put the subject in a
browser-supplied note or replace the human id with a display name. No token,
nonce, confirmation value or cookie is logged. This uses existing event JSON;
no new schema column is required for the subject.
Current #approvedGrant requires via=cli. It must explicitly accept via=console
only when that exact decision event has matching trusted Console human.input
provenance, owner and subject under this design. Merely widening its via string
check is insufficient. CLI proofs remain valid as before. Existing grant
single-consumption and executor rechecks remain authoritative, including the
host and host-enroll starts after commit. Deny, refusal and failed transactions
start no executor. Tests must cover this guard, since a UI success with no
usable grant would fail J2 just as an unauthorized grant would.
## 4. Guards, concurrency and refusal contract
The main listener requires --sso and --send for these routes. --no-send is a
release-tool choice that produces a unit without --send. Read-only mode rejects
both confirmation and resolve. The tester listener never offers either route.
Proxy preview mode, loopback owner access without SSO and a seat credential do
not enable them. Q175 session and Host guards run before any decision lookup.
POST requires one exact Origin equal to the configured HTTPS public origin,
one Sec-Fetch-Site equal to same-origin, and application/json. No CORS, GET
mutation, form-encoded body, absent metadata or duplicate header acceptance.
Use the existing small JSON body limit and strict field validation. The
confirmation token is an additional session-bound action guard, not a reusable
identity credential. Neither confirmation nor resolution trusts a Referer.
| Condition | HTTP/code | Message |
|---|---|---|
| Missing, expired, logged-out or refused session | 401 sign-in-required | Sign in again before answering. |
| Without --sso, without --send, or --no-send | 403 read-only | Console decision answers are not enabled. Use mosaic decide. |
| Wrong/repeated Origin, wrong/missing fetch metadata | 403 cross-origin | Answer from the signed-in Console page. |
| Tester route | 404 not-found | Not found. |
| Agent, seat, user, wrong parent/uid, unknown owner subject | 403 human-required | The bus refused this human identity. |
| Peer helper failure | 403 peercred-required | The bus could not verify this connection. |
| Wrong method on a main Console decision endpoint | 405 method-not-allowed | Use POST. |
| Wrong content type, malformed JSON, unknown/body identity fields | 400 invalid-request | Invalid decision answer. |
| Body over existing limit | 413 request-too-large | Decision answer is too large. |
| Decision absent or in another business | 404 decision-not-found | This decision is unavailable. |
| Open decision outside this grant-only slice | 403 decision-not-eligible | Answer this decision with mosaic decide. |
| Decision closed, superseded or concurrently resolved | 409 decision-closed | This decision is no longer open. Refresh the inbox. |
| Option removed/changed or digest changed | 409 decision-stale | The choices changed. Review the decision again. |
| Missing, expired, consumed or mismatched confirmation | 409 confirmation-required | Review and confirm this answer again. |
| Confirmation store full | 503 confirmation-unavailable | Confirmation is unavailable. Try again later. |
| No bus or lost response after submission | 503 outcome-unknown | The answer may have reached the bus. Refresh before trying again. |
The public messages above are the endpoint contract; map existing bus refusal
names explicitly where they differ. No 200 for a refusal. A successful resolve
returns the recorded decision id, choice and resolution state, plus grant
workflow status if returned by the broker. The UI disables both action buttons
while submitting. A second click cannot reuse a consumed confirmation; a
concurrent fresh confirmation still loses at the broker's closed-state check.
Only one resolution event, human.input event, notification and grant start may
result. On uncertain transport, never retry automatically. Refresh reads the
actual result; a different unresolved answer requires fresh confirmation.
Expiry/logout before either POST refuses. A logout racing an already-admitted
bus transaction cannot cancel an effect already committed; no claim to the
contrary is made. Session checks immediately before spawning bound this limit.
## 5. Inbox without SSO and read-only presentation
Hide the buttons completely without --sso. Keep the exact copyable mosaic decide
lines and explain that Console answers need sign-in and writes enabled. Under
SSO but without --send, also show the terminal lines and read-only explanation.
Do not present a disabled Approve button that suggests sign-in alone grants
write authority. With SSO and send enabled, only eligible grant decisions gain
the two controls. Non-grant decisions keep terminal lines. Closed decisions show
the recorded answer and provenance, with no action controls or confirmation.
Server-provided UI capabilities are presentation only. Direct POSTs receive all
the same guards. A stale page cannot restore a session, write mode or decision.
The live instance stays unchanged until Jason's phase 2 window and an authorized
re-pin whose tree includes this row and its documented flag meaning.
## 6. Stand-in tests, mutants and landing gates
Use row 192's fake OIDC provider, synthetic subjects and scratch HTTP listeners;
use the real test bus and row 196 kernel-peer fixtures. Never use the live
provider, origin, ports, credentials, units or instance roots. All scratch state
uses the approved /mnt/storage/scratch/tmp TMPDIR. No real second uid.
| Evidence | Required cases | Mutants |
|---|---|---|
| Eligibility | bound grant, two actual keys, other decisions/technical/roles/ambiguous options hidden and POST refused | J1 trust browser eligibility; J2 hardcode yes/no |
| Owner provenance | live fake OIDC owner works; header/name/body/seat/user impersonation, agent parent, other tree, bad nonce and wrong peer refuse | J3 use name/header; J4 skip Console-parent walk; J5 accept other nonce namespace |
| Request lifetime | new proof per preview/resolve; session replaced/expired/logged out refuses; cap never returned and releases on success/failure/close | J6 reuse prior session/principal; J7 leak cap |
| HTTP and confirmation | all refusal table cases; missing/duplicate Origin and metadata, token from another session/decision/choice, expiry, capacity, cancel and direct POST | J8 skip CSRF; J9 accept unconfirmed write; J10 unbind confirmation |
| Business and races | foreign business, closed decision, stale digest/key, parallel confirmations, double click and lost reply; exactly one durable result/notification | J11 skip business/digest/closed check; J12 permit confirmation reuse |
| Evidence and grant effect | via=console plus verified subject in same transaction, CLI unchanged; approval consumable once by existing grant path, deny/refusal no executor | J13 write cli or omit subject; J14 widen via without provenance; J15 execute on deny |
| UI/browser | hidden without SSO/send, terminal fallback, escaped text, confirm/cancel, pending state, stale/error refresh, no automatic retry | J16 enable without SSO/send; J17 auto-submit or retry |
Browser tests use the repository's isolated fixture infrastructure. The reviewer
must inspect which runtime/UI boundary each mutant actually weakens. Record
restored hashes and clean Git status for each. Suites and mutant batches need
Sage's slot; stop/report under mos-0823. Final acceptance is Webby's approved
manifest and Sage's whole bus/WebUI/CLI and scripts/test-*.sh gate.
Before implementation, Webby must approve this design and the REQ-WEB-2 /
REQ-DEC-3 amendment must be landed through Mos and Jason. The PRD currently says
human resolution is CLI-only. Decision 00ed58e2 reserves the Console amendment;
this design does not edit the PRD or treat its reservation as landed policy.
Row 252 code branches from row 196's reviewed landing and keeps its own review
and gate. No live enablement, reachability change or independent auth mechanism.