deadbyte-mcp 0.15.29 → 0.15.30

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 (62) hide show
  1. package/MANIFEST.SHA256 +60 -48
  2. package/PROVENANCE.json +19 -14
  3. package/README.txt +14 -14
  4. package/RELEASE.json +25 -13
  5. package/SBOM.cdx.json +3 -3
  6. package/STABILIZATION-PROGRESS.md +60 -0
  7. package/TEST-MATRIX.json +46 -101
  8. package/bin/appcontainer-stage.obj +0 -0
  9. package/bin/bootstrap-advapi32.obj +0 -0
  10. package/bin/bootstrap-exitcode.obj +0 -0
  11. package/bin/bootstrap-kernel32.obj +0 -0
  12. package/bin/child-control-probe.obj +0 -0
  13. package/bin/child-control-stage.obj +0 -0
  14. package/bin/contained-reverse-worker.obj +0 -0
  15. package/bin/contained-transform-worker.obj +0 -0
  16. package/bin/containment-probe.obj +0 -0
  17. package/bin/deadbyte-contain.obj +0 -0
  18. package/bin/deadbyte-exec.obj +0 -0
  19. package/bin/deadbyte-hardware-observer-r45.obj +0 -0
  20. package/bin/deadbyte-process-host.obj +0 -0
  21. package/bin/deadbyte-tunnel-host.obj +0 -0
  22. package/controller/deadbyte-desktop-policy.json +5 -5
  23. package/controller/deadbyte-host-policy.json +170 -0
  24. package/controller/deadbyte-process-policy.json +4 -4
  25. package/package.json +4 -3
  26. package/proof/CONTAINMENT-BUILD.txt +6 -6
  27. package/scripts/gate-windows-r53.ps1 +58 -0
  28. package/scripts/verify-r42-parent.mjs +1 -1
  29. package/scripts/verify-r43-parent.mjs +1 -1
  30. package/scripts/verify-r44-parent.mjs +1 -1
  31. package/scripts/verify-r45-parent.mjs +1 -1
  32. package/scripts/windows-gate-evidence-r53.mjs +102 -0
  33. package/src/authority-smoke.mjs +1 -0
  34. package/src/deadbyte-cli.mjs +4 -2
  35. package/src/doctor-r45.mjs +2 -1
  36. package/src/machine-fs-smoke-client.mjs +1 -0
  37. package/src/mcp-contained-smoke-client.mjs +1 -0
  38. package/src/mcp-host-smoke-client.mjs +1 -0
  39. package/src/mcp-smoke-client.mjs +1 -0
  40. package/src/process-smoke-client.mjs +1 -0
  41. package/src/release-slot.mjs +5 -2
  42. package/src/remote-session-guardian.mjs +12 -4
  43. package/src/runtime-coherence.mjs +13 -4
  44. package/src/runtime-update.mjs +20 -5
  45. package/src/state-root.mjs +8 -0
  46. package/src/trust.mjs +2 -1
  47. package/src/tunnel-stdio-compat-smoke.mjs +1 -0
  48. package/src/version.mjs +1 -1
  49. package/test/commander-biff-reference-r48.test.mjs +1 -1
  50. package/test/r44-finalizer.test.mjs +4 -3
  51. package/test/r49-windows-gate.test.mjs +15 -10
  52. package/test/r52-release-contract.test.mjs +9 -3
  53. package/test/r53-release-contract.test.mjs +66 -0
  54. package/test/release-slot.test.mjs +2 -1
  55. package/test/skill-mcp-r44.test.mjs +3 -0
  56. package/test/stabilization-guardian.test.mjs +25 -0
  57. package/test/stabilization-parent-context.test.mjs +18 -0
  58. package/test/stabilization-powershell.test.mjs +26 -0
  59. package/test/stabilization-semver.test.mjs +23 -0
  60. package/test/stabilization-smoke-root.test.mjs +23 -0
  61. package/test/stabilization-state-root.test.mjs +60 -0
  62. package/test/stabilization-update-child-root.test.mjs +20 -0
@@ -76,7 +76,7 @@ export async function verifyR45Parent(parentFile,{stateRoot=path.join(os.homedir
76
76
  insist(envelope.body.version===parent.parent_version,'R45 parent closure version mismatch');
77
77
  insist(envelope.body.release_label==='R44 REMOTE AGENT SKILL BASELINE','R45 parent closure label mismatch');
78
78
 
79
- const publicKey=createPublicKey(await readFile(path.join(path.resolve(stateRoot),'trust','ed25519-public.pem')));
79
+ const publicKey=createPublicKey(await readFile(path.resolve(process.env.DEADBYTE_PARENT_VERIFY_KEY||path.join(path.resolve(stateRoot),'trust','ed25519-public.pem'))));
80
80
  insist(publicKey.asymmetricKeyType==='ed25519','R45 parent trust key must be Ed25519');
81
81
  const keyId=sha256(Buffer.from(publicKey.export({type:'spki',format:'der'})));
82
82
  insist(keyId===parent.parent_final_closure_signer_key_id,'R45 parent trust key id mismatch');
@@ -0,0 +1,102 @@
1
+ import { createHash } from 'node:crypto';
2
+ import { mkdir, open, readFile } from 'node:fs/promises';
3
+ import os from 'node:os';
4
+ import path from 'node:path';
5
+ import { fileURLToPath } from 'node:url';
6
+ import { doctor } from '../src/doctor-r45.mjs';
7
+
8
+ const root = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..');
9
+ const stateRoot = path.resolve(process.env.DEADBYTE_STATE_ROOT || path.join(os.homedir(), '.deadbyte-mcp'));
10
+ const sha256 = bytes => createHash('sha256').update(bytes).digest('hex');
11
+ const insist = (ok, message) => { if (!ok) throw new Error(message); };
12
+ const pkg = JSON.parse(await readFile(path.join(root, 'package.json'), 'utf8'));
13
+ const release = JSON.parse(await readFile(path.join(root, 'RELEASE.json'), 'utf8'));
14
+ const provenance = JSON.parse(await readFile(path.join(root, 'PROVENANCE.json'), 'utf8'));
15
+ const matrix = JSON.parse(await readFile(path.join(root, 'TEST-MATRIX.json'), 'utf8'));
16
+ const active = JSON.parse(await readFile(path.join(stateRoot, 'active-release.json'), 'utf8'));
17
+ const manifestSha256 = sha256(await readFile(path.join(root, 'MANIFEST.SHA256')));
18
+
19
+ insist(pkg.version === '0.15.30' && release.version === pkg.version && release.train === 'R53', 'R53 candidate identity mismatch');
20
+ insist(
21
+ active.release_id === release.runtime_predecessor.release_id &&
22
+ active.manifest_sha256 === release.runtime_predecessor.manifest_sha256 &&
23
+ active.generation_manifest_sha256 === release.runtime_predecessor.generation_manifest_sha256,
24
+ 'active runtime is not exact sealed 0.15.29 predecessor'
25
+ );
26
+ insist(release.parent.version === '0.15.29', 'R53 parent version mismatch');
27
+ insist(release.parent.release_id === 'v0.15.29-f74eaa1b3d00e6fb', 'R53 parent release id mismatch');
28
+ insist(release.parent.manifest_sha256 === 'f74eaa1b3d00e6fb5a36b2358efb815e3686c9fe9dbd3b84d25a713ef957748c', 'R53 parent manifest mismatch');
29
+ insist(release.parent.generation_manifest_sha256 === '289b4fc065de0681da6864cc1bbe09aab198ca804945ffc27c786438dad13f5a', 'R53 parent generation mismatch');
30
+ insist(release.parent.tarball_sha256 === 'f535d7121a384bed106d34bc7525afa9d4048c7e290f270699e25136d1e82eeb', 'R53 parent tarball mismatch');
31
+ insist(provenance.parent_release_id === release.parent.release_id, 'R53 provenance parent release mismatch');
32
+ insist(provenance.parent_manifest_sha256 === release.parent.manifest_sha256, 'R53 provenance parent manifest mismatch');
33
+ insist(provenance.parent_generation_manifest_sha256 === release.parent.generation_manifest_sha256, 'R53 provenance parent generation mismatch');
34
+ insist(provenance.operator_override?.four_hour_soak === 'NOT_COMPLETED', 'R53 soak bypass missing');
35
+ insist(provenance.operator_override?.stable_final_claim === false, 'R53 must not claim stable/final');
36
+ insist(matrix.operator_override?.four_hour_soak === 'NOT_COMPLETED', 'R53 matrix soak bypass missing');
37
+
38
+ const health = await doctor();
39
+ insist(health.status === 'ok' && health.package_version === pkg.version && health.manifest?.verified === true, 'R53 doctor evidence invalid');
40
+ insist(health.policy?.host_v2_ready === true, 'R53 Host policy v2 is not ready');
41
+
42
+ const evidence = {
43
+ schema: 'deadbyte.r53-windows-gate.v1',
44
+ status: 'passed',
45
+ exit_code: 0,
46
+ version: pkg.version,
47
+ train: 'R53',
48
+ completed_at_utc: new Date().toISOString(),
49
+ manifest_sha256: manifestSha256,
50
+ parent: release.parent,
51
+ runtime_predecessor: release.runtime_predecessor,
52
+ stabilization: release.stabilization,
53
+ operator_override: provenance.operator_override,
54
+ active_predecessor: {
55
+ release_id: active.release_id,
56
+ version: active.version,
57
+ manifest_sha256: active.manifest_sha256,
58
+ generation_manifest_sha256: active.generation_manifest_sha256
59
+ },
60
+ doctor: { verified_entries: health.manifest.verified_entries, host_v2_ready: true },
61
+ gates: {
62
+ r53_focused: true,
63
+ package_boundary: true,
64
+ release_smoke_isolation: true,
65
+ runtime_coherence: true,
66
+ coding_command_singleflight: true,
67
+ foreground_desktop_host_fail_closed: true,
68
+ full_regression: true,
69
+ commander_hardening: true,
70
+ donor_proof: true,
71
+ desktop_wait: true,
72
+ exact_catalog: true,
73
+ stdio_smokes: true,
74
+ manifest_final_verify: true,
75
+ four_hour_soak_completed: false
76
+ }
77
+ };
78
+
79
+ const bytes = Buffer.from(`${JSON.stringify(evidence, null, 2)}\n`);
80
+ const dir = path.join(stateRoot, 'evidence', 'release');
81
+ await mkdir(dir, { recursive: true });
82
+ const target = path.join(dir, `r53-windows-gate-${manifestSha256.slice(0, 16)}.json`);
83
+
84
+ try {
85
+ const handle = await open(target, 'wx', 0o600);
86
+ try { await handle.writeFile(bytes); await handle.sync(); }
87
+ finally { await handle.close(); }
88
+ } catch (error) {
89
+ if (error?.code !== 'EEXIST') throw error;
90
+ const prior = JSON.parse(await readFile(target, 'utf8'));
91
+ insist(
92
+ prior.schema === evidence.schema &&
93
+ prior.status === 'passed' &&
94
+ prior.version === evidence.version &&
95
+ prior.manifest_sha256 === manifestSha256 &&
96
+ prior.parent?.release_id === release.parent.release_id &&
97
+ prior.operator_override?.four_hour_soak === 'NOT_COMPLETED',
98
+ 'conflicting R53 gate evidence identity'
99
+ );
100
+ }
101
+
102
+ console.log(JSON.stringify({ status: 'passed', path: target, sha256: sha256(bytes), manifest_sha256: manifestSha256 }));
@@ -60,6 +60,7 @@ const transport = new StdioClientTransport({
60
60
  cwd: repoRoot,
61
61
  env: {
62
62
  ...getDefaultEnvironment(),
63
+ DEADBYTE_STATE_ROOT: path.resolve(process.env.DEADBYTE_STATE_ROOT||path.join(trust,'state')),
63
64
  DEADBYTE_WORKSPACE_ROOT: workspaceRoot,
64
65
  DEADBYTE_SIGNING_KEY: keys.privateKeyPath,
65
66
  DEADBYTE_VERIFY_KEY: keys.publicKeyPath
@@ -16,6 +16,8 @@ import { printDoctor } from './doctor-r45.mjs';
16
16
  import { createLiveTimelineFilter, createLiveTimelineTracker, createLiveTimelineWriter, formatCompactAuditEvent, formatRemoteLifecycle, formatRemoteToolCall, formatRuntimeDiagnosticLine, selectLiveLogNames } from './remote-live-log.mjs';
17
17
  import { prepareRemoteConsole, remoteDisplayText } from './remote-console.mjs';
18
18
  import { ensureRuntimeCoherence } from './runtime-coherence.mjs';
19
+ import { windowsPowerShellEnvironment } from './runtime-update.mjs';
20
+ import { resolveStateRoot } from './state-root.mjs';
19
21
 
20
22
  const REMOTE_COMMANDS=new Set([
21
23
  'menu','start','resume','park','stop','restart','status','url','test','fulltest','toggle','clean','machinetest',
@@ -42,7 +44,7 @@ const CURRENT_DEPLOY_SCRIPT=path.join(CURRENT_PACKAGE_ROOT,'scripts','deploy-r48
42
44
  const REMOTE_UPDATE_TIMEOUT_MS=900000;
43
45
  const LIVE_LOG_DISCOVERY_INTERVAL_MS=1000;
44
46
 
45
- function stateRoot(){return path.resolve(process.env.DEADBYTE_STATE_ROOT||path.join(os.homedir(),'.deadbyte-mcp'));}
47
+ function stateRoot(){return resolveStateRoot();}
46
48
  async function requireRegular(file,label){const info=await lstat(file);if(!info.isFile()||info.isSymbolicLink()) throw new Error(`${label} must be a regular non-symlink file`);return file;}
47
49
  function printHelp(){
48
50
  const exact=`deadbyte-mcp@${DEADBYTE_VERSION}`;
@@ -171,7 +173,7 @@ function invokeController(ctx,command,{capture=false,allowFailure=false,echoCapt
171
173
  stderrFd=openSync(redirectOutput.stderr_path,'w');
172
174
  stdio=['ignore',stdoutFd,stderrFd];
173
175
  }else stdio=capture?['ignore','pipe','pipe']:'inherit';
174
- const options={stdio,encoding:capture?'utf8':undefined,windowsHide:false,shell:false};
176
+ const options={stdio,encoding:capture?'utf8':undefined,windowsHide:false,shell:false,env:windowsPowerShellEnvironment()};
175
177
  if(Number.isInteger(timeoutMs)&&timeoutMs>0) options.timeout=timeoutMs;
176
178
  result=spawnSync(ctx.powershell,['-NoLogo','-NoProfile','-ExecutionPolicy','Bypass','-File',ctx.launcher,command],options);
177
179
  }finally{
@@ -4,9 +4,10 @@ import os from 'node:os';
4
4
  import path from 'node:path';
5
5
  import { fileURLToPath } from 'node:url';
6
6
  import { DEADBYTE_VERSION, MCP_PROTOCOL_VERSION } from './version.mjs';
7
+ import { resolveStateRoot } from './state-root.mjs';
7
8
 
8
9
  const root=path.resolve(path.dirname(fileURLToPath(import.meta.url)),'..');
9
- const stateRoot=()=>path.resolve(process.env.DEADBYTE_STATE_ROOT||path.join(os.homedir(),'.deadbyte-mcp'));
10
+ const stateRoot=()=>resolveStateRoot();
10
11
  const sha256=b=>createHash('sha256').update(b).digest('hex');
11
12
  async function jsonOrNull(file){try{return JSON.parse((await readFile(file,'utf8')).replace(/^\uFEFF/,''));}catch(e){if(e?.code==='ENOENT')return null;throw e;}}
12
13
  async function fileHash(file){return sha256(await readFile(file));}
@@ -52,6 +52,7 @@ const client = new Client({ name: 'deadbyte-machine-fs-smoke', version: '0.8.0'
52
52
  const transport = new StdioClientTransport({
53
53
  command: process.execPath, args: [serverPath], cwd: repoRoot,
54
54
  env: { ...getDefaultEnvironment(), DEADBYTE_CODING_RUNTIME: '1', DEADBYTE_MACHINE_FS_RUNTIME: '1',
55
+ DEADBYTE_STATE_ROOT: path.resolve(process.env.DEADBYTE_STATE_ROOT||path.join(scratch,'state')),
55
56
  DEADBYTE_PRIVILEGED_POLICY: '',
56
57
  DEADBYTE_CODING_POLICY: policyPath, DEADBYTE_SIGNING_KEY: keys.privateKeyPath,
57
58
  DEADBYTE_VERIFY_KEY: keys.publicKeyPath }, stderr: 'pipe'
@@ -34,6 +34,7 @@ const transport = new StdioClientTransport({
34
34
  cwd: repoRoot,
35
35
  env: {
36
36
  ...getDefaultEnvironment(),
37
+ DEADBYTE_STATE_ROOT: path.resolve(process.env.DEADBYTE_STATE_ROOT||path.join(trust,'state')),
37
38
  DEADBYTE_WORKSPACE_ROOT: workspaceRoot,
38
39
  DEADBYTE_SIGNING_KEY: keys.privateKeyPath,
39
40
  DEADBYTE_VERIFY_KEY: keys.publicKeyPath
@@ -66,6 +66,7 @@ const transport = new StdioClientTransport({
66
66
  cwd: repoRoot,
67
67
  env: {
68
68
  ...getDefaultEnvironment(),
69
+ DEADBYTE_STATE_ROOT: path.resolve(process.env.DEADBYTE_STATE_ROOT||path.join(temp,'state')),
69
70
  DEADBYTE_WORKSPACE_ROOT: path.join(repoRoot, 'workspaces'),
70
71
  DEADBYTE_SIGNING_KEY: keys.privateKeyPath,
71
72
  DEADBYTE_VERIFY_KEY: keys.publicKeyPath,
@@ -37,6 +37,7 @@ const transport = new StdioClientTransport({
37
37
  cwd: repoRoot,
38
38
  env: {
39
39
  ...getDefaultEnvironment(),
40
+ DEADBYTE_STATE_ROOT: path.resolve(process.env.DEADBYTE_STATE_ROOT||path.join(smokeTrust,'state')),
40
41
  DEADBYTE_WORKSPACE_ROOT: workspaceRoot,
41
42
  DEADBYTE_SIGNING_KEY: keys.privateKeyPath,
42
43
  DEADBYTE_VERIFY_KEY: keys.publicKeyPath,
@@ -39,6 +39,7 @@ const client = new Client({ name: 'deadbyte-process-smoke', version: '0.8.0' },
39
39
  const transport = new StdioClientTransport({
40
40
  command: process.execPath, args: [serverPath], cwd: repoRoot,
41
41
  env: { ...getDefaultEnvironment(), DEADBYTE_PRIVILEGED_POLICY: '', DEADBYTE_MACHINE_RUNTIME:'0', DEADBYTE_AGENT_RUNTIME:'0',
42
+ DEADBYTE_STATE_ROOT: path.resolve(process.env.DEADBYTE_STATE_ROOT||path.join(smokeTrust,'state')),
42
43
  DEADBYTE_PROCESS_RUNTIME: '1', DEADBYTE_PROCESS_POLICY: smokePolicyPath, DEADBYTE_CODING_RUNTIME:'0', DEADBYTE_MACHINE_FS_RUNTIME:'0',
43
44
  DEADBYTE_OBSERVATION_RUNTIME:'0', DEADBYTE_DESKTOP_RUNTIME:'0', DEADBYTE_AUTONOMOUS_RUNTIME:'0', DEADBYTE_AUDIT_RUNTIME:'0',
44
45
  DEADBYTE_CAPABILITY_PROFILE:'full', DEADBYTE_SIGNING_KEY: keys.privateKeyPath, DEADBYTE_VERIFY_KEY: keys.publicKeyPath },
@@ -195,7 +195,8 @@ export function renderStableLauncherCmd(){
195
195
  return [
196
196
  '@echo off',
197
197
  'setlocal EnableExtensions DisableDelayedExpansion',
198
- 'powershell.exe -NoLogo -NoProfile -ExecutionPolicy Bypass -File "%USERPROFILE%\\.deadbyte-mcp\\bin\\DEADBYTE-MCP.ps1" %*',
198
+ 'if not defined DEADBYTE_STATE_ROOT set "DEADBYTE_STATE_ROOT=%USERPROFILE%\\.deadbyte-mcp"',
199
+ 'powershell.exe -NoLogo -NoProfile -ExecutionPolicy Bypass -File "%DEADBYTE_STATE_ROOT%\\bin\\DEADBYTE-MCP.ps1" %*',
199
200
  'exit /b %ERRORLEVEL%',
200
201
  ''
201
202
  ].join('\r\n');
@@ -208,7 +209,9 @@ export function renderStableLauncherPs1(){
208
209
  'param([Parameter(ValueFromRemainingArguments=$true)][string[]]$ControllerArgs)',
209
210
  'Set-StrictMode -Version 3.0',
210
211
  "$ErrorActionPreference = 'Stop'",
211
- "$StateRoot = Join-Path $env:USERPROFILE '.deadbyte-mcp'",
212
+ "$StateRoot = if ([string]::IsNullOrEmpty($env:DEADBYTE_STATE_ROOT)) { Join-Path $env:USERPROFILE '.deadbyte-mcp' } else { $env:DEADBYTE_STATE_ROOT }",
213
+ '$StateRoot = [IO.Path]::GetFullPath($StateRoot)',
214
+ '$env:DEADBYTE_STATE_ROOT = $StateRoot',
212
215
  "$PointerPath = Join-Path $StateRoot 'active-release.json'",
213
216
  "function Fail([string]$m) { throw ('DEADBYTE launcher: ' + $m) }",
214
217
  "if (-not (Test-Path -LiteralPath $PointerPath -PathType Leaf)) { Fail 'active release pointer missing' }",
@@ -4,6 +4,8 @@ import { spawnSync } from 'node:child_process';
4
4
  import os from 'node:os';
5
5
  import path from 'node:path';
6
6
  import { setTimeout as sleep } from 'node:timers/promises';
7
+ import { windowsPowerShellEnvironment } from './runtime-update.mjs';
8
+ import { resolveStateRoot } from './state-root.mjs';
7
9
 
8
10
  const CLEANUP=['disarmautonomous','disarmcoding','disarmprocess','disarmdesktop','disarmhost','park'];
9
11
 
@@ -36,9 +38,9 @@ function markerMatches(marker,{ownerPid,nonce}){
36
38
  marker?.owner_pid===ownerPid&&
37
39
  marker?.session_nonce===nonce;
38
40
  }
39
- function invokeCleanup(powershell,launcher,command){
41
+ function invokeCleanup(powershell,launcher,command,stateRoot){
40
42
  const result=spawnSync(powershell,['-NoLogo','-NoProfile','-ExecutionPolicy','Bypass','-File',launcher,command],{
41
- stdio:'ignore',windowsHide:true,shell:false
43
+ stdio:'ignore',windowsHide:true,shell:false,env:windowsPowerShellEnvironment({...process.env,DEADBYTE_STATE_ROOT:stateRoot}),timeout:30000
42
44
  });
43
45
  return !result.error&&result.status===0;
44
46
  }
@@ -46,7 +48,7 @@ function invokeCleanup(powershell,launcher,command){
46
48
  async function main(){
47
49
  if(process.platform!=='win32') return;
48
50
  const args=parseArgs(process.argv.slice(2));
49
- const root=path.resolve(args['state-root']||path.join(os.homedir(),'.deadbyte-mcp'));
51
+ const root=resolveStateRoot(args['state-root']);
50
52
  const ownerPid=Number(args['owner-pid']);
51
53
  const nonce=String(args['session-nonce']||'');
52
54
  if(!Number.isInteger(ownerPid)||ownerPid<1) throw new Error('guardian owner pid invalid');
@@ -66,7 +68,13 @@ async function main(){
66
68
  continue;
67
69
  }
68
70
 
69
- for(const command of CLEANUP) invokeCleanup(powershell,launcher,command);
71
+ const failed=[];
72
+ for(const command of CLEANUP){
73
+ const latest=await readMarker(markerPath);
74
+ if(!latest||!markerMatches(latest,{ownerPid,nonce})) return;
75
+ if(!invokeCleanup(powershell,launcher,command,root)) failed.push(command);
76
+ }
77
+ if(failed.length) throw new Error('guardian cleanup failed; ownership marker retained: '+failed.join(','));
70
78
  const current=await readMarker(markerPath);
71
79
  if(current&&markerMatches(current,{ownerPid,nonce})) await rm(markerPath,{force:true});
72
80
  return;
@@ -1,10 +1,10 @@
1
- const SEMVER=/^(\d+)\.(\d+)\.(\d+)(?:-([0-9A-Za-z.-]+))?(?:\+[0-9A-Za-z.-]+)?$/;
1
+ const SEMVER=/^(0|[1-9][0-9]*)\.(0|[1-9][0-9]*)\.(0|[1-9][0-9]*)(?:-([0-9A-Za-z-]+(?:\.[0-9A-Za-z-]+)*))?(?:\+[0-9A-Za-z-]+(?:\.[0-9A-Za-z-]+)*)?$/;
2
2
  const HEX64=/^[0-9a-f]{64}$/;
3
3
 
4
4
  function parseSemver(value){
5
5
  const match=SEMVER.exec(String(value??''));
6
- if(!match) throw new Error(`runtime version invalid: ${String(value)}`);
7
- return {major:Number(match[1]),minor:Number(match[2]),patch:Number(match[3]),prerelease:match[4]??null};
6
+ if(!match||match[0]!==String(value??'')||(match[4]??'').split('.').some(id=>/^0[0-9]+$/.test(id))) throw new Error(`runtime version invalid: ${String(value)}`);
7
+ return {major:BigInt(match[1]),minor:BigInt(match[2]),patch:BigInt(match[3]),prerelease:match[4]??null};
8
8
  }
9
9
  export function compareRuntimeVersions(a,b){
10
10
  const left=parseSemver(a),right=parseSemver(b);
@@ -12,7 +12,16 @@ export function compareRuntimeVersions(a,b){
12
12
  if(left.prerelease===right.prerelease) return 0;
13
13
  if(left.prerelease===null) return 1;
14
14
  if(right.prerelease===null) return -1;
15
- return left.prerelease.localeCompare(right.prerelease,'en',{numeric:true,sensitivity:'case'});
15
+ const aIds=left.prerelease.split('.'),bIds=right.prerelease.split('.');
16
+ for(let i=0;i<Math.min(aIds.length,bIds.length);i++){
17
+ const x=aIds[i],y=bIds[i];
18
+ if(x===y) continue;
19
+ const xn=/^[0-9]+$/.test(x),yn=/^[0-9]+$/.test(y);
20
+ if(xn&&yn) return BigInt(x)<BigInt(y)?-1:1;
21
+ if(xn!==yn) return xn?-1:1;
22
+ return x<y?-1:1;
23
+ }
24
+ return aIds.length<bIds.length?-1:aIds.length>bIds.length?1:0;
16
25
  }
17
26
  function assertManifest(value,label){
18
27
  if(!HEX64.test(String(value??''))) throw new Error(`${label} must be lowercase SHA-256`);
@@ -17,6 +17,7 @@ import {
17
17
  verifyManifestTree
18
18
  } from './release-slot.mjs';
19
19
  import { createGenerationBundle, verifyGenerationBundle } from './release-generation.mjs';
20
+ import { resolveStateRoot } from './state-root.mjs';
20
21
 
21
22
  const HEX64=/^[0-9a-f]{64}$/;
22
23
  const SEMVER=/^\d+\.\d+\.\d+(?:-[0-9A-Za-z.-]+)?(?:\+[0-9A-Za-z.-]+)?$/;
@@ -25,7 +26,7 @@ const UPDATE_SCHEMA='deadbyte.r27-slot-update-receipt.v1';
25
26
  const HANDOFF_SCHEMA='deadbyte.r27-slot-update-handoff.v1';
26
27
  const FAILURE_SCHEMA='deadbyte.r27-slot-update-failure.v1';
27
28
  const ROLLBACK_SCHEMA='deadbyte.r31-slot-rollback-receipt.v1';
28
- const DEFAULT_STATE_ROOT=path.join(os.homedir(),'.deadbyte-mcp');
29
+ const DEFAULT_STATE_ROOT=undefined;
29
30
  const TOP_LAUNCHER=path.join(os.homedir(),'Downloads','DEADBYTE-MCP.cmd');
30
31
  const RUNTIME_STABILITY_MS=12000;
31
32
  const RUNTIME_STABILITY_POLL_MS=500;
@@ -195,11 +196,21 @@ export function windowsPowerShellEnvironment(base=process.env){
195
196
  const systemRoot=base.SystemRoot||base.SYSTEMROOT||'C:\\Windows';
196
197
  const userProfile=base.USERPROFILE||os.homedir();
197
198
  const programFiles=base.ProgramFiles||base.PROGRAMFILES||'C:\\Program Files';
198
- return {...base,PSModulePath:[
199
+ const environment={...base};
200
+ const inherited=Object.entries(base).filter(([key])=>key.toLowerCase()==='psmodulepath').flatMap(([,value])=>String(value??'').split(';')).filter(Boolean);
201
+ for(const key of Object.keys(environment)) if(key.toLowerCase()==='psmodulepath') delete environment[key];
202
+ const modules=[
203
+ path.join(systemRoot,'System32','WindowsPowerShell','v1.0','Modules'),
199
204
  path.join(userProfile,'Documents','WindowsPowerShell','Modules'),
200
205
  path.join(programFiles,'WindowsPowerShell','Modules'),
201
- path.join(systemRoot,'System32','WindowsPowerShell','v1.0','Modules')
202
- ].join(';')};
206
+ ...inherited
207
+ ];
208
+ const seen=new Set();
209
+ return {...environment,PSModulePath:modules.filter(modulePath=>{
210
+ const key=modulePath.toLowerCase();
211
+ if(seen.has(key)) return false;
212
+ seen.add(key);return true;
213
+ }).join(';')};
203
214
  }
204
215
  function commandEnvironment(exe,base=process.env){
205
216
  return path.resolve(exe).toLowerCase()===path.resolve(powershellPath()).toLowerCase()
@@ -582,7 +593,7 @@ function controllerInvocation(ctx,command){
582
593
  exe:powershellPath(),
583
594
  args:['-NoLogo','-NoProfile','-ExecutionPolicy','Bypass','-File',ctx.controller_path,command],
584
595
  cwd:ctx.cwd,
585
- env:{...process.env,DEADBYTE_CONTROLLER_CONFIG:ctx.pointer.config_path,DEADBYTE_CONTROLLER_RUNTIME_ROOT:ctx.runtime_root}
596
+ env:{...process.env,DEADBYTE_STATE_ROOT:resolveStateRoot(ctx.state_root),DEADBYTE_CONTROLLER_CONFIG:ctx.pointer.config_path,DEADBYTE_CONTROLLER_RUNTIME_ROOT:ctx.runtime_root}
586
597
  };
587
598
  return {exe:powershellPath(),args:['-NoLogo','-NoProfile','-ExecutionPolicy','Bypass','-File',ctx.controller_path,command],cwd:ctx.cwd};
588
599
  }
@@ -1411,6 +1422,7 @@ async function resumeSlotUpdate({handoffPath}){
1411
1422
  }
1412
1423
  }
1413
1424
  export async function recoverInterruptedSlotUpdates({stateRoot=DEFAULT_STATE_ROOT}={}){
1425
+ stateRoot=resolveStateRoot(stateRoot);
1414
1426
  const evidenceRoot=path.join(path.resolve(stateRoot),'evidence','updates');
1415
1427
  if(!existsSync(evidenceRoot)) return {ok:true,launched:0,updates:[]};
1416
1428
  const entries=await readdir(evidenceRoot,{withFileTypes:true});
@@ -1443,6 +1455,7 @@ export async function recoverInterruptedSlotUpdates({stateRoot=DEFAULT_STATE_ROO
1443
1455
  }
1444
1456
 
1445
1457
  export async function performSlotUpdate({stage,developmentRoot,expectedVersion,expectedManifest,sourceRoot=null,stateRoot=DEFAULT_STATE_ROOT,restoreStandingGrants=false,recoverOfflinePredecessor=false,cloudTransport=null,bridgeBootstrap=null,processPolicyBootstrap=null,hostPolicyBootstrap=null,codingPolicyBootstrap=null,desktopPolicyBootstrap=null,semanticLspBootstrap=null,enableMachineFs=false,enableObservation=false,enableDesktop=false,enableAutonomousSupervisor=false,hostCommandPins=[]}){
1458
+ stateRoot=resolveStateRoot(stateRoot);
1446
1459
  if(process.platform!=='win32') throw new Error('R27 slot update is Windows-only');
1447
1460
  const state=path.resolve(stateRoot),stageRoot=path.resolve(stage),development=assertDevelopmentRootSafe(developmentRoot,state);
1448
1461
  const key=await loadUpdateKey(state),updateId='r27-'+expectedManifest.slice(0,16)+'-'+Date.now().toString(36)+'-'+randomBytes(4).toString('hex');
@@ -1527,6 +1540,7 @@ export async function performSlotUpdate({stage,developmentRoot,expectedVersion,e
1527
1540
  }
1528
1541
 
1529
1542
  export async function performSlotRollback({activationReceipt,stateRoot=DEFAULT_STATE_ROOT}){
1543
+ stateRoot=resolveStateRoot(stateRoot);
1530
1544
  if(process.platform!=='win32') throw new Error('R31 slot rollback is Windows-only');
1531
1545
  const state=path.resolve(stateRoot),receiptFile=path.resolve(activationReceipt);
1532
1546
  const key=await loadUpdateKey(state);
@@ -1578,6 +1592,7 @@ export async function performSlotRollback({activationReceipt,stateRoot=DEFAULT_S
1578
1592
  }
1579
1593
 
1580
1594
  export async function runSlotUpdateCli(argv=process.argv.slice(2),{stateRoot=DEFAULT_STATE_ROOT}={}){
1595
+ stateRoot=resolveStateRoot(stateRoot);
1581
1596
  if(argv.includes('--rollback-receipt')){
1582
1597
  const args=parseRollbackArgs(argv);
1583
1598
  const result=await performSlotRollback({activationReceipt:args.activationReceipt,stateRoot});
@@ -0,0 +1,8 @@
1
+ import os from 'node:os';
2
+ import path from 'node:path';
3
+
4
+ export function resolveStateRoot(explicit,environment=process.env){
5
+ const value=explicit===undefined?(environment.DEADBYTE_STATE_ROOT||path.join(os.homedir(),'.deadbyte-mcp')):explicit;
6
+ if(typeof value!=='string'||value.length===0||value.includes('\0')) throw new Error('state root invalid');
7
+ return path.resolve(value);
8
+ }
package/src/trust.mjs CHANGED
@@ -9,11 +9,12 @@ import { mkdir, readFile, realpath, writeFile } from 'node:fs/promises';
9
9
  import os from 'node:os';
10
10
  import path from 'node:path';
11
11
  import { sha256Bytes } from './hash.mjs';
12
+ import { resolveStateRoot } from './state-root.mjs';
12
13
 
13
14
  const DOMAIN = Buffer.from('DEADBYTE-RECEIPT-V1\0', 'utf8');
14
15
 
15
16
  export function defaultTrustDir() {
16
- return path.join(os.homedir(), '.deadbyte-mcp', 'trust');
17
+ return path.join(resolveStateRoot(), 'trust');
17
18
  }
18
19
 
19
20
  export function defaultPrivateKeyPath() {
@@ -101,6 +101,7 @@ async function modernPassThrough() {
101
101
  cwd: repoRoot,
102
102
  env: {
103
103
  ...getDefaultEnvironment(),
104
+ ...(process.env.DEADBYTE_STATE_ROOT?{DEADBYTE_STATE_ROOT:path.resolve(process.env.DEADBYTE_STATE_ROOT)}:{}),
104
105
  DEADBYTE_WORKSPACE_ROOT: workspaceRoot,
105
106
  DEADBYTE_PRIVILEGED_POLICY: '',
106
107
  DEADBYTE_TUNNEL_STDIO_PROBE_COMPAT: '1'
package/src/version.mjs CHANGED
@@ -1,4 +1,4 @@
1
- export const DEADBYTE_VERSION = '0.15.29';
1
+ export const DEADBYTE_VERSION = '0.15.30';
2
2
  export const MCP_PROTOCOL_VERSION = '2026-07-28';
3
3
  export const RECEIPT_SCHEMA_V1 = 'deadbyte.receipt.v1';
4
4
  export const RECEIPT_SCHEMA_V2 = 'deadbyte.receipt.v2';
@@ -6,7 +6,7 @@ import {mkdtemp,readFile,rm,writeFile} from 'node:fs/promises';
6
6
  import os from 'node:os';
7
7
  import path from 'node:path';
8
8
 
9
- const oracleRoot='C:\\Users\\DEADBYTE\\.deadbyte-mcp\\evidence\\commander-biff-oracle';
9
+ const oracleRoot=process.env.DEADBYTE_BIFF_TEST_ORACLE||'C:\\Users\\DEADBYTE\\.deadbyte-mcp\\evidence\\commander-biff-oracle';
10
10
  const sha256=bytes=>createHash('sha256').update(bytes).digest('hex');
11
11
 
12
12
  test('pinned reference rejects a real BIFF8 workbook and writes OOXML bytes under .xls',async()=>{
@@ -34,15 +34,16 @@ test('R44 detached finalizer accepts only exact signed-R43-parent feature gate e
34
34
  assert.throws(()=>validateWindowsGateEvidence({...valid(),features:{...features(),skill_integrity:false}},manifest,parent),/skill_integrity/);
35
35
  });
36
36
 
37
- test('R44 detached finalizer isolates Windows PowerShell modules from the parent runtime',()=>{
37
+ test('R44 detached finalizer prioritizes native Windows modules and preserves custom paths',()=>{
38
38
  const env=windowsPowerShellEnvironment({
39
39
  SystemRoot:'C:\\Windows',USERPROFILE:'C:\\Users\\operator',ProgramFiles:'C:\\Program Files',
40
40
  PSModulePath:'C:\\poisoned-runtime-modules'
41
41
  });
42
42
  assert.equal(env.PSModulePath,[
43
+ 'C:\\Windows\\System32\\WindowsPowerShell\\v1.0\\Modules',
43
44
  'C:\\Users\\operator\\Documents\\WindowsPowerShell\\Modules',
44
45
  'C:\\Program Files\\WindowsPowerShell\\Modules',
45
- 'C:\\Windows\\System32\\WindowsPowerShell\\v1.0\\Modules'
46
+ 'C:\\poisoned-runtime-modules'
46
47
  ].join(';'));
47
- assert.doesNotMatch(env.PSModulePath,/poisoned-runtime-modules/);
48
+ assert.equal(env.PSModulePath.split(';')[0],'C:\\Windows\\System32\\WindowsPowerShell\\v1.0\\Modules');
48
49
  });
@@ -4,9 +4,11 @@ import test from 'node:test';
4
4
 
5
5
  const read = path => readFile(new URL(`../${path}`, import.meta.url), 'utf8');
6
6
 
7
- test('R52 canonical Windows gate is successor-bound and preserves R51/R50/R49 historical gates', async () => {
8
- const [pkgText, gate, evidence, historicalR51, historicalEvidenceR51, historicalR50, historicalR49, deferred] = await Promise.all([
7
+ test('R53 canonical Windows gate is successor-bound and preserves R52/R51/R50/R49 historical gates', async () => {
8
+ const [pkgText, gate, evidence, historicalR52, historicalEvidenceR52, historicalR51, historicalEvidenceR51, historicalR50, historicalR49, deferred] = await Promise.all([
9
9
  read('package.json'),
10
+ read('scripts/gate-windows-r53.ps1'),
11
+ read('scripts/windows-gate-evidence-r53.mjs'),
10
12
  read('scripts/gate-windows-r52.ps1'),
11
13
  read('scripts/windows-gate-evidence-r52.mjs'),
12
14
  read('scripts/gate-windows-r51.ps1'),
@@ -16,17 +18,20 @@ test('R52 canonical Windows gate is successor-bound and preserves R51/R50/R49 hi
16
18
  read('scripts/deferred-slot-operation-r46.mjs')
17
19
  ]);
18
20
  const pkg = JSON.parse(pkgText);
19
- assert.match(pkg.scripts['gate:windows'], /gate-windows-r52\.ps1$/);
20
- assert.match(gate, /DEADBYTE 0\.15\.29 \/ R52 WINDOWS GATE: PASS/);
21
- assert.match(gate, /r52-package-boundary/);
22
- assert.match(gate, /r52-focused-contracts/);
21
+ assert.match(pkg.scripts['gate:windows'], /gate-windows-r53\.ps1$/);
22
+ assert.match(gate, /DEADBYTE 0\.15\.30 \/ R53 WINDOWS GATE: PASS/);
23
+ assert.match(gate, /r53-package-boundary/);
24
+ assert.match(gate, /r53-focused-contracts/);
23
25
  assert.match(gate, /r49-remote-control-donors\.test\.mjs/);
24
26
  assert.match(gate, /desktop-runtime-r40\.test\.mjs/);
25
27
  assert.match(gate, /manifest-final-verify/);
26
- assert.match(evidence, /deadbyte\.r52-windows-gate\.v1/);
27
- assert.match(evidence, /pkg\.version === '0\.15\.29'/);
28
- assert.match(evidence, /release\.train === 'R52'/);
29
- assert.match(evidence, /source_predecessor\.version === '0\.15\.28'/);
28
+ assert.match(evidence, /deadbyte\.r53-windows-gate\.v1/);
29
+ assert.match(evidence, /pkg\.version === '0\.15\.30'/);
30
+ assert.match(evidence, /release\.train === 'R53'/);
31
+ assert.match(evidence, /release\.parent\.version === '0\.15\.29'/);
32
+ assert.match(evidence, /four_hour_soak_completed: false/);
33
+ assert.match(historicalR52, /DEADBYTE 0\.15\.29 \/ R52 WINDOWS GATE: PASS/);
34
+ assert.match(historicalEvidenceR52, /deadbyte\.r52-windows-gate\.v1/);
30
35
  assert.match(historicalR51, /DEADBYTE 0\.15\.28 \/ R51 WINDOWS GATE: PASS/);
31
36
  assert.match(historicalEvidenceR51, /deadbyte\.r51-windows-gate\.v1/);
32
37
  assert.match(historicalR50, /DEADBYTE 0\.15\.27 \/ R50 WINDOWS GATE: PASS/);
@@ -5,8 +5,12 @@ import { readFile } from 'node:fs/promises';
5
5
  const root=new URL('../',import.meta.url);
6
6
  const read=rel=>readFile(new URL(rel,root),'utf8');
7
7
  const json=rel=>read(rel).then(JSON.parse);
8
+ const current=await json('package.json');
9
+ const historical=current.version!=='0.15.29';
10
+ const skipHistorical=t=>{if(historical){t.skip('R52 historical release contract is frozen at 0.15.29; current successor is '+current.version);return true;}return false;};
8
11
 
9
- test('R52 current release identity is coherent across package lock source metadata SBOM and gate',async()=>{
12
+ test('R52 current release identity is coherent across package lock source metadata SBOM and gate',async t=>{
13
+ if(skipHistorical(t))return;
10
14
  const [pkg,lock,release,provenance,matrix,sbom,versionSource]=await Promise.all([
11
15
  json('package.json'),json('package-lock.json'),json('RELEASE.json'),json('PROVENANCE.json'),json('TEST-MATRIX.json'),json('SBOM.cdx.json'),read('src/version.mjs')
12
16
  ]);
@@ -25,7 +29,8 @@ test('R52 current release identity is coherent across package lock source metada
25
29
  assert.match(pkg.scripts['r52:focused:test'],/r52-offline-predecessor-recovery\.test\.mjs/);
26
30
  });
27
31
 
28
- test('R52 lineage binds published 0.15.28 parent separately from 0.15.27 runtime rollback target',async()=>{
32
+ test('R52 lineage binds published 0.15.28 parent separately from 0.15.27 runtime rollback target',async t=>{
33
+ if(skipHistorical(t))return;
29
34
  const [release,provenance]=await Promise.all([json('RELEASE.json'),json('PROVENANCE.json')]);
30
35
  const activeParent={
31
36
  release_id:'v0.15.27-1b1dbe24b45ad06a',
@@ -52,7 +57,8 @@ test('R52 lineage binds published 0.15.28 parent separately from 0.15.27 runtime
52
57
  assert.equal(provenance.source_predecessor_activation_failure_receipt_sha256,'090f165c96da0d5a8e9c0815fd389fe94e8fe36c23a6878ee825b8f46bfb0bc2');
53
58
  });
54
59
 
55
- test('R52 advertises updater-owned offline predecessor Host revocation only after implementation',async()=>{
60
+ test('R52 advertises updater-owned offline predecessor Host revocation only after implementation',async t=>{
61
+ if(skipHistorical(t))return;
56
62
  const release=await json('RELEASE.json');
57
63
  assert.equal(release.capabilities.offline_predecessor_host_revoke_before_capture,true);
58
64
  });
@@ -0,0 +1,66 @@
1
+ import test from 'node:test';
2
+ import assert from 'node:assert/strict';
3
+ import { readFile } from 'node:fs/promises';
4
+
5
+ const root=new URL('../',import.meta.url);
6
+ const read=rel=>readFile(new URL(rel,root),'utf8');
7
+ const json=rel=>read(rel).then(JSON.parse);
8
+
9
+ test('R53 current release identity is coherent across package lock source metadata SBOM and gate',async()=>{
10
+ const [pkg,lock,release,provenance,matrix,sbom,versionSource]=await Promise.all([
11
+ json('package.json'),json('package-lock.json'),json('RELEASE.json'),json('PROVENANCE.json'),json('TEST-MATRIX.json'),json('SBOM.cdx.json'),read('src/version.mjs')
12
+ ]);
13
+ assert.equal(pkg.version,'0.15.30');
14
+ assert.equal(lock.version,pkg.version);
15
+ assert.equal(lock.packages[''].version,pkg.version);
16
+ assert.match(versionSource,/DEADBYTE_VERSION = '0\.15\.30'/);
17
+ assert.equal(release.version,pkg.version);
18
+ assert.equal(release.train,'R53');
19
+ assert.equal(provenance.version,pkg.version);
20
+ assert.equal(matrix.version,pkg.version);
21
+ assert.equal(matrix.train,'R53');
22
+ assert.equal(sbom.metadata.component.version,pkg.version);
23
+ assert.equal(sbom.serialNumber,'urn:cdx:deadbyte-mcp:r53:0.15.30');
24
+ assert.match(pkg.scripts['gate:windows'],/gate-windows-r53\.ps1/);
25
+ assert.match(pkg.scripts['r53:focused:test'],/stabilization-semver\.test\.mjs/);
26
+ });
27
+
28
+ test('R53 lineage binds exact published 0.15.29 as parent and runtime predecessor',async()=>{
29
+ const [release,provenance]=await Promise.all([json('RELEASE.json'),json('PROVENANCE.json')]);
30
+ const parent={
31
+ release_id:'v0.15.29-f74eaa1b3d00e6fb',
32
+ manifest_sha256:'f74eaa1b3d00e6fb5a36b2358efb815e3686c9fe9dbd3b84d25a713ef957748c',
33
+ generation_manifest_sha256:'289b4fc065de0681da6864cc1bbe09aab198ca804945ffc27c786438dad13f5a'
34
+ };
35
+ assert.deepEqual(release.runtime_predecessor,parent);
36
+ assert.equal(release.parent.version,'0.15.29');
37
+ assert.equal(release.parent.release_id,parent.release_id);
38
+ assert.equal(release.parent.manifest_sha256,parent.manifest_sha256);
39
+ assert.equal(release.parent.generation_manifest_sha256,parent.generation_manifest_sha256);
40
+ assert.equal(release.parent.tarball_sha256,'f535d7121a384bed106d34bc7525afa9d4048c7e290f270699e25136d1e82eeb');
41
+ assert.equal(release.source_predecessor.version,'0.15.29');
42
+ assert.equal(release.source_predecessor.tarball_sha256,release.parent.tarball_sha256);
43
+ assert.equal(release.source_predecessor.manifest_file_sha256,release.parent.manifest_sha256);
44
+ assert.equal(release.source_predecessor.generation_manifest_sha256,release.parent.generation_manifest_sha256);
45
+ assert.equal(provenance.parent_release_id,parent.release_id);
46
+ assert.equal(provenance.parent_manifest_sha256,parent.manifest_sha256);
47
+ assert.equal(provenance.parent_generation_manifest_sha256,parent.generation_manifest_sha256);
48
+ assert.equal(provenance.parent_tarball_sha256,release.parent.tarball_sha256);
49
+ assert.deepEqual(provenance.runtime_predecessor,parent);
50
+ });
51
+
52
+ test('R53 records stabilization evidence and explicit soak bypass without STABLE/FINAL claim',async()=>{
53
+ const [release,provenance,matrix]=await Promise.all([json('RELEASE.json'),json('PROVENANCE.json'),json('TEST-MATRIX.json')]);
54
+ assert.equal(release.stabilization.semver_strict_validation,true);
55
+ assert.equal(release.stabilization.powershell_child_environment_normalized,true);
56
+ assert.equal(release.stabilization.explicit_state_root_propagation,true);
57
+ assert.equal(release.stabilization.guardian_cleanup_fail_closed,true);
58
+ assert.equal(release.stabilization.local_isolated_state_acceptance,'PASS');
59
+ assert.equal(release.stabilization.full_regression,'PASS');
60
+ assert.equal(release.stabilization.four_hour_soak,'NOT_COMPLETED_OPERATOR_BYPASS');
61
+ assert.equal(release.stabilization.stable_final_claim,false);
62
+ assert.equal(provenance.operator_override.four_hour_soak,'NOT_COMPLETED');
63
+ assert.equal(provenance.operator_override.stable_final_claim,false);
64
+ assert.equal(matrix.operator_override.four_hour_soak,'NOT_COMPLETED');
65
+ assert.equal(matrix.operator_override.stable_final_claim,false);
66
+ });
@@ -104,7 +104,8 @@ test('R27 active pointer is schema bound and atomically replaceable', async () =
104
104
  test('R27 stable launcher resolves only the active pointer, not a release hardcode', () => {
105
105
  const cmd=renderStableLauncherCmd();
106
106
  const ps1=renderStableLauncherPs1();
107
- assert.match(cmd,/\.deadbyte-mcp\\bin\\DEADBYTE-MCP\.ps1/i);
107
+ assert.match(cmd,/if not defined DEADBYTE_STATE_ROOT set "DEADBYTE_STATE_ROOT=%USERPROFILE%\\\.deadbyte-mcp"/i);
108
+ assert.match(cmd,/%DEADBYTE_STATE_ROOT%\\bin\\DEADBYTE-MCP\.ps1/i);
108
109
  assert.doesNotMatch(cmd,/deadbyte-mcp-v0\.6\.0/i);
109
110
  assert.match(ps1,/active-release\.json/);
110
111
  assert.match(ps1,/DEADBYTE_CONTROLLER_CONFIG/);