deadbyte-mcp 0.15.30 → 0.15.32
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 +19 -11
- package/PROVENANCE.json +22 -15
- package/README.txt +22 -14
- package/RELEASE.json +25 -16
- package/SBOM.cdx.json +3 -3
- package/TEST-MATRIX.json +50 -51
- package/controller/deadbyte-process-policy.json +4 -4
- package/package.json +5 -3
- package/scripts/gate-windows-r54.ps1 +26 -0
- package/scripts/gate-windows-r55.ps1 +27 -0
- package/scripts/windows-gate-evidence-r54.mjs +76 -0
- package/scripts/windows-gate-evidence-r55.mjs +79 -0
- package/src/runtime-update.mjs +44 -8
- package/src/version.mjs +1 -1
- package/test/r49-windows-gate.test.mjs +38 -30
- package/test/r53-release-contract.test.mjs +5 -3
- package/test/r54-offline-predecessor-self-exclusion.test.mjs +56 -0
- package/test/r54-release-contract.test.mjs +61 -0
- package/test/r55-offline-predecessor-guardian-family.test.mjs +51 -0
- package/test/r55-release-contract.test.mjs +61 -0
- package/controller/deadbyte-host-policy.json +0 -170
|
@@ -0,0 +1,76 @@
|
|
|
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.31'&&release.version===pkg.version&&release.train==='R54','R54 candidate identity mismatch');
|
|
20
|
+
insist(release.parent.version==='0.15.30','R54 source parent mismatch');
|
|
21
|
+
insist(release.parent.manifest_sha256==='ddf7c7338d9a371f5667d7b138695e7f9cfced553d14073ac5a44fd2fe0a0399','R54 source parent manifest mismatch');
|
|
22
|
+
insist(release.parent.tarball_sha256==='72881981bf76eb45527946b310caed8387943e2ca8e37ea6e606c32189dd5a70','R54 source parent tarball mismatch');
|
|
23
|
+
insist(
|
|
24
|
+
active.release_id===release.runtime_predecessor.release_id&&
|
|
25
|
+
active.manifest_sha256===release.runtime_predecessor.manifest_sha256&&
|
|
26
|
+
active.generation_manifest_sha256===release.runtime_predecessor.generation_manifest_sha256,
|
|
27
|
+
'active runtime is not exact sealed 0.15.29 predecessor'
|
|
28
|
+
);
|
|
29
|
+
insist(release.hotfix?.activation_ancestry_excluded===true,'R54 activation ancestry fix missing');
|
|
30
|
+
insist(release.hotfix?.unrelated_stale_runtime_fail_closed===true,'R54 stale-runtime negative control missing');
|
|
31
|
+
insist(provenance.operator_override?.stable_final_claim===false,'R54 must not claim stable/final');
|
|
32
|
+
insist(matrix.operator_override?.four_hour_soak==='NOT_COMPLETED','R54 matrix must preserve soak bypass');
|
|
33
|
+
|
|
34
|
+
const health=await doctor();
|
|
35
|
+
insist(health.status==='ok'&&health.package_version===pkg.version&&health.manifest?.verified===true,'R54 doctor evidence invalid');
|
|
36
|
+
|
|
37
|
+
const evidence={
|
|
38
|
+
schema:'deadbyte.r54-windows-gate.v1',
|
|
39
|
+
status:'passed',
|
|
40
|
+
exit_code:0,
|
|
41
|
+
version:pkg.version,
|
|
42
|
+
train:'R54',
|
|
43
|
+
completed_at_utc:new Date().toISOString(),
|
|
44
|
+
manifest_sha256:manifestSha256,
|
|
45
|
+
parent:release.parent,
|
|
46
|
+
runtime_predecessor:release.runtime_predecessor,
|
|
47
|
+
hotfix:release.hotfix,
|
|
48
|
+
operator_override:provenance.operator_override,
|
|
49
|
+
active_predecessor:{
|
|
50
|
+
release_id:active.release_id,
|
|
51
|
+
version:active.version,
|
|
52
|
+
manifest_sha256:active.manifest_sha256,
|
|
53
|
+
generation_manifest_sha256:active.generation_manifest_sha256
|
|
54
|
+
},
|
|
55
|
+
doctor:{verified_entries:health.manifest.verified_entries},
|
|
56
|
+
gates:{
|
|
57
|
+
r54_focused:true,
|
|
58
|
+
package_boundary:true,
|
|
59
|
+
full_regression:true,
|
|
60
|
+
manifest_final_verify:true,
|
|
61
|
+
four_hour_soak_completed:false
|
|
62
|
+
}
|
|
63
|
+
};
|
|
64
|
+
const bytes=Buffer.from(JSON.stringify(evidence,null,2)+'\n');
|
|
65
|
+
const dir=path.join(stateRoot,'evidence','release');
|
|
66
|
+
await mkdir(dir,{recursive:true});
|
|
67
|
+
const target=path.join(dir,`r54-windows-gate-${manifestSha256.slice(0,16)}.json`);
|
|
68
|
+
try{
|
|
69
|
+
const handle=await open(target,'wx',0o600);
|
|
70
|
+
try{await handle.writeFile(bytes);await handle.sync();}finally{await handle.close();}
|
|
71
|
+
}catch(error){
|
|
72
|
+
if(error?.code!=='EEXIST')throw error;
|
|
73
|
+
const prior=JSON.parse(await readFile(target,'utf8'));
|
|
74
|
+
insist(prior.schema===evidence.schema&&prior.status==='passed'&&prior.version===evidence.version&&prior.manifest_sha256===manifestSha256,'conflicting R54 gate evidence identity');
|
|
75
|
+
}
|
|
76
|
+
console.log(JSON.stringify({status:'passed',path:target,sha256:sha256(bytes),manifest_sha256:manifestSha256}));
|
|
@@ -0,0 +1,79 @@
|
|
|
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.32'&&release.version===pkg.version&&release.train==='R55','R55 candidate identity mismatch');
|
|
20
|
+
insist(release.parent.version==='0.15.31','R55 source parent mismatch');
|
|
21
|
+
insist(release.parent.manifest_sha256==='16854fe5b3eb0fa1c6dd2b96e3c5d6f792c5adbc91dd2a71e7fc2771472bbdd4','R55 source parent manifest mismatch');
|
|
22
|
+
insist(release.parent.tarball_sha256==='ff6eba3036cb00ac639ac8907ad6063e6ea8169cf32aca8721f75b1e403e501a','R55 source parent tarball mismatch');
|
|
23
|
+
insist(
|
|
24
|
+
active.release_id===release.runtime_predecessor.release_id&&
|
|
25
|
+
active.manifest_sha256===release.runtime_predecessor.manifest_sha256&&
|
|
26
|
+
active.generation_manifest_sha256===release.runtime_predecessor.generation_manifest_sha256,
|
|
27
|
+
'active runtime is not exact sealed 0.15.29 predecessor'
|
|
28
|
+
);
|
|
29
|
+
insist(release.hotfix?.foreground_guardian_owner_parent_binding===true,'R55 guardian owner/parent binding missing');
|
|
30
|
+
insist(release.hotfix?.foreground_guardian_session_nonce_contract===true,'R55 guardian nonce contract missing');
|
|
31
|
+
insist(release.hotfix?.unrelated_stale_runtime_fail_closed===true,'R55 stale-runtime negative control missing');
|
|
32
|
+
insist(release.hotfix?.full_regression==='PASS','R55 full regression metadata is not PASS');
|
|
33
|
+
insist(matrix.observed_pre_release_evidence?.full_regression?.status==='PASS','R55 full regression evidence is not PASS');
|
|
34
|
+
insist(provenance.operator_override?.stable_final_claim===false,'R55 must not claim stable/final');
|
|
35
|
+
insist(matrix.operator_override?.four_hour_soak==='NOT_COMPLETED','R55 matrix must preserve soak bypass');
|
|
36
|
+
|
|
37
|
+
const health=await doctor();
|
|
38
|
+
insist(health.status==='ok'&&health.package_version===pkg.version&&health.manifest?.verified===true,'R55 doctor evidence invalid');
|
|
39
|
+
|
|
40
|
+
const evidence={
|
|
41
|
+
schema:'deadbyte.r55-windows-gate.v1',
|
|
42
|
+
status:'passed',
|
|
43
|
+
exit_code:0,
|
|
44
|
+
version:pkg.version,
|
|
45
|
+
train:'R55',
|
|
46
|
+
completed_at_utc:new Date().toISOString(),
|
|
47
|
+
manifest_sha256:manifestSha256,
|
|
48
|
+
parent:release.parent,
|
|
49
|
+
runtime_predecessor:release.runtime_predecessor,
|
|
50
|
+
hotfix:release.hotfix,
|
|
51
|
+
operator_override:provenance.operator_override,
|
|
52
|
+
active_predecessor:{
|
|
53
|
+
release_id:active.release_id,
|
|
54
|
+
version:active.version,
|
|
55
|
+
manifest_sha256:active.manifest_sha256,
|
|
56
|
+
generation_manifest_sha256:active.generation_manifest_sha256
|
|
57
|
+
},
|
|
58
|
+
doctor:{verified_entries:health.manifest.verified_entries},
|
|
59
|
+
gates:{
|
|
60
|
+
r55_focused:true,
|
|
61
|
+
package_boundary:true,
|
|
62
|
+
full_regression:true,
|
|
63
|
+
manifest_final_verify:true,
|
|
64
|
+
four_hour_soak_completed:false
|
|
65
|
+
}
|
|
66
|
+
};
|
|
67
|
+
const bytes=Buffer.from(JSON.stringify(evidence,null,2)+'\n');
|
|
68
|
+
const dir=path.join(stateRoot,'evidence','release');
|
|
69
|
+
await mkdir(dir,{recursive:true});
|
|
70
|
+
const target=path.join(dir,`r55-windows-gate-${manifestSha256.slice(0,16)}.json`);
|
|
71
|
+
try{
|
|
72
|
+
const handle=await open(target,'wx',0o600);
|
|
73
|
+
try{await handle.writeFile(bytes);await handle.sync();}finally{await handle.close();}
|
|
74
|
+
}catch(error){
|
|
75
|
+
if(error?.code!=='EEXIST')throw error;
|
|
76
|
+
const prior=JSON.parse(await readFile(target,'utf8'));
|
|
77
|
+
insist(prior.schema===evidence.schema&&prior.status==='passed'&&prior.version===evidence.version&&prior.manifest_sha256===manifestSha256,'conflicting R55 gate evidence identity');
|
|
78
|
+
}
|
|
79
|
+
console.log(JSON.stringify({status:'passed',path:target,sha256:sha256(bytes),manifest_sha256:manifestSha256}));
|
package/src/runtime-update.mjs
CHANGED
|
@@ -647,6 +647,48 @@ async function readLedger(ctx,name){
|
|
|
647
647
|
function sameGeneration(a,b){
|
|
648
648
|
return a.pid===b.pid&&a.instance_nonce===b.instance_nonce&&a.creation_time_utc===b.creation_time_utc&&a.image_sha256===b.image_sha256;
|
|
649
649
|
}
|
|
650
|
+
function activationGuardianOwnedByLineage(item,selfLineage){
|
|
651
|
+
const name=String(item?.Name??'').toLowerCase();
|
|
652
|
+
if(name!=='node.exe') return false;
|
|
653
|
+
const commandLine=String(item?.CommandLine??'');
|
|
654
|
+
if(!commandLine.toLowerCase().includes('remote-session-guardian.mjs')) return false;
|
|
655
|
+
const ownerMatch=/(?:^|\s)--owner-pid(?:=|\s+)(\d+)(?=\s|$)/i.exec(commandLine);
|
|
656
|
+
const nonceMatch=/(?:^|\s)--session-nonce(?:=|\s+)([0-9a-f]{64})(?=\s|$)/i.exec(commandLine);
|
|
657
|
+
if(!ownerMatch||!nonceMatch) return false;
|
|
658
|
+
const ownerPid=Number(ownerMatch[1]);
|
|
659
|
+
const parentPid=Number(item?.ParentProcessId);
|
|
660
|
+
return Number.isInteger(ownerPid)&&ownerPid>0&&parentPid===ownerPid&&selfLineage.has(ownerPid);
|
|
661
|
+
}
|
|
662
|
+
export function selectOfflineRecoveryBlockers(processes,currentPid=process.pid){
|
|
663
|
+
const list=(Array.isArray(processes)?processes:[processes]).filter(Boolean);
|
|
664
|
+
const byPid=new Map();
|
|
665
|
+
for(const item of list){
|
|
666
|
+
const pid=Number(item?.ProcessId);
|
|
667
|
+
if(Number.isInteger(pid)&&pid>0) byPid.set(pid,item);
|
|
668
|
+
}
|
|
669
|
+
const selfLineage=new Set();
|
|
670
|
+
let cursor=Number(currentPid);
|
|
671
|
+
while(Number.isInteger(cursor)&&cursor>0&&!selfLineage.has(cursor)){
|
|
672
|
+
selfLineage.add(cursor);
|
|
673
|
+
const item=byPid.get(cursor);
|
|
674
|
+
if(!item) break;
|
|
675
|
+
const parent=Number(item?.ParentProcessId);
|
|
676
|
+
if(!Number.isInteger(parent)||parent<1||parent===cursor) break;
|
|
677
|
+
cursor=parent;
|
|
678
|
+
}
|
|
679
|
+
const runtimeNames=new Set(['node.exe','cloudflared.exe','deadbyte-tunnel-host.exe']);
|
|
680
|
+
return list.filter(item=>{
|
|
681
|
+
const pid=Number(item?.ProcessId);
|
|
682
|
+
if(selfLineage.has(pid)) return false;
|
|
683
|
+
if(activationGuardianOwnedByLineage(item,selfLineage)) return false;
|
|
684
|
+
const name=String(item?.Name??'').toLowerCase();
|
|
685
|
+
if(!runtimeNames.has(name)) return false;
|
|
686
|
+
const commandLine=String(item?.CommandLine??'').toLowerCase();
|
|
687
|
+
return commandLine.includes('deadbyte-mcp')||commandLine.includes('mcp-server.mjs')||
|
|
688
|
+
commandLine.includes('runtime-self-update.mjs')||commandLine.includes('remote-session-guardian.mjs')||
|
|
689
|
+
commandLine.includes('deadbyte-tunnel-host.exe');
|
|
690
|
+
});
|
|
691
|
+
}
|
|
650
692
|
function cleanOfflinePredecessor(ctx){
|
|
651
693
|
const runtimeRoot=path.resolve(ctx.runtime_root);
|
|
652
694
|
const blockers=[
|
|
@@ -656,19 +698,13 @@ function cleanOfflinePredecessor(ctx){
|
|
|
656
698
|
].filter(name=>existsSync(path.join(runtimeRoot,name)));
|
|
657
699
|
if(blockers.length) throw new Error('offline predecessor recovery blocked by runtime state: '+blockers.join(','));
|
|
658
700
|
|
|
659
|
-
const command="Get-CimInstance Win32_Process |
|
|
701
|
+
const command="Get-CimInstance Win32_Process | Select-Object ProcessId,ParentProcessId,Name,ExecutablePath,CommandLine | ConvertTo-Json -Compress";
|
|
660
702
|
const result=runCaptured(powershellPath(),['-NoLogo','-NoProfile','-Command',command],{timeoutMs:15000});
|
|
661
703
|
const text=assertRun(result,'runtime process query').trim();
|
|
662
704
|
if(text){
|
|
663
705
|
let parsed;
|
|
664
706
|
try { parsed=JSON.parse(text); } catch(error){ throw new Error('runtime process query JSON invalid: '+error.message); }
|
|
665
|
-
const
|
|
666
|
-
const relevant=processes.filter(item=>{
|
|
667
|
-
const commandLine=String(item?.CommandLine??'').toLowerCase();
|
|
668
|
-
return commandLine.includes('deadbyte-mcp')||commandLine.includes('mcp-server.mjs')||
|
|
669
|
-
commandLine.includes('runtime-self-update.mjs')||commandLine.includes('remote-session-guardian.mjs')||
|
|
670
|
-
commandLine.includes('deadbyte-tunnel-host.exe');
|
|
671
|
-
});
|
|
707
|
+
const relevant=selectOfflineRecoveryBlockers(Array.isArray(parsed)?parsed:[parsed],process.pid);
|
|
672
708
|
if(relevant.length) throw new Error('offline predecessor recovery blocked by DEADBYTE runtime processes: '+relevant.map(item=>String(item?.Name??'unknown')+'#'+String(item?.ProcessId??'?')).join(','));
|
|
673
709
|
}
|
|
674
710
|
return {
|
package/src/version.mjs
CHANGED
|
@@ -2,11 +2,15 @@ import assert from 'node:assert/strict';
|
|
|
2
2
|
import { readFile } from 'node:fs/promises';
|
|
3
3
|
import test from 'node:test';
|
|
4
4
|
|
|
5
|
-
const read
|
|
5
|
+
const read=path=>readFile(new URL(`../${path}`,import.meta.url),'utf8');
|
|
6
6
|
|
|
7
|
-
test('
|
|
8
|
-
const [pkgText,
|
|
7
|
+
test('R55 canonical Windows gate is successor-bound and preserves R54/R53/R52/R51/R50/R49 historical gates',async()=>{
|
|
8
|
+
const [pkgText,gate,evidence,historicalR54,historicalEvidenceR54,historicalR53,historicalEvidenceR53,historicalR52,historicalEvidenceR52,historicalR51,historicalEvidenceR51,historicalR50,historicalR49,deferred]=await Promise.all([
|
|
9
9
|
read('package.json'),
|
|
10
|
+
read('scripts/gate-windows-r55.ps1'),
|
|
11
|
+
read('scripts/windows-gate-evidence-r55.mjs'),
|
|
12
|
+
read('scripts/gate-windows-r54.ps1'),
|
|
13
|
+
read('scripts/windows-gate-evidence-r54.mjs'),
|
|
10
14
|
read('scripts/gate-windows-r53.ps1'),
|
|
11
15
|
read('scripts/windows-gate-evidence-r53.mjs'),
|
|
12
16
|
read('scripts/gate-windows-r52.ps1'),
|
|
@@ -17,31 +21,35 @@ test('R53 canonical Windows gate is successor-bound and preserves R52/R51/R50/R4
|
|
|
17
21
|
read('scripts/gate-windows-r49.ps1'),
|
|
18
22
|
read('scripts/deferred-slot-operation-r46.mjs')
|
|
19
23
|
]);
|
|
20
|
-
const pkg
|
|
21
|
-
assert.match(pkg.scripts['gate:windows']
|
|
22
|
-
assert.match(gate
|
|
23
|
-
assert.match(gate
|
|
24
|
-
assert.match(gate
|
|
25
|
-
assert.match(gate
|
|
26
|
-
assert.match(gate
|
|
27
|
-
assert.match(gate
|
|
28
|
-
assert.match(evidence
|
|
29
|
-
assert.match(evidence
|
|
30
|
-
assert.match(evidence
|
|
31
|
-
assert.match(evidence
|
|
32
|
-
assert.match(evidence
|
|
33
|
-
assert.match(
|
|
34
|
-
assert.match(
|
|
35
|
-
assert.match(
|
|
36
|
-
assert.match(
|
|
37
|
-
assert.match(
|
|
38
|
-
assert.match(
|
|
39
|
-
assert.match(
|
|
40
|
-
assert.match(
|
|
41
|
-
assert.match(
|
|
42
|
-
assert.match(
|
|
43
|
-
assert.match(deferred
|
|
44
|
-
assert.match(deferred,
|
|
45
|
-
assert.match(deferred
|
|
46
|
-
assert.
|
|
24
|
+
const pkg=JSON.parse(pkgText);
|
|
25
|
+
assert.match(pkg.scripts['gate:windows'],/gate-windows-r55\.ps1$/);
|
|
26
|
+
assert.match(gate,/DEADBYTE 0\.15\.32 \/ R55 WINDOWS GATE: PASS/);
|
|
27
|
+
assert.match(gate,/r55-package-boundary/);
|
|
28
|
+
assert.match(gate,/r55-focused-contracts/);
|
|
29
|
+
assert.match(gate,/full-regression/);
|
|
30
|
+
assert.match(gate,/manifest-verify/);
|
|
31
|
+
assert.match(gate,/doctor/);
|
|
32
|
+
assert.match(evidence,/deadbyte\.r55-windows-gate\.v1/);
|
|
33
|
+
assert.match(evidence,/pkg\.version==='0\.15\.32'/);
|
|
34
|
+
assert.match(evidence,/release\.train==='R55'/);
|
|
35
|
+
assert.match(evidence,/release\.parent\.version==='0\.15\.31'/);
|
|
36
|
+
assert.match(evidence,/four_hour_soak_completed:false/);
|
|
37
|
+
assert.match(historicalR54,/DEADBYTE 0\.15\.31 \/ R54 WINDOWS GATE: PASS/);
|
|
38
|
+
assert.match(historicalEvidenceR54,/deadbyte\.r54-windows-gate\.v1/);
|
|
39
|
+
assert.match(historicalR53,/DEADBYTE 0\.15\.30 \/ R53 WINDOWS GATE: PASS/);
|
|
40
|
+
assert.match(historicalEvidenceR53,/deadbyte\.r53-windows-gate\.v1/);
|
|
41
|
+
assert.match(historicalR52,/DEADBYTE 0\.15\.29 \/ R52 WINDOWS GATE: PASS/);
|
|
42
|
+
assert.match(historicalEvidenceR52,/deadbyte\.r52-windows-gate\.v1/);
|
|
43
|
+
assert.match(historicalR51,/DEADBYTE 0\.15\.28 \/ R51 WINDOWS GATE: PASS/);
|
|
44
|
+
assert.match(historicalEvidenceR51,/deadbyte\.r51-windows-gate\.v1/);
|
|
45
|
+
assert.match(historicalR50,/DEADBYTE 0\.15\.27 \/ R50 WINDOWS GATE: PASS/);
|
|
46
|
+
assert.match(historicalR49,/DEADBYTE 0\.15\.26 \/ R49 WINDOWS GATE: PASS/);
|
|
47
|
+
assert.match(deferred,/const expectedVersion=packageIdentity\.version/);
|
|
48
|
+
assert.match(deferred,/'--expected-version',expectedVersion/);
|
|
49
|
+
assert.match(deferred,/async function awaitHandoffCompletion/);
|
|
50
|
+
assert.match(deferred,/verifySlotUpdateEnvelope\(envelope,publicKey\)/);
|
|
51
|
+
assert.match(deferred,/active pointer does not match signed activation receipt/);
|
|
52
|
+
assert.match(deferred,/failure-receipt\.json/);
|
|
53
|
+
assert.match(deferred,/await awaitHandoffCompletion\(await runSlotUpdateCli/);
|
|
54
|
+
assert.doesNotMatch(deferred,/'--expected-version','0\.15\.5'/);
|
|
47
55
|
});
|
|
@@ -5,8 +5,10 @@ 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 currentPackage=await json('package.json');
|
|
9
|
+
const r53test=currentPackage.version==='0.15.30'?test:test.skip;
|
|
8
10
|
|
|
9
|
-
|
|
11
|
+
r53test('R53 current release identity is coherent across package lock source metadata SBOM and gate',async()=>{
|
|
10
12
|
const [pkg,lock,release,provenance,matrix,sbom,versionSource]=await Promise.all([
|
|
11
13
|
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
14
|
]);
|
|
@@ -25,7 +27,7 @@ test('R53 current release identity is coherent across package lock source metada
|
|
|
25
27
|
assert.match(pkg.scripts['r53:focused:test'],/stabilization-semver\.test\.mjs/);
|
|
26
28
|
});
|
|
27
29
|
|
|
28
|
-
|
|
30
|
+
r53test('R53 lineage binds exact published 0.15.29 as parent and runtime predecessor',async()=>{
|
|
29
31
|
const [release,provenance]=await Promise.all([json('RELEASE.json'),json('PROVENANCE.json')]);
|
|
30
32
|
const parent={
|
|
31
33
|
release_id:'v0.15.29-f74eaa1b3d00e6fb',
|
|
@@ -49,7 +51,7 @@ test('R53 lineage binds exact published 0.15.29 as parent and runtime predecesso
|
|
|
49
51
|
assert.deepEqual(provenance.runtime_predecessor,parent);
|
|
50
52
|
});
|
|
51
53
|
|
|
52
|
-
|
|
54
|
+
r53test('R53 records stabilization evidence and explicit soak bypass without STABLE/FINAL claim',async()=>{
|
|
53
55
|
const [release,provenance,matrix]=await Promise.all([json('RELEASE.json'),json('PROVENANCE.json'),json('TEST-MATRIX.json')]);
|
|
54
56
|
assert.equal(release.stabilization.semver_strict_validation,true);
|
|
55
57
|
assert.equal(release.stabilization.powershell_child_environment_normalized,true);
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import test from 'node:test';
|
|
2
|
+
import assert from 'node:assert/strict';
|
|
3
|
+
import { pathToFileURL } from 'node:url';
|
|
4
|
+
import path from 'node:path';
|
|
5
|
+
|
|
6
|
+
const target=process.env.R54_RUNTIME_UPDATE_PATH
|
|
7
|
+
? path.resolve(process.env.R54_RUNTIME_UPDATE_PATH)
|
|
8
|
+
: path.resolve(new URL('../src/runtime-update.mjs',import.meta.url).pathname.replace(/^\/(?:[A-Za-z]:)/,m=>m.slice(1)));
|
|
9
|
+
const runtimeUpdate=await import(pathToFileURL(target).href+'?r54='+Date.now());
|
|
10
|
+
|
|
11
|
+
test('R54 exposes behavior-testable offline recovery blocker selection',()=>{
|
|
12
|
+
assert.equal(typeof runtimeUpdate.selectOfflineRecoveryBlockers,'function');
|
|
13
|
+
});
|
|
14
|
+
|
|
15
|
+
test('R54 excludes only the current activation ancestry across non-Node wrapper processes',()=>{
|
|
16
|
+
const processes=[
|
|
17
|
+
{ProcessId:100,ParentProcessId:50,Name:'node.exe',CommandLine:'node npm-cli.js exec -- deadbyte-mcp@latest remote'},
|
|
18
|
+
{ProcessId:200,ParentProcessId:100,Name:'cmd.exe',CommandLine:'cmd /d /s /c deadbyte-mcp remote'},
|
|
19
|
+
{ProcessId:300,ParentProcessId:200,Name:'node.exe',CommandLine:'node C:\\cache\\deadbyte-mcp\\src\\deadbyte-cli.mjs remote'},
|
|
20
|
+
{ProcessId:400,ParentProcessId:300,Name:'node.exe',CommandLine:'node C:\\slot\\deadbyte-mcp\\scripts\\runtime-self-update.mjs --resume-handoff x'},
|
|
21
|
+
{ProcessId:900,ParentProcessId:1,Name:'node.exe',CommandLine:'node C:\\stale\\deadbyte-mcp\\src\\mcp-server.mjs'}
|
|
22
|
+
];
|
|
23
|
+
const blockers=runtimeUpdate.selectOfflineRecoveryBlockers(processes,400);
|
|
24
|
+
assert.deepEqual(blockers.map(item=>item.ProcessId),[900]);
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
test('R54 does not exempt a sibling stale DEADBYTE runtime',()=>{
|
|
28
|
+
const processes=[
|
|
29
|
+
{ProcessId:100,ParentProcessId:50,Name:'node.exe',CommandLine:'node npm-cli.js exec -- deadbyte-mcp@latest remote'},
|
|
30
|
+
{ProcessId:200,ParentProcessId:100,Name:'cmd.exe',CommandLine:'cmd /d /s /c deadbyte-mcp remote'},
|
|
31
|
+
{ProcessId:300,ParentProcessId:200,Name:'node.exe',CommandLine:'node C:\\cache\\deadbyte-mcp\\src\\deadbyte-cli.mjs remote'},
|
|
32
|
+
{ProcessId:400,ParentProcessId:300,Name:'node.exe',CommandLine:'node C:\\slot\\deadbyte-mcp\\scripts\\runtime-self-update.mjs --resume-handoff x'},
|
|
33
|
+
{ProcessId:700,ParentProcessId:300,Name:'node.exe',CommandLine:'node C:\\stale\\deadbyte-mcp\\src\\mcp-server.mjs'}
|
|
34
|
+
];
|
|
35
|
+
const blockers=runtimeUpdate.selectOfflineRecoveryBlockers(processes,400);
|
|
36
|
+
assert.deepEqual(blockers.map(item=>item.ProcessId),[700]);
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
test('R54 still blocks unrelated stale guardian, tunnel, and cloud DEADBYTE processes',()=>{
|
|
40
|
+
const processes=[
|
|
41
|
+
{ProcessId:400,ParentProcessId:300,Name:'node.exe',CommandLine:'node C:\\slot\\deadbyte-mcp\\scripts\\runtime-self-update.mjs --resume-handoff x'},
|
|
42
|
+
{ProcessId:910,ParentProcessId:1,Name:'node.exe',CommandLine:'node C:\\stale\\deadbyte-mcp\\src\\remote-session-guardian.mjs'},
|
|
43
|
+
{ProcessId:920,ParentProcessId:1,Name:'deadbyte-tunnel-host.exe',CommandLine:'C:\\stale\\deadbyte-tunnel-host.exe'},
|
|
44
|
+
{ProcessId:930,ParentProcessId:1,Name:'cloudflared.exe',CommandLine:'cloudflared tunnel --config C:\\deadbyte-mcp\\cloud.yml run'}
|
|
45
|
+
];
|
|
46
|
+
const blockers=runtimeUpdate.selectOfflineRecoveryBlockers(processes,400);
|
|
47
|
+
assert.deepEqual(blockers.map(item=>item.ProcessId),[910,920,930]);
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
test('R54 ignores unrelated Node processes without DEADBYTE runtime identity',()=>{
|
|
51
|
+
const processes=[
|
|
52
|
+
{ProcessId:400,ParentProcessId:300,Name:'node.exe',CommandLine:'node C:\\slot\\deadbyte-mcp\\scripts\\runtime-self-update.mjs --resume-handoff x'},
|
|
53
|
+
{ProcessId:950,ParentProcessId:1,Name:'node.exe',CommandLine:'node C:\\work\\unrelated-app\\server.js'}
|
|
54
|
+
];
|
|
55
|
+
assert.deepEqual(runtimeUpdate.selectOfflineRecoveryBlockers(processes,400),[]);
|
|
56
|
+
});
|
|
@@ -0,0 +1,61 @@
|
|
|
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
|
+
const currentPackage=await json('package.json');
|
|
9
|
+
const r54test=currentPackage.version==='0.15.31'?test:test.skip;
|
|
10
|
+
|
|
11
|
+
r54test('R54 release identity is coherent across package lock source metadata SBOM and gate',async()=>{
|
|
12
|
+
const [pkg,lock,release,provenance,matrix,sbom,versionSource]=await Promise.all([
|
|
13
|
+
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')
|
|
14
|
+
]);
|
|
15
|
+
assert.equal(pkg.version,'0.15.31');
|
|
16
|
+
assert.equal(lock.version,pkg.version);
|
|
17
|
+
assert.equal(lock.packages[''].version,pkg.version);
|
|
18
|
+
assert.match(versionSource,/DEADBYTE_VERSION = '0\.15\.31'/);
|
|
19
|
+
assert.equal(release.version,pkg.version);
|
|
20
|
+
assert.equal(release.train,'R54');
|
|
21
|
+
assert.equal(provenance.version,pkg.version);
|
|
22
|
+
assert.equal(matrix.version,pkg.version);
|
|
23
|
+
assert.equal(matrix.train,'R54');
|
|
24
|
+
assert.equal(sbom.metadata.component.version,pkg.version);
|
|
25
|
+
assert.equal(sbom.serialNumber,'urn:cdx:deadbyte-mcp:r54:0.15.31');
|
|
26
|
+
assert.match(pkg.scripts['gate:windows'],/gate-windows-r54\.ps1/);
|
|
27
|
+
assert.match(pkg.scripts['r54:focused:test'],/r54-offline-predecessor-self-exclusion\.test\.mjs/);
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
r54test('R54 source predecessor is exact immutable published 0.15.30 while runtime predecessor remains exact 0.15.29',async()=>{
|
|
31
|
+
const [release,provenance]=await Promise.all([json('RELEASE.json'),json('PROVENANCE.json')]);
|
|
32
|
+
assert.equal(release.parent.version,'0.15.30');
|
|
33
|
+
assert.equal(release.parent.release_id,'v0.15.30-ddf7c7338d9a371f');
|
|
34
|
+
assert.equal(release.parent.manifest_sha256,'ddf7c7338d9a371f5667d7b138695e7f9cfced553d14073ac5a44fd2fe0a0399');
|
|
35
|
+
assert.equal(release.parent.tarball_sha256,'72881981bf76eb45527946b310caed8387943e2ca8e37ea6e606c32189dd5a70');
|
|
36
|
+
assert.equal(release.parent.activation_status,'PUBLISHED_NOT_ACTIVATED');
|
|
37
|
+
assert.equal(release.source_predecessor.version,'0.15.30');
|
|
38
|
+
assert.equal(release.source_predecessor.tarball_sha256,release.parent.tarball_sha256);
|
|
39
|
+
assert.equal(release.source_predecessor.manifest_file_sha256,release.parent.manifest_sha256);
|
|
40
|
+
assert.deepEqual(release.runtime_predecessor,{
|
|
41
|
+
release_id:'v0.15.29-f74eaa1b3d00e6fb',
|
|
42
|
+
manifest_sha256:'f74eaa1b3d00e6fb5a36b2358efb815e3686c9fe9dbd3b84d25a713ef957748c',
|
|
43
|
+
generation_manifest_sha256:'289b4fc065de0681da6864cc1bbe09aab198ca804945ffc27c786438dad13f5a'
|
|
44
|
+
});
|
|
45
|
+
assert.equal(provenance.parent_manifest_sha256,release.parent.manifest_sha256);
|
|
46
|
+
assert.deepEqual(provenance.runtime_predecessor,release.runtime_predecessor);
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
r54test('R54 records surgical activation recovery fix and preserves explicit non-final claim',async()=>{
|
|
50
|
+
const [release,provenance,matrix]=await Promise.all([json('RELEASE.json'),json('PROVENANCE.json'),json('TEST-MATRIX.json')]);
|
|
51
|
+
assert.equal(release.hotfix.activation_ancestry_excluded,true);
|
|
52
|
+
assert.equal(release.hotfix.unrelated_stale_runtime_fail_closed,true);
|
|
53
|
+
assert.equal(release.hotfix.red_parent_0_15_30,'FAIL_5_OF_5');
|
|
54
|
+
assert.equal(release.hotfix.green_r54_candidate,'PASS_5_OF_5');
|
|
55
|
+
assert.equal(release.hotfix.stable_final_claim,false);
|
|
56
|
+
assert.equal(release.stabilization.four_hour_soak,'NOT_COMPLETED_OPERATOR_BYPASS');
|
|
57
|
+
assert.equal(provenance.operator_override.four_hour_soak,'NOT_COMPLETED');
|
|
58
|
+
assert.equal(provenance.operator_override.stable_final_claim,false);
|
|
59
|
+
assert.equal(matrix.operator_override.four_hour_soak,'NOT_COMPLETED');
|
|
60
|
+
assert.equal(matrix.operator_override.stable_final_claim,false);
|
|
61
|
+
});
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import test from 'node:test';
|
|
2
|
+
import assert from 'node:assert/strict';
|
|
3
|
+
import { pathToFileURL } from 'node:url';
|
|
4
|
+
import path from 'node:path';
|
|
5
|
+
|
|
6
|
+
const target=process.env.R55_RUNTIME_UPDATE_PATH
|
|
7
|
+
? path.resolve(process.env.R55_RUNTIME_UPDATE_PATH)
|
|
8
|
+
: path.resolve(new URL('../src/runtime-update.mjs',import.meta.url).pathname.replace(/^\/(?:[A-Za-z]:)/,m=>m.slice(1)));
|
|
9
|
+
const runtimeUpdate=await import(pathToFileURL(target).href+'?r55='+Date.now());
|
|
10
|
+
const nonce='a'.repeat(64);
|
|
11
|
+
|
|
12
|
+
test('R55 excludes the verified foreground remote guardian sibling owned by an activation ancestor',()=>{
|
|
13
|
+
const processes=[
|
|
14
|
+
{ProcessId:100,ParentProcessId:50,Name:'node.exe',CommandLine:'node npx-cli.js --yes deadbyte-mcp@0.15.32 remote'},
|
|
15
|
+
{ProcessId:200,ParentProcessId:100,Name:'cmd.exe',CommandLine:'cmd /d /s /c deadbyte-mcp remote'},
|
|
16
|
+
{ProcessId:300,ParentProcessId:200,Name:'node.exe',CommandLine:'node C:\\cache\\deadbyte-mcp\\src\\deadbyte-cli.mjs remote'},
|
|
17
|
+
{ProcessId:350,ParentProcessId:300,Name:'node.exe',CommandLine:'node C:\\cache\\deadbyte-mcp\\scripts\\deploy-r48.mjs'},
|
|
18
|
+
{ProcessId:400,ParentProcessId:350,Name:'node.exe',CommandLine:'node C:\\slot\\deadbyte-mcp\\scripts\\runtime-self-update.mjs --resume-handoff x'},
|
|
19
|
+
{ProcessId:500,ParentProcessId:300,Name:'node.exe',CommandLine:`node C:\\cache\\deadbyte-mcp\\src\\remote-session-guardian.mjs --state-root C:\\Users\\DEADBYTE\\.deadbyte-mcp --owner-pid 300 --session-nonce ${nonce}`},
|
|
20
|
+
{ProcessId:900,ParentProcessId:1,Name:'node.exe',CommandLine:'node C:\\stale\\deadbyte-mcp\\src\\mcp-server.mjs'}
|
|
21
|
+
];
|
|
22
|
+
const blockers=runtimeUpdate.selectOfflineRecoveryBlockers(processes,400);
|
|
23
|
+
assert.deepEqual(blockers.map(item=>item.ProcessId),[900]);
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
test('R55 does not exempt a guardian whose parent does not equal its declared ancestor owner',()=>{
|
|
27
|
+
const processes=[
|
|
28
|
+
{ProcessId:300,ParentProcessId:200,Name:'node.exe',CommandLine:'node C:\\cache\\deadbyte-mcp\\src\\deadbyte-cli.mjs remote'},
|
|
29
|
+
{ProcessId:400,ParentProcessId:300,Name:'node.exe',CommandLine:'node C:\\slot\\deadbyte-mcp\\scripts\\runtime-self-update.mjs --resume-handoff x'},
|
|
30
|
+
{ProcessId:510,ParentProcessId:999,Name:'node.exe',CommandLine:`node C:\\cache\\deadbyte-mcp\\src\\remote-session-guardian.mjs --owner-pid 300 --session-nonce ${nonce}`}
|
|
31
|
+
];
|
|
32
|
+
assert.deepEqual(runtimeUpdate.selectOfflineRecoveryBlockers(processes,400).map(item=>item.ProcessId),[510]);
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
test('R55 does not exempt a guardian whose owner is outside the activation lineage',()=>{
|
|
36
|
+
const processes=[
|
|
37
|
+
{ProcessId:300,ParentProcessId:200,Name:'node.exe',CommandLine:'node C:\\cache\\deadbyte-mcp\\src\\deadbyte-cli.mjs remote'},
|
|
38
|
+
{ProcessId:400,ParentProcessId:300,Name:'node.exe',CommandLine:'node C:\\slot\\deadbyte-mcp\\scripts\\runtime-self-update.mjs --resume-handoff x'},
|
|
39
|
+
{ProcessId:520,ParentProcessId:777,Name:'node.exe',CommandLine:`node C:\\cache\\deadbyte-mcp\\src\\remote-session-guardian.mjs --owner-pid 777 --session-nonce ${nonce}`}
|
|
40
|
+
];
|
|
41
|
+
assert.deepEqual(runtimeUpdate.selectOfflineRecoveryBlockers(processes,400).map(item=>item.ProcessId),[520]);
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
test('R55 does not exempt a guardian without the signed-session-shaped nonce contract',()=>{
|
|
45
|
+
const processes=[
|
|
46
|
+
{ProcessId:300,ParentProcessId:200,Name:'node.exe',CommandLine:'node C:\\cache\\deadbyte-mcp\\src\\deadbyte-cli.mjs remote'},
|
|
47
|
+
{ProcessId:400,ParentProcessId:300,Name:'node.exe',CommandLine:'node C:\\slot\\deadbyte-mcp\\scripts\\runtime-self-update.mjs --resume-handoff x'},
|
|
48
|
+
{ProcessId:530,ParentProcessId:300,Name:'node.exe',CommandLine:'node C:\\cache\\deadbyte-mcp\\src\\remote-session-guardian.mjs --owner-pid 300 --session-nonce short'}
|
|
49
|
+
];
|
|
50
|
+
assert.deepEqual(runtimeUpdate.selectOfflineRecoveryBlockers(processes,400).map(item=>item.ProcessId),[530]);
|
|
51
|
+
});
|
|
@@ -0,0 +1,61 @@
|
|
|
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('R55 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.32');
|
|
14
|
+
assert.equal(lock.version,pkg.version);
|
|
15
|
+
assert.equal(lock.packages[''].version,pkg.version);
|
|
16
|
+
assert.match(versionSource,/DEADBYTE_VERSION = '0\.15\.32'/);
|
|
17
|
+
assert.equal(release.version,pkg.version);
|
|
18
|
+
assert.equal(release.train,'R55');
|
|
19
|
+
assert.equal(provenance.version,pkg.version);
|
|
20
|
+
assert.equal(matrix.version,pkg.version);
|
|
21
|
+
assert.equal(matrix.train,'R55');
|
|
22
|
+
assert.equal(sbom.metadata.component.version,pkg.version);
|
|
23
|
+
assert.equal(sbom.serialNumber,'urn:cdx:deadbyte-mcp:r55:0.15.32');
|
|
24
|
+
assert.match(pkg.scripts['gate:windows'],/gate-windows-r55\.ps1/);
|
|
25
|
+
assert.match(pkg.scripts['r55:focused:test'],/r55-offline-predecessor-guardian-family\.test\.mjs/);
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
test('R55 source predecessor is exact immutable published 0.15.31 while runtime predecessor remains exact 0.15.29',async()=>{
|
|
29
|
+
const [release,provenance]=await Promise.all([json('RELEASE.json'),json('PROVENANCE.json')]);
|
|
30
|
+
assert.equal(release.parent.version,'0.15.31');
|
|
31
|
+
assert.equal(release.parent.release_id,'v0.15.31-16854fe5b3eb0fa1');
|
|
32
|
+
assert.equal(release.parent.manifest_sha256,'16854fe5b3eb0fa1c6dd2b96e3c5d6f792c5adbc91dd2a71e7fc2771472bbdd4');
|
|
33
|
+
assert.equal(release.parent.tarball_sha256,'ff6eba3036cb00ac639ac8907ad6063e6ea8169cf32aca8721f75b1e403e501a');
|
|
34
|
+
assert.equal(release.parent.activation_status,'PUBLISHED_NOT_ACTIVATED');
|
|
35
|
+
assert.equal(release.source_predecessor.version,'0.15.31');
|
|
36
|
+
assert.equal(release.source_predecessor.tarball_sha256,release.parent.tarball_sha256);
|
|
37
|
+
assert.equal(release.source_predecessor.manifest_file_sha256,release.parent.manifest_sha256);
|
|
38
|
+
assert.deepEqual(release.runtime_predecessor,{
|
|
39
|
+
release_id:'v0.15.29-f74eaa1b3d00e6fb',
|
|
40
|
+
manifest_sha256:'f74eaa1b3d00e6fb5a36b2358efb815e3686c9fe9dbd3b84d25a713ef957748c',
|
|
41
|
+
generation_manifest_sha256:'289b4fc065de0681da6864cc1bbe09aab198ca804945ffc27c786438dad13f5a'
|
|
42
|
+
});
|
|
43
|
+
assert.equal(provenance.parent_manifest_sha256,release.parent.manifest_sha256);
|
|
44
|
+
assert.deepEqual(provenance.runtime_predecessor,release.runtime_predecessor);
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
test('R55 records the guardian sibling RED to GREEN and preserves fail-closed negative controls',async()=>{
|
|
48
|
+
const [release,provenance,matrix]=await Promise.all([json('RELEASE.json'),json('PROVENANCE.json'),json('TEST-MATRIX.json')]);
|
|
49
|
+
assert.equal(release.hotfix.activation_ancestry_excluded,true);
|
|
50
|
+
assert.equal(release.hotfix.foreground_guardian_owner_parent_binding,true);
|
|
51
|
+
assert.equal(release.hotfix.foreground_guardian_session_nonce_contract,true);
|
|
52
|
+
assert.equal(release.hotfix.unrelated_stale_runtime_fail_closed,true);
|
|
53
|
+
assert.equal(release.hotfix.red_parent_0_15_31,'FAIL_1_OF_4');
|
|
54
|
+
assert.equal(release.hotfix.green_r55_candidate,'PASS_4_OF_4');
|
|
55
|
+
assert.equal(release.hotfix.stable_final_claim,false);
|
|
56
|
+
assert.equal(matrix.observed_pre_release_evidence.red_parent_0_15_31.receipt_sha256,'07ba973aa9224fbb1dde572af35640a1e07ca47273f1e50958d787bfe2aaf429');
|
|
57
|
+
assert.equal(matrix.observed_pre_release_evidence.green_r55_candidate.receipt_sha256,'a217f72cce51a9daf56136f9075ba487adb2a899c291a63af835ea15f8c632f0');
|
|
58
|
+
assert.equal(provenance.operator_override.four_hour_soak,'NOT_COMPLETED');
|
|
59
|
+
assert.equal(provenance.operator_override.stable_final_claim,false);
|
|
60
|
+
assert.equal(matrix.operator_override.stable_final_claim,false);
|
|
61
|
+
});
|