deadbyte-mcp 0.15.39 → 0.15.42

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 (100) hide show
  1. package/DEADBYTE-MCP.cmd +8 -4
  2. package/MANIFEST.SHA256 +99 -45
  3. package/PROVENANCE.json +90 -20
  4. package/README.txt +39 -18
  5. package/RELEASE.json +85 -20
  6. package/SBOM.cdx.json +2 -2
  7. package/TEST-MATRIX.json +134 -34
  8. package/bridge/package.json +1 -1
  9. package/controller/deadbyte-controller.ps1 +132 -12
  10. package/controller/deadbyte-process-policy.json +4 -4
  11. package/docs/R64-EVIDENCE.json +95 -0
  12. package/docs/R64-REFERENCE-GAP-MATRIX.json +98 -0
  13. package/docs/R64-SCOPE.json +41 -0
  14. package/docs/R65-AUTHORITY-CONTINUITY-DESIGN.json +27 -0
  15. package/docs/R65-REFERENCE-GAP-MATRIX.json +22 -0
  16. package/package.json +7 -4
  17. package/scripts/deploy-r48.mjs +123 -7
  18. package/scripts/gate-windows-r63-prepublish.ps1 +45 -0
  19. package/scripts/gate-windows-r64-prepublish.ps1 +68 -0
  20. package/scripts/gate-windows-r65-prepublish.ps1 +65 -0
  21. package/scripts/npm-publication-r65.mjs +139 -0
  22. package/scripts/npm-release-r48.mjs +62 -37
  23. package/scripts/r64-predecessor-reproducer.mjs +32 -0
  24. package/scripts/r64-read-benchmark.mjs +84 -0
  25. package/scripts/r65-canonical-full.mjs +45 -0
  26. package/scripts/r65-focused.mjs +37 -0
  27. package/scripts/verify-r65-parent-source.mjs +75 -0
  28. package/src/activation-continuity-engine-r65.mjs +225 -0
  29. package/src/activation-lock-r65.mjs +155 -0
  30. package/src/activation-preflight-r65.mjs +197 -0
  31. package/src/authority-continuity-r65.mjs +541 -0
  32. package/src/authority-epoch-cli-r65.mjs +10 -0
  33. package/src/autonomous-policy.mjs +7 -3
  34. package/src/coding-plane.mjs +12 -3
  35. package/src/compact-gateway-mcp-tools.mjs +1 -1
  36. package/src/deadbyte-cli.mjs +16 -2
  37. package/src/desktop-policy-r40.mjs +6 -3
  38. package/src/doctor-transactions-r58.mjs +1 -1
  39. package/src/host-plane.mjs +9 -3
  40. package/src/machine-fs-mcp-tools.mjs +1 -1
  41. package/src/machine-fs.mjs +7 -4
  42. package/src/process-policy.mjs +7 -4
  43. package/src/remote-authority.mjs +20 -1
  44. package/src/runtime-update.mjs +15 -5
  45. package/src/version.mjs +1 -1
  46. package/test/audit-context.test.mjs +15 -4
  47. package/test/autonomous-local-supervisor.test.mjs +7 -6
  48. package/test/autonomous-supervisor-integration.test.mjs +8 -7
  49. package/test/capability-resolver.test.mjs +11 -5
  50. package/test/codex-autonomous-r44.test.mjs +7 -6
  51. package/test/coding-observation.test.mjs +4 -3
  52. package/test/commander-real-parity-r47.test.mjs +5 -4
  53. package/test/commander-torture-r47.test.mjs +5 -4
  54. package/test/desktop-policy-r40.test.mjs +6 -4
  55. package/test/desktop-runtime-r40.test.mjs +8 -3
  56. package/test/host-inline-r45.test.mjs +4 -3
  57. package/test/machine-fs-runtime.test.mjs +4 -3
  58. package/test/process-longrun-r36.test.mjs +3 -2
  59. package/test/process-policy.test.mjs +10 -6
  60. package/test/process-runtime.test.mjs +9 -6
  61. package/test/r52-host-revocation.test.mjs +1 -0
  62. package/test/r59-tool-surface-compat.test.mjs +14 -4
  63. package/test/r62-release-contract.test.mjs +2 -1
  64. package/test/r63-one-shot-coherence.test.mjs +38 -0
  65. package/test/r63-release-contract.test.mjs +59 -0
  66. package/test/r64-machine-fs-large-read.test.mjs +136 -0
  67. package/test/r64-release-contract.test.mjs +52 -0
  68. package/test/r65-activation-lock-recovery.test.mjs +72 -0
  69. package/test/r65-activation-lock.test.mjs +59 -0
  70. package/test/r65-activation-orchestration.test.mjs +179 -0
  71. package/test/r65-activation-preflight.test.mjs +35 -0
  72. package/test/r65-authority-continuity-decision.test.mjs +58 -0
  73. package/test/r65-authority-durable-policy-binding.test.mjs +34 -0
  74. package/test/r65-authority-epoch-cli.test.mjs +125 -0
  75. package/test/r65-authority-epoch-races.test.mjs +87 -0
  76. package/test/r65-authority-epoch-recovery.test.mjs +82 -0
  77. package/test/r65-authority-epoch-store.test.mjs +94 -0
  78. package/test/r65-authority-fixture.mjs +41 -0
  79. package/test/r65-authority-grant-capture.test.mjs +37 -0
  80. package/test/r65-authority-observation-epoch.test.mjs +25 -0
  81. package/test/r65-authority-policy-envelope.test.mjs +46 -0
  82. package/test/r65-authority-restored-safe.test.mjs +75 -0
  83. package/test/r65-authority-runtime-epoch.test.mjs +115 -0
  84. package/test/r65-authority-runtime-policy-binding.test.mjs +29 -0
  85. package/test/r65-authority-test-trust.mjs +20 -0
  86. package/test/r65-cli-invocation.test.mjs +38 -0
  87. package/test/r65-controller-authority-epoch-live.test.mjs +100 -0
  88. package/test/r65-controller-authority-epoch.test.mjs +37 -0
  89. package/test/r65-deploy-preflight.test.mjs +55 -0
  90. package/test/r65-epoch-native-activation-integration.test.mjs +141 -0
  91. package/test/r65-epoch-native-continuity-engine.test.mjs +112 -0
  92. package/test/r65-epoch-native-cross-plane.test.mjs +95 -0
  93. package/test/r65-legacy-authority-migration.test.mjs +63 -0
  94. package/test/r65-legacy-auto-restore-rejection.test.mjs +17 -0
  95. package/test/r65-npm-publication.test.mjs +114 -0
  96. package/test/r65-parent-source-verifier.test.mjs +63 -0
  97. package/test/r65-release-contract.test.mjs +74 -0
  98. package/test/r65-transaction-precondition.test.mjs +36 -0
  99. package/test/remote-session-cli.test.mjs +13 -11
  100. package/test/standing-grants.test.mjs +20 -4
@@ -0,0 +1,115 @@
1
+ import test from 'node:test';
2
+ import assert from 'node:assert/strict';
3
+ import {mkdtemp,mkdir,rm,writeFile,readFile} from 'node:fs/promises';
4
+ import {createHash} from 'node:crypto';
5
+ import os from 'node:os';
6
+ import path from 'node:path';
7
+ import {loadCodingPolicy,codingSessionStatus} from '../src/coding-plane.mjs';
8
+ import {createAuthorityEpochStore} from '../src/authority-continuity-r65.mjs';
9
+ import {prepareAuthorityTestTrust} from './r65-authority-test-trust.mjs';
10
+
11
+ test('R65 standing Coding grant is disarmed until the durable epoch binds the exact bytes',async()=>{
12
+ const root=await mkdtemp(path.join(os.tmpdir(),'deadbyte-r65-runtime-epoch-'));
13
+ try{
14
+ const runtime=await prepareAuthorityTestTrust(root);
15
+ const project=path.join(root,'project');
16
+ await mkdir(project);
17
+ const policyPath=path.join(root,'coding.json');
18
+ const grantFile=path.join(runtime,'coding-grant.json');
19
+ await writeFile(policyPath,JSON.stringify({
20
+ schema:'deadbyte.coding-policy.v1',enabled:true,
21
+ lease_file:grantFile,evidence_dir:path.join(root,'evidence'),
22
+ roots:{project:{path:project,read:true,write:true,exec:false,deny_paths:[]}},
23
+ commands:{}
24
+ }));
25
+ const policy=await loadCodingPolicy(policyPath);
26
+ const grant={schema:'deadbyte.coding-grant.v2',policy_sha256:policy.policy_sha256,
27
+ instance_nonce:'a'.repeat(64),root_ids:['project'],granted_at_utc:'2026-10-06T00:00:00Z'};
28
+ await writeFile(grantFile,JSON.stringify(grant));
29
+ const legacy=await codingSessionStatus(policy);
30
+ assert.equal(legacy.armed,false);
31
+ assert.equal(legacy.reason,'legacy-no-epoch');
32
+
33
+ const grantBytes=await readFile(grantFile);
34
+ const store=createAuthorityEpochStore({runtimeDir:path.dirname(grantFile),policyBytes:await readFile(policyPath)});
35
+ await store.append({plane:'coding',action:'grant',grantBytes,grantInstanceNonce:grant.instance_nonce,updateId:null});
36
+ const trusted=await codingSessionStatus(policy);
37
+ assert.equal(trusted.armed,true);
38
+ assert.equal(trusted.reason,'standing-grant');
39
+ assert.equal(trusted.instance_nonce,grant.instance_nonce);
40
+ }finally{
41
+ await rm(root,{recursive:true,force:true});
42
+ }
43
+ });
44
+
45
+
46
+ test('all standing authority planes reject legacy grant bytes until an R65 epoch binds them',async()=>{
47
+ const root=await mkdtemp(path.join(os.tmpdir(),'deadbyte-r65-all-epochs-'));
48
+ try{
49
+ const runtime=await prepareAuthorityTestTrust(root);
50
+ const {processSessionStatus}=await import('../src/process-policy.mjs');
51
+ const {autonomousSessionStatus}=await import('../src/autonomous-policy.mjs');
52
+ const {desktopSessionStatus}=await import('../src/desktop-policy-r40.mjs');
53
+ const {assertHostArmed}=await import('../src/host-plane.mjs');
54
+ const project=path.join(root,'project');
55
+ await mkdir(project);
56
+ const cases=[
57
+ {
58
+ plane:'process',file:path.join(runtime,'process-grant.json'),
59
+ grant:{schema:'deadbyte.process-grant.v1',instance_nonce:'a'.repeat(64),profile_ids:['ping'],root_ids:['core'],granted_at_utc:'2026-10-06T00:00:00Z'},
60
+ status:async policy=>processSessionStatus(policy),
61
+ policy:{schema:'deadbyte.process-policy.v1',enabled:true,grant_file:path.join(runtime,'process-grant.json'),state_dir:runtime,
62
+ roots:{core:{path:project,deny_paths:[]}},profiles:{ping:{executable:process.execPath,root_ids:['core'],prefix_args:[]}}}
63
+ },
64
+ {
65
+ plane:'autonomous',file:path.join(runtime,'autonomous-grant.json'),
66
+ grant:{schema:'deadbyte.autonomous-grant.v2',coding_policy_sha256:'3'.repeat(64),instance_nonce:'b'.repeat(64),root_ids:['core'],granted_at_utc:'2026-10-06T00:00:00Z'},
67
+ status:async policy=>autonomousSessionStatus(policy),
68
+ policy:{schema:'deadbyte.autonomous-policy.v1',enabled:true,lease_file:path.join(runtime,'autonomous-grant.json'),evidence_dir:path.join(root,'evidence'),
69
+ coding_policy_sha256:'3'.repeat(64),root_ids:['core'],planner:{backend:'client_sampling'},limits:{max_iterations:4,max_mutations:4},profiles:{}}
70
+ },
71
+ {
72
+ plane:'desktop',file:path.join(runtime,'desktop-grant.json'),
73
+ grant:{schema:'deadbyte.desktop-grant.v1',instance_nonce:'c'.repeat(64),action_kinds:['click_primary'],granted_at_utc:'2026-10-06T00:00:00Z'},
74
+ status:async policy=>desktopSessionStatus(policy),
75
+ policy:{schema:'deadbyte.desktop-policy.v1',enabled:true,grant_file:path.join(runtime,'desktop-grant.json'),allowed_actions:['click_primary']}
76
+ }
77
+ ];
78
+ for(const item of cases){
79
+ const policyPath=path.join(root,`${item.plane}.json`);
80
+ const policyBytes=Buffer.from(JSON.stringify(item.policy));
81
+ await writeFile(policyPath,policyBytes);
82
+ item.policy.policy_path=policyPath;
83
+ item.policy.policy_sha256=createHash('sha256').update(policyBytes).digest('hex');
84
+ item.grant.policy_sha256=item.policy.policy_sha256;
85
+ const bytes=Buffer.from(JSON.stringify(item.grant));
86
+ await writeFile(item.file,bytes);
87
+ const legacy=await item.status(item.policy);
88
+ assert.equal(legacy.armed,false,item.plane);
89
+ assert.equal(legacy.reason,'legacy-no-epoch',item.plane);
90
+ await createAuthorityEpochStore({runtimeDir:runtime,policyBytes}).append({
91
+ plane:item.plane,action:'grant',grantBytes:bytes,grantInstanceNonce:item.grant.instance_nonce,updateId:null
92
+ });
93
+ const trusted=await item.status(item.policy);
94
+ assert.equal(trusted.armed,true,item.plane);
95
+ }
96
+
97
+ const hostFile=path.join(runtime,'host-grant.json');
98
+ const hostPolicyPath=path.join(root,'host.json');
99
+ const hostPolicyRaw={schema:'deadbyte.host-policy.v1',enabled:true,arm_file:hostFile,roots:{core:{path:project,read:true,write:false,exec:false}},commands:{}};
100
+ const hostPolicyBytes=Buffer.from(JSON.stringify(hostPolicyRaw));
101
+ await writeFile(hostPolicyPath,hostPolicyBytes);
102
+ const hostPolicySha=createHash('sha256').update(hostPolicyBytes).digest('hex');
103
+ const hostGrant={schema:'deadbyte.host-grant.v2',policy_sha256:hostPolicySha,instance_nonce:'d'.repeat(64),granted_at_utc:'2026-10-06T00:00:00Z'};
104
+ const hostBytes=Buffer.from(JSON.stringify(hostGrant));
105
+ await writeFile(hostFile,hostBytes);
106
+ const hostPolicy={policy_sha256:hostPolicySha,policy_path:hostPolicyPath,arm_file:hostFile};
107
+ await assert.rejects(()=>assertHostArmed(hostPolicy),/legacy-no-epoch/);
108
+ await createAuthorityEpochStore({runtimeDir:runtime,policyBytes:hostPolicyBytes}).append({
109
+ plane:'host',action:'grant',grantBytes:hostBytes,grantInstanceNonce:hostGrant.instance_nonce,updateId:null
110
+ });
111
+ assert.equal((await assertHostArmed(hostPolicy)).standing,true);
112
+ }finally{
113
+ await rm(root,{recursive:true,force:true});
114
+ }
115
+ });
@@ -0,0 +1,29 @@
1
+ import test from 'node:test';
2
+ import assert from 'node:assert/strict';
3
+ import {mkdtemp,mkdir,rm,writeFile,readFile} from 'node:fs/promises';
4
+ import os from 'node:os';
5
+ import path from 'node:path';
6
+ import {loadCodingPolicy,codingSessionStatus} from '../src/coding-plane.mjs';
7
+ import {createAuthorityEpochStore} from '../src/authority-continuity-r65.mjs';
8
+ import {prepareAuthorityTestTrust} from './r65-authority-test-trust.mjs';
9
+
10
+ test('runtime refuses a signed grant epoch lacking exact policy security binding',async()=>{
11
+ const root=await mkdtemp(path.join(os.tmpdir(),'deadbyte-r65-policy-unbound-'));
12
+ try{
13
+ const runtime=await prepareAuthorityTestTrust(root),project=path.join(root,'project');
14
+ await mkdir(project);
15
+ const grantFile=path.join(runtime,'coding-grant.json'),policyFile=path.join(root,'coding-policy.json');
16
+ await writeFile(policyFile,JSON.stringify({schema:'deadbyte.coding-policy.v1',enabled:true,
17
+ lease_file:grantFile,evidence_dir:path.join(root,'evidence'),max_read_bytes:4096,max_write_bytes:4096,
18
+ roots:{core:{path:project,read:true,write:true,exec:false,deny_paths:[]}},commands:{}}));
19
+ const policy=await loadCodingPolicy(policyFile);
20
+ const nonce='a'.repeat(64);
21
+ await writeFile(grantFile,JSON.stringify({schema:'deadbyte.coding-grant.v2',policy_sha256:policy.policy_sha256,
22
+ instance_nonce:nonce,root_ids:['core'],granted_at_utc:'2026-10-06T00:00:00Z'}));
23
+ const grantBytes=await readFile(grantFile);
24
+ await createAuthorityEpochStore({runtimeDir:runtime}).append({plane:'coding',action:'grant',grantBytes,grantInstanceNonce:nonce,updateId:null});
25
+ const status=await codingSessionStatus(policy);
26
+ assert.equal(status.armed,false,'a signature alone must not authorize a policy-unbound epoch');
27
+ assert.match(status.reason,/epoch-policy/i);
28
+ }finally{await rm(root,{recursive:true,force:true});}
29
+ });
@@ -0,0 +1,20 @@
1
+ import {generateKeyPairSync,createPrivateKey,createPublicKey} from 'node:crypto';
2
+ import {mkdir,readFile,writeFile} from 'node:fs/promises';
3
+ import path from 'node:path';
4
+
5
+ export async function prepareAuthorityTestTrust(root){
6
+ const runtime=path.join(root,'runtime'),trust=path.join(root,'trust');
7
+ await mkdir(runtime,{recursive:true});
8
+ await mkdir(trust,{recursive:true});
9
+ const privatePath=path.join(trust,'ed25519-private.pem'),publicPath=path.join(trust,'ed25519-public.pem');
10
+ try{
11
+ const privateKey=createPrivateKey(await readFile(privatePath));
12
+ const publicKey=createPublicKey(await readFile(publicPath));
13
+ if(createPublicKey(privateKey).export({format:'der',type:'spki'}).equals(publicKey.export({format:'der',type:'spki'})))return runtime;
14
+ throw new Error('test authority trust key mismatch');
15
+ }catch(error){if(error?.code!=='ENOENT')throw error;}
16
+ const {privateKey,publicKey}=generateKeyPairSync('ed25519');
17
+ await writeFile(privatePath,privateKey.export({format:'pem',type:'pkcs8'}),{flag:'wx'});
18
+ await writeFile(publicPath,publicKey.export({format:'pem',type:'spki'}),{flag:'wx'});
19
+ return runtime;
20
+ }
@@ -0,0 +1,38 @@
1
+ import test from 'node:test';
2
+ import assert from 'node:assert/strict';
3
+ import {spawnSync} from 'node:child_process';
4
+ import {mkdtemp,rm,readFile} from 'node:fs/promises';
5
+ import os from 'node:os';
6
+ import path from 'node:path';
7
+ import {fileURLToPath} from 'node:url';
8
+
9
+ const root=path.resolve(fileURLToPath(new URL('..',import.meta.url)));
10
+
11
+ test('local source CLI remains deterministic by absolute Node entrypoint',()=>{
12
+ const run=spawnSync(process.execPath,[path.join(root,'src','deadbyte-cli.mjs'),'--version'],{cwd:root,encoding:'utf8',windowsHide:true});
13
+ assert.equal(run.status,0,run.stderr);
14
+ assert.match(run.stdout,/^\d+\.\d+\.\d+\s*$/);
15
+ });
16
+
17
+ test('repository compatibility CMD refuses to masquerade as an exact npm package bin', {skip:process.platform!=='win32'},()=>{
18
+ const run=spawnSync('cmd.exe',['/d','/c','DEADBYTE-MCP.cmd','--version'],{
19
+ cwd:root,env:{...process.env,npm_execpath:'C:\\fixture\\npm-cli.js'},encoding:'utf8',windowsHide:true,timeout:10000});
20
+ assert.equal(run.status,2);
21
+ assert.match(run.stderr,/shadows npm package bin/);
22
+ assert.doesNotMatch(run.stderr,/deadbyte-controller\.ps1/);
23
+ });
24
+
25
+ test('isolated exact local tarball invocation reaches its package bin outside the source tree', {skip:process.platform!=='win32',timeout:120000},async()=>{
26
+ const fixture=await mkdtemp(path.join(os.tmpdir(),'deadbyte-r65-cli-package-'));
27
+ try{
28
+ const npmCli=process.env.npm_execpath||path.join(path.dirname(process.execPath),'node_modules','npm','bin','npm-cli.js');
29
+ const pkg=JSON.parse(await readFile(path.join(root,'package.json'),'utf8'));
30
+ const pack=spawnSync(process.execPath,[npmCli,'pack','--json','--pack-destination',fixture],{cwd:root,encoding:'utf8',timeout:45000,windowsHide:true});
31
+ assert.equal(pack.status,0,(pack.stderr||'').slice(-1000));
32
+ const packed=JSON.parse(pack.stdout);const tarball=path.join(fixture,packed[0]?.filename??packed['deadbyte-mcp']?.filename);
33
+ const run=spawnSync(process.execPath,[npmCli,'exec','--offline','--yes',`--package=file:${tarball}`,'--','deadbyte-mcp','--version'],{
34
+ cwd:fixture,encoding:'utf8',timeout:60000,windowsHide:true});
35
+ assert.equal(run.status,0,(run.stderr||'').slice(-1000));
36
+ assert.equal(run.stdout.trim(),pkg.version);
37
+ }finally{await rm(fixture,{recursive:true,force:true});}
38
+ });
@@ -0,0 +1,100 @@
1
+ import test from 'node:test';
2
+ import assert from 'node:assert/strict';
3
+ import {mkdtemp,mkdir,readFile,readdir,rm,stat,writeFile} from 'node:fs/promises';
4
+ import {spawnSync} from 'node:child_process';
5
+ import {createHash} from 'node:crypto';
6
+ import os from 'node:os';
7
+ import path from 'node:path';
8
+ import {fileURLToPath} from 'node:url';
9
+ import {windowsPowerShellEnvironment} from '../src/runtime-update.mjs';
10
+ import {decodeUtf8Strict} from '../src/text-policy.mjs';
11
+ import {prepareAuthorityTestTrust} from './r65-authority-test-trust.mjs';
12
+
13
+ const core=path.resolve(fileURLToPath(new URL('..',import.meta.url)));
14
+ const controller=path.join(core,'controller','deadbyte-controller.ps1');
15
+ const sha=b=>createHash('sha256').update(b).digest('hex');
16
+ const quote=s=>"'"+String(s).replaceAll("'","''")+"'";
17
+
18
+ test('R65 real isolated controller arms and revokes Coding through durable epoch state', {skip:process.platform!=='win32'}, async()=>{
19
+ const root=await mkdtemp(path.join(os.tmpdir(),'deadbyte-r65-controller-epoch-live-'));
20
+ try{
21
+ const runtime=path.join(root,'runtime'),workspace=path.join(root,'workspace');
22
+ await prepareAuthorityTestTrust(root);
23
+ await mkdir(workspace,{recursive:true});
24
+
25
+ const bridgeScript=path.join(root,'bridge.mjs');
26
+ const bridgeHelper=path.join(root,'public-tool-definitions.mjs');
27
+ await writeFile(bridgeScript,'// unlaunched bridge fixture\n');
28
+ await writeFile(bridgeHelper,'// pinned helper fixture\n');
29
+ const nodeHash=sha(await readFile(process.execPath));
30
+ const bridgeSpec=`@{FilePath=${quote(process.execPath)};FileSha256='${nodeHash}';Arguments='UNLAUNCHED';WorkingDirectory=${quote(root)};ScriptPath=${quote(bridgeScript)};ScriptSha256='${sha(await readFile(bridgeScript))}';Dependencies=@(@{Name='bridge-validator';Path=${quote(bridgeHelper)};Sha256='${sha(await readFile(bridgeHelper))}'})}`;
31
+ const cloudSpec=`@{FilePath=${quote(process.execPath)};FileSha256='${nodeHash}';Arguments='UNLAUNCHED';WorkingDirectory=${quote(root)};Dependencies=@()}`;
32
+
33
+ const codingPolicy=path.join(root,'coding-policy.json');
34
+ const codingGrant=path.join(runtime,'coding-grant.json');
35
+ await writeFile(codingPolicy,JSON.stringify({
36
+ schema:'deadbyte.coding-policy.v1',enabled:true,
37
+ lease_file:codingGrant,evidence_dir:path.join(root,'evidence','coding'),
38
+ max_read_bytes:65536,max_write_bytes:65536,
39
+ roots:{fixture:{path:workspace,read:true,write:true,exec:false,deny_paths:[]}},
40
+ commands:{}
41
+ }));
42
+
43
+ const config=path.join(root,'config.psd1');
44
+ await writeFile(config,`@{Schema='deadbyte.controller.v1';CoreRoot=${quote(core)};WorkspaceRoot=${quote(workspace)};Bridge=${bridgeSpec};Cloud=${cloudSpec};MachineRuntimeEnabled=$false;AgentRuntimeEnabled=$false;ProcessRuntimeEnabled=$false;ProcessPolicyFile='';CodingRuntimeEnabled=$true;CodingPolicyFile=${quote(codingPolicy)};MachineFsRuntimeEnabled=$false;ObservationRuntimeEnabled=$false;DesktopRuntimeEnabled=$false;DesktopPolicyFile='';AutonomousRuntimeEnabled=$false;AutonomousSupervisorEnabled=$false;AutonomousPolicyFile='';HostPolicyFile='';LocalReadyUri='http://127.0.0.1:1/health';UrlRegex='';McpUrlFile='';McpPathFile='';StartTimeoutSeconds=1;ProbeTimeoutSeconds=1}\n`);
45
+
46
+ const ps=path.join(process.env.SystemRoot,'System32/WindowsPowerShell/v1.0/powershell.exe');
47
+ const env=windowsPowerShellEnvironment({...process.env,DEADBYTE_STATE_ROOT:root,DEADBYTE_CONTROLLER_CONFIG:config,DEADBYTE_CONTROLLER_RUNTIME_ROOT:runtime});
48
+ const run=(command,json=false)=>spawnSync(ps,['-NoLogo','-NoProfile','-NonInteractive','-File',controller,command,...(json?['-Json']:[])],{env,encoding:'utf8',timeout:20000,windowsHide:true});
49
+
50
+ const armed=run('armcoding');
51
+ assert.equal(armed.status,0,armed.stderr);
52
+ assert.match(armed.stdout,/CODING PLANE ENABLED UNTIL EXPLICIT REVOKE/);
53
+ assert.match(armed.stdout,/Coding plane\s+: ARMED \[standing-grant\]/);
54
+
55
+ const grant=JSON.parse(decodeUtf8Strict(await readFile(codingGrant)));
56
+ const epochDir=path.join(runtime,'authority-epochs','coding');
57
+ const head=JSON.parse(await readFile(path.join(epochDir,'head.json'),'utf8'));
58
+ assert.equal(head.action,'grant');
59
+ assert.equal(head.grant_instance_nonce,grant.instance_nonce);
60
+ assert.equal(head.grant_sha256,sha(await readFile(codingGrant)));
61
+ assert.equal((await stat(path.join(epochDir,'events',head.event_file))).isFile(),true);
62
+
63
+ const observed=run('status',true);
64
+ assert.equal(observed.status,0,observed.stderr);
65
+ const status=JSON.parse(observed.stdout);
66
+ assert.equal(status.authority.coding.armed,true);
67
+ assert.equal(status.authority.coding.epoch_seq,1);
68
+ assert.match(status.authority.coding.epoch_sha256,/^[0-9a-f]{64}$/);
69
+
70
+ const revoked=run('disarmcoding');
71
+ assert.equal(revoked.status,0,revoked.stderr);
72
+ assert.match(revoked.stdout,/CODING PLANE DISARMED/);
73
+ await assert.rejects(()=>stat(codingGrant),error=>error?.code==='ENOENT');
74
+ const revokedHead=JSON.parse(await readFile(path.join(epochDir,'head.json'),'utf8'));
75
+ assert.equal(revokedHead.action,'user_revoke');
76
+ assert.equal(revokedHead.seq,head.seq+1);
77
+
78
+ const after=run('status',true);
79
+ assert.equal(after.status,0,after.stderr);
80
+ assert.equal(JSON.parse(after.stdout).authority.coding.armed,false);
81
+ assert.ok((await readdir(path.join(epochDir,'events'))).length>=2);
82
+
83
+ // An attacker who can rewrite unsigned hashes but not the local signing key
84
+ // must not turn a revoked standing grant back into controller authority.
85
+ const grantBytes=Buffer.from(JSON.stringify(grant));
86
+ await writeFile(codingGrant,grantBytes);
87
+ const revokedEventPath=path.join(epochDir,'events',revokedHead.event_file);
88
+ const forgedEvent=JSON.parse(await readFile(revokedEventPath,'utf8'));
89
+ forgedEvent.action='grant';
90
+ delete forgedEvent.attestation;
91
+ const forgedBytes=Buffer.from(JSON.stringify(forgedEvent));
92
+ const forgedHash=sha(forgedBytes);
93
+ const forgedName=`${String(revokedHead.seq).padStart(16,'0')}-${forgedHash}.json`;
94
+ await writeFile(path.join(epochDir,'events',forgedName),forgedBytes);
95
+ await writeFile(path.join(epochDir,'head.json'),JSON.stringify({...revokedHead,action:'grant',grant_sha256:sha(grantBytes),event_sha256:forgedHash,event_file:forgedName}));
96
+ const forged=run('status',true);
97
+ assert.equal(forged.status,0,forged.stderr);
98
+ assert.equal(JSON.parse(forged.stdout).authority.coding.armed,false);
99
+ }finally{await rm(root,{recursive:true,force:true});}
100
+ });
@@ -0,0 +1,37 @@
1
+ import test from 'node:test';
2
+ import assert from 'node:assert/strict';
3
+ import {readFile} from 'node:fs/promises';
4
+ import {fileURLToPath} from 'node:url';
5
+
6
+ const controller=fileURLToPath(new URL('../controller/deadbyte-controller.ps1',import.meta.url));
7
+
8
+ function block(source,name,next){
9
+ const start=source.indexOf(`function ${name}`);
10
+ const end=source.indexOf(`function ${next}`,start);
11
+ assert.ok(start>=0&&end>start,`${name} block missing`);
12
+ return source.slice(start,end);
13
+ }
14
+
15
+ test('R65 controller binds standing grants to durable epochs with crash-safe ordering',async()=>{
16
+ const source=await readFile(controller,'utf8');
17
+ assert.match(source,/function Get-AuthorityEpochStatus/);
18
+ assert.match(source,/authority-epoch-cli-r65\.mjs/);
19
+ assert.match(source,/Invoke-AuthorityEpochCommand \$Cfg \$Plane 'status' \$GrantFile/,
20
+ 'controller status must use the signed Node verifier, not a local hash-only interpretation');
21
+
22
+ const arm=block(source,'Arm-Coding','Disarm-Coding');
23
+ assert.ok(arm.indexOf('Move-Item')>=0);
24
+ assert.ok(arm.indexOf("Invoke-AuthorityEpochCommand $Cfg 'coding' 'grant'")>arm.indexOf('Move-Item'),
25
+ 'grant epoch must commit only after grant bytes exist');
26
+
27
+ const disarm=block(source,'Disarm-Coding','Show-CodingStatus');
28
+ assert.ok(disarm.indexOf("Invoke-AuthorityEpochCommand $Cfg 'coding' 'user-revoke'")>=0);
29
+ assert.ok(disarm.indexOf('Remove-Item')>disarm.indexOf("Invoke-AuthorityEpochCommand $Cfg 'coding' 'user-revoke'"),
30
+ 'user revoke epoch must commit before grant deletion');
31
+
32
+ for(const plane of ['Host','Process','Autonomous','Desktop']){
33
+ const status=block(source,`Get-${plane}${plane==='Host'?'ArmStatus':plane==='Process'?'GrantStatus':plane==='Autonomous'?'LeaseStatus':'GrantStatus'}`,
34
+ plane==='Host'?'Arm-Host':plane==='Process'?'Arm-Process':plane==='Autonomous'?'Arm-Autonomous':'Arm-Desktop');
35
+ assert.match(status,/Get-AuthorityEpochStatus/,`${plane} status must verify epoch`);
36
+ }
37
+ });
@@ -0,0 +1,55 @@
1
+ import test from 'node:test';
2
+ import assert from 'node:assert/strict';
3
+ import {mkdtemp,mkdir,readFile,readdir,rm,writeFile} from 'node:fs/promises';
4
+ import {spawnSync} from 'node:child_process';
5
+ import os from 'node:os';
6
+ import path from 'node:path';
7
+ import {fileURLToPath} from 'node:url';
8
+
9
+ const root=path.resolve(fileURLToPath(new URL('..',import.meta.url)));
10
+
11
+ test('deploy dry preflight observes isolated authority without opening an update transaction', {skip:process.platform!=='win32'},async()=>{
12
+ const isolated=await mkdtemp(path.join(os.tmpdir(),'deadbyte-r65-deploy-preflight-'));
13
+ try{
14
+ const state=path.join(isolated,'state'),bin=path.join(state,'bin');
15
+ await mkdir(bin,{recursive:true});
16
+ const release=JSON.parse(await readFile(path.join(root,'RELEASE.json'),'utf8'));
17
+ const parent=release.parent;
18
+ const generationRoot=path.join(state,'generations',`${parent.release_id}-fixture`);
19
+ const policyRoot=path.join(state,'policies');
20
+ await mkdir(generationRoot,{recursive:true});await mkdir(policyRoot,{recursive:true});
21
+ const fields={process:'ProcessPolicyFile',coding:'CodingPolicyFile',autonomous:'AutonomousPolicyFile',desktop:'DesktopPolicyFile',host:'HostPolicyFile'};
22
+ const policyLines=[];
23
+ for(const [plane,key] of Object.entries(fields)){
24
+ const file=path.join(policyRoot,`${plane}.json`);
25
+ await writeFile(file,JSON.stringify({schema:`fixture.${plane}`,enabled:true}));
26
+ policyLines.push(`${key} = '${file.replaceAll("'","''")}'`);
27
+ }
28
+ await writeFile(path.join(generationRoot,'controller-config.psd1'),`@{ ${policyLines.join('; ')} }\n`);
29
+ await writeFile(path.join(state,'active-release.json'),JSON.stringify({schema:'deadbyte.active-release.v1',...parent,
30
+ release_root:path.join(state,'releases',parent.release_id),generation_root:generationRoot,
31
+ launcher_sha256:'a'.repeat(64),config_path:path.join(generationRoot,'controller-config.psd1')}));
32
+ const ps1=`param([string]$Command,[switch]$Json)\nif($Command -ne 'status' -or -not $Json){throw 'unexpected mutating command'}\n@{schema='deadbyte.controller-observation.v1';state='ONLINE';local_ready=$true;bridge=@{alive=$true;reason='verified';pid=10};core=@{alive=$true;reason='verified';pid=11};cloud=@{alive=$true;reason='verified';pid=12};supervisor=@{running=$true};authority=@{process=@{armed=$true;configured=$true;reason='fixture'};coding=@{armed=$true;configured=$true;reason='fixture'};autonomous=@{armed=$true;configured=$true;reason='fixture'};desktop=@{armed=$false;configured=$true;reason='fixture'};host=@{armed=$false;configured=$true;reason='fixture'}}} | ConvertTo-Json -Depth 6 -Compress\n`;
33
+ await writeFile(path.join(bin,'DEADBYTE-MCP.ps1'),ps1);
34
+ const run=spawnSync(process.execPath,[path.join(root,'scripts','deploy-r48.mjs'),'--preflight'],{
35
+ env:{...process.env,DEADBYTE_STATE_ROOT:state},cwd:root,encoding:'utf8',timeout:20000,windowsHide:true});
36
+ assert.equal(run.status,0,run.stderr);
37
+ const result=JSON.parse(run.stdout.trim());
38
+ assert.equal(result.state,'ONLINE_READY');
39
+ assert.equal(result.authority_continuity,'REAUTH_REQUIRED');
40
+ assert.deepEqual(result.reauth_planes,['process','coding','autonomous']);
41
+ const hostOnly=ps1.replace('process=@{armed=$true','process=@{armed=$false')
42
+ .replace('coding=@{armed=$true','coding=@{armed=$false')
43
+ .replace('autonomous=@{armed=$true','autonomous=@{armed=$false')
44
+ .replace('host=@{armed=$false','host=@{armed=$true');
45
+ await writeFile(path.join(bin,'DEADBYTE-MCP.ps1'),hostOnly);
46
+ const hostRun=spawnSync(process.execPath,[path.join(root,'scripts','deploy-r48.mjs'),'--preflight'],{
47
+ env:{...process.env,DEADBYTE_STATE_ROOT:state},cwd:root,encoding:'utf8',timeout:20000,windowsHide:true});
48
+ assert.equal(hostRun.status,0,hostRun.stderr);
49
+ const hostResult=JSON.parse(hostRun.stdout.trim());
50
+ assert.equal(hostResult.state,'ONLINE_HOST_ARMED');
51
+ assert.equal(hostResult.authority_continuity,'REAUTH_REQUIRED');
52
+ assert.deepEqual(hostResult.reauth_planes,['host']);
53
+ assert.deepEqual((await readdir(state)).sort(),['active-release.json','bin','generations','policies']);
54
+ }finally{await rm(isolated,{recursive:true,force:true});}
55
+ });
@@ -0,0 +1,141 @@
1
+ import test from 'node:test';
2
+ import assert from 'node:assert/strict';
3
+ import {createHash,generateKeyPairSync} from 'node:crypto';
4
+ import {mkdtemp,mkdir,readFile,rm,writeFile} from 'node:fs/promises';
5
+ import os from 'node:os';
6
+ import path from 'node:path';
7
+ import {orchestrateActivation} from '../src/activation-preflight-r65.mjs';
8
+ import {createAuthorityEpochStore} from '../src/authority-continuity-r65.mjs';
9
+ import {createEpochNativeContinuityEngine} from '../src/activation-continuity-engine-r65.mjs';
10
+
11
+ const sha=bytes=>createHash('sha256').update(bytes).digest('hex');
12
+ const parent={release_id:'v0.15.41-parent',version:'0.15.41',manifest_sha256:'a'.repeat(64),generation_manifest_sha256:'b'.repeat(64)};
13
+ const candidate={version:'0.15.42',manifest_sha256:'c'.repeat(64)};
14
+ const ids={bridge:{alive:true,reason:'verified',pid:10},core:{alive:true,reason:'verified',pid:11},cloud:{alive:true,reason:'verified',pid:12}};
15
+ const dummy={coding:'2'.repeat(64),autonomous:'3'.repeat(64),desktop:'4'.repeat(64),host:'5'.repeat(64)};
16
+
17
+ async function optional(file){try{return await readFile(file);}catch(error){if(error?.code==='ENOENT')return null;throw error;}}
18
+
19
+ test('epoch-native orchestrator uses transaction continuity engine instead of generic authority commands',async()=>{
20
+ const root=await mkdtemp(path.join(os.tmpdir(),'deadbyte-r65-epoch-activation-'));
21
+ try{
22
+ const state=path.join(root,'state'),runtime=path.join(state,'runtime'),trust=path.join(state,'trust'),policies=path.join(root,'policies');
23
+ await mkdir(runtime,{recursive:true});await mkdir(trust,{recursive:true});await mkdir(policies,{recursive:true});
24
+ const {privateKey,publicKey}=generateKeyPairSync('ed25519');
25
+ await writeFile(path.join(trust,'ed25519-private.pem'),privateKey.export({format:'pem',type:'pkcs8'}));
26
+ await writeFile(path.join(trust,'ed25519-public.pem'),publicKey.export({format:'pem',type:'spki'}));
27
+ const grantFile=path.join(runtime,'process-grant.json');
28
+ const policy={schema:'deadbyte.process-policy.v1',enabled:true,grant_file:grantFile,state_dir:path.join(state,'evidence','process'),
29
+ native_host:{path:'C:\\Windows\\System32\\cmd.exe',sha256:'6'.repeat(64)},
30
+ roots:{core:{path:'D:\\Projects',deny_paths:[]}},
31
+ profiles:{safe:{executable:'C:\\Windows\\System32\\cmd.exe',executable_sha256:'7'.repeat(64),root_ids:['core'],prefix_args:['/d'],max_input_bytes:4096}}};
32
+ const beforePath=path.join(policies,'before.json'),afterPath=path.join(policies,'after.json'),policyBytes=Buffer.from(JSON.stringify(policy));
33
+ await writeFile(beforePath,policyBytes);await writeFile(afterPath,policyBytes);
34
+ const oldGrant={schema:'deadbyte.process-grant.v1',policy_sha256:sha(policyBytes),instance_nonce:'8'.repeat(64),
35
+ profile_ids:['safe'],root_ids:['core'],granted_at_utc:'2026-10-07T00:00:00Z'};
36
+ const oldBytes=Buffer.from(JSON.stringify(oldGrant));await writeFile(grantFile,oldBytes);
37
+ const store=createAuthorityEpochStore({runtimeDir:runtime,policyBytes});
38
+ await store.append({plane:'process',action:'grant',grantBytes:oldBytes,grantInstanceNonce:oldGrant.instance_nonce});
39
+ let switched=false;
40
+ const observe=async()=>{
41
+ const bytes=await optional(grantFile),head=await store.readHead({plane:'process'});
42
+ let armed=false;
43
+ if(bytes){
44
+ const grant=JSON.parse(bytes);
45
+ armed=(await store.verifyGrant({plane:'process',grantBytes:bytes,grantInstanceNonce:grant.instance_nonce,policyBytes})).trusted;
46
+ }
47
+ return {state:'ONLINE',local_ready:true,...ids,supervisor:{running:true},authority:{
48
+ process:{configured:true,armed,reason:armed?'standing-grant':'disarmed',epoch_seq:armed?head.seq:null,epoch_sha256:armed?head.event_sha256:null},
49
+ coding:{configured:true,armed:false,reason:'disarmed',epoch_seq:null,epoch_sha256:null},
50
+ autonomous:{configured:true,armed:false,reason:'disarmed',epoch_seq:null,epoch_sha256:null},
51
+ desktop:{configured:true,armed:false,reason:'disarmed',epoch_seq:null,epoch_sha256:null},
52
+ host:{configured:true,armed:false,reason:'disarmed',epoch_seq:null,epoch_sha256:null}
53
+ },policy_sha256:{process:sha(policyBytes),...dummy},
54
+ active_release_id:switched?'v0.15.42-candidate':parent.release_id,
55
+ active_manifest_sha256:switched?candidate.manifest_sha256:parent.manifest_sha256,
56
+ active_generation_manifest_sha256:switched?'9'.repeat(64):parent.generation_manifest_sha256};
57
+ };
58
+ const engine=createEpochNativeContinuityEngine({stateRoot:state,
59
+ predecessorPolicyPaths:{process:beforePath,coding:null,autonomous:null,desktop:null,host:null},
60
+ successorPolicyPaths:async()=>({process:afterPath,coding:null,autonomous:null,desktop:null,host:null}),
61
+ updateId:'r65-integrated-activation'});
62
+ const genericCommands=[];
63
+ const result=await orchestrateActivation({pointer:parent,expectedParent:parent,candidate,expectedCandidate:candidate,
64
+ observe,command:async name=>{genericCommands.push(name);throw new Error('generic authority command forbidden for epoch-native path');},
65
+ activate:async()=>{const before=await observe();assert.equal(before.authority.process.armed,false);switched=true;return {verified:true,release_id:'v0.15.42-candidate'};},
66
+ continuityEngine:engine});
67
+ assert.equal(result.state,'ACTIVATED');
68
+ assert.equal(result.authority_continuity,'RESTORED_EXACT');
69
+ assert.equal(result.authority_outcomes.process,'RESTORED_EXACT');
70
+ assert.deepEqual(genericCommands,[]);
71
+ assert.equal(result.final_authority.process,true);
72
+ const restoredBytes=await readFile(grantFile),restored=JSON.parse(restoredBytes);
73
+ assert.notEqual(restored.instance_nonce,oldGrant.instance_nonce);
74
+ const head=await store.readHead({plane:'process'});
75
+ assert.equal(head.action,'transaction_restore');
76
+ assert.equal(head.restore_binding.candidate_manifest_sha256,candidate.manifest_sha256);
77
+ assert.equal(head.restore_binding.continuity_subject_sha256,result.subject_sha256);
78
+ }finally{await rm(root,{recursive:true,force:true});}
79
+ });
80
+
81
+
82
+ test('epoch-native explicit user revoke after transaction revoke wins over orchestrated restore',async()=>{
83
+ const root=await mkdtemp(path.join(os.tmpdir(),'deadbyte-r65-epoch-revoke-race-'));
84
+ try{
85
+ const state=path.join(root,'state'),runtime=path.join(state,'runtime'),trust=path.join(state,'trust'),policies=path.join(root,'policies');
86
+ await mkdir(runtime,{recursive:true});await mkdir(trust,{recursive:true});await mkdir(policies,{recursive:true});
87
+ const {privateKey,publicKey}=generateKeyPairSync('ed25519');
88
+ await writeFile(path.join(trust,'ed25519-private.pem'),privateKey.export({format:'pem',type:'pkcs8'}));
89
+ await writeFile(path.join(trust,'ed25519-public.pem'),publicKey.export({format:'pem',type:'spki'}));
90
+ const grantFile=path.join(runtime,'process-grant.json');
91
+ const policy={schema:'deadbyte.process-policy.v1',enabled:true,grant_file:grantFile,state_dir:path.join(state,'evidence','process'),
92
+ native_host:{path:'C:\\Windows\\System32\\cmd.exe',sha256:'6'.repeat(64)},
93
+ roots:{core:{path:'D:\\Projects',deny_paths:[]}},
94
+ profiles:{safe:{executable:'C:\\Windows\\System32\\cmd.exe',executable_sha256:'7'.repeat(64),root_ids:['core'],prefix_args:['/d'],max_input_bytes:4096}}};
95
+ const beforePath=path.join(policies,'before.json'),afterPath=path.join(policies,'after.json'),policyBytes=Buffer.from(JSON.stringify(policy));
96
+ await writeFile(beforePath,policyBytes);await writeFile(afterPath,policyBytes);
97
+ const oldGrant={schema:'deadbyte.process-grant.v1',policy_sha256:sha(policyBytes),instance_nonce:'a'.repeat(64),
98
+ profile_ids:['safe'],root_ids:['core'],granted_at_utc:'2026-10-07T00:00:00Z'};
99
+ const oldBytes=Buffer.from(JSON.stringify(oldGrant));await writeFile(grantFile,oldBytes);
100
+ const store=createAuthorityEpochStore({runtimeDir:runtime,policyBytes});
101
+ await store.append({plane:'process',action:'grant',grantBytes:oldBytes,grantInstanceNonce:oldGrant.instance_nonce});
102
+ let switched=false;
103
+ const observe=async()=>{
104
+ const bytes=await optional(grantFile),head=await store.readHead({plane:'process'});
105
+ let armed=false;
106
+ if(bytes){
107
+ const grant=JSON.parse(bytes);
108
+ armed=(await store.verifyGrant({plane:'process',grantBytes:bytes,grantInstanceNonce:grant.instance_nonce,policyBytes})).trusted;
109
+ }
110
+ return {state:'ONLINE',local_ready:true,...ids,supervisor:{running:true},authority:{
111
+ process:{configured:true,armed,reason:armed?'standing-grant':'disarmed',epoch_seq:armed?head.seq:null,epoch_sha256:armed?head.event_sha256:null},
112
+ coding:{configured:true,armed:false,reason:'disarmed',epoch_seq:null,epoch_sha256:null},
113
+ autonomous:{configured:true,armed:false,reason:'disarmed',epoch_seq:null,epoch_sha256:null},
114
+ desktop:{configured:true,armed:false,reason:'disarmed',epoch_seq:null,epoch_sha256:null},
115
+ host:{configured:true,armed:false,reason:'disarmed',epoch_seq:null,epoch_sha256:null}
116
+ },policy_sha256:{process:sha(policyBytes),...dummy},
117
+ active_release_id:switched?'v0.15.42-candidate':parent.release_id,
118
+ active_manifest_sha256:switched?candidate.manifest_sha256:parent.manifest_sha256,
119
+ active_generation_manifest_sha256:switched?'9'.repeat(64):parent.generation_manifest_sha256};
120
+ };
121
+ const engine=createEpochNativeContinuityEngine({stateRoot:state,
122
+ predecessorPolicyPaths:{process:beforePath,coding:null,autonomous:null,desktop:null,host:null},
123
+ successorPolicyPaths:async()=>({process:afterPath,coding:null,autonomous:null,desktop:null,host:null}),
124
+ updateId:'r65-integrated-user-revoke'});
125
+ const result=await orchestrateActivation({pointer:parent,expectedParent:parent,candidate,expectedCandidate:candidate,
126
+ observe,command:async()=>{throw new Error('generic authority command forbidden for epoch-native path');},
127
+ activate:async()=>{
128
+ const head=await store.readHead({plane:'process'});
129
+ assert.equal(head.action,'transaction_revoke');
130
+ await store.append({plane:'process',action:'user_revoke',grantBytes:oldBytes,grantInstanceNonce:oldGrant.instance_nonce,updateId:null});
131
+ switched=true;
132
+ return {verified:true,release_id:'v0.15.42-candidate'};
133
+ },continuityEngine:engine});
134
+ assert.equal(result.state,'ACTIVATED');
135
+ assert.equal(result.authority_continuity,'REAUTH_REQUIRED');
136
+ assert.equal(result.authority_outcomes.process,'REVOKED_DURING_UPDATE');
137
+ assert.equal(result.final_authority.process,false);
138
+ assert.equal((await store.readHead({plane:'process'})).action,'user_revoke');
139
+ assert.equal(await optional(grantFile),null);
140
+ }finally{await rm(root,{recursive:true,force:true});}
141
+ });