deadbyte-mcp 0.15.0 → 0.15.2

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 (110) hide show
  1. package/MANIFEST.SHA256 +109 -64
  2. package/PROVENANCE.json +11 -0
  3. package/R45-PARENT.json +18 -0
  4. package/README.txt +36 -20
  5. package/RELEASE.json +53 -0
  6. package/SBOM.cdx.json +46 -0
  7. package/TEST-MATRIX.json +31 -0
  8. package/bin/deadbyte-hardware-observer-r45.exe +0 -0
  9. package/bin/deadbyte-hardware-observer-r45.obj +0 -0
  10. package/controller/README.TXT +2 -2
  11. package/controller/deadbyte-controller.ps1 +51 -3
  12. package/controller/deadbyte-process-policy.json +63 -1
  13. package/docs/ARCHITECTURE.md +3 -4
  14. package/native/deadbyte-hardware-observer.cpp +308 -0
  15. package/package.json +14 -7
  16. package/scripts/autonomous-tests.ps1 +2 -2
  17. package/scripts/build-hardware-r45.ps1 +71 -0
  18. package/scripts/deferred-slot-operation-r45.mjs +126 -0
  19. package/scripts/deferred-slot-operation-r46.mjs +126 -0
  20. package/scripts/derive-r45-parent.mjs +24 -0
  21. package/scripts/derive-r45-release.mjs +72 -0
  22. package/scripts/derive-r45-tests.mjs +20 -0
  23. package/scripts/final-closure-r45.mjs +121 -0
  24. package/scripts/final-closure-r46.mjs +98 -0
  25. package/scripts/final-closure-verify-r45.mjs +220 -0
  26. package/scripts/final-closure-verify-r46.mjs +119 -0
  27. package/scripts/finalize-r45.mjs +219 -0
  28. package/scripts/full-regression-r45.mjs +20 -0
  29. package/scripts/full-regression-r46.mjs +20 -0
  30. package/scripts/full-regression-summary-r45.mjs +14 -0
  31. package/scripts/gate-windows-r45.ps1 +44 -0
  32. package/scripts/gate-windows-r46.ps1 +56 -0
  33. package/scripts/init-host-policy.ps1 +39 -1
  34. package/scripts/inspect-active-release-r45.mjs +20 -0
  35. package/scripts/refresh-process-policy.mjs +64 -7
  36. package/scripts/release-manifest.mjs +3 -1
  37. package/scripts/release-parity-r45.mjs +253 -0
  38. package/scripts/release-parity-r46.mjs +187 -0
  39. package/scripts/release-parity-tests-r45.ps1 +8 -0
  40. package/scripts/verify-r45-parent.mjs +142 -0
  41. package/scripts/windows-gate-evidence-r45.mjs +60 -0
  42. package/scripts/windows-gate-evidence-r46.mjs +110 -0
  43. package/src/autonomous-runtime.mjs +12 -7
  44. package/src/capability-ledger.mjs +2 -0
  45. package/src/compact-gateway-mcp-tools.mjs +11 -7
  46. package/src/deadbyte-cli.mjs +24 -12
  47. package/src/doctor-r45.mjs +65 -0
  48. package/src/host-plane.mjs +133 -85
  49. package/src/machine-fs-mcp-tools.mjs +3 -3
  50. package/src/machine-fs-output-contracts.mjs +1 -1
  51. package/src/machine-fs-read-worker.mjs +46 -0
  52. package/src/machine-fs.mjs +63 -10
  53. package/src/mcp-server.mjs +68 -23
  54. package/src/mcp-smoke-client.mjs +4 -1
  55. package/src/native-diff-v2.mjs +9 -4
  56. package/src/native-diff.mjs +29 -7
  57. package/src/observation-mcp-tools.mjs +5 -1
  58. package/src/observation-output-contracts.mjs +8 -0
  59. package/src/observation-runtime.mjs +57 -5
  60. package/src/observation-smoke-client.mjs +6 -2
  61. package/src/output-contracts.mjs +27 -11
  62. package/src/process-mcp-tools.mjs +18 -5
  63. package/src/process-output-contracts.mjs +20 -3
  64. package/src/process-policy.mjs +3 -1
  65. package/src/process-runtime.mjs +99 -19
  66. package/src/process-smoke-client.mjs +67 -22
  67. package/src/r45-compact-disarmed-probe.mjs +74 -0
  68. package/src/r45-full-surface-probe.mjs +34 -0
  69. package/src/release-generation.mjs +23 -3
  70. package/src/remote-device-r44.mjs +53 -15
  71. package/src/remote-mcp-r45-parity-client.mjs +206 -0
  72. package/src/remote-mcp-smoke-client.mjs +1 -1
  73. package/src/runtime-update.mjs +36 -8
  74. package/src/stdio-console-guard.mjs +24 -0
  75. package/src/version.mjs +1 -1
  76. package/test/agent-runtime.test.mjs +1 -0
  77. package/test/autonomous-loop.test.mjs +24 -0
  78. package/test/autonomous-runtime.test.mjs +1 -0
  79. package/test/cli-entrypoint.test.mjs +4 -4
  80. package/test/coding-plane.test.mjs +1 -0
  81. package/test/compact-gateway-r36.test.mjs +28 -0
  82. package/test/compact-max-catalog-r45.test.mjs +99 -0
  83. package/test/contained.test.mjs +23 -11
  84. package/test/controller.test.mjs +5 -1
  85. package/test/core.test.mjs +1 -1
  86. package/test/full-max-catalog-r45.test.mjs +91 -0
  87. package/test/full-max-catalog-r46.test.mjs +92 -0
  88. package/test/hardware-observation-r45.test.mjs +44 -0
  89. package/test/host-inline-r45.test.mjs +67 -0
  90. package/test/machine-fs-mcp-tools.test.mjs +4 -1
  91. package/test/machine-fs-runtime.test.mjs +18 -2
  92. package/test/machine-mcp-integration.test.mjs +1 -0
  93. package/test/observation-mcp-tools.test.mjs +4 -3
  94. package/test/observation-runtime.test.mjs +29 -2
  95. package/test/output-schema.test.mjs +1 -0
  96. package/test/package-boundary.test.mjs +1 -1
  97. package/test/process-mcp-tools.test.mjs +23 -5
  98. package/test/process-policy-refresh.test.mjs +87 -10
  99. package/test/process-runtime.test.mjs +111 -1
  100. package/test/process-supervision.test.mjs +4 -2
  101. package/test/production-docs.test.mjs +3 -3
  102. package/test/r33-closeout-regression.test.mjs +20 -19
  103. package/test/r33-finalization.test.mjs +3 -3
  104. package/test/r45-autonomous-quick-test.test.mjs +10 -0
  105. package/test/r45-finalization.test.mjs +84 -0
  106. package/test/release-generation.test.mjs +26 -11
  107. package/test/release-version.test.mjs +112 -46
  108. package/test/remote-device-r44.test.mjs +75 -0
  109. package/test/remote-r24-catalog.test.mjs +2 -2
  110. package/test/runtime-host-bootstrap-r45.test.mjs +46 -0
@@ -0,0 +1,84 @@
1
+ import test from 'node:test';
2
+ import assert from 'node:assert/strict';
3
+ import { readFile } from 'node:fs/promises';
4
+ import { validateFinalizerAuthorityStatus, validateWindowsGateEvidence } from '../scripts/finalize-r45.mjs';
5
+ import { R45_PREDECESSOR_RELEASE_ID, R45_PREDECESSOR_MANIFEST } from '../scripts/release-parity-r45.mjs';
6
+
7
+ const manifest='a'.repeat(64);
8
+ const parent={
9
+ release_id:'v0.15.0-c15fa41719be61b3',
10
+ manifest_sha256:'c15fa41719be61b3186e49b2367a5f4c057365cfb465102dbefb7d7355f45483',
11
+ generation_manifest_sha256:'6d7429c050b7fe893492e102f00282019116e0c9e54e4f25a5791defa1661d30',
12
+ closure_sha256:'533b7e0ff9ded04086dd90ed34e3b87bc427acc3c55c25bb9f4ec66cb448e667'
13
+ };
14
+ const features=()=>({
15
+ skill_integrity:true,agent_backend_persistence:true,codex_protocol_contract:true,
16
+ remote_device_identity:true,durable_job_supervisor:true,real_external_supervisor_smoke:true,
17
+ full_regression_required:true,codex_live_binary_required:false,
18
+ host_inline_native:true,native_hardware_observation:true,doctor_release_truth:true
19
+ });
20
+ const valid=()=>({
21
+ schema:'deadbyte.r45-windows-gate.v1',status:'passed',exit_code:0,version:'0.15.1',
22
+ marker:'DEADBYTE V0.15.1 / R45 MACHINE FREEDOM WINDOWS BATCH GATE: PASS',
23
+ manifest_sha256:manifest,
24
+ parent:{release_id:parent.release_id,manifest_sha256:parent.manifest_sha256,
25
+ generation_manifest_sha256:parent.generation_manifest_sha256,final_closure_sha256:parent.closure_sha256},
26
+ features:features()
27
+ });
28
+
29
+ test('R45 detached finalizer requires Process+Coding+Autonomous and Desktop+Host DISARMED',()=>{
30
+ assert.equal(validateFinalizerAuthorityStatus({process_armed:true,coding_armed:true,autonomous_armed:true,desktop_armed:false,host_armed:false}),true);
31
+ assert.throws(()=>validateFinalizerAuthorityStatus({process_armed:false,coding_armed:true,autonomous_armed:true,desktop_armed:false,host_armed:false}),/Process standing grant/);
32
+ assert.throws(()=>validateFinalizerAuthorityStatus({process_armed:true,coding_armed:true,autonomous_armed:true,desktop_armed:true,host_armed:false}),/Desktop DISARMED/);
33
+ assert.throws(()=>validateFinalizerAuthorityStatus({process_armed:true,coding_armed:true,autonomous_armed:true,desktop_armed:false,host_armed:true}),/Host DISARMED/);
34
+ });
35
+
36
+ test('R45 finalizer binds exact R44 parent and all Machine Freedom gate proofs',()=>{
37
+ assert.equal(validateWindowsGateEvidence(valid(),manifest,parent),true);
38
+ for(const key of ['host_inline_native','native_hardware_observation','doctor_release_truth']){
39
+ assert.throws(()=>validateWindowsGateEvidence({...valid(),features:{...features(),[key]:false}},manifest,parent),new RegExp(key));
40
+ }
41
+ assert.throws(()=>validateWindowsGateEvidence({...valid(),manifest_sha256:'b'.repeat(64)},manifest,parent),/candidate mismatch/);
42
+ });
43
+
44
+ test('R45 release machinery binds 0.15.1, 82/43 surfaces, Host v2 bootstrap and FINAL closure',async()=>{
45
+ const root=new URL('../',import.meta.url);
46
+ const text=rel=>readFile(new URL(rel,root),'utf8');
47
+ const [gate,parity,compactClient,writer,verifier,finalizer,evidence,manifestSource]=await Promise.all([
48
+ text('scripts/gate-windows-r45.ps1'),text('scripts/release-parity-r45.mjs'),text('src/remote-mcp-r45-parity-client.mjs'),text('scripts/final-closure-r45.mjs'),
49
+ text('scripts/final-closure-verify-r45.mjs'),text('scripts/finalize-r45.mjs'),text('scripts/windows-gate-evidence-r45.mjs'),text('scripts/release-manifest.mjs')
50
+ ]);
51
+ assert.equal(R45_PREDECESSOR_RELEASE_ID,parent.release_id);
52
+ assert.equal(R45_PREDECESSOR_MANIFEST,parent.manifest_sha256);
53
+ assert.match(gate,/host-policy-refresh/);assert.match(gate,/doctor/);assert.match(gate,/hardware-build/);
54
+ const hermetic=gate.indexOf('host-policy-hermetic-clean');
55
+ const focused=gate.indexOf('r45-feature-focused');
56
+ const regression=gate.indexOf("Invoke-GateStep -Name 'full-regression'");
57
+ const agentSmoke=gate.indexOf("Invoke-GateStep -Name 'mcp-agent-smoke'");
58
+ const containedSmoke=gate.indexOf("Invoke-GateStep -Name 'mcp-contained-smoke'");
59
+ const hostRefresh=gate.indexOf("Invoke-GateStep -Name 'host-policy-refresh'");
60
+ const manifestWrite=gate.indexOf("Invoke-GateStep -Name 'release-manifest-write'");
61
+ const hostSmoke=gate.indexOf("Invoke-GateStep -Name 'mcp-host-smoke'");
62
+ assert.ok(hermetic>=0&&focused>hermetic&&regression>focused&&agentSmoke>regression&&containedSmoke>agentSmoke&&hostRefresh>containedSmoke&&manifestWrite>hostRefresh&&hostSmoke>manifestWrite,'R45 gate must keep generated Host policy out of hermetic regression and non-Host smoke surfaces');
63
+ assert.match(gate,/DEADBYTE V0\.15\.1 \/ R45 MACHINE FREEDOM WINDOWS BATCH GATE: PASS/);
64
+ assert.match(parity,/tool_count===82/);assert.match(parity,/tool_count===43/);assert.match(compactClient,/hardware_query/);
65
+ assert.match(parity,/waitForExactPointer/);
66
+ assert.match(parity,/rollbackPair\.completed\.active_release===R45_PREDECESSOR_RELEASE_ID/);
67
+ assert.match(parity,/version:'0\.15\.0',manifestSha256:R45_PREDECESSOR_MANIFEST/);
68
+ assert.match(parity,/restorePair\.completed\.active_release===active\.release_id/);
69
+ assert.match(parity,/version:'0\.15\.1',manifestSha256:candidateManifest/);
70
+ assert.match(parity,/predecessor_disarmed\?\.tool_count===43/);
71
+ assert.doesNotMatch(parity,/predecessor_disarmed\?\.tool_count===42/);
72
+ assert.doesNotMatch(parity,/R45 exact R44 predecessor identity mismatch[^\n]*0\.14\.1/);
73
+ for(const source of [writer,verifier]){
74
+ assert.match(source,/DEADBYTE-R45-FINAL-CLOSURE-V1/);
75
+ assert.match(source,/deadbyte\.r45-final-closure\.v1/);
76
+ assert.match(source,/R45 MACHINE FREEDOM FINAL BASELINE/);
77
+ }
78
+ assert.match(finalizer,/--host-policy/);assert.match(finalizer,/--host-policy-sha256/);
79
+ assert.match(finalizer,/host_inline_native/);assert.match(finalizer,/native_hardware_observation/);assert.match(finalizer,/doctor_release_truth/);
80
+ assert.match(evidence,/host_inline_native:true/);assert.match(evidence,/native_hardware_observation:true/);assert.match(evidence,/doctor_release_truth:true/);
81
+ assert.match(manifestSource,/excludedPackagePrefixes = \[[^\]]*'tmp\/'[^\]]*'\.deadbyte-machine-fs-recovery\/'[^\]]*\]/);
82
+ assert.match(manifestSource,/excludedPackagePaths = new Set\(\['controller\/deadbyte-host-policy\.json'\]\)/);
83
+ assert.match(manifestSource,/excludedPackagePrefixes\.some\(prefix => rel\.startsWith\(prefix\)\)/);
84
+ });
@@ -16,7 +16,12 @@ function command(executable='C:/Windows/System32/cmd.exe'){
16
16
  test('R27 generation bundle rebinds authority and mutable state outside the release slot', async () => {
17
17
  const base=await mkdtemp(path.join(os.tmpdir(),'deadbyte-r27-generation-'));
18
18
  const state=path.join(base,'state'),release=path.join(base,'release'),workspace=path.join(base,'workspace');
19
- await mkdir(path.join(release,'scripts'),{recursive:true}); await mkdir(path.join(release,'bin'),{recursive:true}); await mkdir(workspace,{recursive:true});
19
+ const systemRoot=path.join(base,'Windows'),system32=path.join(systemRoot,'System32');
20
+ const powershellDir=path.join(system32,'WindowsPowerShell','v1.0');
21
+ await mkdir(path.join(release,'scripts'),{recursive:true}); await mkdir(path.join(release,'bin'),{recursive:true}); await mkdir(workspace,{recursive:true}); await mkdir(powershellDir,{recursive:true});
22
+ const cmdPath=path.join(system32,'cmd.exe'),cmdBytes=Buffer.from('target-cmd-bytes\\n');
23
+ const powershellPath=path.join(powershellDir,'powershell.exe'),powershellBytes=Buffer.from('target-powershell-bytes\\n');
24
+ await writeFile(cmdPath,cmdBytes); await writeFile(powershellPath,powershellBytes);
20
25
  await writeFile(path.join(release,'scripts','gate.ps1'),'Write-Output gate\n');
21
26
  await writeFile(path.join(release,'bin','deadbyte-process-host.exe'),'process-host-bytes\n');
22
27
  const desktopObserver=Buffer.from('desktop-observer-bytes\n');
@@ -47,7 +52,10 @@ test('R27 generation bundle rebinds authority and mutable state outside the rele
47
52
  const processPolicy={schema:'deadbyte.process-policy.v1',enabled:true,grant_file:'C:/old/process-grant.json',state_dir:'C:/old/process-state',
48
53
  native_host:{path:'C:/old/deadbyte-process-host.exe',sha256:'0'.repeat(64)},
49
54
  roots:{core:{path:'C:/old/core',deny_paths:['node_modules']}},
50
- profiles:{probe:{executable:'C:/Windows/System32/ping.exe',executable_sha256:EXE,root_ids:['core'],prefix_args:['127.0.0.1','-n','2'],max_args:0,max_arg_bytes:256,max_sessions:1,max_input_bytes:4096,max_retained_output_bytes:65536,env_allow:['PATH','TEMP','TMP'],input_actions:{}}}};
55
+ profiles:{
56
+ ping_hold:{executable:'C:/stale/cmd.exe',executable_sha256:EXE,root_ids:['core'],prefix_args:['/d','/q'],transport:'pipe',max_args:0,max_arg_bytes:256,max_sessions:1,max_input_bytes:4096,max_retained_output_bytes:65536,env_allow:['PATH','TEMP','TMP'],input_actions:{ping:'echo DEADBYTE_ACTION_PING\\r\\n'}},
57
+ powershell:{executable:'C:/stale/powershell.exe',executable_sha256:EXE,root_ids:['core'],prefix_args:['-NoLogo','-NoProfile','-NonInteractive','-Command'],transport:'pipe',max_args:1,max_arg_bytes:8192,max_sessions:4,max_input_bytes:65536,max_retained_output_bytes:1048576,env_allow:['PATH','TEMP','TMP','SystemRoot','WINDIR','USERPROFILE','PSModulePath','COMSPEC','PATHEXT'],input_actions:{}}
58
+ }};
51
59
  const desktopPolicy={schema:'deadbyte.desktop-policy.v1',enabled:true,grant_file:'C:/old/desktop-grant.json',evidence_dir:'C:/old/desktop-evidence',
52
60
  allowed_input_desktops:['Default'],allowed_actions:['click_primary','key_chord'],
53
61
  limits:{max_frame_age_ms:2000,max_frame_bytes:8388608,capture_timeout_ms:5000,input_timeout_ms:2000,ground_timeout_ms:5000,post_observe_timeout_ms:5000,max_preview_bytes:1048576,max_text_bytes:4096},
@@ -59,7 +67,8 @@ test('R27 generation bundle rebinds authority and mutable state outside the rele
59
67
  await writeFile(path.join(development,'scripts','gate-windows-old.ps1'),'Write-Output old-gate\n');
60
68
  await writeFile(path.join(development,'scripts','gate-windows-current.ps1'),'Write-Output current-gate\n');
61
69
  await writeFile(path.join(development,'package.json'),JSON.stringify({scripts:{'gate:windows':'powershell -NoProfile -ExecutionPolicy Bypass -File scripts/gate-windows-current.ps1'}}));
62
- const result=await createGenerationBundle({stateRoot:state,releaseRoot:release,developmentRoot:development,workspaceRoot:workspace,releaseId:'v0.8.3-aaaaaaaaaaaaaaaa',currentConfig,codingPolicy:coding,autonomousPolicy:autonomous,hostPolicy:host,processPolicy,desktopPolicy});
70
+ const build=args=>createGenerationBundle({...args,systemRoot});
71
+ const result=await build({stateRoot:state,releaseRoot:release,developmentRoot:development,workspaceRoot:workspace,releaseId:'v0.8.3-aaaaaaaaaaaaaaaa',currentConfig,codingPolicy:coding,autonomousPolicy:autonomous,hostPolicy:host,processPolicy,desktopPolicy});
63
72
  const verified=await verifyGenerationBundle(result.generation_root,result.generation_manifest_sha256);
64
73
  assert.equal(verified.files,7);
65
74
  const generatedProcess=JSON.parse(await readFile(result.process_policy_path,'utf8'));
@@ -68,6 +77,11 @@ test('R27 generation bundle rebinds authority and mutable state outside the rele
68
77
  assert.equal(generatedProcess.roots.core.path,path.resolve(development));
69
78
  assert.equal(generatedProcess.native_host.path,path.join(path.resolve(release),'bin','deadbyte-process-host.exe'));
70
79
  assert.equal(generatedProcess.native_host.sha256,sha(Buffer.from('process-host-bytes\n')));
80
+ assert.equal(generatedProcess.profiles.ping_hold.executable,cmdPath);
81
+ assert.equal(generatedProcess.profiles.ping_hold.executable_sha256,sha(cmdBytes));
82
+ assert.equal(generatedProcess.profiles.powershell.executable,powershellPath);
83
+ assert.equal(generatedProcess.profiles.powershell.executable_sha256,sha(powershellBytes));
84
+ assert.deepEqual(generatedProcess.profiles.powershell.prefix_args,['-NoLogo','-NoProfile','-NonInteractive','-Command']);
71
85
  const generatedDesktop=JSON.parse(await readFile(result.desktop_policy_path,'utf8'));
72
86
  assert.equal(generatedDesktop.grant_file,path.join(path.resolve(state),'runtime','desktop-grant.json'));
73
87
  assert.equal(generatedDesktop.evidence_dir,path.join(path.resolve(state),'runtime','desktop-evidence'));
@@ -92,6 +106,7 @@ test('R27 generation bundle rebinds authority and mutable state outside the rele
92
106
  assert.equal(generatedAuto.profiles.gate.script_sha256,sha(Buffer.from('Write-Output gate\n')));
93
107
  assert.equal(generatedAuto.profiles['windows-release'].script_path,'scripts/gate-windows-current.ps1');
94
108
  assert.equal(generatedAuto.profiles['windows-release'].script_sha256,sha(Buffer.from('Write-Output current-gate\n')));
109
+ assert.equal(generatedAuto.profiles['windows-release'].timeout_ms,600000);
95
110
  assert.equal(generatedAuto.profiles['windows-release'].description,'Full Windows target release gate; runs only when finish is requested.');
96
111
  const generatedHost=JSON.parse(await readFile(result.host_policy_path,'utf8'));
97
112
  assert.equal(generatedHost.roots.core.path,path.resolve(development));
@@ -128,44 +143,44 @@ test('R27 generation bundle rebinds authority and mutable state outside the rele
128
143
  assert.match(path.basename(result.generation_root),/^v0\.8\.3-aaaaaaaaaaaaaaaa-g[0-9a-f]{16}$/);
129
144
  const changedConfig=structuredClone(currentConfig);
130
145
  changedConfig.Bridge.Arguments='bridge-v2.mjs';
131
- const second=await createGenerationBundle({stateRoot:state,releaseRoot:release,developmentRoot:development,workspaceRoot:workspace,releaseId:'v0.8.3-aaaaaaaaaaaaaaaa',currentConfig:changedConfig,codingPolicy:coding,autonomousPolicy:autonomous,hostPolicy:host,processPolicy,desktopPolicy});
146
+ const second=await build({stateRoot:state,releaseRoot:release,developmentRoot:development,workspaceRoot:workspace,releaseId:'v0.8.3-aaaaaaaaaaaaaaaa',currentConfig:changedConfig,codingPolicy:coding,autonomousPolicy:autonomous,hostPolicy:host,processPolicy,desktopPolicy});
132
147
  assert.notEqual(second.generation_root,result.generation_root);
133
148
  assert.notEqual(second.generation_seed_sha256,result.generation_seed_sha256);
134
149
  const endpointChanged=structuredClone(currentConfig);
135
150
  endpointChanged.McpUrlFileSha256='2'.repeat(64);
136
- const third=await createGenerationBundle({stateRoot:state,releaseRoot:release,developmentRoot:development,workspaceRoot:workspace,releaseId:'v0.8.3-aaaaaaaaaaaaaaaa',currentConfig:endpointChanged,codingPolicy:coding,autonomousPolicy:autonomous,hostPolicy:host,processPolicy,desktopPolicy});
151
+ const third=await build({stateRoot:state,releaseRoot:release,developmentRoot:development,workspaceRoot:workspace,releaseId:'v0.8.3-aaaaaaaaaaaaaaaa',currentConfig:endpointChanged,codingPolicy:coding,autonomousPolicy:autonomous,hostPolicy:host,processPolicy,desktopPolicy});
137
152
  assert.notEqual(third.generation_root,result.generation_root);
138
153
  assert.notEqual(third.generation_seed_sha256,result.generation_seed_sha256);
139
154
  const machineDisabled=structuredClone(currentConfig);
140
155
  machineDisabled.MachineRuntimeEnabled=false;
141
- const fourth=await createGenerationBundle({stateRoot:state,releaseRoot:release,developmentRoot:development,workspaceRoot:workspace,releaseId:'v0.8.3-aaaaaaaaaaaaaaaa',currentConfig:machineDisabled,codingPolicy:coding,autonomousPolicy:autonomous,hostPolicy:host,processPolicy,desktopPolicy});
156
+ const fourth=await build({stateRoot:state,releaseRoot:release,developmentRoot:development,workspaceRoot:workspace,releaseId:'v0.8.3-aaaaaaaaaaaaaaaa',currentConfig:machineDisabled,codingPolicy:coding,autonomousPolicy:autonomous,hostPolicy:host,processPolicy,desktopPolicy});
142
157
  assert.notEqual(fourth.generation_root,result.generation_root);
143
158
  assert.notEqual(fourth.generation_seed_sha256,result.generation_seed_sha256);
144
159
  const machineFsDisabled=structuredClone(currentConfig);
145
160
  machineFsDisabled.MachineFsRuntimeEnabled=false;
146
- const fifth=await createGenerationBundle({stateRoot:state,releaseRoot:release,developmentRoot:development,workspaceRoot:workspace,releaseId:'v0.8.3-aaaaaaaaaaaaaaaa',currentConfig:machineFsDisabled,codingPolicy:coding,autonomousPolicy:autonomous,hostPolicy:host,processPolicy,desktopPolicy});
161
+ const fifth=await build({stateRoot:state,releaseRoot:release,developmentRoot:development,workspaceRoot:workspace,releaseId:'v0.8.3-aaaaaaaaaaaaaaaa',currentConfig:machineFsDisabled,codingPolicy:coding,autonomousPolicy:autonomous,hostPolicy:host,processPolicy,desktopPolicy});
147
162
  assert.notEqual(fifth.generation_root,result.generation_root);
148
163
  assert.notEqual(fifth.generation_seed_sha256,result.generation_seed_sha256);
149
164
  const observationDisabled=structuredClone(currentConfig);
150
165
  observationDisabled.ObservationRuntimeEnabled=false;
151
- const sixth=await createGenerationBundle({stateRoot:state,releaseRoot:release,developmentRoot:development,workspaceRoot:workspace,releaseId:'v0.8.3-aaaaaaaaaaaaaaaa',currentConfig:observationDisabled,codingPolicy:coding,autonomousPolicy:autonomous,hostPolicy:host,processPolicy,desktopPolicy});
166
+ const sixth=await build({stateRoot:state,releaseRoot:release,developmentRoot:development,workspaceRoot:workspace,releaseId:'v0.8.3-aaaaaaaaaaaaaaaa',currentConfig:observationDisabled,codingPolicy:coding,autonomousPolicy:autonomous,hostPolicy:host,processPolicy,desktopPolicy});
152
167
  assert.notEqual(sixth.generation_root,result.generation_root);
153
168
  assert.notEqual(sixth.generation_seed_sha256,result.generation_seed_sha256);
154
169
  const desktopDisabled=structuredClone(currentConfig);
155
170
  desktopDisabled.DesktopRuntimeEnabled=false;
156
- const desktopVariant=await createGenerationBundle({stateRoot:state,releaseRoot:release,developmentRoot:development,workspaceRoot:workspace,releaseId:'v0.8.3-aaaaaaaaaaaaaaaa',currentConfig:desktopDisabled,codingPolicy:coding,autonomousPolicy:autonomous,hostPolicy:host,processPolicy,desktopPolicy});
171
+ const desktopVariant=await build({stateRoot:state,releaseRoot:release,developmentRoot:development,workspaceRoot:workspace,releaseId:'v0.8.3-aaaaaaaaaaaaaaaa',currentConfig:desktopDisabled,codingPolicy:coding,autonomousPolicy:autonomous,hostPolicy:host,processPolicy,desktopPolicy});
157
172
  assert.notEqual(desktopVariant.generation_root,result.generation_root);
158
173
  assert.notEqual(desktopVariant.generation_seed_sha256,result.generation_seed_sha256);
159
174
  const desktopTampered=structuredClone(desktopPolicy);
160
175
  desktopTampered.input.sha256='f'.repeat(64);
161
- await assert.rejects(createGenerationBundle({stateRoot:state,releaseRoot:release,developmentRoot:development,workspaceRoot:workspace,releaseId:'v0.8.3-aaaaaaaaaaaaaaaa',currentConfig,codingPolicy:coding,autonomousPolicy:autonomous,hostPolicy:host,processPolicy,desktopPolicy:desktopTampered}),/desktop input helper SHA-256 mismatch/);
176
+ await assert.rejects(build({stateRoot:state,releaseRoot:release,developmentRoot:development,workspaceRoot:workspace,releaseId:'v0.8.3-aaaaaaaaaaaaaaaa',currentConfig,codingPolicy:coding,autonomousPolicy:autonomous,hostPolicy:host,processPolicy,desktopPolicy:desktopTampered}),/desktop input helper SHA-256 mismatch/);
162
177
  const semanticConfig2Path=path.join(base,'semantic-lsp-v2.json');
163
178
  const semanticConfig2Bytes=Buffer.from(JSON.stringify({schema:'deadbyte.semantic-lsp-config.v1',servers:[{id:'changed'}]})+'\n','utf8');
164
179
  await writeFile(semanticConfig2Path,semanticConfig2Bytes);
165
180
  const semanticChanged=structuredClone(currentConfig);
166
181
  semanticChanged.SemanticLspConfigFile=semanticConfig2Path;
167
182
  semanticChanged.SemanticLspConfigSha256=sha(semanticConfig2Bytes);
168
- const seventh=await createGenerationBundle({stateRoot:state,releaseRoot:release,developmentRoot:development,workspaceRoot:workspace,releaseId:'v0.8.3-aaaaaaaaaaaaaaaa',currentConfig:semanticChanged,codingPolicy:coding,autonomousPolicy:autonomous,hostPolicy:host,processPolicy,desktopPolicy});
183
+ const seventh=await build({stateRoot:state,releaseRoot:release,developmentRoot:development,workspaceRoot:workspace,releaseId:'v0.8.3-aaaaaaaaaaaaaaaa',currentConfig:semanticChanged,codingPolicy:coding,autonomousPolicy:autonomous,hostPolicy:host,processPolicy,desktopPolicy});
169
184
  assert.notEqual(seventh.generation_root,result.generation_root);
170
185
  assert.notEqual(seventh.generation_seed_sha256,result.generation_seed_sha256);
171
186
  await verifyGenerationBundle(result.generation_root,result.generation_manifest_sha256);
@@ -2,58 +2,124 @@ import test from 'node:test';
2
2
  import assert from 'node:assert/strict';
3
3
  import { readFile } from 'node:fs/promises';
4
4
  import { createHash } from 'node:crypto';
5
- const root=new URL('../',import.meta.url);const text=rel=>readFile(new URL(rel,root),'utf8');
6
- const PARENT_RELEASE='v0.14.1-0a5a833892593283';
7
- const PARENT_MANIFEST='0a5a8338925932830986421dda60ec5643f62f600404de4992fda2a5e79d09a9';
8
- const PARENT_CLOSURE='2f29e94dd726638c23b2e520a608dcf2a6919e43348855be3940599d557f920b';
9
5
 
10
- test('R44 current authority agrees on V0.15.0 and preserves exact sealed R43 parent',async()=>{
6
+ const root=new URL('../',import.meta.url);
7
+ const text=rel=>readFile(new URL(rel,root),'utf8');
8
+ const PARENT_RELEASE='v0.15.1-d55f00b5144dc31a';
9
+ const PARENT_MANIFEST='d55f00b5144dc31a7fe39e4dc60d640a94780da6aaeca0918e87636b816d7854';
10
+ const PARENT_GENERATION='00cde7bf540cc2804d679a39040d5b07193c5f8a0d49cac3b882402f8f4e6ad8';
11
+
12
+ test('R46 current authority agrees on V0.15.2 and preserves exact R45 parent identity',async()=>{
11
13
  const pkg=JSON.parse(await text('package.json'));
12
- const [version,controller,gate,readme,architecture,controllerReadme,deferred,parity,closure,closureVerify,windowsGate,parent,r43Gate,r43Parity,r43Closure]=await Promise.all([
13
- text('src/version.mjs'),text('controller/deadbyte-controller.ps1'),text('scripts/gate-windows-r44.ps1'),text('README.txt'),
14
- text('docs/ARCHITECTURE.md'),text('controller/README.TXT'),text('scripts/deferred-slot-operation-r44.mjs'),
15
- text('scripts/release-parity-r44.mjs'),text('scripts/final-closure-r44.mjs'),text('scripts/final-closure-verify-r44.mjs'),
16
- text('scripts/windows-gate-evidence-r44.mjs'),text('R44-PARENT.json'),
17
- text('scripts/gate-windows-r43.ps1'),text('scripts/release-parity-r43.mjs'),text('scripts/final-closure-r43.mjs')
14
+ const release=JSON.parse(await text('RELEASE.json'));
15
+ const [version,controller,gate,readme,architecture,controllerReadme,deferred,parity,closure,closureVerify,windowsGate,processPolicy]=await Promise.all([
16
+ text('src/version.mjs'),
17
+ text('controller/deadbyte-controller.ps1'),
18
+ text('scripts/gate-windows-r46.ps1'),
19
+ text('README.txt'),
20
+ text('docs/ARCHITECTURE.md'),
21
+ text('controller/README.TXT'),
22
+ text('scripts/deferred-slot-operation-r46.mjs'),
23
+ text('scripts/release-parity-r46.mjs'),
24
+ text('scripts/final-closure-r46.mjs'),
25
+ text('scripts/final-closure-verify-r46.mjs'),
26
+ text('scripts/windows-gate-evidence-r46.mjs'),
27
+ text('controller/deadbyte-process-policy.json')
18
28
  ]);
19
- assert.equal(pkg.version,'0.15.0');
20
- assert.equal(pkg.scripts['gate:windows'],'powershell -NoProfile -ExecutionPolicy Bypass -File scripts/gate-windows-r44.ps1');
21
- assert.equal(pkg.scripts['r44:parent:verify'],'node scripts/verify-r44-parent.mjs');
22
- assert.equal(pkg.scripts['r44:full:test'],'node scripts/full-regression-r44.mjs');
23
- assert.equal(pkg.scripts['self-update:deferred'],'node scripts/deferred-slot-operation-r44.mjs');
24
- assert.equal(pkg.scripts['release:parity'],'node scripts/release-parity-r44.mjs');
25
- assert.equal(pkg.scripts['release:final:write'],'node scripts/final-closure-r44.mjs');
26
- assert.equal(pkg.scripts['release:final:verify'],'node scripts/final-closure-verify-r44.mjs');
27
- assert.equal(pkg.scripts['finalize:r44'],'node scripts/finalize-r44.mjs');
28
- assert.match(version,/DEADBYTE_VERSION = '0\.15\.0'/);
29
- assert.match(controller,/CONTROLLER V0\.15\.0 \/ R44/);
30
- assert.match(gate,/DEADBYTE V0\.15\.0 \/ R44 REMOTE AGENT SKILL WINDOWS BATCH GATE: PASS/);
31
- assert.match(readme,/^DEADBYTE MCP V0\.15\.0 \/ R44 REMOTE AGENT \+ SKILL BASELINE$/m);
32
- assert.match(architecture,/^# DEADBYTE MCP V0\.15\.0 \/ R44 ARCHITECTURE$/m);
33
- assert.match(controllerReadme,/^DEADBYTE MCP V0\.15\.0 \/ R44 CONTROLLER$/m);
34
- assert.match(deferred,/--expected-version','0\.15\.0'/);
35
- assert.match(parity,/R44_PREDECESSOR_RELEASE_ID='v0\.14\.1-0a5a833892593283'/);assert.match(parity,new RegExp(PARENT_MANIFEST));
36
- assert.match(parity,/tool_count===81/);assert.match(parity,/tool_count===43/);
37
- assert.match(closure,/version:'0\.15\.0'/);assert.match(closure,/DEADBYTE-R44-FINAL-CLOSURE-V1/);
38
- assert.match(closureVerify,/R44_FULL_TOOL_COUNT=81/);assert.match(closureVerify,/R44_COMPACT_TOOL_COUNT=43/);
39
- assert.match(windowsGate,/deadbyte\.r44-windows-gate\.v1/);
40
- const parentRecord=JSON.parse(parent);
41
- assert.equal(parentRecord.parent_release_id,PARENT_RELEASE);assert.equal(parentRecord.parent_manifest_sha256,PARENT_MANIFEST);
42
- assert.equal(parentRecord.parent_final_closure_sha256,PARENT_CLOSURE);
43
- assert.match(r43Gate,/DEADBYTE V0\.14\.1 \/ R43 AUTHORITY HARDENING WINDOWS BATCH GATE: PASS/);
44
- assert.match(r43Parity,/deadbyte\.r43-parity\.v1/);assert.match(r43Closure,/version:'0\.14\.1'/);
29
+
30
+ assert.equal(pkg.version,'0.15.2');
31
+ assert.equal(pkg.scripts['gate:windows'],'powershell -NoProfile -ExecutionPolicy Bypass -File scripts/gate-windows-r46.ps1');
32
+ assert.equal(pkg.scripts['self-update:deferred'],'node scripts/deferred-slot-operation-r46.mjs');
33
+ assert.equal(pkg.scripts['release:parity'],'node scripts/release-parity-r46.mjs');
34
+ assert.equal(pkg.scripts['release:final:write'],'node scripts/final-closure-r46.mjs');
35
+ assert.equal(pkg.scripts['release:final:verify'],'node scripts/final-closure-verify-r46.mjs');
36
+ assert.equal(pkg.scripts['r46:fileless:test'],"node --test --test-concurrency=1 test/process-policy.test.mjs test/process-policy-refresh.test.mjs test/release-generation.test.mjs test/process-runtime.test.mjs test/process-mcp-tools.test.mjs test/compact-gateway-r36.test.mjs test/capability-ledger-r36.test.mjs test/remote-device-r44.test.mjs test/observation-runtime.test.mjs test/observation-mcp-tools.test.mjs");
37
+ assert.equal(pkg.scripts['r46:full:test'],'node scripts/full-regression-r46.mjs');
38
+
39
+ assert.match(version,/DEADBYTE_VERSION = '0\.15\.2'/);
40
+ assert.match(controller,/CONTROLLER V0\.15\.2 \/ R46/);
41
+ assert.match(gate,/DEADBYTE V0\.15\.2 \/ R46 FILELESS POWERSHELL WINDOWS BATCH GATE: PASS/);
42
+ assert.match(readme,/^DEADBYTE MCP V0\.15\.2 \/ R46 FILELESS POWERSHELL PRODUCT BASELINE$/m);
43
+ assert.match(readme,/powershell\.exe -Command <source>/);
44
+ assert.match(architecture,/^# DEADBYTE MCP V0\.15\.2 \/ R46 ARCHITECTURE$/m);
45
+ assert.match(controllerReadme,/^DEADBYTE MCP V0\.15\.2 \/ R46 CONTROLLER$/m);
46
+ assert.match(deferred,/--expected-version','0\.15\.2'/);
47
+ assert.match(parity,/deadbyte\.r46-parity\.v1/);
48
+ assert.match(parity,/DB_R46_FILELESS_PARITY/);
49
+ assert.match(parity,/target_repin_verified:true/);
50
+ assert.match(parity,/DB_R46_REPL_VALUE=42/);
51
+ assert.match(parity,/raw_input_not_journaled:true/);
52
+ assert.match(parity,/eviction_verified:true/);
53
+ assert.match(windowsGate,/full_tool_count:83/);
54
+ assert.match(windowsGate,/remote_reachability_ttl:true/);
55
+ assert.match(windowsGate,/observation_search_terminal_ttl:true/);
56
+ assert.match(windowsGate,/observation_search_hard_cap:128/);
57
+ assert.match(closure,/deadbyte\.r46-final-closure\.v1/);
58
+ assert.match(closure,/DEADBYTE-R46-FINAL-CLOSURE-V1/);
59
+ assert.match(closureVerify,/DEADBYTE-R46-FINAL-CLOSURE-V1/);
60
+ assert.match(windowsGate,/deadbyte\.r46-windows-gate\.v1/);
61
+
62
+ assert.equal(release.version,'0.15.2');
63
+ assert.equal(release.train,'R46');
64
+ assert.equal(release.label,'FILELESS POWERSHELL PRODUCT BASELINE');
65
+ assert.equal(release.parent.release_id,PARENT_RELEASE);
66
+ assert.equal(release.parent.manifest_sha256,PARENT_MANIFEST);
67
+ assert.equal(release.parent.generation_manifest_sha256,PARENT_GENERATION);
68
+ assert.deepEqual(release.capabilities.process_profiles,['ping_hold','powershell','powershell_repl']);
69
+ assert.equal(release.capabilities.full_tool_count,83);
70
+ assert.equal(release.capabilities.compact_tool_count,43);
71
+ assert.equal(release.capabilities.powershell_persistent_repl,true);
72
+ assert.equal(release.capabilities.process_raw_input_profile_opt_in,true);
73
+ assert.equal(release.capabilities.powershell_fileless,true);
74
+ assert.equal(release.capabilities.powershell_source_max_arg_bytes,8192);
75
+
76
+ const policy=JSON.parse(processPolicy);
77
+ assert.ok(policy.profiles.ping_hold);
78
+ assert.ok(policy.profiles.powershell);
79
+ assert.ok(policy.profiles.powershell_repl);
80
+ assert.equal(policy.profiles.powershell_repl.allow_raw_input,true);
81
+ assert.deepEqual(policy.profiles.powershell_repl.prefix_args,['-NoLogo','-NoProfile','-NonInteractive']);
82
+ assert.deepEqual(policy.profiles.powershell.prefix_args,['-NoLogo','-NoProfile','-NonInteractive','-Command']);
83
+ assert.equal(policy.profiles.powershell.max_args,1);
84
+ assert.equal(policy.profiles.powershell.max_arg_bytes,8192);
85
+ assert.equal(policy.profiles.powershell.transport,'pipe');
45
86
  });
46
87
 
47
- test('R44 keeps ONLINE reuse and explicit Desktop authority',async()=>{
48
- const [cli,controller]=await Promise.all([text('src/deadbyte-cli.mjs'),text('controller/deadbyte-controller.ps1')]);
49
- assert.match(cli,/controllerRuntimeReady/);assert.match(cli,/Runtime already ONLINE; reusing verified bridge\/cloud\/supervisor/);
50
- const start=controller.indexOf('function Recover-AutonomousSupervisor'),end=controller.indexOf('function Show-State',start);
51
- assert.ok(start>=0&&end>start);const block=controller.slice(start,end);
52
- assert.doesNotMatch(block,/\$host\s*=/i);assert.match(block,/\$hostStatus\s*=\s*Get-HostArmStatus/);
53
- assert.match(controller,/armdesktop/);assert.match(controller,/disarmdesktop/);
88
+ test('R46 leaves sealed R45 release artifacts historical and unchanged in identity',async()=>{
89
+ const [gate,deferred,parity,closure,closureVerify,finalizer,parent]=await Promise.all([
90
+ text('scripts/gate-windows-r45.ps1'),
91
+ text('scripts/deferred-slot-operation-r45.mjs'),
92
+ text('scripts/release-parity-r45.mjs'),
93
+ text('scripts/final-closure-r45.mjs'),
94
+ text('scripts/final-closure-verify-r45.mjs'),
95
+ text('scripts/finalize-r45.mjs'),
96
+ text('R45-PARENT.json')
97
+ ]);
98
+ assert.match(gate,/DEADBYTE V0\.15\.1 \/ R45 MACHINE FREEDOM WINDOWS BATCH GATE: PASS/);
99
+ assert.match(deferred,/--expected-version','0\.15\.1'/);
100
+ assert.match(parity,/deadbyte\.r45-parity\.v1/);
101
+ assert.match(closure,/version:'0\.15\.1'/);
102
+ assert.match(closure,/DEADBYTE-R45-FINAL-CLOSURE-V1/);
103
+ assert.match(closureVerify,/R45_FULL_TOOL_COUNT=82/);
104
+ assert.match(finalizer,/EXPECTED_VERSION='0\.15\.1'/);
105
+ const parentRecord=JSON.parse(parent);
106
+ assert.equal(parentRecord.parent_release_id,'v0.15.0-c15fa41719be61b3');
54
107
  });
55
108
 
56
- test('semantic config remains byte-pinned across R44 feature work',async()=>{
57
- const semantic=await text('controller/deadbyte-semantic-lsp-config.json');
109
+ test('R46 keeps ONLINE reuse, explicit Desktop authority, and semantic config pin',async()=>{
110
+ const [cli,controller,semantic]=await Promise.all([
111
+ text('src/deadbyte-cli.mjs'),
112
+ text('controller/deadbyte-controller.ps1'),
113
+ text('controller/deadbyte-semantic-lsp-config.json')
114
+ ]);
115
+ assert.match(cli,/controllerRuntimeReady/);
116
+ assert.match(cli,/Runtime already ONLINE; reusing verified bridge\/cloud\/supervisor/);
117
+ const start=controller.indexOf('function Recover-AutonomousSupervisor'),end=controller.indexOf('function Show-State',start);
118
+ assert.ok(start>=0&&end>start);
119
+ const block=controller.slice(start,end);
120
+ assert.doesNotMatch(block,/\$host\s*=/i);
121
+ assert.match(block,/\$hostStatus\s*=\s*Get-HostArmStatus/);
122
+ assert.match(controller,/armdesktop/);
123
+ assert.match(controller,/disarmdesktop/);
58
124
  assert.equal(createHash('sha256').update(Buffer.from(semantic,'utf8')).digest('hex'),'d274b099a298c1033fea8f1e8849751693bbc0448778d619400acb82d4f53a3b');
59
125
  });
@@ -102,3 +102,78 @@ test('R44 remote state corruption fails closed',async()=>{
102
102
  await assert.rejects(createRemoteDevicePlaneR44({stateDir:root,relay:new FakeRelay(),authorizeCapability:async()=>true}),/state JSON invalid/);
103
103
  }finally{await rm(root,{recursive:true,force:true});}
104
104
  });
105
+
106
+
107
+ test('R46 remote readiness probe retries only the idempotent probe and remains bounded by timeout',async()=>{
108
+ const root=await mkdtemp(path.join(os.tmpdir(),'deadbyte-r46-remote-probe-timeout-'));
109
+ const key=deviceKey();
110
+ let attempts=0;
111
+ const relay={
112
+ requests:[],
113
+ async probe(value){
114
+ attempts+=1;
115
+ if(attempts===1) return await new Promise(()=>{});
116
+ return {device_id:value.device_id,epoch:value.epoch,nonce:value.nonce,ready:true};
117
+ },
118
+ async request(value){this.requests.push(value);return {ack:'accepted',request_id:value.request_id,terminal:{status:'completed',result_sha256:'a'.repeat(64)}};}
119
+ };
120
+ try{
121
+ const plane=await createRemoteDevicePlaneR44({
122
+ stateDir:root,relay,authorizeCapability:async()=>true,
123
+ relayTimeoutMs:100,probeRetries:1,reachableTtlMs:5000
124
+ });
125
+ await plane.pair({publicKeySpkiBase64:key.spki,displayName:'lab'});
126
+ await plane.reportOnline({deviceId:key.deviceId,epoch:1,connectionId:'retry-c'});
127
+ const ready=await plane.probe({deviceId:key.deviceId,epoch:1});
128
+ assert.equal(attempts,2);
129
+ assert.equal(ready.reachable,true);
130
+ }finally{await rm(root,{recursive:true,force:true});}
131
+ });
132
+
133
+ test('R46 remote reachability expires after the bounded probe freshness window',async()=>{
134
+ const root=await mkdtemp(path.join(os.tmpdir(),'deadbyte-r46-remote-stale-'));
135
+ const relay=new FakeRelay(),key=deviceKey();
136
+ try{
137
+ const plane=await createRemoteDevicePlaneR44({
138
+ stateDir:root,relay,authorizeCapability:async()=>true,
139
+ relayTimeoutMs:1000,probeRetries:0,reachableTtlMs:1000
140
+ });
141
+ await plane.pair({publicKeySpkiBase64:key.spki,displayName:'lab'});
142
+ await plane.reportOnline({deviceId:key.deviceId,epoch:1,connectionId:'stale-c'});
143
+ await plane.probe({deviceId:key.deviceId,epoch:1});
144
+ assert.equal((await plane.status({deviceId:key.deviceId})).reachable,true);
145
+ await new Promise(resolve=>setTimeout(resolve,1100));
146
+ assert.equal((await plane.status({deviceId:key.deviceId})).reachable,false);
147
+ await assert.rejects(
148
+ plane.dispatch({deviceId:key.deviceId,epoch:1,capabilityId:'machine.status',payload:{},requestId:'8'.repeat(64)}),
149
+ /not reachable|stale/i
150
+ );
151
+ assert.equal(relay.requests.length,0);
152
+ }finally{await rm(root,{recursive:true,force:true});}
153
+ });
154
+
155
+ test('R46 remote dispatch timeout is not retried and downgrades reachability',async()=>{
156
+ const root=await mkdtemp(path.join(os.tmpdir(),'deadbyte-r46-remote-dispatch-timeout-'));
157
+ const key=deviceKey();
158
+ let requestCount=0;
159
+ const relay={
160
+ async probe(value){return {device_id:value.device_id,epoch:value.epoch,nonce:value.nonce,ready:true};},
161
+ async request(){requestCount+=1;return await new Promise(()=>{});}
162
+ };
163
+ try{
164
+ const plane=await createRemoteDevicePlaneR44({
165
+ stateDir:root,relay,authorizeCapability:async()=>true,
166
+ relayTimeoutMs:100,probeRetries:0,reachableTtlMs:5000
167
+ });
168
+ await plane.pair({publicKeySpkiBase64:key.spki,displayName:'lab'});
169
+ await plane.reportOnline({deviceId:key.deviceId,epoch:1,connectionId:'dispatch-c'});
170
+ await plane.probe({deviceId:key.deviceId,epoch:1});
171
+ const result=await plane.dispatch({
172
+ deviceId:key.deviceId,epoch:1,capabilityId:'coding.file_read',payload:{path:'README.md'},requestId:'9'.repeat(64)
173
+ });
174
+ assert.equal(result.state,'failed');
175
+ assert.equal(result.error_code,'transport_error');
176
+ assert.equal(requestCount,1,'side-effecting dispatch must never auto-retry');
177
+ assert.equal((await plane.status({deviceId:key.deviceId})).reachable,false);
178
+ }finally{await rm(root,{recursive:true,force:true});}
179
+ });
@@ -32,10 +32,10 @@ test('R29 remote gate accepts machine plane as an exact optional catalog dimensi
32
32
  assert.match(source, /if \(machine\) items\.push\(\.\.\.machineTools\)/);
33
33
  assert.match(source, /machine_runtime: machineRuntime/);
34
34
  });
35
- test('R32 remote gate accepts six-tool process supervision as one exact optional catalog dimension', async () => {
35
+ test('R46 remote gate accepts seven-tool process supervision as one exact optional catalog dimension', async () => {
36
36
  const source = await readFile(sourcePath, 'utf8');
37
37
  assert.match(source, /const processTools = \[/);
38
- for (const name of ['process_session_start','process_session_read','process_session_status','process_session_list','process_session_terminate','process_session_action']) {
38
+ for (const name of ['process_session_start','process_session_read','process_session_status','process_session_list','process_session_terminate','process_session_action','process_session_write']) {
39
39
  assert.match(source, new RegExp(`'${name}'`));
40
40
  }
41
41
  assert.match(source, /for \(const processPlane of \[false, true\]\)/);
@@ -0,0 +1,46 @@
1
+ import test from 'node:test';
2
+ import assert from 'node:assert/strict';
3
+ import { createHash } from 'node:crypto';
4
+ import { mkdtemp, readFile, writeFile } from 'node:fs/promises';
5
+ import os from 'node:os';
6
+ import path from 'node:path';
7
+ import { parseUpdateArgs, prepareHostPolicyBootstrap } from '../scripts/runtime-self-update.mjs';
8
+
9
+ const hex=ch=>ch.repeat(64);
10
+ const sha=bytes=>createHash('sha256').update(bytes).digest('hex');
11
+
12
+ test('R45 slot-update CLI binds Host policy bootstrap as one exact path plus SHA-256 pair',()=>{
13
+ const base=['--stage','stage','--development-root','dev','--expected-version','0.15.1','--expected-manifest',hex('a')];
14
+ assert.throws(()=>parseUpdateArgs([...base,'--host-policy','C:/candidate/host.json']),/host policy.*pair/i);
15
+ assert.throws(()=>parseUpdateArgs([...base,'--host-policy-sha256',hex('b')]),/host policy.*pair/i);
16
+ const parsed=parseUpdateArgs([...base,'--host-policy','C:/candidate/host.json','--host-policy-sha256',hex('b')]);
17
+ assert.equal(parsed.hostPolicyBootstrap.path,path.resolve('C:/candidate/host.json'));
18
+ assert.equal(parsed.hostPolicyBootstrap.sha256,hex('b'));
19
+ });
20
+
21
+ test('R45 Host policy bootstrap accepts exact v2 bytes and rejects hash or native contract drift',async()=>{
22
+ const root=await mkdtemp(path.join(os.tmpdir(),'deadbyte-r45-host-bootstrap-'));
23
+ const file=path.join(root,'host.json');
24
+ const policy={
25
+ schema:'deadbyte.host-policy.v2',enabled:true,arm_file:'C:/state/host-grant.json',
26
+ roots:{core:{path:'C:/core',read:true,write:true,exec:true}},
27
+ commands:{node_inline:{kind:'inline',executable:'C:/node.exe',executable_sha256:hex('1'),root_ids:['core'],prefix_args:['-e'],timeout_ms:1000,max_output_bytes:4096,max_command_chars:1024}},
28
+ native_exec:{enabled:true,root_ids:['core'],timeout_ms:1000,max_output_bytes:4096,max_args:8,max_arg_chars:1024}
29
+ };
30
+ const bytes=Buffer.from(JSON.stringify(policy)+'\n','utf8'); await writeFile(file,bytes);
31
+ const accepted=await prepareHostPolicyBootstrap({path:file,sha256:sha(bytes)});
32
+ assert.equal(accepted.sha256,sha(bytes)); assert.deepEqual(accepted.policy,policy);
33
+ await assert.rejects(prepareHostPolicyBootstrap({path:file,sha256:hex('f')}),/host policy.*SHA-256 mismatch/i);
34
+ const bad=path.join(root,'bad.json');
35
+ const badBytes=Buffer.from(JSON.stringify({...policy,native_exec:{...policy.native_exec,enabled:false}})+'\n','utf8');
36
+ await writeFile(bad,badBytes);
37
+ await assert.rejects(prepareHostPolicyBootstrap({path:bad,sha256:sha(await readFile(bad))}),/native_exec contract/i);
38
+ });
39
+
40
+ test('R45 updater threads the bootstrapped Host v2 policy into successor generation',async()=>{
41
+ const source=await readFile(new URL('../src/runtime-update.mjs',import.meta.url),'utf8');
42
+ assert.match(source,/prepareHostPolicyBootstrap\(hostPolicyBootstrap\)/);
43
+ assert.match(source,/control\.hostPolicy=hostBootstrap\.policy/);
44
+ assert.match(source,/hostPolicy:control\.hostPolicy/);
45
+ assert.match(source,/hostPolicyBootstrap:args\.hostPolicyBootstrap/);
46
+ });