deadbyte-mcp 0.15.1 → 0.15.3

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 (96) hide show
  1. package/MANIFEST.SHA256 +95 -64
  2. package/PROVENANCE.json +6 -5
  3. package/README.txt +36 -20
  4. package/RELEASE.json +24 -8
  5. package/SBOM.cdx.json +3 -3
  6. package/controller/README.TXT +2 -2
  7. package/controller/deadbyte-controller.ps1 +50 -2
  8. package/controller/deadbyte-process-policy.json +63 -1
  9. package/docs/ARCHITECTURE.md +3 -4
  10. package/package.json +88 -86
  11. package/scripts/autonomous-build.ps1 +1 -0
  12. package/scripts/autonomous-tests.ps1 +1 -0
  13. package/scripts/check-agent-backend-r46.mjs +77 -0
  14. package/scripts/check-atomic-resource-r46.mjs +84 -0
  15. package/scripts/check-autonomous-race-r46.mjs +160 -0
  16. package/scripts/check-coding-task-deadline-r46.mjs +59 -0
  17. package/scripts/check-deadline-cancellation-r46.mjs +49 -0
  18. package/scripts/check-process-deadline-r46.mjs +52 -0
  19. package/scripts/check-r46-background.mjs +5 -0
  20. package/scripts/deferred-slot-operation-r46.mjs +126 -0
  21. package/scripts/extract-r46-failures.mjs +5 -0
  22. package/scripts/final-closure-r46.mjs +98 -0
  23. package/scripts/final-closure-verify-r46.mjs +119 -0
  24. package/scripts/full-regression-r46.mjs +28 -0
  25. package/scripts/gate-windows-r46.ps1 +58 -0
  26. package/scripts/inspect-r46-active.mjs +2 -0
  27. package/scripts/r47-core-failures.mjs +1 -0
  28. package/scripts/refresh-process-policy.mjs +64 -7
  29. package/scripts/release-manifest.mjs +1 -1
  30. package/scripts/release-parity-r46.mjs +187 -0
  31. package/scripts/run-r46-full-regression-background.mjs +7 -0
  32. package/scripts/run-r46-windows-gate-background.mjs +6 -0
  33. package/scripts/run-r46-windows-gate-background.ps1 +10 -0
  34. package/scripts/windows-gate-evidence-r46.mjs +111 -0
  35. package/scripts/workload-contract-regression-r47.mjs +14 -0
  36. package/scripts/workload-stream-regression.mjs +16 -0
  37. package/scripts/workload-substrate-r47-regression.mjs +12 -0
  38. package/src/agent-backend-r44.mjs +75 -10
  39. package/src/atomic-mutation.mjs +55 -0
  40. package/src/autonomous-mcp-tools.mjs +4 -3
  41. package/src/autonomous-output-contracts.mjs +16 -3
  42. package/src/autonomous-runtime.mjs +83 -30
  43. package/src/capability-ledger.mjs +1 -0
  44. package/src/coding-mcp-tools.mjs +2 -2
  45. package/src/coding-output-contracts.mjs +18 -4
  46. package/src/coding-plane.mjs +3 -24
  47. package/src/coding-search.mjs +11 -2
  48. package/src/coding-task-runtime.mjs +41 -6
  49. package/src/compact-gateway-mcp-tools.mjs +7 -5
  50. package/src/deadbyte-cli.mjs +7 -3
  51. package/src/deadline-cancellation.mjs +83 -0
  52. package/src/machine-fs-mcp-tools.mjs +3 -3
  53. package/src/machine-fs-output-contracts.mjs +1 -1
  54. package/src/machine-fs-read-worker.mjs +46 -0
  55. package/src/machine-fs-smoke-client.mjs +1 -1
  56. package/src/machine-fs.mjs +66 -33
  57. package/src/mcp-server.mjs +10 -0
  58. package/src/mcp-smoke-client.mjs +4 -1
  59. package/src/observation-runtime.mjs +25 -4
  60. package/src/observation-smoke-client.mjs +1 -1
  61. package/src/process-mcp-tools.mjs +29 -8
  62. package/src/process-output-contracts.mjs +43 -3
  63. package/src/process-policy.mjs +3 -1
  64. package/src/process-runtime.mjs +152 -23
  65. package/src/process-smoke-client.mjs +67 -22
  66. package/src/release-generation.mjs +22 -3
  67. package/src/remote-device-r44.mjs +53 -15
  68. package/src/remote-mcp-smoke-client.mjs +1 -1
  69. package/src/resource-identity.mjs +59 -0
  70. package/src/runtime-update.mjs +526 -162
  71. package/src/stdio-console-guard.mjs +24 -0
  72. package/src/version.mjs +1 -1
  73. package/src/workload-substrate.mjs +87 -0
  74. package/test/cli-entrypoint.test.mjs +3 -3
  75. package/test/coding-runtime.test.mjs +12 -0
  76. package/test/coding-search-r36.test.mjs +7 -0
  77. package/test/compact-gateway-r36.test.mjs +28 -0
  78. package/test/core.test.mjs +1 -1
  79. package/test/full-max-catalog-r46.test.mjs +92 -0
  80. package/test/helpers/autonomous-r34-fixture.mjs +10 -1
  81. package/test/machine-fs-mcp-tools.test.mjs +4 -1
  82. package/test/machine-fs-runtime.test.mjs +18 -2
  83. package/test/observation-runtime.test.mjs +29 -2
  84. package/test/process-mcp-tools.test.mjs +27 -6
  85. package/test/process-policy-refresh.test.mjs +87 -10
  86. package/test/process-runtime.test.mjs +129 -2
  87. package/test/process-supervision.test.mjs +4 -2
  88. package/test/production-docs.test.mjs +3 -3
  89. package/test/r33-finalization.test.mjs +3 -3
  90. package/test/r45-finalization.test.mjs +1 -1
  91. package/test/release-generation.test.mjs +25 -11
  92. package/test/release-version.test.mjs +99 -44
  93. package/test/remote-device-r44.test.mjs +75 -0
  94. package/test/remote-r24-catalog.test.mjs +2 -2
  95. package/test/runtime-self-update.test.mjs +118 -1
  96. package/test/workload-substrate.test.mjs +22 -0
@@ -5,54 +5,113 @@ import { createHash } from 'node:crypto';
5
5
 
6
6
  const root=new URL('../',import.meta.url);
7
7
  const text=rel=>readFile(new URL(rel,root),'utf8');
8
- const PARENT_RELEASE='v0.15.0-c15fa41719be61b3';
9
- const PARENT_MANIFEST='c15fa41719be61b3186e49b2367a5f4c057365cfb465102dbefb7d7355f45483';
10
- const PARENT_GENERATION='6d7429c050b7fe893492e102f00282019116e0c9e54e4f25a5791defa1661d30';
11
- const PARENT_CLOSURE='533b7e0ff9ded04086dd90ed34e3b87bc427acc3c55c25bb9f4ec66cb448e667';
8
+ const PARENT_RELEASE='v0.15.1-d55f00b5144dc31a';
9
+ const PARENT_MANIFEST='d55f00b5144dc31a7fe39e4dc60d640a94780da6aaeca0918e87636b816d7854';
10
+ const PARENT_GENERATION='00cde7bf540cc2804d679a39040d5b07193c5f8a0d49cac3b882402f8f4e6ad8';
12
11
 
13
- test('R45 current authority agrees on V0.15.1 and preserves exact sealed R44 parent',async()=>{
12
+ test('R46 current authority agrees on V0.15.2 and preserves exact R45 parent identity',async()=>{
14
13
  const pkg=JSON.parse(await text('package.json'));
15
- const [version,controller,gate,readme,architecture,controllerReadme,deferred,parity,closure,closureVerify,windowsGate,parent,r44Gate,r44Parity,r44Closure]=await Promise.all([
16
- text('src/version.mjs'),text('controller/deadbyte-controller.ps1'),text('scripts/gate-windows-r45.ps1'),text('README.txt'),
17
- text('docs/ARCHITECTURE.md'),text('controller/README.TXT'),text('scripts/deferred-slot-operation-r45.mjs'),
18
- text('scripts/release-parity-r45.mjs'),text('scripts/final-closure-r45.mjs'),text('scripts/final-closure-verify-r45.mjs'),
19
- text('scripts/windows-gate-evidence-r45.mjs'),text('R45-PARENT.json'),
20
- text('scripts/gate-windows-r44.ps1'),text('scripts/release-parity-r44.mjs'),text('scripts/final-closure-r44.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')
28
+ ]);
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:84/);
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');
86
+ });
87
+
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')
21
97
  ]);
22
- assert.equal(pkg.version,'0.15.1');
23
- assert.equal(pkg.scripts['gate:windows'],'powershell -NoProfile -ExecutionPolicy Bypass -File scripts/gate-windows-r45.ps1');
24
- assert.equal(pkg.scripts['r45:parent:verify'],'node scripts/verify-r45-parent.mjs');
25
- assert.equal(pkg.scripts['r45:full:test'],'node scripts/full-regression-r45.mjs');
26
- assert.equal(pkg.scripts['self-update:deferred'],'node scripts/deferred-slot-operation-r45.mjs');
27
- assert.equal(pkg.scripts['release:parity'],'node scripts/release-parity-r45.mjs');
28
- assert.equal(pkg.scripts['release:final:write'],'node scripts/final-closure-r45.mjs');
29
- assert.equal(pkg.scripts['release:final:verify'],'node scripts/final-closure-verify-r45.mjs');
30
- assert.equal(pkg.scripts['finalize:r45'],'node scripts/finalize-r45.mjs');
31
- assert.match(version,/DEADBYTE_VERSION = '0\.15\.1'/);
32
- assert.match(controller,/CONTROLLER V0\.15\.1 \/ R45/);
33
98
  assert.match(gate,/DEADBYTE V0\.15\.1 \/ R45 MACHINE FREEDOM WINDOWS BATCH GATE: PASS/);
34
- assert.match(readme,/^DEADBYTE MCP V0\.15\.1 \/ R45 MACHINE FREEDOM FINAL BASELINE$/m);
35
- assert.match(architecture,/^# DEADBYTE MCP V0\.15\.1 \/ R45 ARCHITECTURE$/m);
36
- assert.match(controllerReadme,/^DEADBYTE MCP V0\.15\.1 \/ R45 CONTROLLER$/m);
37
99
  assert.match(deferred,/--expected-version','0\.15\.1'/);
38
- assert.match(parity,/R45_PREDECESSOR_RELEASE_ID='v0\.15\.0-c15fa41719be61b3'/);
39
- assert.match(parity,new RegExp(PARENT_MANIFEST));
40
- assert.match(parity,/tool_count===82/);assert.match(parity,/tool_count===43/);
41
- assert.match(closure,/version:'0\.15\.1'/);assert.match(closure,/DEADBYTE-R45-FINAL-CLOSURE-V1/);
42
- assert.match(closureVerify,/R45_FULL_TOOL_COUNT=82/);assert.match(closureVerify,/R45_COMPACT_TOOL_COUNT=43/);
43
- assert.match(windowsGate,/deadbyte\.r45-windows-gate\.v1/);
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'/);
44
105
  const parentRecord=JSON.parse(parent);
45
- assert.equal(parentRecord.parent_release_id,PARENT_RELEASE);
46
- assert.equal(parentRecord.parent_manifest_sha256,PARENT_MANIFEST);
47
- assert.equal(parentRecord.parent_generation_manifest_sha256,PARENT_GENERATION);
48
- assert.equal(parentRecord.parent_final_closure_sha256,PARENT_CLOSURE);
49
- assert.match(r44Gate,/DEADBYTE V0\.15\.0 \/ R44 REMOTE AGENT SKILL WINDOWS BATCH GATE: PASS/);
50
- assert.match(r44Parity,/deadbyte\.r44-parity\.v1/);
51
- assert.match(r44Closure,/version:'0\.15\.0'/);
106
+ assert.equal(parentRecord.parent_release_id,'v0.15.0-c15fa41719be61b3');
52
107
  });
53
108
 
54
- test('R45 keeps ONLINE reuse and explicit Desktop authority',async()=>{
55
- const [cli,controller]=await Promise.all([text('src/deadbyte-cli.mjs'),text('controller/deadbyte-controller.ps1')]);
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
+ ]);
56
115
  assert.match(cli,/controllerRuntimeReady/);
57
116
  assert.match(cli,/Runtime already ONLINE; reusing verified bridge\/cloud\/supervisor/);
58
117
  const start=controller.indexOf('function Recover-AutonomousSupervisor'),end=controller.indexOf('function Show-State',start);
@@ -62,9 +121,5 @@ test('R45 keeps ONLINE reuse and explicit Desktop authority',async()=>{
62
121
  assert.match(block,/\$hostStatus\s*=\s*Get-HostArmStatus/);
63
122
  assert.match(controller,/armdesktop/);
64
123
  assert.match(controller,/disarmdesktop/);
65
- });
66
-
67
- test('semantic config remains byte-pinned across R45 machine-freedom work',async()=>{
68
- const semantic=await text('controller/deadbyte-semantic-lsp-config.json');
69
124
  assert.equal(createHash('sha256').update(Buffer.from(semantic,'utf8')).digest('hex'),'d274b099a298c1033fea8f1e8849751693bbc0448778d619400acb82d4f53a3b');
70
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\]\)/);
@@ -4,6 +4,7 @@ import { mkdtemp, mkdir, readFile, writeFile } from 'node:fs/promises';
4
4
  import os from 'node:os';
5
5
  import path from 'node:path';
6
6
  import { createHash } from 'node:crypto';
7
+ import { spawn } from 'node:child_process';
7
8
  import {
8
9
  assertDevelopmentRootSafe,
9
10
  legacyContext,
@@ -213,11 +214,105 @@ test('R28 updater requires a stabilization window and public MCP smoke before co
213
214
  assert.match(source,/McpUrlFileSha256\s*=\s*stableMcpUrl\.sha256/);
214
215
  assert.match(source,/mcp_url_restored/);
215
216
  const stableIndex=source.indexOf('stabilizeRuntime');
216
- const receiptIndex=source.indexOf('const body={schema:UPDATE_SCHEMA');
217
+ const receiptIndex=source.indexOf('const bodyReceipt={');
217
218
  assert.ok(stableIndex>=0&&receiptIndex>stableIndex,'stability/public gate must happen before update receipt');
218
219
  });
219
220
 
220
221
 
222
+
223
+ test('R47 runtime updater arms a detached supervisor before any destructive stop', async () => {
224
+ const source=await readFile(new URL('../src/runtime-update.mjs',import.meta.url),'utf8');
225
+ const start=source.indexOf('export async function performSlotUpdate');
226
+ const end=source.indexOf('export async function performSlotRollback',start);
227
+ assert.ok(start>=0&&end>start);
228
+ const perform=source.slice(start,end);
229
+ assert.match(source,/const HANDOFF_SCHEMA='deadbyte\.r27-slot-update-handoff\.v1'/);
230
+ assert.match(source,/shutdown_handoff_armed/);
231
+ assert.match(source,/detached:true/);
232
+ assert.match(source,/proc\.unref\(\)/);
233
+ const handoffIndex=perform.indexOf('const handoff=await writeHandoff');
234
+ const spawnIndex=perform.indexOf('await spawnDetachedSupervisor',handoffIndex);
235
+ const returnIndex=perform.indexOf('return {ok:true,handed_off:true',spawnIndex);
236
+ assert.ok(handoffIndex>=0&&spawnIndex>handoffIndex&&returnIndex>spawnIndex);
237
+ assert.equal(perform.indexOf("controllerCaptured(control.ctx,'stop'"),-1);
238
+ assert.equal(perform.indexOf('await revokeAuthority(control.ctx'),-1);
239
+ assert.equal(perform.indexOf("writeState(statePath,journal,'supervisor_spawned'"),-1);
240
+ });
241
+
242
+ test('R47 supervisor resume parser is exact and recovery scans only durable update state', () => {
243
+ assert.deepEqual(runtimeUpdate.parseResumeHandoffArgs(['--resume-handoff','C:/state/evidence/updates/u/handoff.json']),{
244
+ handoff:path.resolve('C:/state/evidence/updates/u/handoff.json')
245
+ });
246
+ assert.throws(()=>runtimeUpdate.parseResumeHandoffArgs([]),/resume-handoff is required/i);
247
+ assert.throws(()=>runtimeUpdate.parseResumeHandoffArgs(['--resume-handoff','x','--bad']),/unknown supervisor argument/i);
248
+ });
249
+
250
+ test('R47 interrupted transition has explicit durable recovery phases', async () => {
251
+ const source=await readFile(new URL('../src/runtime-update.mjs',import.meta.url),'utf8');
252
+ for(const phase of ['shutdown_handoff_armed','supervisor_spawned','authority_revoked','old_runtime_stopped','workspace_ready','launcher_installed','pointer_committed','fresh_runtime_verified','authority_verified','completed']){
253
+ assert.match(source,new RegExp(phase));
254
+ }
255
+ assert.match(source,/recoverInterruptedSlotUpdates/);
256
+ assert.match(source,/phaseRank\(phase\)<phaseRank\('authority_revoked'\)/);
257
+ assert.match(source,/recovery observed predecessor already OFFLINE/);
258
+ });
259
+
260
+
261
+ test('R47 MCP core startup invokes durable interrupted-update recovery before serving', async () => {
262
+ const source=await readFile(new URL('../src/mcp-server.mjs',import.meta.url),'utf8');
263
+ assert.match(source,/import \{ recoverInterruptedSlotUpdates \} from '\.\/runtime-update\.mjs';/);
264
+ assert.match(source,/await recoverInterruptedSlotUpdates\(\{ stateRoot: runtimeStateRoot \}\)/);
265
+ const recoveryIndex=source.indexOf('await recoverInterruptedSlotUpdates');
266
+ const serveIndex=source.indexOf('serveStdio(');
267
+ assert.ok(recoveryIndex>=0&&serveIndex>recoveryIndex,'startup recovery must run before MCP serving starts');
268
+ });
269
+
270
+
271
+ test('R47 Windows detached supervisor survives termination of its parent process', { skip: process.platform !== 'win32' }, async () => {
272
+ const root=await mkdtemp(path.join(os.tmpdir(),'deadbyte-r47-detached-survival-'));
273
+ const parentPath=path.join(root,'parent.mjs');
274
+ const survivorPath=path.join(root,'survivor.mjs');
275
+ const pidPath=path.join(root,'survivor.pid');
276
+ await writeFile(survivorPath,[
277
+ "import { writeFile } from 'node:fs/promises';",
278
+ "const pidPath=process.argv[2];",
279
+ "await writeFile(pidPath,String(process.pid),'ascii');",
280
+ "setInterval(()=>{},1000);"
281
+ ].join('\n'));
282
+ await writeFile(parentPath,[
283
+ "import { spawn } from 'node:child_process';",
284
+ "const survivor=spawn(process.execPath,[process.argv[2],process.argv[3]],{detached:true,windowsHide:true,stdio:'ignore'});",
285
+ "survivor.unref();",
286
+ "setInterval(()=>{},1000);"
287
+ ].join('\n'));
288
+ const parent=spawn(process.execPath,[parentPath,survivorPath,pidPath],{windowsHide:true,stdio:'ignore'});
289
+ try{
290
+ const deadline=Date.now()+5000;
291
+ let survivorPid=0;
292
+ while(Date.now()<deadline){
293
+ try{
294
+ survivorPid=Number((await readFile(pidPath,'ascii')).trim());
295
+ if(Number.isInteger(survivorPid)&&survivorPid>0) break;
296
+ }catch{}
297
+ await new Promise(resolve=>setTimeout(resolve,50));
298
+ }
299
+ assert.ok(survivorPid>0,'detached survivor did not report PID');
300
+ parent.kill('SIGKILL');
301
+ await new Promise((resolve,reject)=>{
302
+ const timer=setTimeout(()=>reject(new Error('parent did not terminate after SIGKILL')),5000);
303
+ parent.once('exit',()=>{clearTimeout(timer);resolve();});
304
+ parent.once('error',error=>{clearTimeout(timer);reject(error);});
305
+ });
306
+ await new Promise(resolve=>setTimeout(resolve,350));
307
+ assert.doesNotThrow(()=>process.kill(survivorPid,0),'detached supervisor was terminated with its parent');
308
+ } finally {
309
+ const textPid=(await readFile(pidPath,'ascii').catch(()=>'' )).trim();
310
+ const cleanupPid=Number(textPid);
311
+ if(Number.isInteger(cleanupPid)&&cleanupPid>0){try{process.kill(cleanupPid,'SIGKILL');}catch{}}
312
+ try{process.kill(parent.pid,'SIGKILL');}catch{}
313
+ }
314
+ });
315
+
221
316
  test('R29 slot-update CLI requires explicit hash-bound host repins and offline recovery opt-in', () => {
222
317
  const base=['--stage','stage','--development-root','dev','--expected-version','0.8.5','--expected-manifest',hex('a')];
223
318
  const parsed=parseUpdateArgs([...base,'--host-command-pin',`powershell_script=${hex('b')}`,'--host-command-pin',`cmd_script=${hex('c')}`,'--recover-offline-predecessor']);
@@ -286,6 +381,15 @@ test('R31 slot-update CLI binds process-policy bootstrap and machine-FS enable e
286
381
  assert.equal(parsed.enableMachineFs,true);
287
382
  });
288
383
 
384
+ test('R46 slot-update CLI binds coding policy bootstrap as one exact pair', () => {
385
+ const base=['--stage','stage','--development-root','dev','--expected-version','0.15.2','--expected-manifest',hex('a')];
386
+ assert.throws(()=>parseUpdateArgs([...base,'--coding-policy','C:/candidate/coding.json']),/coding policy.*pair/i);
387
+ assert.throws(()=>parseUpdateArgs([...base,'--coding-policy-sha256',hex('b')]),/coding policy.*pair/i);
388
+ const parsed=parseUpdateArgs([...base,'--coding-policy','C:/candidate/coding.json','--coding-policy-sha256',hex('b')]);
389
+ assert.equal(parsed.codingPolicyBootstrap.path,path.resolve('C:/candidate/coding.json'));
390
+ assert.equal(parsed.codingPolicyBootstrap.sha256,hex('b'));
391
+ });
392
+
289
393
  test('R40 slot-update CLI binds Desktop policy as one exact pair and enables runtime only explicitly', () => {
290
394
  const base=['--stage','stage','--development-root','dev','--expected-version','0.12.0','--expected-manifest',hex('a')];
291
395
  assert.throws(()=>parseUpdateArgs([...base,'--desktop-policy','C:/candidate/desktop.json']),/desktop policy.*pair/i);
@@ -299,6 +403,19 @@ test('R40 slot-update CLI binds Desktop policy as one exact pair and enables run
299
403
  assert.throws(()=>parseUpdateArgs([...base,'--enable-desktop','--enable-desktop']),/duplicate self-update argument/i);
300
404
  });
301
405
 
406
+ test('R46 coding policy bootstrap accepts only exact pinned enabled policy bytes',async()=>{
407
+ const root=await mkdtemp(path.join(os.tmpdir(),'deadbyte-r46-coding-bootstrap-'));
408
+ const file=path.join(root,'coding.json');
409
+ const policy={schema:'deadbyte.coding-policy.v1',enabled:true,lease_file:'C:/old/grant.json',evidence_dir:'C:/old/evidence',roots:{core:{path:'C:/core',read:true,write:true,exec:true}},commands:{powershell_script:{executable:'C:/Windows/System32/WindowsPowerShell/v1.0/powershell.exe',executable_sha256:hex('1')}}};
410
+ const bytes=Buffer.from(JSON.stringify(policy)+'\n','utf8');await writeFile(file,bytes);
411
+ assert.equal(typeof runtimeUpdate.prepareCodingPolicyBootstrap,'function');
412
+ const accepted=await runtimeUpdate.prepareCodingPolicyBootstrap({path:file,sha256:sha(bytes)});
413
+ assert.equal(accepted.sha256,sha(bytes));assert.deepEqual(accepted.policy,policy);
414
+ await assert.rejects(runtimeUpdate.prepareCodingPolicyBootstrap({path:file,sha256:hex('f')}),/coding policy.*SHA-256 mismatch/i);
415
+ const bad=path.join(root,'bad.json');await writeFile(bad,JSON.stringify({...policy,enabled:false}));
416
+ assert.rejects(runtimeUpdate.prepareCodingPolicyBootstrap({path:bad,sha256:sha(await readFile(bad))}),/coding policy schema/i);
417
+ });
418
+
302
419
  test('R40 Desktop policy bootstrap accepts only exact pinned enabled policy bytes',async()=>{
303
420
  const root=await mkdtemp(path.join(os.tmpdir(),'deadbyte-r40-desktop-bootstrap-'));
304
421
  const file=path.join(root,'desktop.json');
@@ -0,0 +1,22 @@
1
+ import assert from 'node:assert/strict';
2
+ import { createSequenceStream, assertSequenceStreamSnapshot } from '../src/workload-substrate.mjs';
3
+
4
+ const stream=createSequenceStream({streamId:'test',maxItems:2,maxBytes:1_000_000});
5
+ const a=stream.append({text:'a'},{producer:'p',channel:'stdout'});
6
+ const snap=stream.snapshot();
7
+ const b=stream.append({text:'b'},{producer:'p',channel:'stdout'});
8
+ assert.equal(stream.delta(snap).items[0].seq,b.seq);
9
+ stream.append({text:'c'},{producer:'p',channel:'stderr'});
10
+ const coverage=stream.coverage();
11
+ assert.equal(stream.snapshot().next_seq,4);
12
+ assert.equal(coverage.logical_item_count,3);
13
+ assert.equal(coverage.retained_item_count,2);
14
+ assert.equal(coverage.truncated,true);
15
+ assert.equal(coverage.complete,false);
16
+ assert.equal(stream.read(1,10).missing,true);
17
+ assert.equal(stream.read(2,10).items.length,2);
18
+ assert.equal(coverage.missing_ranges[0].from,1);
19
+ assert.equal(assertSequenceStreamSnapshot(snap).stream_id,'test');
20
+ stream.close();
21
+ assert.throws(()=>stream.append({text:'d'}),/not open/);
22
+ console.log(JSON.stringify({status:'pass',stream_id:stream.streamId,logical_last_seq:coverage.logical_last_seq,retained_first_seq:coverage.retained_first_seq,truncated:coverage.truncated}));