deadbyte-mcp 0.15.27 → 0.15.29

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.
Files changed (42) hide show
  1. package/MANIFEST.SHA256 +41 -25
  2. package/PROVENANCE.json +16 -10
  3. package/README.txt +32 -14
  4. package/RELEASE.json +55 -15
  5. package/SBOM.cdx.json +3 -3
  6. package/TEST-MATRIX.json +147 -23
  7. package/bin/deadbyte-process-host.exe +0 -0
  8. package/bin/deadbyte-process-host.obj +0 -0
  9. package/controller/deadbyte-controller.ps1 +4 -1
  10. package/controller/deadbyte-process-policy.json +1 -1
  11. package/docs/R51-PROVENANCE.json +18 -0
  12. package/docs/R51-RELEASE.json +115 -0
  13. package/docs/R51-TEST-MATRIX.json +166 -0
  14. package/docs/R52-ACTIVATION-FORENSICS.md +85 -0
  15. package/native/deadbyte-process-host.cpp +5 -3
  16. package/package.json +95 -93
  17. package/scripts/deploy-r48.mjs +1 -1
  18. package/scripts/gate-windows-r51.ps1 +58 -0
  19. package/scripts/gate-windows-r52.ps1 +58 -0
  20. package/scripts/windows-gate-evidence-r51.mjs +84 -0
  21. package/scripts/windows-gate-evidence-r52.mjs +94 -0
  22. package/src/coding-plane.mjs +190 -28
  23. package/src/coding-task-runtime.mjs +10 -4
  24. package/src/deadbyte-cli.mjs +88 -28
  25. package/src/remote-live-log.mjs +17 -5
  26. package/src/remote-session-guardian.mjs +1 -1
  27. package/src/runtime-coherence.mjs +50 -0
  28. package/src/runtime-update.mjs +8 -2
  29. package/src/version.mjs +1 -1
  30. package/test/coding-runtime.test.mjs +104 -1
  31. package/test/helpers/coding-disconnect-server.mjs +11 -0
  32. package/test/production-docs.test.mjs +5 -3
  33. package/test/r48-release-contract.test.mjs +7 -30
  34. package/test/r49-windows-gate.test.mjs +17 -11
  35. package/test/r51-live-log-polling.test.mjs +28 -0
  36. package/test/r51-release-contract.test.mjs +58 -0
  37. package/test/r51-runtime-coherence.test.mjs +59 -0
  38. package/test/r52-host-revocation.test.mjs +65 -0
  39. package/test/r52-offline-predecessor-recovery.test.mjs +77 -0
  40. package/test/r52-release-contract.test.mjs +58 -0
  41. package/test/remote-live-log-r37.test.mjs +14 -0
  42. 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
+ }
@@ -704,7 +704,7 @@ async function captureRuntime(ctx){
704
704
  const bridge=await readLedger(ctx,'bridge'); const cloud=await readLedger(ctx,'cloud'); const core=coreProcess(ctx.core_root,bridge);
705
705
  return {status,status_text:statusText.trim(),bridge,cloud,core};
706
706
  }
707
- async function capturePredecessor(ctx,recoverOfflinePredecessor){
707
+ export async function capturePredecessor(ctx,recoverOfflinePredecessor){
708
708
  let statusText,status;
709
709
  try{
710
710
  statusText=controllerCaptured(ctx,'status',15000);
@@ -738,7 +738,13 @@ async function capturePredecessor(ctx,recoverOfflinePredecessor){
738
738
  }
739
739
  if(!recoverOfflinePredecessor) throw new Error(`slot update requires ONLINE runtime: ${statusText}`);
740
740
  if(!status.offline||status.bridge_pid||status.cloud_pid||status.local_ready) throw new Error(`offline predecessor recovery requires clean OFFLINE controller state: ${statusText}`);
741
- if(status.host_armed) throw new Error('offline predecessor recovery requires host plane DISARMED');
741
+ if(status.host_armed){
742
+ controllerCaptured(ctx,'disarmhost',15000);
743
+ statusText=controllerCaptured(ctx,'status',15000);
744
+ status=parseControllerStatus(statusText);
745
+ if(!status.offline||status.bridge_pid||status.cloud_pid||status.local_ready) throw new Error(`offline predecessor recovery changed controller state while revoking Host authority: ${statusText}`);
746
+ if(status.host_armed) throw new Error('offline predecessor recovery requires host plane DISARMED');
747
+ }
742
748
  if(status.autonomous_armed&&!status.coding_armed) throw new Error('invalid runtime authority: autonomous armed without coding');
743
749
  return {mode:'offline-recovery',status,status_text:statusText.trim(),runtime:null};
744
750
  }
package/src/version.mjs CHANGED
@@ -1,4 +1,4 @@
1
- export const DEADBYTE_VERSION = '0.15.27';
1
+ export const DEADBYTE_VERSION = '0.15.29';
2
2
  export const MCP_PROTOCOL_VERSION = '2026-07-28';
3
3
  export const RECEIPT_SCHEMA_V1 = 'deadbyte.receipt.v1';
4
4
  export const RECEIPT_SCHEMA_V2 = 'deadbyte.receipt.v2';
@@ -7,6 +7,75 @@ import path from 'node:path';
7
7
  import { generateTrustKeypair } from '../src/trust.mjs';
8
8
  import { assertCodingArmed, createCodingRuntime, loadCodingPolicy, recheckCodingAuthority } from '../src/coding-plane.mjs';
9
9
  import { createCodingTaskRuntime } from '../src/coding-task-runtime.mjs';
10
+ import { fork } from 'node:child_process';
11
+ import { fileURLToPath } from 'node:url';
12
+
13
+ test('R52 caller disconnect retains one durable execution, status, attempts and terminal receipt',async()=>{
14
+ const f=await fixture();
15
+ let server;
16
+ try{
17
+ const script=path.join(f.project,'disconnect.mjs');
18
+ await writeFile(script,"import {appendFileSync} from 'node:fs';appendFileSync('disconnect-runs.txt',process.pid+'\\n');await new Promise(r=>setTimeout(r,750));process.stdout.write('survived-disconnect');");
19
+ const submission={submissionId:'caller-disconnect',steps:[{step_id:'gate',kind:'command',root_id:'project',command_id:'node_script',script_path:'disconnect.mjs',script_sha256:await sha256File(script),timeout_ms:10000}]};
20
+ const task=await f.tasks.submit(submission);
21
+ server=fork(fileURLToPath(new URL('./helpers/coding-disconnect-server.mjs',import.meta.url)),
22
+ [f.policyPath,f.keys.privateKeyPath,f.keys.publicKeyPath],{stdio:['ignore','ignore','ignore','ipc'],windowsHide:true,execArgv:[]});
23
+ const closed=new Promise(resolve=>server.once('exit',resolve));
24
+ server.send({taskId:task.task_id});
25
+ const deadline=Date.now()+10000;
26
+ let started=false;
27
+ while(Date.now()<deadline){
28
+ try{await readFile(path.join(f.project,'disconnect-runs.txt'));started=true;break;}catch{}
29
+ await new Promise(r=>setTimeout(r,20));
30
+ }
31
+ assert.ok(started,'independent runtime did not start');
32
+ server.disconnect(); // Caller transport goes away; execution owner stays live.
33
+ assert.doesNotThrow(()=>process.kill(server.pid,0));
34
+ const duplicate=await f.tasks.submit(submission);
35
+ assert.equal(duplicate.task_id,task.task_id);
36
+ assert.equal(duplicate.status,'existing');
37
+ assert.equal((await f.tasks.status({taskId:task.task_id})).steps[0].attempts,1);
38
+ await closed;
39
+ assert.equal(server.exitCode,0);
40
+ const recovered=createCodingTaskRuntime({codingRuntime:f.runtime,signingKeyPath:f.keys.privateKeyPath,verifyKeyPath:f.keys.publicKeyPath});
41
+ const status=await recovered.status({taskId:task.task_id});
42
+ assert.equal(status.state,'succeeded');
43
+ assert.equal(status.steps[0].attempts,1);
44
+ assert.equal((await recovered.verify({taskId:task.task_id})).ok,true);
45
+ assert.equal((await recovered.run({taskId:task.task_id})).status,'terminal');
46
+ assert.equal((await readFile(path.join(f.project,'disconnect-runs.txt'),'utf8')).trim().split(/\r?\n/).length,1);
47
+ }finally{if(server?.exitCode===null){server.kill();}await rm(f.root,{recursive:true,force:true});}
48
+ });
49
+
50
+ test('R52 cancelling a running durable command stops its owned descendants before terminal state',async()=>{
51
+ const f=await fixture();
52
+ let running;
53
+ try{
54
+ const script=path.join(f.project,'cancel-tree.mjs');
55
+ await writeFile(script,"import {spawn} from 'node:child_process'; import {writeFileSync} from 'node:fs'; const child=spawn(process.execPath,['-e',\"setTimeout(()=>require('node:fs').writeFileSync('escaped.txt','escaped'),4000)\"],{stdio:'inherit'}); writeFileSync('started.txt',String(child.pid)); setTimeout(()=>{},4000);");
56
+ const submission={submissionId:'cancel-running-tree',steps:[{step_id:'gate',kind:'command',root_id:'project',command_id:'node_script',script_path:'cancel-tree.mjs',script_sha256:await sha256File(script),timeout_ms:10000}]};
57
+ const task=await f.tasks.submit(submission);
58
+ running=f.tasks.run({taskId:task.task_id});
59
+ let descendant;
60
+ const deadline=Date.now()+3000;
61
+ while(Date.now()<deadline){
62
+ try{descendant=Number(await readFile(path.join(f.project,'started.txt'),'utf8'));break;}catch{}
63
+ await new Promise(r=>setTimeout(r,20));
64
+ }
65
+ assert.ok(descendant,'fixture did not start descendant');
66
+ const cancelledAt=Date.now();
67
+ await f.tasks.cancel({taskId:task.task_id,reason:'owned-tree-test'});
68
+ const result=await running;
69
+ assert.equal(result.status,'cancelled');
70
+ assert.ok(Date.now()-cancelledAt<2000,'cancellation waited for command to finish');
71
+ assert.throws(()=>process.kill(descendant,0),'owned descendant is still alive after terminal cancellation');
72
+ await assert.rejects(readFile(path.join(f.project,'escaped.txt')),e=>e.code==='ENOENT');
73
+ const status=await f.tasks.status({taskId:task.task_id});
74
+ assert.equal(status.steps[0].attempts,1);
75
+ const executionReceipt=await f.runtime.receiptVerify({receiptId:status.steps[0].receipt_id});
76
+ assert.equal(executionReceipt.ok,true,'cancelled execution must retain its signed terminal observation');
77
+ }finally{await running?.catch(()=>{});await rm(f.root,{recursive:true,force:true});}
78
+ });
10
79
 
11
80
  function sha256Bytes(bytes) { return createHash('sha256').update(bytes).digest('hex'); }
12
81
  async function sha256File(file) { return sha256Bytes(await readFile(file)); }
@@ -39,7 +108,7 @@ async function fixture({ armed = true } = {}) {
39
108
  }
40
109
  const runtime = createCodingRuntime({ policy, signingKeyPath: keys.privateKeyPath, verifyKeyPath: keys.publicKeyPath });
41
110
  const tasks = createCodingTaskRuntime({ codingRuntime: runtime, signingKeyPath: keys.privateKeyPath, verifyKeyPath: keys.publicKeyPath });
42
- return { root, project, policy, runtime, tasks };
111
+ return { root, project, policyPath, policy, runtime, tasks, keys };
43
112
  }
44
113
  test('coding plane edits files under lease and verifies signed action receipts', async () => {
45
114
  const f = await fixture();
@@ -233,3 +302,37 @@ test('coding task rejects recomputed journal content without the private signing
233
302
  await assert.rejects(() => f.tasks.status({ taskId: submitted.task_id }), /signature verification failed|hash-chain mismatch/);
234
303
  } finally { await rm(f.root, { recursive: true, force: true }); }
235
304
  });
305
+
306
+
307
+ test('R51 coding command retries reuse one in-flight execution and completed receipt', async () => {
308
+ const f = await fixture();
309
+ try {
310
+ const script = path.join(f.project, 'dedup.mjs');
311
+ 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');");
312
+ const args={rootId:'project',commandId:'node_script',scriptPath:'dedup.mjs',scriptSha256:await sha256File(script),timeoutMs:5000};
313
+ const [a,b]=await Promise.all([f.runtime.commandRun(args),f.runtime.commandRun(args)]);
314
+ assert.equal(a.status,'passed',a.stderr);assert.equal(b.status,'passed',b.stderr);
315
+ assert.equal(a.request_sha256,b.request_sha256);
316
+ assert.equal(a.receipt_id,b.receipt_id,'same logical request must reuse the same signed execution receipt');
317
+ const c=await f.runtime.commandRun(args);
318
+ assert.equal(c.receipt_id,a.receipt_id,'completed retry must reuse bounded cached execution result');
319
+ const runs=(await readFile(path.join(f.project,'runs.txt'),'utf8')).trim().split(/\\r?\\n/).filter(Boolean);
320
+ assert.equal(runs.length,1,'retry spawned duplicate child process');
321
+ } finally { await rm(f.root,{recursive:true,force:true}); }
322
+ });
323
+
324
+ test('R51 coding command single-flight spans independent runtime instances sharing evidence state', async () => {
325
+ const f = await fixture();
326
+ try {
327
+ const script = path.join(f.project, 'cross-runtime-dedup.mjs');
328
+ 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');");
329
+ const second = createCodingRuntime({ policy:f.policy, signingKeyPath:f.keys.privateKeyPath, verifyKeyPath:f.keys.publicKeyPath });
330
+ const args={rootId:'project',commandId:'node_script',scriptPath:'cross-runtime-dedup.mjs',scriptSha256:await sha256File(script),timeoutMs:5000};
331
+ const [a,b]=await Promise.all([f.runtime.commandRun(args),second.commandRun(args)]);
332
+ assert.equal(a.status,'passed',a.stderr);assert.equal(b.status,'passed',b.stderr);
333
+ assert.equal(a.request_sha256,b.request_sha256);
334
+ assert.equal(a.receipt_id,b.receipt_id,'shared evidence state must converge on one signed execution receipt');
335
+ const runs=(await readFile(path.join(f.project,'cross-runs.txt'),'utf8')).trim().split(/\\r?\\n/).filter(Boolean);
336
+ assert.equal(runs.length,1,'independent runtime instances spawned duplicate child processes');
337
+ } finally { await rm(f.root,{recursive:true,force:true}); }
338
+ });
@@ -0,0 +1,11 @@
1
+ import { loadCodingPolicy,createCodingRuntime } from '../../src/coding-plane.mjs';
2
+ import { createCodingTaskRuntime } from '../../src/coding-task-runtime.mjs';
3
+ const [policyPath,signingKeyPath,verifyKeyPath]=process.argv.slice(2);
4
+ const policy=await loadCodingPolicy(policyPath);
5
+ const codingRuntime=createCodingRuntime({policy,signingKeyPath,verifyKeyPath});
6
+ const tasks=createCodingTaskRuntime({codingRuntime,signingKeyPath,verifyKeyPath});
7
+ process.once('message',async({taskId})=>{
8
+ try{await tasks.run({taskId});process.exitCode=0;}
9
+ catch(error){console.error(error);process.exitCode=1;}
10
+ finally{if(process.connected)process.disconnect();}
11
+ });
@@ -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,/npx -y deadbyte-mcp@0\.15\.5 mcp/);
14
+ assert.match(text,new RegExp(`npx -y deadbyte-mcp@${versionPattern} mcp`));
13
15
  assert.match(text,/CLAUDE DESKTOP/);
14
- assert.match(text,/"mcpServers"[\s\S]*?"command"\s*:\s*"npx"[\s\S]*?"-y"[\s\S]*?"deadbyte-mcp@0\.15\.5"[\s\S]*?"mcp"/);
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,/args\s*=\s*\["-y",\s*"deadbyte-mcp@0\.15\.5",\s*"mcp"\]/);
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('R50 0.15.27 successor binds exact published immutable 0.15.26 parent', async () => {
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
- read('package.json').then(JSON.parse),
12
- read('package-lock.json').then(JSON.parse),
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
- lock.version,
21
- lock.packages[''].version,
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,33 @@ import test from 'node:test';
4
4
 
5
5
  const read = path => readFile(new URL(`../${path}`, import.meta.url), 'utf8');
6
6
 
7
- test('R50 canonical Windows gate is successor-bound and preserves R49/R48 historical gates', async () => {
8
- const [pkgText, gate, evidence, historicalR49, historicalR48, deferred] = await Promise.all([
7
+ test('R52 canonical Windows gate is successor-bound and preserves R51/R50/R49 historical gates', async () => {
8
+ const [pkgText, gate, evidence, historicalR51, historicalEvidenceR51, historicalR50, historicalR49, deferred] = await Promise.all([
9
9
  read('package.json'),
10
+ read('scripts/gate-windows-r52.ps1'),
11
+ read('scripts/windows-gate-evidence-r52.mjs'),
12
+ read('scripts/gate-windows-r51.ps1'),
13
+ read('scripts/windows-gate-evidence-r51.mjs'),
10
14
  read('scripts/gate-windows-r50.ps1'),
11
- read('scripts/windows-gate-evidence-r50.mjs'),
12
15
  read('scripts/gate-windows-r49.ps1'),
13
- read('scripts/gate-windows-r48.ps1'),
14
16
  read('scripts/deferred-slot-operation-r46.mjs')
15
17
  ]);
16
18
  const pkg = JSON.parse(pkgText);
17
- assert.match(pkg.scripts['gate:windows'], /gate-windows-r50\.ps1$/);
18
- assert.match(gate, /DEADBYTE 0\.15\.27 \/ R50 WINDOWS GATE: PASS/);
19
- assert.match(gate, /r50-package-boundary/);
19
+ assert.match(pkg.scripts['gate:windows'], /gate-windows-r52\.ps1$/);
20
+ assert.match(gate, /DEADBYTE 0\.15\.29 \/ R52 WINDOWS GATE: PASS/);
21
+ assert.match(gate, /r52-package-boundary/);
22
+ assert.match(gate, /r52-focused-contracts/);
20
23
  assert.match(gate, /r49-remote-control-donors\.test\.mjs/);
21
24
  assert.match(gate, /desktop-runtime-r40\.test\.mjs/);
22
25
  assert.match(gate, /manifest-final-verify/);
23
- assert.match(evidence, /deadbyte\.r50-windows-gate\.v1/);
24
- assert.match(evidence, /pkg\.version === '0\.15\.27'/);
25
- assert.match(evidence, /release\.train === 'R50'/);
26
+ assert.match(evidence, /deadbyte\.r52-windows-gate\.v1/);
27
+ assert.match(evidence, /pkg\.version === '0\.15\.29'/);
28
+ assert.match(evidence, /release\.train === 'R52'/);
29
+ assert.match(evidence, /source_predecessor\.version === '0\.15\.28'/);
30
+ assert.match(historicalR51, /DEADBYTE 0\.15\.28 \/ R51 WINDOWS GATE: PASS/);
31
+ assert.match(historicalEvidenceR51, /deadbyte\.r51-windows-gate\.v1/);
32
+ assert.match(historicalR50, /DEADBYTE 0\.15\.27 \/ R50 WINDOWS GATE: PASS/);
26
33
  assert.match(historicalR49, /DEADBYTE 0\.15\.26 \/ R49 WINDOWS GATE: PASS/);
27
- assert.match(historicalR48, /DEADBYTE 0\.15\.25 \/ R48 WINDOWS GATE: PASS/);
28
34
  assert.match(deferred, /const expectedVersion=packageIdentity\.version/);
29
35
  assert.match(deferred, /'--expected-version',expectedVersion/);
30
36
  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,58 @@
1
+ import test from 'node:test';
2
+ import assert from 'node:assert/strict';
3
+ import { createHash } from 'node:crypto';
4
+ import { readFile } from 'node:fs/promises';
5
+
6
+ const root=new URL('../',import.meta.url);
7
+ const read=rel=>readFile(new URL(rel,root));
8
+ const json=rel=>read(rel).then(bytes=>JSON.parse(bytes.toString('utf8')));
9
+ const sha256=bytes=>createHash('sha256').update(bytes).digest('hex');
10
+
11
+ test('R51 historical release metadata remains byte-frozen at published 0.15.28',async()=>{
12
+ const [releaseBytes,provenanceBytes,matrixBytes]=await Promise.all([
13
+ read('docs/R51-RELEASE.json'),
14
+ read('docs/R51-PROVENANCE.json'),
15
+ read('docs/R51-TEST-MATRIX.json')
16
+ ]);
17
+ assert.equal(sha256(releaseBytes),'b89d0eaf4f07d704d2170782f10e4647daec55b5c5bdc70473865c0dae88650b');
18
+ assert.equal(sha256(provenanceBytes),'78e6798e92c2d9134b2e66557b2de7863367124efbcecdd5c91012bfed19a48f');
19
+ assert.equal(sha256(matrixBytes),'eb4d8047fe4ead69a73b3d89f2d443b6fdd88bd39bd8455b80ecaf512cb49abf');
20
+ const release=JSON.parse(releaseBytes.toString('utf8'));
21
+ const provenance=JSON.parse(provenanceBytes.toString('utf8'));
22
+ const matrix=JSON.parse(matrixBytes.toString('utf8'));
23
+ assert.equal(release.version,'0.15.28');
24
+ assert.equal(release.train,'R51');
25
+ assert.equal(provenance.version,'0.15.28');
26
+ assert.equal(matrix.version,'0.15.28');
27
+ assert.equal(matrix.train,'R51');
28
+ });
29
+
30
+ test('R51 historical lineage binds exact 0.15.27 parent and 0.15.27 source predecessor',async()=>{
31
+ const [release,provenance]=await Promise.all([json('docs/R51-RELEASE.json'),json('docs/R51-PROVENANCE.json')]);
32
+ const expected={
33
+ release_id:'v0.15.27-1b1dbe24b45ad06a',
34
+ manifest_sha256:'1b1dbe24b45ad06a62771d15f510662952f3d7c3e5aa08053c6c61511c523e47',
35
+ generation_manifest_sha256:'f87a1637b3c55ba050d079d8a5e0862a2244b306be7e985880708e0d8ded3db0'
36
+ };
37
+ assert.deepEqual(release.parent,expected);
38
+ assert.equal(release.source_predecessor.version,'0.15.27');
39
+ assert.equal(release.source_predecessor.tarball_sha256,'e728031d097d8678dfef80f8648cce65e72a96611052c31969c80f884506d1d7');
40
+ assert.equal(release.source_predecessor.manifest_file_sha256,expected.manifest_sha256);
41
+ assert.equal(provenance.parent_release_id,expected.release_id);
42
+ assert.equal(provenance.parent_manifest_sha256,expected.manifest_sha256);
43
+ assert.equal(provenance.parent_generation_manifest_sha256,expected.generation_manifest_sha256);
44
+ });
45
+
46
+ test('R51 historical capability claims remain frozen',async()=>{
47
+ const release=await json('docs/R51-RELEASE.json');
48
+ for(const key of [
49
+ 'runtime_package_auto_coherence',
50
+ 'coding_command_singleflight',
51
+ 'coding_command_result_dedup_cache',
52
+ 'foreground_desktop_host_fail_closed',
53
+ 'human_livelog_slow_call_marker',
54
+ 'human_livelog_peak_inflight',
55
+ 'human_livelog_bounded_discovery',
56
+ 'human_livelog_single_parse'
57
+ ]) assert.equal(release.capabilities[key],true,key);
58
+ });
@@ -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
+ });
@@ -0,0 +1,65 @@
1
+ import test from 'node:test';
2
+ import assert from 'node:assert/strict';
3
+ import { readFile } from 'node:fs/promises';
4
+ import { spawnSync } from 'node:child_process';
5
+
6
+ async function runRevoke(t,prelude){
7
+ if(process.platform!=='win32'){t.skip('Windows controller behavior');return;}
8
+ const source=await readFile(new URL('../controller/deadbyte-controller.ps1',import.meta.url),'utf8');
9
+ const begin=source.indexOf('function Disarm-Host(');
10
+ const end=source.indexOf('function Show-HostStatus(',begin);
11
+ assert.ok(begin>=0&&end>begin);
12
+ // Execute the maintained function, not a copied implementation. Simulate a
13
+ // filesystem that refuses deletion while Test-Path still sees the grant.
14
+ const script=`$ErrorActionPreference='Stop'
15
+ function Get-HostPolicyMetadata($Cfg) { return @{ArmFile='fixture-host-grant'} }
16
+ ${prelude}
17
+ ${source.slice(begin,end)}
18
+ try { Disarm-Host @{}; exit 0 } catch { Write-Output $_.Exception.Message; exit 7 }
19
+ `;
20
+ return spawnSync('C:\\Windows\\System32\\WindowsPowerShell\\v1.0\\powershell.exe',
21
+ ['-NoLogo','-NoProfile','-NonInteractive','-EncodedCommand',Buffer.from(script,'utf16le').toString('base64')],
22
+ {encoding:'utf8',windowsHide:true,timeout:15000});
23
+ }
24
+
25
+ test('R52 controller never reports successful Host revoke while grant removal failed',async t=>{
26
+ const result=await runRevoke(t,`
27
+ function Remove-Item { param($LiteralPath,[switch]$Force,$ErrorAction) if($ErrorAction -eq 'Stop'){throw 'fixture access denied'} }
28
+ function Test-Path { param($LiteralPath) return $true }
29
+ `);
30
+ if(!result)return;
31
+ assert.equal(result.status,7,`Host revoke falsely succeeded: ${result.stdout} ${result.stderr}`);
32
+ assert.doesNotMatch(result.stdout,/PRIVILEGED HOST PLANE DISARMED/);
33
+ });
34
+
35
+ test('R52 controller rejects a grant that remains after nominally successful removal',async t=>{
36
+ const result=await runRevoke(t,`
37
+ function Remove-Item { param($LiteralPath,[switch]$Force,$ErrorAction) }
38
+ function Test-Path { param($LiteralPath) return $true }
39
+ `);
40
+ if(!result)return;
41
+ assert.equal(result.status,7);
42
+ assert.match(result.stdout,/grant remains present/);
43
+ assert.doesNotMatch(result.stdout,/PRIVILEGED HOST PLANE DISARMED/);
44
+ });
45
+
46
+ test('R52 controller revocation is idempotent when no grant exists',async t=>{
47
+ const result=await runRevoke(t,`
48
+ function Remove-Item { throw 'must not remove absent grant' }
49
+ function Test-Path { param($LiteralPath) return $false }
50
+ `);
51
+ if(!result)return;
52
+ assert.equal(result.status,0);
53
+ assert.match(result.stdout,/PRIVILEGED HOST PLANE DISARMED/);
54
+ });
55
+
56
+ test('R52 controller reports disarmed only after observing successful removal',async t=>{
57
+ const result=await runRevoke(t,`
58
+ $script:present=$true
59
+ function Remove-Item { param($LiteralPath,[switch]$Force,$ErrorAction) $script:present=$false }
60
+ function Test-Path { param($LiteralPath) return $script:present }
61
+ `);
62
+ if(!result)return;
63
+ assert.equal(result.status,0);
64
+ assert.match(result.stdout,/PRIVILEGED HOST PLANE DISARMED/);
65
+ });