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.
- package/MANIFEST.SHA256 +60 -48
- package/PROVENANCE.json +19 -14
- package/README.txt +14 -14
- package/RELEASE.json +25 -13
- package/SBOM.cdx.json +3 -3
- package/STABILIZATION-PROGRESS.md +60 -0
- package/TEST-MATRIX.json +46 -101
- package/bin/appcontainer-stage.obj +0 -0
- package/bin/bootstrap-advapi32.obj +0 -0
- package/bin/bootstrap-exitcode.obj +0 -0
- package/bin/bootstrap-kernel32.obj +0 -0
- package/bin/child-control-probe.obj +0 -0
- package/bin/child-control-stage.obj +0 -0
- package/bin/contained-reverse-worker.obj +0 -0
- package/bin/contained-transform-worker.obj +0 -0
- package/bin/containment-probe.obj +0 -0
- package/bin/deadbyte-contain.obj +0 -0
- package/bin/deadbyte-exec.obj +0 -0
- package/bin/deadbyte-hardware-observer-r45.obj +0 -0
- package/bin/deadbyte-process-host.obj +0 -0
- package/bin/deadbyte-tunnel-host.obj +0 -0
- package/controller/deadbyte-desktop-policy.json +5 -5
- package/controller/deadbyte-host-policy.json +170 -0
- package/controller/deadbyte-process-policy.json +4 -4
- package/package.json +4 -3
- package/proof/CONTAINMENT-BUILD.txt +6 -6
- package/scripts/gate-windows-r53.ps1 +58 -0
- package/scripts/verify-r42-parent.mjs +1 -1
- package/scripts/verify-r43-parent.mjs +1 -1
- package/scripts/verify-r44-parent.mjs +1 -1
- package/scripts/verify-r45-parent.mjs +1 -1
- package/scripts/windows-gate-evidence-r53.mjs +102 -0
- package/src/authority-smoke.mjs +1 -0
- package/src/deadbyte-cli.mjs +4 -2
- package/src/doctor-r45.mjs +2 -1
- package/src/machine-fs-smoke-client.mjs +1 -0
- package/src/mcp-contained-smoke-client.mjs +1 -0
- package/src/mcp-host-smoke-client.mjs +1 -0
- package/src/mcp-smoke-client.mjs +1 -0
- package/src/process-smoke-client.mjs +1 -0
- package/src/release-slot.mjs +5 -2
- package/src/remote-session-guardian.mjs +12 -4
- package/src/runtime-coherence.mjs +13 -4
- package/src/runtime-update.mjs +20 -5
- package/src/state-root.mjs +8 -0
- package/src/trust.mjs +2 -1
- package/src/tunnel-stdio-compat-smoke.mjs +1 -0
- package/src/version.mjs +1 -1
- package/test/commander-biff-reference-r48.test.mjs +1 -1
- package/test/r44-finalizer.test.mjs +4 -3
- package/test/r49-windows-gate.test.mjs +15 -10
- package/test/r52-release-contract.test.mjs +9 -3
- package/test/r53-release-contract.test.mjs +66 -0
- package/test/release-slot.test.mjs +2 -1
- package/test/skill-mcp-r44.test.mjs +3 -0
- package/test/stabilization-guardian.test.mjs +25 -0
- package/test/stabilization-parent-context.test.mjs +18 -0
- package/test/stabilization-powershell.test.mjs +26 -0
- package/test/stabilization-semver.test.mjs +23 -0
- package/test/stabilization-smoke-root.test.mjs +23 -0
- package/test/stabilization-state-root.test.mjs +60 -0
- 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 }));
|
package/src/authority-smoke.mjs
CHANGED
|
@@ -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
|
package/src/deadbyte-cli.mjs
CHANGED
|
@@ -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
|
|
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{
|
package/src/doctor-r45.mjs
CHANGED
|
@@ -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=()=>
|
|
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,
|
package/src/mcp-smoke-client.mjs
CHANGED
|
@@ -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 },
|
package/src/release-slot.mjs
CHANGED
|
@@ -195,7 +195,8 @@ export function renderStableLauncherCmd(){
|
|
|
195
195
|
return [
|
|
196
196
|
'@echo off',
|
|
197
197
|
'setlocal EnableExtensions DisableDelayedExpansion',
|
|
198
|
-
'
|
|
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=
|
|
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
|
-
|
|
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=/^(
|
|
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:
|
|
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
|
-
|
|
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`);
|
package/src/runtime-update.mjs
CHANGED
|
@@ -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=
|
|
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
|
-
|
|
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
|
-
|
|
202
|
-
]
|
|
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(
|
|
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
|
@@ -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
|
|
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:\\
|
|
46
|
+
'C:\\poisoned-runtime-modules'
|
|
46
47
|
].join(';'));
|
|
47
|
-
assert.
|
|
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('
|
|
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-
|
|
20
|
-
assert.match(gate, /DEADBYTE 0\.15\.
|
|
21
|
-
assert.match(gate, /
|
|
22
|
-
assert.match(gate, /
|
|
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\.
|
|
27
|
-
assert.match(evidence, /pkg\.version === '0\.15\.
|
|
28
|
-
assert.match(evidence, /release\.train === '
|
|
29
|
-
assert.match(evidence, /
|
|
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
|
|
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/);
|