deadbyte-mcp 0.15.27 → 0.15.28
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/MANIFEST.SHA256 +24 -18
- package/PROVENANCE.json +10 -10
- package/README.txt +30 -14
- package/RELEASE.json +48 -15
- package/SBOM.cdx.json +3 -3
- package/TEST-MATRIX.json +134 -23
- package/package.json +4 -3
- package/scripts/deploy-r48.mjs +1 -1
- package/scripts/gate-windows-r51.ps1 +58 -0
- package/scripts/windows-gate-evidence-r51.mjs +84 -0
- package/src/coding-plane.mjs +148 -17
- package/src/deadbyte-cli.mjs +88 -28
- package/src/remote-live-log.mjs +17 -5
- package/src/remote-session-guardian.mjs +1 -1
- package/src/runtime-coherence.mjs +50 -0
- package/src/version.mjs +1 -1
- package/test/coding-runtime.test.mjs +35 -1
- package/test/production-docs.test.mjs +5 -3
- package/test/r48-release-contract.test.mjs +7 -30
- package/test/r49-windows-gate.test.mjs +12 -11
- package/test/r51-live-log-polling.test.mjs +28 -0
- package/test/r51-release-contract.test.mjs +56 -0
- package/test/r51-runtime-coherence.test.mjs +59 -0
- package/test/remote-live-log-r37.test.mjs +14 -0
- package/test/remote-session-cli.test.mjs +24 -24
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
const SEMVER=/^(\d+)\.(\d+)\.(\d+)(?:-([0-9A-Za-z.-]+))?(?:\+[0-9A-Za-z.-]+)?$/;
|
|
2
|
+
const HEX64=/^[0-9a-f]{64}$/;
|
|
3
|
+
|
|
4
|
+
function parseSemver(value){
|
|
5
|
+
const match=SEMVER.exec(String(value??''));
|
|
6
|
+
if(!match) throw new Error(`runtime version invalid: ${String(value)}`);
|
|
7
|
+
return {major:Number(match[1]),minor:Number(match[2]),patch:Number(match[3]),prerelease:match[4]??null};
|
|
8
|
+
}
|
|
9
|
+
export function compareRuntimeVersions(a,b){
|
|
10
|
+
const left=parseSemver(a),right=parseSemver(b);
|
|
11
|
+
for(const key of ['major','minor','patch']) if(left[key]!==right[key]) return left[key]<right[key]?-1:1;
|
|
12
|
+
if(left.prerelease===right.prerelease) return 0;
|
|
13
|
+
if(left.prerelease===null) return 1;
|
|
14
|
+
if(right.prerelease===null) return -1;
|
|
15
|
+
return left.prerelease.localeCompare(right.prerelease,'en',{numeric:true,sensitivity:'case'});
|
|
16
|
+
}
|
|
17
|
+
function assertManifest(value,label){
|
|
18
|
+
if(!HEX64.test(String(value??''))) throw new Error(`${label} must be lowercase SHA-256`);
|
|
19
|
+
return value;
|
|
20
|
+
}
|
|
21
|
+
export async function ensureRuntimeCoherence({
|
|
22
|
+
cliVersion,packageManifestSha256,readActive,disarmPrivileged,activateCurrentPackage,onEvent=()=>{}
|
|
23
|
+
}={}){
|
|
24
|
+
parseSemver(cliVersion);assertManifest(packageManifestSha256,'package manifest');
|
|
25
|
+
if(typeof readActive!=='function'||typeof disarmPrivileged!=='function'||typeof activateCurrentPackage!=='function'||typeof onEvent!=='function') {
|
|
26
|
+
throw new Error('runtime coherence callbacks invalid');
|
|
27
|
+
}
|
|
28
|
+
const initial=await readActive();
|
|
29
|
+
if(!initial) return {status:'unbound',active:null};
|
|
30
|
+
const activeVersion=String(initial.version??'');
|
|
31
|
+
const relation=compareRuntimeVersions(cliVersion,activeVersion);
|
|
32
|
+
if(relation===0){
|
|
33
|
+
if(String(initial.manifest_sha256??'')!==packageManifestSha256) {
|
|
34
|
+
throw new Error(`runtime manifest skew for ${cliVersion}: package ${packageManifestSha256}; active ${String(initial.manifest_sha256??'missing')}`);
|
|
35
|
+
}
|
|
36
|
+
return {status:'coherent',active:initial};
|
|
37
|
+
}
|
|
38
|
+
if(relation<0) throw new Error(`active runtime ${activeVersion} is newer than CLI package ${cliVersion}; refusing automatic downgrade`);
|
|
39
|
+
onEvent({phase:'upgrade_start',from_version:activeVersion,to_version:cliVersion,from_release_id:initial.release_id??null});
|
|
40
|
+
await disarmPrivileged();
|
|
41
|
+
await activateCurrentPackage();
|
|
42
|
+
const active=await readActive();
|
|
43
|
+
if(!active) throw new Error('runtime coherence activation completed without active release pointer');
|
|
44
|
+
if(String(active.version??'')!==cliVersion) throw new Error(`runtime coherence activation version mismatch: expected ${cliVersion}, found ${String(active.version??'missing')}`);
|
|
45
|
+
if(String(active.manifest_sha256??'')!==packageManifestSha256) {
|
|
46
|
+
throw new Error(`runtime coherence activation manifest mismatch: expected ${packageManifestSha256}, found ${String(active.manifest_sha256??'missing')}`);
|
|
47
|
+
}
|
|
48
|
+
onEvent({phase:'upgrade_complete',from_version:activeVersion,to_version:cliVersion,release_id:active.release_id??null});
|
|
49
|
+
return {status:'upgraded',active};
|
|
50
|
+
}
|
package/src/version.mjs
CHANGED
|
@@ -39,7 +39,7 @@ async function fixture({ armed = true } = {}) {
|
|
|
39
39
|
}
|
|
40
40
|
const runtime = createCodingRuntime({ policy, signingKeyPath: keys.privateKeyPath, verifyKeyPath: keys.publicKeyPath });
|
|
41
41
|
const tasks = createCodingTaskRuntime({ codingRuntime: runtime, signingKeyPath: keys.privateKeyPath, verifyKeyPath: keys.publicKeyPath });
|
|
42
|
-
return { root, project, policy, runtime, tasks };
|
|
42
|
+
return { root, project, policy, runtime, tasks, keys };
|
|
43
43
|
}
|
|
44
44
|
test('coding plane edits files under lease and verifies signed action receipts', async () => {
|
|
45
45
|
const f = await fixture();
|
|
@@ -233,3 +233,37 @@ test('coding task rejects recomputed journal content without the private signing
|
|
|
233
233
|
await assert.rejects(() => f.tasks.status({ taskId: submitted.task_id }), /signature verification failed|hash-chain mismatch/);
|
|
234
234
|
} finally { await rm(f.root, { recursive: true, force: true }); }
|
|
235
235
|
});
|
|
236
|
+
|
|
237
|
+
|
|
238
|
+
test('R51 coding command retries reuse one in-flight execution and completed receipt', async () => {
|
|
239
|
+
const f = await fixture();
|
|
240
|
+
try {
|
|
241
|
+
const script = path.join(f.project, 'dedup.mjs');
|
|
242
|
+
await writeFile(script, "import { appendFileSync } from 'node:fs'; appendFileSync('runs.txt', process.pid+'\\n'); await new Promise(r=>setTimeout(r,250)); process.stdout.write('dedup-ok');");
|
|
243
|
+
const args={rootId:'project',commandId:'node_script',scriptPath:'dedup.mjs',scriptSha256:await sha256File(script),timeoutMs:5000};
|
|
244
|
+
const [a,b]=await Promise.all([f.runtime.commandRun(args),f.runtime.commandRun(args)]);
|
|
245
|
+
assert.equal(a.status,'passed',a.stderr);assert.equal(b.status,'passed',b.stderr);
|
|
246
|
+
assert.equal(a.request_sha256,b.request_sha256);
|
|
247
|
+
assert.equal(a.receipt_id,b.receipt_id,'same logical request must reuse the same signed execution receipt');
|
|
248
|
+
const c=await f.runtime.commandRun(args);
|
|
249
|
+
assert.equal(c.receipt_id,a.receipt_id,'completed retry must reuse bounded cached execution result');
|
|
250
|
+
const runs=(await readFile(path.join(f.project,'runs.txt'),'utf8')).trim().split(/\\r?\\n/).filter(Boolean);
|
|
251
|
+
assert.equal(runs.length,1,'retry spawned duplicate child process');
|
|
252
|
+
} finally { await rm(f.root,{recursive:true,force:true}); }
|
|
253
|
+
});
|
|
254
|
+
|
|
255
|
+
test('R51 coding command single-flight spans independent runtime instances sharing evidence state', async () => {
|
|
256
|
+
const f = await fixture();
|
|
257
|
+
try {
|
|
258
|
+
const script = path.join(f.project, 'cross-runtime-dedup.mjs');
|
|
259
|
+
await writeFile(script, "import { appendFileSync } from 'node:fs'; appendFileSync('cross-runs.txt', process.pid+'\\n'); await new Promise(r=>setTimeout(r,500)); process.stdout.write('cross-ok');");
|
|
260
|
+
const second = createCodingRuntime({ policy:f.policy, signingKeyPath:f.keys.privateKeyPath, verifyKeyPath:f.keys.publicKeyPath });
|
|
261
|
+
const args={rootId:'project',commandId:'node_script',scriptPath:'cross-runtime-dedup.mjs',scriptSha256:await sha256File(script),timeoutMs:5000};
|
|
262
|
+
const [a,b]=await Promise.all([f.runtime.commandRun(args),second.commandRun(args)]);
|
|
263
|
+
assert.equal(a.status,'passed',a.stderr);assert.equal(b.status,'passed',b.stderr);
|
|
264
|
+
assert.equal(a.request_sha256,b.request_sha256);
|
|
265
|
+
assert.equal(a.receipt_id,b.receipt_id,'shared evidence state must converge on one signed execution receipt');
|
|
266
|
+
const runs=(await readFile(path.join(f.project,'cross-runs.txt'),'utf8')).trim().split(/\\r?\\n/).filter(Boolean);
|
|
267
|
+
assert.equal(runs.length,1,'independent runtime instances spawned duplicate child processes');
|
|
268
|
+
} finally { await rm(f.root,{recursive:true,force:true}); }
|
|
269
|
+
});
|
|
@@ -3,15 +3,17 @@ import assert from 'node:assert/strict';
|
|
|
3
3
|
import { readFile } from 'node:fs/promises';
|
|
4
4
|
import path from 'node:path';
|
|
5
5
|
import { fileURLToPath } from 'node:url';
|
|
6
|
+
import { DEADBYTE_VERSION } from '../src/version.mjs';
|
|
6
7
|
|
|
7
8
|
const root=path.resolve(fileURLToPath(new URL('..',import.meta.url)));
|
|
8
9
|
const readme=()=>readFile(path.join(root,'README.txt'),'utf8');
|
|
10
|
+
const versionPattern=DEADBYTE_VERSION.replace(/[.*+?^$()|[\]\\]/g,'\\$&');
|
|
9
11
|
|
|
10
12
|
test('production README documents one-command stdio configs for major MCP clients',async()=>{
|
|
11
13
|
const text=await readme();
|
|
12
|
-
assert.match(text
|
|
14
|
+
assert.match(text,new RegExp(`npx -y deadbyte-mcp@${versionPattern} mcp`));
|
|
13
15
|
assert.match(text,/CLAUDE DESKTOP/);
|
|
14
|
-
assert.match(text
|
|
16
|
+
assert.match(text,new RegExp(`"mcpServers"[\\s\\S]*?"command"\\s*:\\s*"npx"[\\s\\S]*?"-y"[\\s\\S]*?"deadbyte-mcp@${versionPattern}"[\\s\\S]*?"mcp"`));
|
|
15
17
|
assert.match(text,/CURSOR/);
|
|
16
18
|
assert.match(text,/\.cursor\/mcp\.json/);
|
|
17
19
|
assert.match(text,/VS CODE/);
|
|
@@ -20,7 +22,7 @@ test('production README documents one-command stdio configs for major MCP client
|
|
|
20
22
|
assert.match(text,/CODEX/);
|
|
21
23
|
assert.match(text,/\[mcp_servers\.deadbyte\]/);
|
|
22
24
|
assert.match(text,/command\s*=\s*"npx"/);
|
|
23
|
-
assert.match(text
|
|
25
|
+
assert.match(text,new RegExp(`args\\s*=\\s*\\["-y",\\s*"deadbyte-mcp@${versionPattern}",\\s*"mcp"\\]`));
|
|
24
26
|
});
|
|
25
27
|
|
|
26
28
|
test('production README records tracer bullet, semantic scope, remote UX, safety and document extension',async()=>{
|
|
@@ -5,37 +5,18 @@ import { DEADBYTE_VERSION } from '../src/version.mjs';
|
|
|
5
5
|
|
|
6
6
|
const root = new URL('../', import.meta.url);
|
|
7
7
|
const read = rel => readFile(new URL(rel, root), 'utf8');
|
|
8
|
+
const json = rel => read(rel).then(JSON.parse);
|
|
8
9
|
|
|
9
|
-
test('
|
|
10
|
+
test('R48 compatibility release contract remains coherent in the current successor', async () => {
|
|
10
11
|
const [pkg, lock, release, provenance, matrix] = await Promise.all([
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
read('RELEASE.json').then(JSON.parse),
|
|
14
|
-
read('PROVENANCE.json').then(JSON.parse),
|
|
15
|
-
read('TEST-MATRIX.json').then(JSON.parse)
|
|
12
|
+
json('package.json'), json('package-lock.json'), json('RELEASE.json'),
|
|
13
|
+
json('PROVENANCE.json'), json('TEST-MATRIX.json')
|
|
16
14
|
]);
|
|
17
15
|
|
|
18
16
|
for (const value of [
|
|
19
|
-
pkg.version,
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
DEADBYTE_VERSION,
|
|
23
|
-
release.version,
|
|
24
|
-
provenance.version,
|
|
25
|
-
matrix.version
|
|
26
|
-
]) assert.equal(value, '0.15.27');
|
|
27
|
-
|
|
28
|
-
assert.equal(release.train, 'R50');
|
|
29
|
-
assert.equal(release.label, 'PUBLIC PACKAGE BOUNDARY HOTFIX');
|
|
30
|
-
assert.equal(release.parent.release_id, 'v0.15.26-669ea65d2307ee2c');
|
|
31
|
-
assert.equal(release.parent.manifest_sha256, '669ea65d2307ee2cce06be960e0a8c7243dc6135f7f129a88d99f53096433cc8');
|
|
32
|
-
assert.equal(release.parent.generation_manifest_sha256, '08364adaf1dab9730c314e29121632c6371ed76fc677e83cb7f6aa6b244eab94');
|
|
33
|
-
|
|
34
|
-
assert.equal(release.source_predecessor.version, '0.15.26');
|
|
35
|
-
assert.equal(release.source_predecessor.tarball_sha256, '3272679e3a18610d47e83dc8d3161e949d75a57da145a43c2ab50c865a83bb86');
|
|
36
|
-
assert.equal(release.source_predecessor.manifest_file_sha256, '669ea65d2307ee2cce06be960e0a8c7243dc6135f7f129a88d99f53096433cc8');
|
|
37
|
-
assert.equal(release.source_predecessor.reference_server_sha256, 'dfc303bb7dd2e8d01debac50fc43bb8644d00089bffdf2887e149497cff8b305');
|
|
38
|
-
assert.equal(release.source_predecessor.reference_schemas_sha256, 'f342345e34e48769dabaefe1b932f09d41e2e207b422f7794a15ee50ffa8f140');
|
|
17
|
+
pkg.version, lock.version, lock.packages[''].version, DEADBYTE_VERSION,
|
|
18
|
+
release.version, provenance.version, matrix.version
|
|
19
|
+
]) assert.equal(value, DEADBYTE_VERSION);
|
|
39
20
|
|
|
40
21
|
assert.equal(release.capabilities.full_tool_count, 117);
|
|
41
22
|
assert.equal(release.capabilities.desktop_bounded_semantic_wait, true);
|
|
@@ -50,8 +31,4 @@ test('R50 0.15.27 successor binds exact published immutable 0.15.26 parent', asy
|
|
|
50
31
|
assert.equal(provenance.parent_manifest_sha256, release.parent.manifest_sha256);
|
|
51
32
|
assert.equal(provenance.parent_generation_manifest_sha256, release.parent.generation_manifest_sha256);
|
|
52
33
|
assert.equal(provenance.source_predecessor_tarball_sha256, release.source_predecessor.tarball_sha256);
|
|
53
|
-
|
|
54
|
-
assert.equal(matrix.train, 'R50');
|
|
55
|
-
assert.equal(matrix.predecessor_r48_full_regression_partition_proof.files, 211);
|
|
56
|
-
assert.equal(matrix.predecessor_r48_full_regression_partition_proof.not_valid_as_r50_proof, true);
|
|
57
34
|
});
|
|
@@ -4,27 +4,28 @@ import test from 'node:test';
|
|
|
4
4
|
|
|
5
5
|
const read = path => readFile(new URL(`../${path}`, import.meta.url), 'utf8');
|
|
6
6
|
|
|
7
|
-
test('
|
|
8
|
-
const [pkgText, gate, evidence,
|
|
7
|
+
test('R51 canonical Windows gate is successor-bound and preserves R50/R49 historical gates', async () => {
|
|
8
|
+
const [pkgText, gate, evidence, historicalR50, historicalR49, deferred] = await Promise.all([
|
|
9
9
|
read('package.json'),
|
|
10
|
+
read('scripts/gate-windows-r51.ps1'),
|
|
11
|
+
read('scripts/windows-gate-evidence-r51.mjs'),
|
|
10
12
|
read('scripts/gate-windows-r50.ps1'),
|
|
11
|
-
read('scripts/windows-gate-evidence-r50.mjs'),
|
|
12
13
|
read('scripts/gate-windows-r49.ps1'),
|
|
13
|
-
read('scripts/gate-windows-r48.ps1'),
|
|
14
14
|
read('scripts/deferred-slot-operation-r46.mjs')
|
|
15
15
|
]);
|
|
16
16
|
const pkg = JSON.parse(pkgText);
|
|
17
|
-
assert.match(pkg.scripts['gate:windows'], /gate-windows-
|
|
18
|
-
assert.match(gate, /DEADBYTE 0\.15\.
|
|
19
|
-
assert.match(gate, /
|
|
17
|
+
assert.match(pkg.scripts['gate:windows'], /gate-windows-r51\.ps1$/);
|
|
18
|
+
assert.match(gate, /DEADBYTE 0\.15\.28 \/ R51 WINDOWS GATE: PASS/);
|
|
19
|
+
assert.match(gate, /r51-package-boundary/);
|
|
20
|
+
assert.match(gate, /r51-focused-contracts/);
|
|
20
21
|
assert.match(gate, /r49-remote-control-donors\.test\.mjs/);
|
|
21
22
|
assert.match(gate, /desktop-runtime-r40\.test\.mjs/);
|
|
22
23
|
assert.match(gate, /manifest-final-verify/);
|
|
23
|
-
assert.match(evidence, /deadbyte\.
|
|
24
|
-
assert.match(evidence, /pkg\.version === '0\.15\.
|
|
25
|
-
assert.match(evidence, /release\.train === '
|
|
24
|
+
assert.match(evidence, /deadbyte\.r51-windows-gate\.v1/);
|
|
25
|
+
assert.match(evidence, /pkg\.version === '0\.15\.28'/);
|
|
26
|
+
assert.match(evidence, /release\.train === 'R51'/);
|
|
27
|
+
assert.match(historicalR50, /DEADBYTE 0\.15\.27 \/ R50 WINDOWS GATE: PASS/);
|
|
26
28
|
assert.match(historicalR49, /DEADBYTE 0\.15\.26 \/ R49 WINDOWS GATE: PASS/);
|
|
27
|
-
assert.match(historicalR48, /DEADBYTE 0\.15\.25 \/ R48 WINDOWS GATE: PASS/);
|
|
28
29
|
assert.match(deferred, /const expectedVersion=packageIdentity\.version/);
|
|
29
30
|
assert.match(deferred, /'--expected-version',expectedVersion/);
|
|
30
31
|
assert.match(deferred, /async function awaitHandoffCompletion/);
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import test from 'node:test';
|
|
2
|
+
import assert from 'node:assert/strict';
|
|
3
|
+
import { selectLiveLogNames } from '../src/remote-live-log.mjs';
|
|
4
|
+
import { readFile } from 'node:fs/promises';
|
|
5
|
+
|
|
6
|
+
test('R51 live log discovery tails only current and previous audit segments plus runtime logs',()=>{
|
|
7
|
+
const selected=selectLiveLogNames({
|
|
8
|
+
auditNames:['segment-00000001.jsonl','junk.txt','segment-00000003.jsonl','segment-00000002.jsonl'],
|
|
9
|
+
runtimeNames:['bridge.stdout.log','bridge.stderr.log','note.txt']
|
|
10
|
+
});
|
|
11
|
+
assert.deepEqual(selected.audit,['segment-00000002.jsonl','segment-00000003.jsonl']);
|
|
12
|
+
assert.deepEqual(selected.runtime,['bridge.stderr.log','bridge.stdout.log']);
|
|
13
|
+
});
|
|
14
|
+
|
|
15
|
+
test('R51 live tail caches directory discovery instead of rescanning every 250ms poll',async()=>{
|
|
16
|
+
const cli=await readFile(new URL('../src/deadbyte-cli.mjs',import.meta.url),'utf8');
|
|
17
|
+
assert.match(cli,/LIVE_LOG_DISCOVERY_INTERVAL_MS\s*=\s*1000/);
|
|
18
|
+
assert.match(cli,/nextDiscoveryAt/);
|
|
19
|
+
assert.doesNotMatch(cli,/for\(const file of await listLogFiles\(root\)\)/);
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
test('R51 live tail parses each audit line once and reuses the decoded event',async()=>{
|
|
23
|
+
const cli=await readFile(new URL('../src/deadbyte-cli.mjs',import.meta.url),'utf8');
|
|
24
|
+
assert.match(cli,/parseAuditLogLine/);
|
|
25
|
+
assert.match(cli,/auditEvent/);
|
|
26
|
+
assert.match(cli,/auditTimelineRecord\(file,line,auditEvent\)/);
|
|
27
|
+
assert.match(cli,/formatLogLine\(file,line,\{auditEvent,/);
|
|
28
|
+
});
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import test from 'node:test';
|
|
2
|
+
import assert from 'node:assert/strict';
|
|
3
|
+
import { readFile } from 'node:fs/promises';
|
|
4
|
+
|
|
5
|
+
const root=new URL('../',import.meta.url);
|
|
6
|
+
const read=rel=>readFile(new URL(rel,root),'utf8');
|
|
7
|
+
const json=rel=>read(rel).then(JSON.parse);
|
|
8
|
+
|
|
9
|
+
test('R51 release identity is coherent across package lock source metadata SBOM and gate',async()=>{
|
|
10
|
+
const [pkg,lock,release,provenance,matrix,sbom,versionSource]=await Promise.all([
|
|
11
|
+
json('package.json'),json('package-lock.json'),json('RELEASE.json'),json('PROVENANCE.json'),json('TEST-MATRIX.json'),json('SBOM.cdx.json'),read('src/version.mjs')
|
|
12
|
+
]);
|
|
13
|
+
assert.equal(pkg.version,'0.15.28');
|
|
14
|
+
assert.equal(lock.version,pkg.version);
|
|
15
|
+
assert.equal(lock.packages[''].version,pkg.version);
|
|
16
|
+
assert.match(versionSource,/DEADBYTE_VERSION = '0\.15\.28'/);
|
|
17
|
+
assert.equal(release.version,pkg.version);
|
|
18
|
+
assert.equal(release.train,'R51');
|
|
19
|
+
assert.equal(provenance.version,pkg.version);
|
|
20
|
+
assert.equal(matrix.version,pkg.version);
|
|
21
|
+
assert.equal(matrix.train,'R51');
|
|
22
|
+
assert.equal(sbom.metadata.component.version,pkg.version);
|
|
23
|
+
assert.equal(sbom.serialNumber,'urn:cdx:deadbyte-mcp:r51:0.15.28');
|
|
24
|
+
assert.match(pkg.scripts['gate:windows'],/gate-windows-r51\.ps1/);
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
test('R51 lineage binds exact immutable published 0.15.27 parent',async()=>{
|
|
28
|
+
const [release,provenance]=await Promise.all([json('RELEASE.json'),json('PROVENANCE.json')]);
|
|
29
|
+
const expected={
|
|
30
|
+
release_id:'v0.15.27-1b1dbe24b45ad06a',
|
|
31
|
+
manifest_sha256:'1b1dbe24b45ad06a62771d15f510662952f3d7c3e5aa08053c6c61511c523e47',
|
|
32
|
+
generation_manifest_sha256:'f87a1637b3c55ba050d079d8a5e0862a2244b306be7e985880708e0d8ded3db0'
|
|
33
|
+
};
|
|
34
|
+
assert.deepEqual(release.parent,expected);
|
|
35
|
+
assert.equal(release.source_predecessor.version,'0.15.27');
|
|
36
|
+
assert.equal(release.source_predecessor.tarball_sha256,'e728031d097d8678dfef80f8648cce65e72a96611052c31969c80f884506d1d7');
|
|
37
|
+
assert.equal(release.source_predecessor.manifest_file_sha256,expected.manifest_sha256);
|
|
38
|
+
assert.equal(provenance.parent_release_id,expected.release_id);
|
|
39
|
+
assert.equal(provenance.parent_manifest_sha256,expected.manifest_sha256);
|
|
40
|
+
assert.equal(provenance.parent_generation_manifest_sha256,expected.generation_manifest_sha256);
|
|
41
|
+
assert.equal(provenance.source_predecessor_tarball_sha256,release.source_predecessor.tarball_sha256);
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
test('R51 release advertises only implemented coherence single-flight authority and livelog hardening',async()=>{
|
|
45
|
+
const release=await json('RELEASE.json');
|
|
46
|
+
for(const key of [
|
|
47
|
+
'runtime_package_auto_coherence',
|
|
48
|
+
'coding_command_singleflight',
|
|
49
|
+
'coding_command_result_dedup_cache',
|
|
50
|
+
'foreground_desktop_host_fail_closed',
|
|
51
|
+
'human_livelog_slow_call_marker',
|
|
52
|
+
'human_livelog_peak_inflight',
|
|
53
|
+
'human_livelog_bounded_discovery',
|
|
54
|
+
'human_livelog_single_parse'
|
|
55
|
+
]) assert.equal(release.capabilities[key],true,key);
|
|
56
|
+
});
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import test from 'node:test';
|
|
2
|
+
import assert from 'node:assert/strict';
|
|
3
|
+
import { ensureRuntimeCoherence } from '../src/runtime-coherence.mjs';
|
|
4
|
+
|
|
5
|
+
const hex=value=>value.repeat(64).slice(0,64);
|
|
6
|
+
|
|
7
|
+
test('R51 runtime coherence upgrades older active slot exactly once and verifies final identity',async()=>{
|
|
8
|
+
let active={version:'0.15.27',manifest_sha256:hex('1'),release_id:'old'};
|
|
9
|
+
let disarms=0,activations=0;
|
|
10
|
+
const events=[];
|
|
11
|
+
const result=await ensureRuntimeCoherence({
|
|
12
|
+
cliVersion:'0.15.28',packageManifestSha256:hex('2'),
|
|
13
|
+
readActive:async()=>active,
|
|
14
|
+
disarmPrivileged:async()=>{disarms++;},
|
|
15
|
+
activateCurrentPackage:async()=>{activations++;active={version:'0.15.28',manifest_sha256:hex('2'),release_id:'new'};},
|
|
16
|
+
onEvent:event=>events.push(event)
|
|
17
|
+
});
|
|
18
|
+
assert.equal(result.status,'upgraded');
|
|
19
|
+
assert.equal(result.active.version,'0.15.28');
|
|
20
|
+
assert.equal(disarms,1);assert.equal(activations,1);
|
|
21
|
+
assert.deepEqual(events.map(event=>event.phase),['upgrade_start','upgrade_complete']);
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
test('R51 runtime coherence is non-mutating when version and manifest already match',async()=>{
|
|
25
|
+
let mutations=0;
|
|
26
|
+
const active={version:'0.15.28',manifest_sha256:hex('a'),release_id:'same'};
|
|
27
|
+
const result=await ensureRuntimeCoherence({cliVersion:'0.15.28',packageManifestSha256:hex('a'),readActive:async()=>active,
|
|
28
|
+
disarmPrivileged:async()=>{mutations++;},activateCurrentPackage:async()=>{mutations++;}});
|
|
29
|
+
assert.equal(result.status,'coherent');assert.equal(mutations,0);
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
test('R51 runtime coherence refuses downgrade and same-version manifest drift',async()=>{
|
|
33
|
+
await assert.rejects(()=>ensureRuntimeCoherence({cliVersion:'0.15.28',packageManifestSha256:hex('a'),
|
|
34
|
+
readActive:async()=>({version:'0.15.29',manifest_sha256:hex('b')}),disarmPrivileged:async()=>{},activateCurrentPackage:async()=>{}}),/newer than CLI/i);
|
|
35
|
+
await assert.rejects(()=>ensureRuntimeCoherence({cliVersion:'0.15.28',packageManifestSha256:hex('a'),
|
|
36
|
+
readActive:async()=>({version:'0.15.28',manifest_sha256:hex('b')}),disarmPrivileged:async()=>{},activateCurrentPackage:async()=>{}}),/manifest skew/i);
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
test('R51 bare remote wires coherence before live generation binding and deploy honors state root override',async()=>{
|
|
41
|
+
const {readFile}=await import('node:fs/promises');
|
|
42
|
+
const cli=await readFile(new URL('../src/deadbyte-cli.mjs',import.meta.url),'utf8');
|
|
43
|
+
const deploy=await readFile(new URL('../scripts/deploy-r48.mjs',import.meta.url),'utf8');
|
|
44
|
+
assert.match(cli,/ensureRuntimeCoherence/);
|
|
45
|
+
assert.match(cli,/synchronizeForegroundRuntime/);
|
|
46
|
+
const syncAt=cli.indexOf('await synchronizeForegroundRuntime');
|
|
47
|
+
const rootsAt=cli.indexOf('await readLiveRootPaths');
|
|
48
|
+
assert.ok(syncAt>=0&&rootsAt>syncAt,'runtime coherence must complete before binding generation root paths');
|
|
49
|
+
assert.match(deploy,/process\.env\.DEADBYTE_STATE_ROOT/);
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
test('R51 foreground authority contract explicitly verifies and cleans desktop plus host',async()=>{
|
|
54
|
+
const {readFile}=await import('node:fs/promises');
|
|
55
|
+
const cli=await readFile(new URL('../src/deadbyte-cli.mjs',import.meta.url),'utf8');
|
|
56
|
+
assert.match(cli,/\['desktopstatus',\/\^Desktop plane/);
|
|
57
|
+
assert.match(cli,/SESSION_CLEANUP=.*disarmdesktop.*disarmhost/s);
|
|
58
|
+
assert.match(cli,/desktop, host DISARMED/);
|
|
59
|
+
});
|
|
@@ -204,3 +204,17 @@ test('expanded session summary reports outcome classes latency and slowest tool'
|
|
|
204
204
|
assert.match(summary,/p50 75ms \| p95 200ms/);
|
|
205
205
|
assert.match(summary,/slowest host_exec 200ms/);
|
|
206
206
|
});
|
|
207
|
+
|
|
208
|
+
|
|
209
|
+
test('R51 live timeline marks slow calls and reports peak in-flight concurrency',()=>{
|
|
210
|
+
let now=1000;
|
|
211
|
+
const tracker=createLiveTimelineTracker({clock:()=>now,slowCallMs:1000});
|
|
212
|
+
tracker.observe({event_type:'tool.request',tool:'coding_command_run',request_id:'slow-a-12345678',args:{root_id:'core',command_id:'node_script',script_path:'a.mjs'}});
|
|
213
|
+
tracker.observe({event_type:'tool.request',tool:'coding_file_read',request_id:'slow-b-12345678',args:{root_id:'core',path:'b.txt'}});
|
|
214
|
+
now=2500;
|
|
215
|
+
const line=tracker.observe({event_type:'tool.result',tool:'coding_command_run',request_id:'slow-a-12345678',outcome:{is_error:false,duration_ms:1500}});
|
|
216
|
+
assert.match(line,/1500ms|1\.50s/);
|
|
217
|
+
assert.match(line,/\[SLOW\]/);
|
|
218
|
+
tracker.observe({event_type:'tool.result',tool:'coding_file_read',request_id:'slow-b-12345678',outcome:{is_error:false,duration_ms:5}});
|
|
219
|
+
assert.match(tracker.summary(),/peak in-flight 2/);
|
|
220
|
+
});
|
|
@@ -23,7 +23,7 @@ async function fixture({failCommand='',statusOnline=false}={}){
|
|
|
23
23
|
const spawnedPid=path.join(dir,'spawned.pid');
|
|
24
24
|
await mkdir(bin,{recursive:true});
|
|
25
25
|
const launcher=path.join(bin,'DEADBYTE-MCP.ps1');
|
|
26
|
-
const ps1=`param([string]$Command)\n$ErrorActionPreference='Stop'\nfor($i=0;$i -lt 20;$i++){ try { [IO.File]::AppendAllText($env:DEADBYTE_REMOTE_TEST_TRACE,(\"$Command\" + [Environment]::NewLine),[Text.Encoding]::UTF8); break } catch { if($i -eq 19){ throw }; Start-Sleep -Milliseconds 10 } }\nif ($env:DEADBYTE_REMOTE_TEST_FAIL -and $Command -eq $env:DEADBYTE_REMOTE_TEST_FAIL) { $runtime=Join-Path $env:DEADBYTE_STATE_ROOT 'runtime'; New-Item -ItemType Directory -Path $runtime -Force | Out-Null; $failure=@{ schema='deadbyte.controller-start-failure.v1'; command=$Command; failed_at_utc='2026-09-26T00:00:00.000Z'; message='fixture controller evidence detail' } | ConvertTo-Json -Compress; [IO.File]::WriteAllText((Join-Path $runtime 'controller-start-failure.json'),$failure,[Text.Encoding]::UTF8); Write-Error \"forced $Command failure\"; exit 9 }\nswitch ($Command) {\n 'start' { if ($env:DEADBYTE_REMOTE_TEST_SPAWN_CHILD -eq '1') { $node=(Get-Command node.exe).Source; $spawned=Start-Process -FilePath $node -ArgumentList @('-e','setTimeout(()=>process.exit(0),10000)') -PassThru -WindowStyle Hidden -RedirectStandardOutput $env:DEADBYTE_REMOTE_TEST_SPAWN_STDOUT -RedirectStandardError $env:DEADBYTE_REMOTE_TEST_SPAWN_STDERR; if ($env:DEADBYTE_REMOTE_TEST_SPAWN_PID) { [IO.File]::WriteAllText($env:DEADBYTE_REMOTE_TEST_SPAWN_PID,[string]$spawned.Id,[Text.Encoding]::UTF8) } }; if ($env:DEADBYTE_REMOTE_TEST_STARTED) { [IO.File]::WriteAllText($env:DEADBYTE_REMOTE_TEST_STARTED,'1',[Text.Encoding]::UTF8) }; Write-Host 'DEADBYTE MCP = ONLINE' }\n 'status' { if ($env:DEADBYTE_REMOTE_TEST_STATUS_ONLINE -eq '1' -or ($env:DEADBYTE_REMOTE_TEST_STARTED -and (Test-Path -LiteralPath $env:DEADBYTE_REMOTE_TEST_STARTED))) { Write-Host 'DEADBYTE MCP = ONLINE'; Write-Host 'Bridge PID : 1001 [verified]'; Write-Host 'Cloud PID : 1002 [verified]'; Write-Host 'Local ready : True'; Write-Host 'Loop supervisor: RUNNING [verified]' } else { Write-Host 'DEADBYTE MCP = OFFLINE' } }\n 'processstatus' { Write-Host 'Process plane : ARMED [standing-grant]' }\n 'codingstatus' { Write-Host 'Coding plane : ARMED [standing-grant]' }\n 'autonomousstatus' { Write-Host 'Autonomous : ARMED [standing-grant]' }\n 'hoststatus' { Write-Host 'Host plane : DISARMED [disarmed]' }\n 'park' { if ($env:DEADBYTE_REMOTE_TEST_STARTED) { Remove-Item -LiteralPath $env:DEADBYTE_REMOTE_TEST_STARTED -Force -ErrorAction SilentlyContinue }; Write-Host 'DEADBYTE MCP = PARKED' }\n default { Write-Host \"$Command OK\" }\n}\nexit 0\n`;
|
|
26
|
+
const ps1=`param([string]$Command)\n$ErrorActionPreference='Stop'\nfor($i=0;$i -lt 20;$i++){ try { [IO.File]::AppendAllText($env:DEADBYTE_REMOTE_TEST_TRACE,(\"$Command\" + [Environment]::NewLine),[Text.Encoding]::UTF8); break } catch { if($i -eq 19){ throw }; Start-Sleep -Milliseconds 10 } }\nif ($env:DEADBYTE_REMOTE_TEST_FAIL -and $Command -eq $env:DEADBYTE_REMOTE_TEST_FAIL) { $runtime=Join-Path $env:DEADBYTE_STATE_ROOT 'runtime'; New-Item -ItemType Directory -Path $runtime -Force | Out-Null; $failure=@{ schema='deadbyte.controller-start-failure.v1'; command=$Command; failed_at_utc='2026-09-26T00:00:00.000Z'; message='fixture controller evidence detail' } | ConvertTo-Json -Compress; [IO.File]::WriteAllText((Join-Path $runtime 'controller-start-failure.json'),$failure,[Text.Encoding]::UTF8); Write-Error \"forced $Command failure\"; exit 9 }\nswitch ($Command) {\n 'start' { if ($env:DEADBYTE_REMOTE_TEST_SPAWN_CHILD -eq '1') { $node=(Get-Command node.exe).Source; $spawned=Start-Process -FilePath $node -ArgumentList @('-e','setTimeout(()=>process.exit(0),10000)') -PassThru -WindowStyle Hidden -RedirectStandardOutput $env:DEADBYTE_REMOTE_TEST_SPAWN_STDOUT -RedirectStandardError $env:DEADBYTE_REMOTE_TEST_SPAWN_STDERR; if ($env:DEADBYTE_REMOTE_TEST_SPAWN_PID) { [IO.File]::WriteAllText($env:DEADBYTE_REMOTE_TEST_SPAWN_PID,[string]$spawned.Id,[Text.Encoding]::UTF8) } }; if ($env:DEADBYTE_REMOTE_TEST_STARTED) { [IO.File]::WriteAllText($env:DEADBYTE_REMOTE_TEST_STARTED,'1',[Text.Encoding]::UTF8) }; Write-Host 'DEADBYTE MCP = ONLINE' }\n 'status' { if ($env:DEADBYTE_REMOTE_TEST_STATUS_ONLINE -eq '1' -or ($env:DEADBYTE_REMOTE_TEST_STARTED -and (Test-Path -LiteralPath $env:DEADBYTE_REMOTE_TEST_STARTED))) { Write-Host 'DEADBYTE MCP = ONLINE'; Write-Host 'Bridge PID : 1001 [verified]'; Write-Host 'Cloud PID : 1002 [verified]'; Write-Host 'Local ready : True'; Write-Host 'Loop supervisor: RUNNING [verified]' } else { Write-Host 'DEADBYTE MCP = OFFLINE' } }\n 'processstatus' { Write-Host 'Process plane : ARMED [standing-grant]' }\n 'codingstatus' { Write-Host 'Coding plane : ARMED [standing-grant]' }\n 'autonomousstatus' { Write-Host 'Autonomous : ARMED [standing-grant]' }\n 'desktopstatus' { Write-Host 'Desktop plane : DISARMED [disarmed]' }\n 'hoststatus' { Write-Host 'Host plane : DISARMED [disarmed]' }\n 'park' { if ($env:DEADBYTE_REMOTE_TEST_STARTED) { Remove-Item -LiteralPath $env:DEADBYTE_REMOTE_TEST_STARTED -Force -ErrorAction SilentlyContinue }; Write-Host 'DEADBYTE MCP = PARKED' }\n default { Write-Host \"$Command OK\" }\n}\nexit 0\n`;
|
|
27
27
|
await writeFile(launcher,ps1,'utf8');
|
|
28
28
|
return {dir,state,trace,env:{...process.env,DEADBYTE_STATE_ROOT:state,DEADBYTE_REMOTE_TEST_TRACE:trace,DEADBYTE_REMOTE_TEST_FAIL:failCommand,DEADBYTE_REMOTE_TEST_STATUS_ONLINE:statusOnline?'1':'0',DEADBYTE_REMOTE_TEST_STARTED:startedMarker,DEADBYTE_REMOTE_TEST_SPAWN_STDOUT:spawnedStdout,DEADBYTE_REMOTE_TEST_SPAWN_STDERR:spawnedStderr,DEADBYTE_REMOTE_TEST_SPAWN_PID:spawnedPid},spawnedPid};
|
|
29
29
|
}
|
|
@@ -77,17 +77,17 @@ winTest('bare remote reuses a fully verified ONLINE runtime without invoking blo
|
|
|
77
77
|
child=spawn(process.execPath,[cliPath,'remote'],{env:fx.env,stdio:['pipe','pipe','pipe'],windowsHide:true});
|
|
78
78
|
const output=await collect(child);
|
|
79
79
|
const ready=await waitFor(fx.trace,commands=>commands.includes('hoststatus'),{timeoutMs:8000});
|
|
80
|
-
assert.deepEqual(ready.slice(0,
|
|
80
|
+
assert.deepEqual(ready.slice(0,9),[
|
|
81
81
|
'status','armprocess','armcoding','armautonomous',
|
|
82
|
-
'processstatus','codingstatus','autonomousstatus','hoststatus'
|
|
82
|
+
'processstatus','codingstatus','autonomousstatus','desktopstatus','hoststatus'
|
|
83
83
|
]);
|
|
84
84
|
assert.doesNotMatch(ready.join(','),/(^|,)start(,|$)/);
|
|
85
85
|
assert.equal(child.exitCode,null,'remote session must remain foreground after reusing ONLINE runtime');
|
|
86
86
|
assert.match(output.stdout,/Runtime already ONLINE; reusing verified bridge\/cloud\/supervisor/);
|
|
87
87
|
const authDeadline=Date.now()+3000;
|
|
88
|
-
while(Date.now()<authDeadline&&!/\[AUTH\] ARMED: process, coding, autonomous \| host DISARMED/.test(output.stdout)) await sleep(50);
|
|
88
|
+
while(Date.now()<authDeadline&&!/\[AUTH\] ARMED: process, coding, autonomous \| desktop, host DISARMED/.test(output.stdout)) await sleep(50);
|
|
89
89
|
assert.match(output.stdout,/\[DISPLAY\] ASCII fallback: stdout_not_tty/);
|
|
90
|
-
assert.match(output.stdout,/\[AUTH\] ARMED: process, coding, autonomous \| host DISARMED/);
|
|
90
|
+
assert.match(output.stdout,/\[AUTH\] ARMED: process, coding, autonomous \| desktop, host DISARMED/);
|
|
91
91
|
child.kill('SIGINT');
|
|
92
92
|
await output.closed;
|
|
93
93
|
}finally{
|
|
@@ -106,9 +106,9 @@ winTest('bare remote PARKED startup does not wait for a long-lived child holding
|
|
|
106
106
|
const output=await collect(child);
|
|
107
107
|
const ready=await waitFor(fx.trace,commands=>commands.includes('hoststatus'),{timeoutMs:8000});
|
|
108
108
|
const elapsed=Date.now()-startedAt;
|
|
109
|
-
assert.deepEqual(ready.slice(0,
|
|
109
|
+
assert.deepEqual(ready.slice(0,11),[
|
|
110
110
|
'status','start','status','armprocess','armcoding','armautonomous',
|
|
111
|
-
'processstatus','codingstatus','autonomousstatus','hoststatus'
|
|
111
|
+
'processstatus','codingstatus','autonomousstatus','desktopstatus','hoststatus'
|
|
112
112
|
]);
|
|
113
113
|
assert.ok(elapsed<7000,`PARKED startup waited on inherited child pipe handles: ${elapsed}ms`);
|
|
114
114
|
assert.equal(child.exitCode,null,'remote session must remain foreground after PARKED startup');
|
|
@@ -133,13 +133,13 @@ winTest('bare remote uses one unified live timeline, keeps authority semantics,
|
|
|
133
133
|
child=spawn(process.execPath,[cliPath,'remote'],{env:fx.env,stdio:['pipe','pipe','pipe'],windowsHide:true});
|
|
134
134
|
const output=await collect(child);
|
|
135
135
|
const ready=await waitFor(fx.trace,commands=>commands.slice(0,10).join(',')==='status,start,status,armprocess,armcoding,armautonomous,processstatus,codingstatus,autonomousstatus,hoststatus');
|
|
136
|
-
assert.deepEqual(ready.slice(0,
|
|
136
|
+
assert.deepEqual(ready.slice(0,11),[
|
|
137
137
|
'status','start','status','armprocess','armcoding','armautonomous',
|
|
138
|
-
'processstatus','codingstatus','autonomousstatus','hoststatus'
|
|
138
|
+
'processstatus','codingstatus','autonomousstatus','desktopstatus','hoststatus'
|
|
139
139
|
]);
|
|
140
140
|
assert.equal(child.exitCode,null,'remote session must remain foreground after becoming ready');
|
|
141
141
|
const tailDeadline=Date.now()+3000;
|
|
142
|
-
while(Date.now()<tailDeadline&&!/\[AUTH\] ARMED: process, coding, autonomous \| host DISARMED/.test(output.stdout)) await sleep(50);
|
|
142
|
+
while(Date.now()<tailDeadline&&!/\[AUTH\] ARMED: process, coding, autonomous \| desktop, host DISARMED/.test(output.stdout)) await sleep(50);
|
|
143
143
|
assert.match(output.stdout,/\[DISPLAY\] ASCII fallback: stdout_not_tty/);
|
|
144
144
|
assert.match(output.stdout,/\[DEVICE\] Starting DEADBYTE MCP/);
|
|
145
145
|
assert.match(output.stdout,/\[BRIDGE\] Local bridge connected/);
|
|
@@ -148,7 +148,7 @@ winTest('bare remote uses one unified live timeline, keeps authority semantics,
|
|
|
148
148
|
assert.match(output.stdout,/\[BOOT\] Starting local \+ remote runtime/);
|
|
149
149
|
assert.match(output.stdout,/\[OK\] ONLINE/);
|
|
150
150
|
assert.match(output.stdout,/\[READY\] DEADBYTE MCP online/);
|
|
151
|
-
assert.match(output.stdout,/\[AUTH\] ARMED: process, coding, autonomous \| host DISARMED/);
|
|
151
|
+
assert.match(output.stdout,/\[AUTH\] ARMED: process, coding, autonomous \| desktop, host DISARMED/);
|
|
152
152
|
assert.match(output.stdout,/\[LOG\] Human live timeline\. Transport IN\/OUT hidden; full replay\/audit detail remains on disk\./);
|
|
153
153
|
assert.doesNotMatch(output.stdout,/Process plane\s*:/);
|
|
154
154
|
assert.doesNotMatch(output.stdout,/Coding plane\s*:/);
|
|
@@ -172,10 +172,10 @@ winTest('bare remote uses one unified live timeline, keeps authority semantics,
|
|
|
172
172
|
'[TOOL OUT] 2026-09-16T00:00:00.002Z #000002 coding_file_read {"is_error":true}'
|
|
173
173
|
].join('\n')+'\n','utf8');
|
|
174
174
|
const logDeadline=Date.now()+3000;
|
|
175
|
-
while(Date.now()<logDeadline&&!/^R .*#4 \[fedcba98\] coding_file_read projects:src\/deadbyte-cli\.mjs 1\.73s ERR$/m.test(output.stdout)) await sleep(50);
|
|
175
|
+
while(Date.now()<logDeadline&&!/^R .*#4 \[fedcba98\] coding_file_read projects:src\/deadbyte-cli\.mjs 1\.73s \[SLOW\] ERR$/m.test(output.stdout)) await sleep(50);
|
|
176
176
|
assert.doesNotMatch(output.stdout,/\| Arguments:/,'default human view must not print request JSON');
|
|
177
177
|
assert.match(output.stdout,/^S .*#2 \[abcdef12\] coding_tree projects:src 42ms OK$/m);
|
|
178
|
-
assert.match(output.stdout,/^R .*#4 \[fedcba98\] coding_file_read projects:src\/deadbyte-cli\.mjs 1\.73s ERR$/m);
|
|
178
|
+
assert.match(output.stdout,/^R .*#4 \[fedcba98\] coding_file_read projects:src\/deadbyte-cli\.mjs 1\.73s \[SLOW\] ERR$/m);
|
|
179
179
|
assert.equal((output.stdout.match(/\[abcdef12\].*coding_tree.*42ms.*OK/g)||[]).length,1,'duplicate terminal result must print once');
|
|
180
180
|
assert.doesNotMatch(output.stdout,/\[cursor r1:/,'full replay cursor belongs in the journal, not every human line');
|
|
181
181
|
const resultJournal=JSON.parse(await readFile(path.join(fx.state,'runtime','remote-results',`${marker.result_session_id}.json`),'utf8'));
|
|
@@ -194,11 +194,11 @@ winTest('bare remote uses one unified live timeline, keeps authority semantics,
|
|
|
194
194
|
const ended=await Promise.race([output.closed,sleep(6000).then(()=>({timeout:true}))]);
|
|
195
195
|
assert.equal(ended.timeout,undefined,'remote session did not exit after foreground input closed');
|
|
196
196
|
const commands=await traceCommands(fx.trace);
|
|
197
|
-
assert.deepEqual(commands.slice(-
|
|
197
|
+
assert.deepEqual(commands.slice(-6),['disarmautonomous','disarmcoding','disarmprocess','disarmdesktop','disarmhost','park']);
|
|
198
198
|
assert.match(output.stdout,/^DEADBYTE MCP$/m);
|
|
199
199
|
assert.match(output.stdout,/Human live timeline/i);
|
|
200
200
|
assert.match(output.stdout,/\[AUTH\] Revoking session authority/);
|
|
201
|
-
assert.doesNotMatch(output.stdout,/disarmautonomous OK|disarmcoding OK|disarmprocess OK|disarmhost OK|DEADBYTE MCP = PARKED/);
|
|
201
|
+
assert.doesNotMatch(output.stdout,/disarmautonomous OK|disarmcoding OK|disarmprocess OK|disarmdesktop OK|disarmhost OK|DEADBYTE MCP = PARKED/);
|
|
202
202
|
assert.doesNotMatch(output.stdout,/\p{Extended_Pictographic}/u,'piped Windows fallback must remain ASCII-safe');
|
|
203
203
|
}finally{
|
|
204
204
|
if(child?.exitCode===null) child.kill();
|
|
@@ -293,7 +293,7 @@ winTest('remote --verbose enables sanitized request detail while preserving fore
|
|
|
293
293
|
assert.equal(ended.timeout,undefined);
|
|
294
294
|
assert.equal(ended.code,0,output.stderr);
|
|
295
295
|
const commands=await traceCommands(fx.trace);
|
|
296
|
-
assert.deepEqual(commands.slice(-
|
|
296
|
+
assert.deepEqual(commands.slice(-6),['disarmautonomous','disarmcoding','disarmprocess','disarmdesktop','disarmhost','park']);
|
|
297
297
|
}finally{
|
|
298
298
|
if(child?.exitCode===null) child.kill('SIGKILL');
|
|
299
299
|
await rm(fx.dir,{recursive:true,force:true});
|
|
@@ -307,9 +307,9 @@ winTest('detached guardian revokes authority, parks, and removes the session mar
|
|
|
307
307
|
child=spawn(process.execPath,[cliPath,'remote'],{env:fx.env,stdio:['pipe','pipe','pipe'],windowsHide:true});
|
|
308
308
|
const output=await collect(child);
|
|
309
309
|
const ready=await waitFor(fx.trace,commands=>commands.slice(0,10).join(',')==='status,start,status,armprocess,armcoding,armautonomous,processstatus,codingstatus,autonomousstatus,hoststatus');
|
|
310
|
-
assert.deepEqual(ready.slice(0,
|
|
310
|
+
assert.deepEqual(ready.slice(0,11),[
|
|
311
311
|
'status','start','status','armprocess','armcoding','armautonomous',
|
|
312
|
-
'processstatus','codingstatus','autonomousstatus','hoststatus'
|
|
312
|
+
'processstatus','codingstatus','autonomousstatus','desktopstatus','hoststatus'
|
|
313
313
|
]);
|
|
314
314
|
const markerPath=path.join(fx.state,'runtime','remote-session.json');
|
|
315
315
|
const markerDeadline=Date.now()+3000;
|
|
@@ -327,8 +327,8 @@ winTest('detached guardian revokes authority, parks, and removes the session mar
|
|
|
327
327
|
assert.equal(killed,true,'failed to hard-kill remote owner');
|
|
328
328
|
await Promise.race([output.closed,sleep(3000)]);
|
|
329
329
|
|
|
330
|
-
const cleaned=await waitFor(fx.trace,commands=>commands.slice(-
|
|
331
|
-
assert.deepEqual(cleaned.slice(-
|
|
330
|
+
const cleaned=await waitFor(fx.trace,commands=>commands.slice(-6).join(',')==='disarmautonomous,disarmcoding,disarmprocess,disarmhost,park',{timeoutMs:10000});
|
|
331
|
+
assert.deepEqual(cleaned.slice(-6),['disarmautonomous','disarmcoding','disarmprocess','disarmdesktop','disarmhost','park']);
|
|
332
332
|
const markerGoneDeadline=Date.now()+3000;
|
|
333
333
|
let markerGone=false;
|
|
334
334
|
while(Date.now()<markerGoneDeadline){
|
|
@@ -352,7 +352,7 @@ winTest('bare remote rolls back authority and parks if one plane fails to arm',a
|
|
|
352
352
|
assert.notEqual(ended.code,0);
|
|
353
353
|
const commands=await traceCommands(fx.trace);
|
|
354
354
|
assert.deepEqual(commands.slice(0,5),['status','start','status','armprocess','armcoding']);
|
|
355
|
-
assert.deepEqual(commands.slice(-
|
|
355
|
+
assert.deepEqual(commands.slice(-6),['disarmautonomous','disarmcoding','disarmprocess','disarmdesktop','disarmhost','park']);
|
|
356
356
|
assert.doesNotMatch(output.stdout,/Live logs/i);
|
|
357
357
|
}finally{await rm(fx.dir,{recursive:true,force:true});}
|
|
358
358
|
});
|
|
@@ -367,9 +367,9 @@ winTest('bare remote recovers a stale crashed session before arming a new one',a
|
|
|
367
367
|
child=spawn(process.execPath,[cliPath,'remote'],{env:fx.env,stdio:['pipe','pipe','pipe'],windowsHide:true});
|
|
368
368
|
const output=await collect(child);
|
|
369
369
|
const ready=await waitFor(fx.trace,commands=>commands.includes('hoststatus'));
|
|
370
|
-
assert.deepEqual(ready.slice(0,
|
|
371
|
-
assert.equal(ready[
|
|
372
|
-
assert.equal(ready[
|
|
370
|
+
assert.deepEqual(ready.slice(0,6),['disarmautonomous','disarmcoding','disarmprocess','disarmdesktop','disarmhost','park']);
|
|
371
|
+
assert.equal(ready[6],'status');
|
|
372
|
+
assert.equal(ready[7],'start');
|
|
373
373
|
assert.match(output.stdout,/\[RECOVERY\] Stale remote session pid=2147483000; revoking authority before restart\./);
|
|
374
374
|
child.stdin.end();
|
|
375
375
|
const ended=await Promise.race([output.closed,sleep(6000).then(()=>({timeout:true}))]);
|