gander: cover activation interpreter and rehearsal transport boundaries

This commit is contained in:
jason.woltje committed 2026-10-21 15:22:48 -05:00
1 parent c997aa72f0
commit 527b9a1e6c
18 files changed
+908

No files matched your search

+4
View File
@@ -117,3 +117,7 @@ changes are planned before the batch. M1-M29 remain prepared and unexecuted.
2026-10-21 sage-2124 continuation completed on 05e940117: CLI whole 735/0; eight union files 166/0; ten scripts/test-*.sh 429/0. Combined with carried bus/WebUI: 2256 pass, 0 fail, 1 editor skip; 30/30 mutants killed. Counts/times are script-summed in r5/resume/summary.json. Sweep gate leftovers 0, eight pre-existing helpers remain. Slot returned gander-0056. MANIFEST.md, CANDIDATE.sha256 and PRODUCT-FILES.json freeze immutable 05e940117 for Duckworth. No live activation or merge authorized.
2026-10-21 sage-2127/2128 review handoff: final manifest and logs pushed at 93f3332b2, freezing tested candidate 05e940117. Queue move gander-196-manifest-r5 opened round1 on #1694 at rev2968; own-token transport refused and outcome rev2969 records failed. No credential read or retry. Duckworth received gander-0058; Sage received gander-0059 to post as jarvis. Queue writes finished before Megavolt slot; no more writes until returned. Details: r5/REVIEW-REQUEST.md.
2026-10-21 r6, sage-2140/2142 and duckworth-0096/0097: B1 now tests plan/start-log interpreter JSON equality, receipt refusal with interpreter-receipt gate failure, restored pointer and no success log, replacement identity at the gate, plus missing/unsafe verifier preflight with zero start/stop calls. B2 records stand-in Console argv and config dataRoot, asserting the private flag only for a marked copy and the scratch rehearsal root for both. Focused B1/B2 GREEN: 9/9 in 16.474 seconds on the first row222 rebase. Sage clarified pattern/single runs need the slot too; that focused run preceded the new grant, nothing else was running, and all further tests use sage-2142's grant.
N1 documented/deferred to R3: user-cli.mjs and authenticateUser wiring are absent by design, so user envelopes refuse; R3 must add and test both. N2 deferred to a later row: dead testerNoSignIn parameters/comments stay, avoiding a product cleanup in this test review. N3 documented, no code change: direct serve without a supplied peerReader needs a safe system Python even for caps; runtime supplies its peerReader, and the fallback fails closed. N4 fixed in docs/TOOLS.md: the longer rehearsal socket path can fail closed after the live preflight passes.
Row222 integration checked on the rebased source: --send library.sync uses the existing call/busCaller, exact HUMAN_CLI child with fresh human nonce, server SO_PEERCRED and runtime verifyPrincipal before owner-cap library dispatch. The internal library sync timer is bus-local, not another caller identity mechanism. r6/REBASE.md records this path, both preserved rebases and the full whole-file union. Rebased final base is f48725cfc, branch gander/row196-r6-prd19. No product code edit for B1/B2. Full golden results await the granted gate; r5 golden results are not carried.
@@ -0,0 +1,12 @@
# R6 added mutants
- M30: gate skips interpreter receipt.
- M31: gate ignores changed interpreter.
- M32: marked target without verifier accepted.
- M33: unsafe interpreter inspection not refused.
- M34: started log omits interpreter identity.
- M35: plan omits interpreter identity.
- M36: marked rehearsal omits tester socket.
- M37: legacy rehearsal receives tester socket.
M17 also reruns because row222 changed broker.mjs. The other 29 original target blobs are checked by SHA256 before carry. No original mutant targets runtime.mjs or webui/src/cli.mjs. New tests use fixture interpreter identities, no real receipt or live unit. All 38 definitions pass the unique replacement guard before the batch.
+11
View File
@@ -0,0 +1,11 @@
# Row196 r6
Claimant move gander-196-r6-changes recorded rev2975 after Duckworth's changes receipt rev2973, comment30145. Sage2140/2142 authorizes the rebuild and full gate. The first rebase onto row222 0b33f5e9d is preserved on gander/row196-r6 at 2a989d466. A second new branch gander/row196-r6-prd19 rebases onto f48725cfc as Sage2142 requested; rebased tip before B1/B2 is c997aa72f. No branch was deleted or force-pushed.
Both rebases are conflict-free. range-diff.txt compares r5 with the first rebase; range-diff-prd19.txt compares r5 with the final base. The first product patch differs only in Console startServer context retaining librarySync. The other ten commits are equal. Patch ids are recorded alongside. No source resolution edited row222's behavior.
The Console's --send branch assigns librarySync=args=>call('library.sync',args). call is the existing busCaller, using HUMAN_CLI by default. humanCall spawns the exact human-cli.mjs with a fresh MOSAIC_BUS_CLI_NONCE, which emits a human proof. server.mjs reads SO_PEERCRED on that connection before runtime's authenticateHuman calls verifyPrincipal and binds an owner cap. LIBRARY_VERBS then dispatches library.sync under that cap with awaited release. Row222's internal sync timer calls the library implementation inside the bus; it exposes no new socket authentication path. Runtime retains the interpreter receipt, peer reader, shared verifier and timer stop/cleanup. No user binder or second principal mechanism was added.
B1 and B2 add only fixture tests; N4 adds a rehearsal path-length note to docs/TOOLS.md. No product code was changed beyond the base's row222 and requirements changes. Original mutant target SHA comparison is target-shas.json: M17's broker blob changed and reruns; the other 29 targets match. Eight new mutants M30-M37 cover B1/B2. All38 definitions pass unique-target static checks. No original definition targets runtime.mjs or webui/src/cli.mjs.
The fresh full gate runs bus, WebUI and CLI whole, all ten shell scripts, and the whole touched-file union outside those suites. That union retains the previous eight row215 files and adds conversation/cohort, library/mapping, library/sync and tasks/requirements from the newer base. Whole golden results from r5 do not carry.
+9
View File
@@ -0,0 +1,9 @@
import {spawnSync} from 'node:child_process';import {writeFileSync} from 'node:fs';import {performance} from 'node:perf_hooks';
const root='/mnt/storage/scratch/tmp/g196-tree-tn651z',out=new URL('.',import.meta.url).pathname;
const jobs=[['tester',['packages/bus/tests/tester.test.mjs']],['activation',['--test-name-pattern=row196 interpreter activation','packages/cli/tests/stack-release.test.mjs']],['rehearsal',['--test-name-pattern=row196 rehearsal selects','packages/cli/tests/stack-release.test.mjs']]];
const results=[];
for(const [name,args] of jobs){
const start=performance.now(),r=spawnSync('bash',['-c','set -o pipefail; python3 "$G196_BUSY" && "$G196_NODE" --test "$@" 2>&1 | tee "$G196_LOG"','--',...args],{cwd:root,env:{...process.env,G196_NODE:process.execPath,G196_LOG:out+'base-'+name+'.log'},encoding:'utf8',maxBuffer:4*1024*1024});
const seconds=(performance.now()-start)/1000;const result={name,args,seconds,status:r.status,signal:r.signal};results.push(result);writeFileSync(out+'base-results.json',JSON.stringify(results,null,2)+'\n');console.log(JSON.stringify(result));
if(r.status!==0||seconds>30){process.exitCode=1;break;}
}
@@ -0,0 +1,90 @@
import { guardMutation } from '../mutant-guard.mjs';
// 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=process.env.G196_ROOT??'/mnt/storage/scratch/tmp/g196-tree-tn651z';
const out='/home/jwoltje/.t3/worktrees/mosaic-stack/gander-row196/agents/gander/work/queue-196/r6/killers';mkdirSync(out,{recursive:true});
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('finish(false, Object.freeze(p));\n } catch { finish(true); }','finish(false, Object.freeze(p));\n } 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.replace('constants.O_WRONLY | constants.O_APPEND | constants.O_CREAT | constants.O_NOFOLLOW | constants.O_NONBLOCK, 0o600','constants.O_WRONLY | constants.O_APPEND | constants.O_CREAT | constants.O_NOFOLLOW, 0o600')],
['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('case "${without_proof[i]}" in --send | --tester-port | --tester-socket) break','case "${without_proof[i]}" in --send | --tester-port) break')],
['M28b','scripts/stack-release.sh','SSO placed after tester socket flag',['--test-name-pattern=row196 SSO socket','packages/cli/tests/stack-release.test.mjs'],t=>t.replace('case "${kept[i]}" in --send | --tester-port | --tester-socket) break','case "${kept[i]}" in --send | --tester-port) break')],
['M29','scripts/stack-release.sh','unsafe tester path reaches service stop',release,t=>t.replace(t.split('\n').find(line=>line.includes('|| check_refuse tester-socket-path'))+'\n','')],
["M30","scripts/stack-release.sh","gate skips interpreter receipt",["--test-name-pattern=row196 interpreter activation","packages/cli/tests/stack-release.test.mjs"],t=>t.replace(" if [ -f \"$copy/packages/bus/src/interpreter.mjs\" ]; then\n GATE=interpreter-receipt\n node -e 'import(require(\"url\").pathToFileURL(process.argv[1]).href).then(m=>{const expected=m.inspectInterpreter();if(process.argv[4]&&JSON.stringify(expected)!==process.argv[4])process.exit(1);m.verifyInterpreterReceipt(process.argv[2],expected,process.argv[3]);}).catch(()=>process.exit(1));' -- \"$copy/packages/bus/src/interpreter.mjs\" \"$DATA/bus\" \"$START_MS\" \"$EXPECTED_INTERPRETER\" || return 1\n fi\n","")],
["M31","scripts/stack-release.sh","gate ignores changed interpreter",["--test-name-pattern=row196 interpreter activation","packages/cli/tests/stack-release.test.mjs"],t=>t.replace("if(process.argv[4]&&JSON.stringify(expected)!==process.argv[4])process.exit(1);","")],
["M32","scripts/stack-release.sh","marked target without verifier accepted",["--test-name-pattern=row196 interpreter activation","packages/cli/tests/stack-release.test.mjs"],t=>t.replace("[ -f \"$module\" ] || check_refuse interpreter-unsafe \"$TO lacks its interpreter verifier\"",":")],
["M33","scripts/stack-release.sh","unsafe interpreter inspection not refused",["--test-name-pattern=row196 interpreter activation","packages/cli/tests/stack-release.test.mjs"],t=>t.replace("|| check_refuse interpreter-unsafe \"system Python preflight refused\"","|| :")],
["M34","scripts/stack-release.sh","started log omits interpreter identity",["--test-name-pattern=row196 interpreter activation","packages/cli/tests/stack-release.test.mjs"],t=>t.replace("log_line result started interpreter \"$EXPECTED_INTERPRETER\"","log_line result started")],
["M35","scripts/stack-release.sh","plan omits interpreter identity",["--test-name-pattern=row196 interpreter activation","packages/cli/tests/stack-release.test.mjs"],t=>t.replace(" [ -z \"$EXPECTED_INTERPRETER\" ] || echo \"interpreter: $EXPECTED_INTERPRETER\"\n","")],
["M36","scripts/stack-release.sh","marked rehearsal omits tester socket",["--test-name-pattern=row196 rehearsal selects","packages/cli/tests/stack-release.test.mjs"],t=>t.replace("then tester_rehearsal=(--tester-socket); fi","then tester_rehearsal=(); fi")],
["M37","scripts/stack-release.sh","legacy rehearsal receives tester socket",["--test-name-pattern=row196 rehearsal selects","packages/cli/tests/stack-release.test.mjs"],t=>t.replace(" local -a tester_rehearsal=()"," local -a tester_rehearsal=(--tester-socket)")],
];
const hash=x=>createHash('sha256').update(x).digest('hex');
const results=[];
for (const [id,file,,,mutate] of specs) {
try { guardMutation(readFileSync(join(root,file),'utf8'),mutate); }
catch(error) { throw Error(id+': '+error.message); }
}
if(process.argv.includes('--static')){writeFileSync(new URL('./uniqueness.json',import.meta.url),JSON.stringify(specs.map(([id,file,description,test,mutate])=>({id,file,description,test,checks:guardMutation(readFileSync(join(root,file),'utf8'),mutate).checks})),null,2)+'\n');console.log('STATIC '+specs.length+' unique definitions');process.exit(0);}
const scratch=mkdtempSync(join(tmpdir(),'g196-mut-'));
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.filter(row=>['M17',...Array.from({length:8},(_,i)=>'M'+(i+30))].includes(row[0]))) {
const bases=JSON.parse(readFileSync(new URL('./base-results.json',import.meta.url),'utf8'));
const killerFile=Array.isArray(test)?test.at(-1):test;
const base=bases.find(b=>JSON.stringify(b.args)===JSON.stringify(Array.isArray(test)?test:[test]));
if(!base||base.status!==0||base.seconds>30)throw Error(id+' missing or slow passing base');
const at=join(scratch,file),original=readFileSync(at,'utf8'),before=hash(original),changed=guardMutation(original,mutate).text;
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 started=Date.now();
const run=spawnSync('timeout',['--signal=TERM','--kill-after=2s','60s','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',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);
// Syntax validity is decided exclusively by the node --check preflight above.
// GNU timeout owns a process group and terminates its shell, tee and test descendants.
const timedOut=run.status===124||run.status===137;
const killed=timedOut||(run.status!==0&&failed);
const result={id,description,test,args,status:run.status,signal:run.signal,killed,timedOut,wallSeconds:(Date.now()-started)/1000,baseWallSeconds:base.seconds,baseArgs:base.args,restoredGitStatus:clean,timeoutSeconds:60,timeoutKillAfterSeconds:2,processGroupTimeout:true,syntaxStatus:syntax.status,restoredSha256:before};results.push(result);
writeFileSync(join(out,'results.json'),JSON.stringify(results,null,2)+'\n');
console.log(id+' '+(killed?'KILLED':'SURVIVED/INVALID')+' base='+base.seconds+'s timeout='+timedOut);
if(timedOut){process.exitCode=1;console.log('STOP non-FIFO timeout');break;}
if(!killed) {process.exitCode=1;break;}
}
} finally {rmSync(scratch,{recursive:true,force:true});}
@@ -0,0 +1,66 @@
TAP version 13
# Subtest: row196 interpreter activation checks receipt, identity, plan and startup log
# Subtest: plan and started log preserve the same interpreter JSON
ok 1 - plan and started log preserve the same interpreter JSON
---
duration_ms: 2212.92386
type: 'test'
...
# Subtest: interpreter-receipt-refuse fails at the gate and restores the pointer
ok 2 - interpreter-receipt-refuse fails at the gate and restores the pointer
---
duration_ms: 3386.348438
type: 'test'
...
# Subtest: interpreter-drift fails at the gate and restores the pointer
ok 3 - interpreter-drift fails at the gate and restores the pointer
---
duration_ms: 3277.695131
type: 'test'
...
# Subtest: missing-verifier refuses before any start or stop
ok 4 - missing-verifier refuses before any start or stop
---
duration_ms: 964.884915
type: 'test'
...
# Subtest: interpreter-unsafe refuses before any start or stop
ok 5 - interpreter-unsafe refuses before any start or stop
---
duration_ms: 1034.342937
type: 'test'
...
1..5
ok 1 - row196 interpreter activation checks receipt, identity, plan and startup log
---
duration_ms: 10878.021503
type: 'test'
...
# Subtest: row196 rehearsal selects socket only for marked copies and uses scratch dataRoot
# Subtest: socket copy
ok 1 - socket copy
---
duration_ms: 2429.791314
type: 'test'
...
# Subtest: legacy copy
ok 2 - legacy copy
---
duration_ms: 2417.488686
type: 'test'
...
1..2
ok 2 - row196 rehearsal selects socket only for marked copies and uses scratch dataRoot
---
duration_ms: 4847.998691
type: 'test'
...
1..2
# tests 9
# suites 0
# pass 9
# fail 0
# cancelled 0
# skipped 0
# todo 0
# duration_ms 16473.537123
@@ -0,0 +1 @@
/mnt/storage/scratch/tmp/g0dR
@@ -0,0 +1 @@
0cf62338e3362a2b2f10b25cf7ef7c203f0ef3cd 0000000000000000000000000000000000000000
@@ -0,0 +1 @@
b7512faa138a4a725ade9fadcb3ac658518b2193 0000000000000000000000000000000000000000
@@ -0,0 +1 @@
0cf62338e3362a2b2f10b25cf7ef7c203f0ef3cd 0000000000000000000000000000000000000000
@@ -0,0 +1,20 @@
1: 19073a8e0 ! 1: 6e4ecd9b3 gander: preserve row 196 human boundary build for rebase
@@ packages/webui/src/cli.mjs: try {
}
const reads = consoleReads({ root: REPO, queueRoot: options.checkout ?? REPO, system, configDir: configDir(), rolesDir: rolesDir(), business: options.business ?? null });
@@ packages/webui/src/cli.mjs: try {
- server = await startServer({ board: options.board, port: options.port, bus, reads, preview: options.preview, sso, proof, send, settingsWrite, projectFiles, projectFile, libraryFile, libraryStatus, projectSave, chat, nameWrite, assistantStop, instancesWrite, routesWrite });
+ server = await startServer({ board: options.board, port: options.port, bus, reads, preview: options.preview, sso, proof, send, settingsWrite, projectFiles, projectFile, libraryFile, libraryStatus, librarySync, projectSave, chat, nameWrite, assistantStop, instancesWrite, routesWrite });
} catch (err) { sso?.close(); throw err; }
let testerServer = null;
- if (testerPort !== null) {
2: 6c6efc125 = 2: b292f8b73 gander: record row 196 rebase gate stop and row 252 design
3: 4519a14b2 = 3: 3172950da gander: classify runtime mutation failures and bound process groups
4: 0ca104acc = 4: 114c972c2 gander: record r2 stop two timings and approved row 252 notes
5: 84aea3063 = 5: d7df90be7 gander: pin SSO and tester socket release argument order
6: f8a59e6aa = 6: fff89b233 gander: guard unique mutation targets and separate SSO ordering mutant
7: a415de022 = 7: 2ecd765c2 gander: migrate library CLI activation to tester socket
8: 91e3696ef = 8: aa2fb0c64 gander: publish row 196 rebase gate and migration audit records
9: 05e940117 = 9: 7a396d00f gander: record fourth gate busy preflight stop
10: 93f3332b2 = 10: d2d981969 gander: publish completed row 196 gate and merge review manifest
11: c21465992 = 11: c997aa72f gander: record merge scoped review queue handoff
@@ -0,0 +1,20 @@
1: 19073a8e0 ! 1: 763651cb7 gander: preserve row 196 human boundary build for rebase
@@ packages/webui/src/cli.mjs: try {
}
const reads = consoleReads({ root: REPO, queueRoot: options.checkout ?? REPO, system, configDir: configDir(), rolesDir: rolesDir(), business: options.business ?? null });
@@ packages/webui/src/cli.mjs: try {
- server = await startServer({ board: options.board, port: options.port, bus, reads, preview: options.preview, sso, proof, send, settingsWrite, projectFiles, projectFile, libraryFile, libraryStatus, projectSave, chat, nameWrite, assistantStop, instancesWrite, routesWrite });
+ server = await startServer({ board: options.board, port: options.port, bus, reads, preview: options.preview, sso, proof, send, settingsWrite, projectFiles, projectFile, libraryFile, libraryStatus, librarySync, projectSave, chat, nameWrite, assistantStop, instancesWrite, routesWrite });
} catch (err) { sso?.close(); throw err; }
let testerServer = null;
- if (testerPort !== null) {
2: 6c6efc125 = 2: 50abbc3da gander: record row 196 rebase gate stop and row 252 design
3: 4519a14b2 = 3: 3a3054df6 gander: classify runtime mutation failures and bound process groups
4: 0ca104acc = 4: dee1d6632 gander: record r2 stop two timings and approved row 252 notes
5: 84aea3063 = 5: 917f75917 gander: pin SSO and tester socket release argument order
6: f8a59e6aa = 6: fd01c64c7 gander: guard unique mutation targets and separate SSO ordering mutant
7: a415de022 = 7: 791fce02e gander: migrate library CLI activation to tester socket
8: 91e3696ef = 8: 6ebf3c1fd gander: publish row 196 rebase gate and migration audit records
9: 05e940117 = 9: c9bd1eb9a gander: record fourth gate busy preflight stop
10: 93f3332b2 = 10: 0073a3343 gander: publish completed row 196 gate and merge review manifest
11: c21465992 = 11: 2a989d466 gander: record merge scoped review queue handoff
@@ -0,0 +1,268 @@
[
{
"id": "M1",
"file": "packages/webui/src/serve.mjs",
"r5Sha256": "da624f188c69e016ff82e1d1dcae1c579e68a57f0f25d773bb5d8bca56b80ee1",
"r6Sha256": "da624f188c69e016ff82e1d1dcae1c579e68a57f0f25d773bb5d8bca56b80ee1",
"carry": true
},
{
"id": "M2",
"file": "packages/webui/src/tester-socket.mjs",
"r5Sha256": "1c1fe0c795adac97f28502564a75f26d5666d9be5ae51169f7dd5bb7741fad16",
"r6Sha256": "1c1fe0c795adac97f28502564a75f26d5666d9be5ae51169f7dd5bb7741fad16",
"carry": true
},
{
"id": "M3",
"file": "packages/webui/src/serve.mjs",
"r5Sha256": "da624f188c69e016ff82e1d1dcae1c579e68a57f0f25d773bb5d8bca56b80ee1",
"r6Sha256": "da624f188c69e016ff82e1d1dcae1c579e68a57f0f25d773bb5d8bca56b80ee1",
"carry": true
},
{
"id": "M4",
"file": "packages/bus/src/principal.mjs",
"r5Sha256": "32ef39d80b523bdd586c29620d7a752c309c8e1aa70befc1414125468f3411dd",
"r6Sha256": "32ef39d80b523bdd586c29620d7a752c309c8e1aa70befc1414125468f3411dd",
"carry": true
},
{
"id": "M5",
"file": "packages/bus/src/peercred.mjs",
"r5Sha256": "29c88655e8b9aed8db604573c9213fd9f3c40ef35ff04f4eda8fbe8f1b8a32d7",
"r6Sha256": "29c88655e8b9aed8db604573c9213fd9f3c40ef35ff04f4eda8fbe8f1b8a32d7",
"carry": true
},
{
"id": "M6",
"file": "packages/bus/src/principal.mjs",
"r5Sha256": "32ef39d80b523bdd586c29620d7a752c309c8e1aa70befc1414125468f3411dd",
"r6Sha256": "32ef39d80b523bdd586c29620d7a752c309c8e1aa70befc1414125468f3411dd",
"carry": true
},
{
"id": "M7",
"file": "packages/webui/src/tester-socket.mjs",
"r5Sha256": "1c1fe0c795adac97f28502564a75f26d5666d9be5ae51169f7dd5bb7741fad16",
"r6Sha256": "1c1fe0c795adac97f28502564a75f26d5666d9be5ae51169f7dd5bb7741fad16",
"carry": true
},
{
"id": "M8",
"file": "packages/bus/src/human.mjs",
"r5Sha256": "0c6d96abe4607cba0b02b0638a2fc217ff8135bac9a47761a0ef015fbe9d3079",
"r6Sha256": "0c6d96abe4607cba0b02b0638a2fc217ff8135bac9a47761a0ef015fbe9d3079",
"carry": true
},
{
"id": "M9",
"file": "packages/bus/src/human.mjs",
"r5Sha256": "0c6d96abe4607cba0b02b0638a2fc217ff8135bac9a47761a0ef015fbe9d3079",
"r6Sha256": "0c6d96abe4607cba0b02b0638a2fc217ff8135bac9a47761a0ef015fbe9d3079",
"carry": true
},
{
"id": "M10",
"file": "packages/bus/src/principal.mjs",
"r5Sha256": "32ef39d80b523bdd586c29620d7a752c309c8e1aa70befc1414125468f3411dd",
"r6Sha256": "32ef39d80b523bdd586c29620d7a752c309c8e1aa70befc1414125468f3411dd",
"carry": true
},
{
"id": "M11",
"file": "packages/bus/src/principal.mjs",
"r5Sha256": "32ef39d80b523bdd586c29620d7a752c309c8e1aa70befc1414125468f3411dd",
"r6Sha256": "32ef39d80b523bdd586c29620d7a752c309c8e1aa70befc1414125468f3411dd",
"carry": true
},
{
"id": "M12",
"file": "packages/bus/src/human.mjs",
"r5Sha256": "0c6d96abe4607cba0b02b0638a2fc217ff8135bac9a47761a0ef015fbe9d3079",
"r6Sha256": "0c6d96abe4607cba0b02b0638a2fc217ff8135bac9a47761a0ef015fbe9d3079",
"carry": true
},
{
"id": "M13",
"file": "packages/bus/src/server.mjs",
"r5Sha256": "7be306c830aa98763b34b329995b40e9df443483fe20fcd69100a3acb6311bd6",
"r6Sha256": "7be306c830aa98763b34b329995b40e9df443483fe20fcd69100a3acb6311bd6",
"carry": true
},
{
"id": "M14",
"file": "packages/bus/src/principal.mjs",
"r5Sha256": "32ef39d80b523bdd586c29620d7a752c309c8e1aa70befc1414125468f3411dd",
"r6Sha256": "32ef39d80b523bdd586c29620d7a752c309c8e1aa70befc1414125468f3411dd",
"carry": true
},
{
"id": "M15",
"file": "scripts/stack-release.sh",
"r5Sha256": "b916a2793311d21e3ce7117e9b5e0768e6e33c841fa3aa39fb7ffc9c5974acd2",
"r6Sha256": "b916a2793311d21e3ce7117e9b5e0768e6e33c841fa3aa39fb7ffc9c5974acd2",
"carry": true
},
{
"id": "M16",
"file": "scripts/stack-release.sh",
"r5Sha256": "b916a2793311d21e3ce7117e9b5e0768e6e33c841fa3aa39fb7ffc9c5974acd2",
"r6Sha256": "b916a2793311d21e3ce7117e9b5e0768e6e33c841fa3aa39fb7ffc9c5974acd2",
"carry": true
},
{
"id": "M17",
"file": "packages/bus/src/broker.mjs",
"r5Sha256": "762234259d72db87aff510751fe14003099759ae37bc8eaf14f1ea1de61d2005",
"r6Sha256": "c2f2f558aae5768d3f5fe2ecc113760cc4027d868bb74627a6702c6babd96db2",
"carry": false
},
{
"id": "M18",
"file": "packages/bus/src/principal.mjs",
"r5Sha256": "32ef39d80b523bdd586c29620d7a752c309c8e1aa70befc1414125468f3411dd",
"r6Sha256": "32ef39d80b523bdd586c29620d7a752c309c8e1aa70befc1414125468f3411dd",
"carry": true
},
{
"id": "M19",
"file": "packages/cli/src/tester-http.mjs",
"r5Sha256": "4bd7bdf0898844684ebf9444b110d2c5b6fa874b2681183e54f1f62f97823192",
"r6Sha256": "4bd7bdf0898844684ebf9444b110d2c5b6fa874b2681183e54f1f62f97823192",
"carry": true
},
{
"id": "M20",
"file": "packages/bus/src/peercred.mjs",
"r5Sha256": "29c88655e8b9aed8db604573c9213fd9f3c40ef35ff04f4eda8fbe8f1b8a32d7",
"r6Sha256": "29c88655e8b9aed8db604573c9213fd9f3c40ef35ff04f4eda8fbe8f1b8a32d7",
"carry": true
},
{
"id": "M21",
"file": "packages/bus/src/interpreter.mjs",
"r5Sha256": "d5d98a9133a1b449acc48143fd559d890a470c6ea210b30b9b70e491e6cd855a",
"r6Sha256": "d5d98a9133a1b449acc48143fd559d890a470c6ea210b30b9b70e491e6cd855a",
"carry": true
},
{
"id": "M22",
"file": "packages/bus/src/server.mjs",
"r5Sha256": "7be306c830aa98763b34b329995b40e9df443483fe20fcd69100a3acb6311bd6",
"r6Sha256": "7be306c830aa98763b34b329995b40e9df443483fe20fcd69100a3acb6311bd6",
"carry": true
},
{
"id": "M23",
"file": "packages/bus/src/interpreter.mjs",
"r5Sha256": "d5d98a9133a1b449acc48143fd559d890a470c6ea210b30b9b70e491e6cd855a",
"r6Sha256": "d5d98a9133a1b449acc48143fd559d890a470c6ea210b30b9b70e491e6cd855a",
"carry": true
},
{
"id": "M24",
"file": "packages/webui/src/tester-socket.mjs",
"r5Sha256": "1c1fe0c795adac97f28502564a75f26d5666d9be5ae51169f7dd5bb7741fad16",
"r6Sha256": "1c1fe0c795adac97f28502564a75f26d5666d9be5ae51169f7dd5bb7741fad16",
"carry": true
},
{
"id": "M25",
"file": "packages/bus/src/interpreter.mjs",
"r5Sha256": "d5d98a9133a1b449acc48143fd559d890a470c6ea210b30b9b70e491e6cd855a",
"r6Sha256": "d5d98a9133a1b449acc48143fd559d890a470c6ea210b30b9b70e491e6cd855a",
"carry": true
},
{
"id": "M26",
"file": "packages/bus/src/peercred.mjs",
"r5Sha256": "29c88655e8b9aed8db604573c9213fd9f3c40ef35ff04f4eda8fbe8f1b8a32d7",
"r6Sha256": "29c88655e8b9aed8db604573c9213fd9f3c40ef35ff04f4eda8fbe8f1b8a32d7",
"carry": true
},
{
"id": "M27",
"file": "packages/bus/src/peercred.mjs",
"r5Sha256": "29c88655e8b9aed8db604573c9213fd9f3c40ef35ff04f4eda8fbe8f1b8a32d7",
"r6Sha256": "29c88655e8b9aed8db604573c9213fd9f3c40ef35ff04f4eda8fbe8f1b8a32d7",
"carry": true
},
{
"id": "M28",
"file": "scripts/stack-release.sh",
"r5Sha256": "b916a2793311d21e3ce7117e9b5e0768e6e33c841fa3aa39fb7ffc9c5974acd2",
"r6Sha256": "b916a2793311d21e3ce7117e9b5e0768e6e33c841fa3aa39fb7ffc9c5974acd2",
"carry": true
},
{
"id": "M28b",
"file": "scripts/stack-release.sh",
"r5Sha256": "b916a2793311d21e3ce7117e9b5e0768e6e33c841fa3aa39fb7ffc9c5974acd2",
"r6Sha256": "b916a2793311d21e3ce7117e9b5e0768e6e33c841fa3aa39fb7ffc9c5974acd2",
"carry": true
},
{
"id": "M29",
"file": "scripts/stack-release.sh",
"r5Sha256": "b916a2793311d21e3ce7117e9b5e0768e6e33c841fa3aa39fb7ffc9c5974acd2",
"r6Sha256": "b916a2793311d21e3ce7117e9b5e0768e6e33c841fa3aa39fb7ffc9c5974acd2",
"carry": true
},
{
"id": "M30",
"file": "scripts/stack-release.sh",
"r5Sha256": "b916a2793311d21e3ce7117e9b5e0768e6e33c841fa3aa39fb7ffc9c5974acd2",
"r6Sha256": "b916a2793311d21e3ce7117e9b5e0768e6e33c841fa3aa39fb7ffc9c5974acd2",
"carry": false
},
{
"id": "M31",
"file": "scripts/stack-release.sh",
"r5Sha256": "b916a2793311d21e3ce7117e9b5e0768e6e33c841fa3aa39fb7ffc9c5974acd2",
"r6Sha256": "b916a2793311d21e3ce7117e9b5e0768e6e33c841fa3aa39fb7ffc9c5974acd2",
"carry": false
},
{
"id": "M32",
"file": "scripts/stack-release.sh",
"r5Sha256": "b916a2793311d21e3ce7117e9b5e0768e6e33c841fa3aa39fb7ffc9c5974acd2",
"r6Sha256": "b916a2793311d21e3ce7117e9b5e0768e6e33c841fa3aa39fb7ffc9c5974acd2",
"carry": false
},
{
"id": "M33",
"file": "scripts/stack-release.sh",
"r5Sha256": "b916a2793311d21e3ce7117e9b5e0768e6e33c841fa3aa39fb7ffc9c5974acd2",
"r6Sha256": "b916a2793311d21e3ce7117e9b5e0768e6e33c841fa3aa39fb7ffc9c5974acd2",
"carry": false
},
{
"id": "M34",
"file": "scripts/stack-release.sh",
"r5Sha256": "b916a2793311d21e3ce7117e9b5e0768e6e33c841fa3aa39fb7ffc9c5974acd2",
"r6Sha256": "b916a2793311d21e3ce7117e9b5e0768e6e33c841fa3aa39fb7ffc9c5974acd2",
"carry": false
},
{
"id": "M35",
"file": "scripts/stack-release.sh",
"r5Sha256": "b916a2793311d21e3ce7117e9b5e0768e6e33c841fa3aa39fb7ffc9c5974acd2",
"r6Sha256": "b916a2793311d21e3ce7117e9b5e0768e6e33c841fa3aa39fb7ffc9c5974acd2",
"carry": false
},
{
"id": "M36",
"file": "scripts/stack-release.sh",
"r5Sha256": "b916a2793311d21e3ce7117e9b5e0768e6e33c841fa3aa39fb7ffc9c5974acd2",
"r6Sha256": "b916a2793311d21e3ce7117e9b5e0768e6e33c841fa3aa39fb7ffc9c5974acd2",
"carry": false
},
{
"id": "M37",
"file": "scripts/stack-release.sh",
"r5Sha256": "b916a2793311d21e3ce7117e9b5e0768e6e33c841fa3aa39fb7ffc9c5974acd2",
"r6Sha256": "b916a2793311d21e3ce7117e9b5e0768e6e33c841fa3aa39fb7ffc9c5974acd2",
"carry": false
}
]
@@ -0,0 +1,278 @@
[
{
"id": "M1",
"file": "packages/webui/src/serve.mjs",
"description": "tester uses TCP",
"test": "packages/webui/tests/tester-socket.test.mjs"
},
{
"id": "M2",
"file": "packages/webui/src/tester-socket.mjs",
"description": "socket mode widened",
"test": "packages/webui/tests/tester-socket.test.mjs"
},
{
"id": "M3",
"file": "packages/webui/src/serve.mjs",
"description": "sign-in accepted on tester",
"test": "packages/webui/tests/tester-socket.test.mjs"
},
{
"id": "M4",
"file": "packages/bus/src/principal.mjs",
"description": "copied PID accepted",
"test": "packages/bus/tests/principal.test.mjs"
},
{
"id": "M5",
"file": "packages/bus/src/peercred.mjs",
"description": "helper parse error ignored",
"test": "packages/bus/tests/peercred.test.mjs"
},
{
"id": "M6",
"file": "packages/bus/src/principal.mjs",
"description": "foreign uid accepted",
"test": "packages/bus/tests/principal.test.mjs"
},
{
"id": "M7",
"file": "packages/webui/src/tester-socket.mjs",
"description": "unsafe parent accepted",
"test": "packages/webui/tests/tester-socket.test.mjs"
},
{
"id": "M8",
"file": "packages/bus/src/human.mjs",
"description": "agent environment walk skipped",
"test": "packages/bus/tests/principal.test.mjs"
},
{
"id": "M9",
"file": "packages/bus/src/human.mjs",
"description": "leaf identity not reread",
"test": "packages/bus/tests/principal.test.mjs"
},
{
"id": "M10",
"file": "packages/bus/src/principal.mjs",
"description": "Console direct-parent check removed",
"test": "packages/bus/tests/principal.test.mjs"
},
{
"id": "M11",
"file": "packages/bus/src/principal.mjs",
"description": "Console basename accepted",
"test": "packages/bus/tests/principal.test.mjs"
},
{
"id": "M12",
"file": "packages/bus/src/human.mjs",
"description": "unknown proof identity accepted",
"test": "packages/bus/tests/principal.test.mjs"
},
{
"id": "M13",
"file": "packages/bus/src/server.mjs",
"description": "transient cap leaked",
"test": "packages/bus/tests/peer-transport.test.mjs"
},
{
"id": "M14",
"file": "packages/bus/src/principal.mjs",
"description": "tester walk used for user",
"test": "packages/bus/tests/principal.test.mjs"
},
{
"id": "M15",
"file": "scripts/stack-release.sh",
"description": "TCP retained during conversion",
"test": [
"--test-name-pattern=row196 explicit socket",
"packages/cli/tests/stack-release.test.mjs"
]
},
{
"id": "M16",
"file": "scripts/stack-release.sh",
"description": "TCP reopened on downgrade",
"test": [
"--test-name-pattern=row196 explicit socket",
"packages/cli/tests/stack-release.test.mjs"
]
},
{
"id": "M17",
"file": "packages/bus/src/broker.mjs",
"description": "tester switch ignored",
"test": "packages/bus/tests/tester.test.mjs"
},
{
"id": "M18",
"file": "packages/bus/src/principal.mjs",
"description": "user kind becomes owner",
"test": "packages/bus/tests/principal.test.mjs"
},
{
"id": "M19",
"file": "packages/cli/src/tester-http.mjs",
"description": "selected instance root ignored",
"test": "packages/webui/tests/tester-socket.test.mjs"
},
{
"id": "M20",
"file": "packages/bus/src/peercred.mjs",
"description": "helper gets another pending socket",
"test": "packages/bus/tests/peercred.test.mjs"
},
{
"id": "M21",
"file": "packages/bus/src/interpreter.mjs",
"description": "user-owned interpreter accepted",
"test": "packages/bus/tests/interpreter.test.mjs"
},
{
"id": "M22",
"file": "packages/bus/src/server.mjs",
"description": "cap requests invoke helper",
"test": "packages/bus/tests/peer-transport.test.mjs"
},
{
"id": "M23",
"file": "packages/bus/src/interpreter.mjs",
"description": "restart retains stale identity",
"test": "packages/bus/tests/interpreter.test.mjs"
},
{
"id": "M24",
"file": "packages/webui/src/tester-socket.mjs",
"description": "listener binds final path directly",
"test": "packages/webui/tests/tester-socket.test.mjs"
},
{
"id": "M25",
"file": "packages/bus/src/interpreter.mjs",
"description": "FIFO receipt blocks before type refusal",
"test": [
"--test-name-pattern=non-regular startup receipt",
"packages/bus/tests/interpreter.test.mjs"
]
},
{
"id": "M26",
"file": "packages/bus/src/peercred.mjs",
"description": "missing helper accepted at startup",
"test": [
"--test-name-pattern=missing or non-regular peer helper",
"packages/bus/tests/peercred.test.mjs"
]
},
{
"id": "M27",
"file": "packages/bus/src/peercred.mjs",
"description": "shutdown returns before helpers are reaped",
"test": [
"--test-name-pattern=shutdown waits",
"packages/bus/tests/peercred.test.mjs"
]
},
{
"id": "M28",
"file": "scripts/stack-release.sh",
"description": "proof pair placed after tester socket flag",
"test": [
"--test-name-pattern=row196 explicit socket",
"packages/cli/tests/stack-release.test.mjs"
]
},
{
"id": "M28b",
"file": "scripts/stack-release.sh",
"description": "SSO placed after tester socket flag",
"test": [
"--test-name-pattern=row196 SSO socket",
"packages/cli/tests/stack-release.test.mjs"
]
},
{
"id": "M29",
"file": "scripts/stack-release.sh",
"description": "unsafe tester path reaches service stop",
"test": [
"--test-name-pattern=row196 explicit socket",
"packages/cli/tests/stack-release.test.mjs"
]
},
{
"id": "M30",
"file": "scripts/stack-release.sh",
"description": "gate skips interpreter receipt",
"test": [
"--test-name-pattern=row196 interpreter activation",
"packages/cli/tests/stack-release.test.mjs"
]
},
{
"id": "M31",
"file": "scripts/stack-release.sh",
"description": "gate ignores changed interpreter",
"test": [
"--test-name-pattern=row196 interpreter activation",
"packages/cli/tests/stack-release.test.mjs"
]
},
{
"id": "M32",
"file": "scripts/stack-release.sh",
"description": "marked target without verifier accepted",
"test": [
"--test-name-pattern=row196 interpreter activation",
"packages/cli/tests/stack-release.test.mjs"
]
},
{
"id": "M33",
"file": "scripts/stack-release.sh",
"description": "unsafe interpreter inspection not refused",
"test": [
"--test-name-pattern=row196 interpreter activation",
"packages/cli/tests/stack-release.test.mjs"
]
},
{
"id": "M34",
"file": "scripts/stack-release.sh",
"description": "started log omits interpreter identity",
"test": [
"--test-name-pattern=row196 interpreter activation",
"packages/cli/tests/stack-release.test.mjs"
]
},
{
"id": "M35",
"file": "scripts/stack-release.sh",
"description": "plan omits interpreter identity",
"test": [
"--test-name-pattern=row196 interpreter activation",
"packages/cli/tests/stack-release.test.mjs"
]
},
{
"id": "M36",
"file": "scripts/stack-release.sh",
"description": "marked rehearsal omits tester socket",
"test": [
"--test-name-pattern=row196 rehearsal selects",
"packages/cli/tests/stack-release.test.mjs"
]
},
{
"id": "M37",
"file": "scripts/stack-release.sh",
"description": "legacy rehearsal receives tester socket",
"test": [
"--test-name-pattern=row196 rehearsal selects",
"packages/cli/tests/stack-release.test.mjs"
]
}
]
@@ -0,0 +1,25 @@
{
"rule": "whole touched files plus prior row215 union",
"baseFiles": [
"packages/bus/tests/library-sync.test.mjs",
"packages/bus/tests/requirements.test.mjs",
"packages/conversation/tests/cohort.test.mjs",
"packages/library/tests/mapping.test.mjs",
"packages/library/tests/sync.test.mjs",
"packages/tasks/tests/requirements.test.mjs"
],
"extraOutsideWholePackageSuites": [
"packages/business/tests/business.test.mjs",
"packages/business/tests/cli.test.mjs",
"packages/business/tests/instance-settings.test.mjs",
"packages/business/tests/library-projects-protected.test.mjs",
"packages/business/tests/resolve.test.mjs",
"packages/conversation/tests/cohort.test.mjs",
"packages/library/tests/mapping.test.mjs",
"packages/library/tests/mirror.test.mjs",
"packages/library/tests/sync.test.mjs",
"packages/project/tests/project.test.mjs",
"packages/seat/tests/checkout.test.mjs",
"packages/tasks/tests/requirements.test.mjs"
]
}
+23
View File
@@ -0,0 +1,23 @@
import {spawn} from 'node:child_process';import {readFileSync,writeFileSync,readdirSync} from 'node:fs';import {join} from 'node:path';import {performance} from 'node:perf_hooks';
const root='/mnt/storage/scratch/tmp/g196-tree-tn651z',out=new URL('.',import.meta.url).pathname;
const jobs=[];
for(const pkg of ['bus','webui','cli']){const files=readdirSync(join(root,'packages',pkg,'tests')).filter(n=>n.endsWith('.test.mjs')).sort().map(n=>`packages/${pkg}/tests/${n}`);jobs.push({name:pkg,command:process.execPath,args:['--test','--test-reporter=tap','--test-concurrency=2',...files],kind:'node',wholeFiles:files});}
const extras=JSON.parse(readFileSync(new URL('./whole-test-union.json',import.meta.url),'utf8')).extraOutsideWholePackageSuites;
for(const file of extras)jobs.push({name:file.replaceAll('/','_'),command:process.execPath,args:['--test','--test-reporter=tap',file],kind:'node',wholeFiles:[file]});
for(const file of readdirSync(join(root,'scripts')).filter(n=>/^test-.*\.sh$/.test(n)).sort())jobs.push({name:file,command:'bash',args:['scripts/'+file],kind:'shell',wholeFiles:['scripts/'+file]});
writeFileSync(out+'whole-plan.json',JSON.stringify(jobs,null,2)+'\n');
const results=[];
for(const job of jobs){
const log=out+job.name+'.log',start=performance.now();let text='',buffer='',failures=0,stopped=false;
console.log('START '+job.name+' '+job.wholeFiles.length+' whole file(s)');
const child=spawn('bash',['-c','set -o pipefail; { python3 "$G196_BUSY" && "$@"; } 2>&1 | tee "$G196_LOG"','--',job.command,...job.args],{cwd:root,env:{...process.env,G196_LOG:log},stdio:['ignore','pipe','pipe'],detached:true});
const consume=data=>{const part=String(data);text+=part;buffer+=part;const lines=buffer.split('\n');buffer=lines.pop();for(const raw of lines){const line=raw.replace(/\x1b\[[0-9;]*m/g,'');if(/^\s*not ok \d/.test(line)||/^FAIL(?:\s|:)/.test(line)){failures++;if(failures>=2&&!stopped){stopped=true;process.kill(-child.pid,'SIGTERM');}}}};
child.stdout.on('data',consume);child.stderr.on('data',consume);
const status=await new Promise(resolve=>child.on('close',(code,signal)=>resolve({code,signal})));
const summary=key=>{const matches=[...text.matchAll(new RegExp('^# '+key+' (\\d+)','gm'))];return matches.length?Number(matches.at(-1)[1]):null;};
const shell=[...text.matchAll(/(\d+) passed,\s*(\d+) failed/g)].at(-1);
const result={...job,log,...status,wallSeconds:(performance.now()-start)/1000,pass:job.kind==='node'?summary('pass'):shell?Number(shell[1]):null,fail:job.kind==='node'?summary('fail'):shell?Number(shell[2]):null,skip:job.kind==='node'?summary('skipped'):0,observedFailures:failures,stopped,complete:!stopped};
results.push(result);writeFileSync(out+'whole-results.json',JSON.stringify(results,null,2)+'\n');
console.log('END '+job.name+' '+JSON.stringify({code:status.code,signal:status.signal,pass:result.pass,fail:result.fail,skip:result.skip,wallSeconds:result.wallSeconds,stopped}));
if(status.code!==0||failures||result.pass===null){process.exitCode=1;console.log('STOP '+job.name);break;}
}
+6
View File
@@ -693,6 +693,12 @@ backup's checks it prints `BACKUP_STORE <activation id> schema <version>`.
it never picks another port. `--timeout` (default 60 seconds) bounds the
gate and each stop.
A copy marked for the private tester receives `--tester-socket` during rehearsal;
a legacy copy receives no tester flag. The socket is under the rehearsal's
scratch dataRoot, at `data/console/tester.sock`. That path is longer than the live
one. A long dataRoot can pass activation's pathname preflight and still make the
rehearsal Console refuse its socket path; the rehearsal then fails closed.
Before it makes anything, it checks the pointer, the copy (present,
reachable from refactor, a readable schema, a bus host with `--no-launch`),
the port and the backup's `SHA256SUMS`. Then it makes
+72
View File
@@ -1744,6 +1744,7 @@ function rConsole(){
const id=path.basename(path.resolve(import.meta.dirname,'..','..','..')),home=process.env.HOME,a=process.argv.slice(2);
const fault=(n)=>fs.existsSync(path.join(home,'faults',n));
fs.appendFileSync(path.join(home,'console-args.log'),`${a.join(' ')}\n`);
fs.appendFileSync(path.join(home,'console-context.jsonl'),JSON.stringify({argv:a,dataRoot:JSON.parse(fs.readFileSync(process.env.MOSAIC_CONFIG,'utf8')).dataRoot})+'\n');
if(a[0]!=='serve'){console.error('fixture Console: refused');process.exit(1)}
const s=http.createServer((q,r)=>{r.writeHead(q.url==='/healthz'&&!fault(`healthz-${id}`)?200:500);r.end()});
s.listen(Number(a[a.indexOf('--port')+1]),'127.0.0.1',()=>console.log(`Mosaic Console: http://127.0.0.1:${s.address().port}/\nBoard: fixture`));
@@ -2319,3 +2320,74 @@ test('row196 SSO socket conversion pins pointer and unit argv order without send
const status=await prun(e,'status','acme');assert.equal(status.code,0,status.out+status.err);assert.match(status.out,/unit flags: match the pointer/);
await noLeak(e);
});
// T10 activation: exercise the shell caller as well as interpreter.mjs itself.
test('row196 interpreter activation checks receipt, identity, plan and startup log',async t=>{
const cli=join(COPIES,C1,'packages/webui/src/cli.mjs'),old=readFileSync(cli,'utf8');
const module=join(COPIES,C1,'packages/bus/src/interpreter.mjs');
const identity={path:'/usr/bin/python3',sha256:'fixture-root-owned-ELF'};
const source=`import fs from 'node:fs';import {join} from 'node:path';
const root=process.env.HOME,sd=process.env.SD_STATE;
const fault=n=>fs.existsSync(join(sd,'faults',n));
export function inspectInterpreter(){
if(fault('interpreter-unsafe'))throw Error('unsafe interpreter');
const seen=join(root,'interpreter-inspected'),changed=fault('interpreter-drift')&&fs.existsSync(seen);
fs.writeFileSync(seen,'inspected');
return {...${JSON.stringify(identity)},...(changed?{sha256:'replacement-ELF'}:{})};
}
export function verifyInterpreterReceipt(directory,expected,since){
fs.appendFileSync(join(root,'interpreter-verified.jsonl'),JSON.stringify({directory,expected,since})+'\\n');
if(fault('interpreter-receipt-refuse'))throw Error('receipt does not match started bus');
return true;
}
`;
const putModule=()=>{chmodSync(dirname(module),0o755);writeFileSync(module,source);chmodSync(module,0o444);chmodSync(dirname(module),0o555);};
chmodSync(cli,0o644);writeFileSync(cli,old+'\n// MOSAIC_TESTER_SOCKET_V1\n');chmodSync(cli,0o444);putModule();
t.after(()=>{chmodSync(cli,0o644);writeFileSync(cli,old);chmodSync(cli,0o444);chmodSync(dirname(module),0o755);rmSync(module,{force:true});chmodSync(dirname(module),0o555);});
await t.test('plan and started log preserve the same interpreter JSON',async t=>{
const e=await mk(t);await seed(e);
const plan=await run(e,'activate',C1,'acme','--tester-socket','--plan');assert.equal(plan.code,0,plan.out+plan.err);
const line=/^interpreter: (.+)$/m.exec(plan.out);assert.ok(line,plan.out);assert.deepEqual(JSON.parse(line[1]),identity);
const r=await run(e,'activate',C1,'acme','--tester-socket');assert.equal(r.code,0,r.out+r.err);
const started=logLines(e).find(x=>x.op==='activate'&&x.result==='started');assert.ok(started);
assert.equal(started.interpreter,line[1]);assert.deepEqual(JSON.parse(started.interpreter),identity);
const verified=lines(join(e.env.HOME,'interpreter-verified.jsonl')).map(JSON.parse);
assert.ok(verified.length>0);assert.equal(verified[0].directory,join(e.data,'bus'));assert.deepEqual(verified[0].expected,identity);assert.ok(Number(verified[0].since)>0);
});
for(const scenario of ['interpreter-receipt-refuse','interpreter-drift'])await t.test(scenario+' fails at the gate and restores the pointer',async t=>{
const e=await mk(t);await seed(e);const before=readFileSync(e.pointer,'utf8'),n=calls(e).length,logs=logLines(e).length;fault(e,scenario);
const r=await run(e,'activate',C1,'acme','--tester-socket','--timeout','1');
assert.equal(r.code,1,r.out+r.err);assert.match(r.out+r.err,/interpreter-receipt/);assert.equal(readFileSync(e.pointer,'utf8'),before);
assert.ok(startsStops(e,n).some(x=>x.startsWith('start ')),startsStops(e,n).join('\n'));
const added=logLines(e).slice(logs);assert.ok(added.some(x=>x.result==='gate-failed'&&x.code==='interpreter-receipt'&&x.rollback==='ok'),JSON.stringify(added));
assert.equal(added.some(x=>x.result==='ok'),false,JSON.stringify(added));
});
for(const scenario of ['missing-verifier','interpreter-unsafe'])await t.test(scenario+' refuses before any start or stop',async t=>{
const e=await mk(t);await seed(e);const before=readFileSync(e.pointer,'utf8'),n=calls(e).length;
if(scenario==='missing-verifier'){chmodSync(dirname(module),0o755);rmSync(module);chmodSync(dirname(module),0o555);}else fault(e,scenario);
try{
for(const flags of [['--plan'],[]]){
const r=await run(e,'activate',C1,'acme','--tester-socket',...flags);
assert.equal(r.code,3,r.out+r.err);assert.match(r.out+r.err,/interpreter-unsafe/);
assert.equal(readFileSync(e.pointer,'utf8'),before);assert.deepEqual(startsStops(e,n),[]);
}
}finally{if(scenario==='missing-verifier')putModule();}
});
});
test('row196 rehearsal selects socket only for marked copies and uses scratch dataRoot',async t=>{
const file=join(RCOPIES,B,'packages/webui/src/cli.mjs'),old=readFileSync(file,'utf8');
const put=text=>{chmodSync(file,0o644);writeFileSync(file,text);chmodSync(file,0o444);};
t.after(()=>put(old));
for(const marked of [true,false])await t.test(marked?'socket copy':'legacy copy',async t=>{
put(old+(marked?'\n// MOSAIC_TESTER_SOCKET_V1\n':''));
const e=await rmk(t),before=untouched(e),port=await freePort();
const r=await rehearse(e,B,'--backup',e.act,'--port',port,'--timeout','10');assert.equal(r.code,0,r.out+r.err);
const contexts=lines(rh(e,'console-context.jsonl')).map(JSON.parse);assert.equal(contexts.length,1);
const context=contexts[0];assert.deepEqual(context.argv,['serve','--port',port,'--business','acme',...(marked?['--tester-socket']:[])]);
assert.equal(context.argv.includes('--tester-socket'),marked);assert.equal(context.argv.includes('--tester-port'),false);
assert.equal(context.dataRoot,join(r.dir,'data'));assert.notEqual(context.dataRoot,e.data);
assert.equal(JSON.parse(readFileSync(join(r.dir,'config/config.json'),'utf8')).dataRoot,context.dataRoot);
assert.equal(untouched(e),before);
});
});