deadbyte-mcp 0.15.69 → 0.15.71
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 +49 -26
- package/PROVENANCE.json +62 -9
- package/README.txt +31 -23
- package/RELEASE.json +70 -9
- package/SBOM.cdx.json +2 -2
- package/TEST-MATRIX.json +63 -2
- package/bridge/package.json +1 -1
- package/controller/deadbyte-process-policy.json +4 -4
- package/docs/R184-package-lock.json +109 -16
- package/package.json +1 -1
- package/scripts/deploy-r48.mjs +17 -4
- package/scripts/r191-acceptance-readonly.mjs +7 -0
- package/scripts/r191-existing-regression.mjs +5 -0
- package/scripts/r191-existing-stage-audit.mjs +21 -0
- package/scripts/r191-extended-security-gate.mjs +14 -0
- package/scripts/r191-focused-tests.mjs +3 -0
- package/scripts/r191-full-runner.mjs +18 -0
- package/scripts/r191-historical-release-regression.mjs +9 -0
- package/scripts/r191-live-claim-inspect.mjs +15 -0
- package/scripts/r191-npm-registry-readonly.mjs +14 -0
- package/scripts/r191-old-version-audit.mjs +12 -0
- package/scripts/r191-privilege-readonly.mjs +6 -0
- package/scripts/r191-process-policy-focused.mjs +1 -0
- package/scripts/r191-protected-failure-gate.mjs +3 -0
- package/scripts/r191-release-contract-runner.mjs +1 -0
- package/scripts/r191-unreached-focused.mjs +29 -0
- package/src/activation-lock-r65.mjs +9 -1
- package/src/deadbyte-cli.mjs +62 -0
- package/src/foreground-owner-recovery-r190.mjs +117 -0
- package/src/runtime-update.mjs +2 -2
- package/src/trusted-epoch-bootstrap-r158.mjs +8 -0
- package/src/version.mjs +1 -1
- package/test/process-policy-refresh.test.mjs +11 -1
- package/test/r130-release-security-contract.test.mjs +8 -8
- package/test/r132-release-identity.test.mjs +8 -8
- package/test/r153-prepublish-identity.test.mjs +1 -1
- package/test/r163-isolated-slot-rollback-restore.test.mjs +4 -4
- package/test/r165-live-cli-skew.test.mjs +1 -1
- package/test/r167-release-contract.test.mjs +8 -8
- package/test/r172-release-contract.test.mjs +6 -6
- package/test/r173-release-contract.test.mjs +15 -15
- package/test/r179-release-contract.test.mjs +11 -11
- package/test/r190-foreground-cli.test.mjs +51 -0
- package/test/r190-foreground-owner-recovery.test.mjs +61 -0
- package/test/r190-release-contract.test.mjs +33 -0
- package/test/r191-epoch-root-selection.test.mjs +24 -0
- package/test/r191-epoch-source-claim.test.mjs +88 -0
- package/test/r191-inherited-epoch-preflight.test.mjs +65 -0
- package/test/r191-release-contract.test.mjs +31 -0
- package/test/r59-tool-surface-compat.test.mjs +1 -1
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
// Explicit, marker-only recovery for pre-R175 foreground ownership records.
|
|
2
|
+
// It never starts/stops runtime processes and never creates, revokes or restores
|
|
3
|
+
// authority. The caller must provide native CAS deletion and no-clobber evidence.
|
|
4
|
+
import {createHash} from 'node:crypto';
|
|
5
|
+
import path from 'node:path';
|
|
6
|
+
import {canonicalJson} from './canonical-json.mjs';
|
|
7
|
+
import {foregroundIdentityMatches,validForegroundIdentity} from './foreground-process-identity.mjs';
|
|
8
|
+
|
|
9
|
+
const SHA=/^[0-9a-f]{64}$/;
|
|
10
|
+
const PLANES=['process','coding','autonomous','desktop','host'];
|
|
11
|
+
const hash=value=>createHash('sha256').update(Buffer.from(canonicalJson(value))).digest('hex');
|
|
12
|
+
function insist(condition,message){if(!condition)throw new Error(`R190 foreground recovery blocked: ${message}`);}
|
|
13
|
+
function pointerSubject(pointer){
|
|
14
|
+
const fields=['release_id','version','manifest_sha256','generation_manifest_sha256','generation_root'];
|
|
15
|
+
insist(pointer&&typeof pointer==='object','active pointer missing');
|
|
16
|
+
for(const field of fields)insist(typeof pointer[field]==='string'&&pointer[field].length>0,`active pointer ${field} invalid`);
|
|
17
|
+
insist(SHA.test(pointer.manifest_sha256)&&SHA.test(pointer.generation_manifest_sha256),'active pointer digest invalid');
|
|
18
|
+
insist(path.isAbsolute(pointer.generation_root),'active generation root invalid');
|
|
19
|
+
return Object.freeze(Object.fromEntries(fields.map(field=>[field,pointer[field]])));
|
|
20
|
+
}
|
|
21
|
+
function authoritySubject(observation){
|
|
22
|
+
insist(observation?.state==='ONLINE'&&observation.local_ready===true,'runtime is not verified ONLINE');
|
|
23
|
+
insist(observation?.supervisor?.running===true,'supervisor is not verified running');
|
|
24
|
+
const subject={state:'ONLINE',local_ready:true,supervisor_running:true};
|
|
25
|
+
for(const plane of PLANES){
|
|
26
|
+
const value=observation.authority?.[plane];
|
|
27
|
+
insist(value?.configured===true&&typeof value.armed==='boolean'&&typeof value.reason==='string',`${plane} authority unverified`);
|
|
28
|
+
subject[plane]=Object.freeze({configured:true,armed:value.armed,reason:value.reason});
|
|
29
|
+
}
|
|
30
|
+
return Object.freeze(subject);
|
|
31
|
+
}
|
|
32
|
+
function staleIdentity(identity,queryIdentity,label){
|
|
33
|
+
insist(validForegroundIdentity(identity),`${label} identity invalid`);
|
|
34
|
+
let actual;
|
|
35
|
+
try{actual=queryIdentity(identity);}catch{throw new Error(`R190 foreground recovery blocked: ${label} identity observation unavailable`);}
|
|
36
|
+
if(actual===null)return Object.freeze({pid:identity.pid,state:'absent'});
|
|
37
|
+
insist(validForegroundIdentity(actual),`${label} current identity invalid`);
|
|
38
|
+
insist(!foregroundIdentityMatches(identity,actual),`${label} process is still active`);
|
|
39
|
+
return Object.freeze({pid:identity.pid,state:'pid_reused',actual_creation_utc:actual.creation_utc,
|
|
40
|
+
actual_executable_path:actual.executable_path});
|
|
41
|
+
}
|
|
42
|
+
function terminalFor(intent,status,completedAt){
|
|
43
|
+
const body={schema:'deadbyte.r190-foreground-recovery-terminal.v1',status,
|
|
44
|
+
intent_sha256:intent.intent_sha256,marker_sha256:intent.marker_sha256,
|
|
45
|
+
pointer:intent.pointer,authority:intent.authority,completed_at_utc:completedAt};
|
|
46
|
+
return Object.freeze({...body,receipt_sha256:hash(body)});
|
|
47
|
+
}
|
|
48
|
+
function validateIntent(intent,expected,pointer,authority){
|
|
49
|
+
insist(intent?.schema==='deadbyte.r190-foreground-recovery-intent.v1','recovery intent schema invalid');
|
|
50
|
+
insist(intent.marker_sha256===expected,'recovery intent marker mismatch');
|
|
51
|
+
const body={...intent};delete body.intent_sha256;
|
|
52
|
+
insist(SHA.test(String(intent.intent_sha256))&&hash(body)===intent.intent_sha256,'recovery intent digest invalid');
|
|
53
|
+
insist(canonicalJson(intent.pointer)===canonicalJson(pointer),'active pointer drift after marker delete');
|
|
54
|
+
insist(canonicalJson(intent.authority)===canonicalJson(authority),'authority drift after marker delete');
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
export async function recoverLegacyForegroundOwnership({
|
|
58
|
+
expectedMarkerSha256,readMarkerSnapshot,deleteMarkerIfHash,readActivePointer,readAuthority,
|
|
59
|
+
queryIdentity,loadIntent,loadTerminal,publishIntent,publishTerminal,now=()=>new Date()
|
|
60
|
+
}={}){
|
|
61
|
+
insist(typeof expectedMarkerSha256==='string'&&SHA.test(expectedMarkerSha256),'confirmation SHA-256 invalid');
|
|
62
|
+
for(const [name,fn] of Object.entries({readMarkerSnapshot,deleteMarkerIfHash,readActivePointer,
|
|
63
|
+
readAuthority,queryIdentity,loadIntent,loadTerminal,publishIntent,publishTerminal}))
|
|
64
|
+
insist(typeof fn==='function',`${name} callback missing`);
|
|
65
|
+
const pointer=pointerSubject(await readActivePointer());
|
|
66
|
+
const authority=authoritySubject(await readAuthority());
|
|
67
|
+
const snapshot=await readMarkerSnapshot();
|
|
68
|
+
const priorIntent=await loadIntent();
|
|
69
|
+
const priorTerminal=await loadTerminal();
|
|
70
|
+
if(snapshot===null){
|
|
71
|
+
insist(priorIntent,'marker absent without a durable R190 intent');
|
|
72
|
+
validateIntent(priorIntent,expectedMarkerSha256,pointer,authority);
|
|
73
|
+
if(priorTerminal){
|
|
74
|
+
insist(priorTerminal.intent_sha256===priorIntent.intent_sha256&&priorTerminal.marker_sha256===expectedMarkerSha256,
|
|
75
|
+
'terminal identity mismatch');
|
|
76
|
+
return Object.freeze({status:'MARKER_ALREADY_RECONCILED',marker_sha256:expectedMarkerSha256,
|
|
77
|
+
intent_sha256:priorIntent.intent_sha256,receipt_sha256:priorTerminal.receipt_sha256});
|
|
78
|
+
}
|
|
79
|
+
const terminal=terminalFor(priorIntent,'MARKER_RECONCILED_AFTER_DELETE',now().toISOString());
|
|
80
|
+
await publishTerminal(terminal);
|
|
81
|
+
return Object.freeze({status:terminal.status,marker_sha256:expectedMarkerSha256,
|
|
82
|
+
intent_sha256:priorIntent.intent_sha256,receipt_sha256:terminal.receipt_sha256});
|
|
83
|
+
}
|
|
84
|
+
insist(snapshot?.sha256===expectedMarkerSha256,'marker hash differs from explicit confirmation');
|
|
85
|
+
const marker=snapshot.marker;
|
|
86
|
+
insist(marker?.schema==='deadbyte.remote-session.v2','marker schema is not recoverable');
|
|
87
|
+
insist(marker.authority_scope===undefined||marker.authority_scope===null,'scoped marker must use its normal lifecycle');
|
|
88
|
+
insist(Number.isInteger(marker.owner_pid)&&marker.owner_pid>0&&marker.owner_identity,'owner marker identity invalid');
|
|
89
|
+
insist(Number.isInteger(marker.guardian_pid)&&marker.guardian_pid>0&&marker.guardian_identity,'guardian marker identity invalid');
|
|
90
|
+
const owner=staleIdentity(marker.owner_identity,queryIdentity,'owner');
|
|
91
|
+
const guardian=staleIdentity(marker.guardian_identity,queryIdentity,'guardian');
|
|
92
|
+
const intentBody={schema:'deadbyte.r190-foreground-recovery-intent.v1',action:'reconcile_legacy_foreground_marker_only',
|
|
93
|
+
marker_sha256:expectedMarkerSha256,pointer,authority,owner,guardian,requested_at_utc:now().toISOString(),
|
|
94
|
+
authority_mutation_allowed:false,runtime_mutation_allowed:false};
|
|
95
|
+
const intent=Object.freeze({...intentBody,intent_sha256:hash(intentBody)});
|
|
96
|
+
if(priorIntent)validateIntent(priorIntent,expectedMarkerSha256,pointer,authority);
|
|
97
|
+
else await publishIntent(intent);
|
|
98
|
+
const pinnedIntent=priorIntent??intent;
|
|
99
|
+
const pointerBeforeDelete=pointerSubject(await readActivePointer());
|
|
100
|
+
const authorityBeforeDelete=authoritySubject(await readAuthority());
|
|
101
|
+
insist(canonicalJson(pointerBeforeDelete)===canonicalJson(pointer),'active pointer drift before CAS delete');
|
|
102
|
+
insist(canonicalJson(authorityBeforeDelete)===canonicalJson(authority),'authority drift before CAS delete');
|
|
103
|
+
const current=await readMarkerSnapshot();
|
|
104
|
+
insist(current?.sha256===expectedMarkerSha256,'marker changed before CAS delete');
|
|
105
|
+
staleIdentity(marker.owner_identity,queryIdentity,'owner');
|
|
106
|
+
staleIdentity(marker.guardian_identity,queryIdentity,'guardian');
|
|
107
|
+
insist(await deleteMarkerIfHash(expectedMarkerSha256)==='REMOVED','native marker CAS did not remove exact identity');
|
|
108
|
+
insist(await readMarkerSnapshot()===null,'marker remained or reappeared after CAS delete');
|
|
109
|
+
const finalPointer=pointerSubject(await readActivePointer());
|
|
110
|
+
const finalAuthority=authoritySubject(await readAuthority());
|
|
111
|
+
insist(canonicalJson(finalPointer)===canonicalJson(pointer),'active pointer drift after CAS delete');
|
|
112
|
+
insist(canonicalJson(finalAuthority)===canonicalJson(authority),'authority drift after CAS delete');
|
|
113
|
+
const terminal=terminalFor(pinnedIntent,'MARKER_RECONCILED',now().toISOString());
|
|
114
|
+
await publishTerminal(terminal);
|
|
115
|
+
return Object.freeze({status:terminal.status,marker_sha256:expectedMarkerSha256,
|
|
116
|
+
intent_sha256:pinnedIntent.intent_sha256,receipt_sha256:terminal.receipt_sha256});
|
|
117
|
+
}
|
package/src/runtime-update.mjs
CHANGED
|
@@ -23,7 +23,7 @@ import {assertStageOwnedPath,assertStagingBootstrapInputs,assertStagingControlCo
|
|
|
23
23
|
import { verifyProjectOwnerApproval } from './r70-owner-approval.mjs';
|
|
24
24
|
import {windowsPowerShellEnvironment} from './windows-powershell-environment.mjs';
|
|
25
25
|
import {verifyNarrowedPolicyGeneration} from './clean-activation-recovery-r153.mjs';
|
|
26
|
-
import {parseProtectedEpochRoot,verifyTrustedEpochRelease} from './trusted-epoch-bootstrap-r158.mjs';
|
|
26
|
+
import {parseProtectedEpochRoot,selectActivationEpochRoot,verifyTrustedEpochRelease} from './trusted-epoch-bootstrap-r158.mjs';
|
|
27
27
|
|
|
28
28
|
export {windowsPowerShellEnvironment};
|
|
29
29
|
|
|
@@ -1913,7 +1913,7 @@ export async function performSlotUpdate({stage,developmentRoot,expectedVersion,e
|
|
|
1913
1913
|
workspace=await workspacePlan(control.workspaceRoot,state);launchers=await launcherPlan(state);
|
|
1914
1914
|
// A protected installation is an explicit owner-provisioned candidate.
|
|
1915
1915
|
// No discovery/fallback to writable npm roots and no automatic grant.
|
|
1916
|
-
const requestedEpochRoot=
|
|
1916
|
+
const requestedEpochRoot=selectActivationEpochRoot({stageRoot,configuredEpochRoot:control.config.AuthorityEpochRoot,explicitEpochRoot:authorityEpochRoot});
|
|
1917
1917
|
if(requestedEpochRoot){
|
|
1918
1918
|
const proof=await verifyTrustedEpochRelease({
|
|
1919
1919
|
epochRoot:requestedEpochRoot,releaseRoot:slot.release_root,
|
|
@@ -13,6 +13,14 @@ export function parseProtectedEpochRoot(input){
|
|
|
13
13
|
throw new Error('authority epoch root may not be a drive root');
|
|
14
14
|
return root;
|
|
15
15
|
}
|
|
16
|
+
// An inherited protected root belongs to the predecessor. Successor bytes
|
|
17
|
+
// must be verified against their own exact, independently ACL-trusted stage.
|
|
18
|
+
export function selectActivationEpochRoot({stageRoot,configuredEpochRoot=null,explicitEpochRoot=null}={}){
|
|
19
|
+
if(explicitEpochRoot!==null)return parseProtectedEpochRoot(explicitEpochRoot);
|
|
20
|
+
if(!configuredEpochRoot)return null;
|
|
21
|
+
parseProtectedEpochRoot(configuredEpochRoot);
|
|
22
|
+
return parseProtectedEpochRoot(stageRoot);
|
|
23
|
+
}
|
|
16
24
|
// This is a read-only proof, not installation or an authority grant.
|
|
17
25
|
// Windows ACL verification must run against OS Get-Acl, never a staged snapshot.
|
|
18
26
|
export async function verifyTrustedEpochRelease({epochRoot,releaseRoot,nodeExecutable}){
|
package/src/version.mjs
CHANGED
|
@@ -67,7 +67,17 @@ async function fixture() {
|
|
|
67
67
|
|
|
68
68
|
test('R167 process policy binds coherently to the explicit development root', async () => {
|
|
69
69
|
const repoRoot = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..');
|
|
70
|
-
|
|
70
|
+
// The distributed policy is pinned to its explicit development provenance,
|
|
71
|
+
// not to the physical extraction path of the exact immutable npm tarball.
|
|
72
|
+
const release = JSON.parse(await readFile(path.join(repoRoot, 'RELEASE.json'), 'utf8'));
|
|
73
|
+
const approvedRoot = release.r191_activation_repair?.development_policy_root;
|
|
74
|
+
assert.equal(typeof approvedRoot, 'string', 'R191 development policy root must be explicit in release metadata');
|
|
75
|
+
assert.ok(path.isAbsolute(approvedRoot), 'R191 development policy root must be absolute');
|
|
76
|
+
const expectedRoot = path.resolve(approvedRoot);
|
|
77
|
+
if (process.env.DEADBYTE_EXPECTED_DEVELOPMENT_ROOT) {
|
|
78
|
+
assert.equal(path.resolve(process.env.DEADBYTE_EXPECTED_DEVELOPMENT_ROOT), expectedRoot,
|
|
79
|
+
'caller-supplied development root cannot override R191 release provenance');
|
|
80
|
+
}
|
|
71
81
|
const policyPath = path.join(repoRoot, 'controller', 'deadbyte-process-policy.json');
|
|
72
82
|
const policy = JSON.parse(await readFile(policyPath, 'utf8'));
|
|
73
83
|
assert.equal(path.resolve(policy.roots.core.path), expectedRoot);
|
|
@@ -8,19 +8,19 @@ import {readReleaseLock} from './helpers/release-lock.mjs';
|
|
|
8
8
|
|
|
9
9
|
const root=path.resolve(fileURLToPath(new URL('..',import.meta.url)));
|
|
10
10
|
const read=async file=>JSON.parse(await readFile(path.join(root,file),'utf8'));
|
|
11
|
-
test('current successor retains the R130 security baseline with truthful
|
|
11
|
+
test('current successor retains the R130 security baseline with truthful R190 identity',async()=>{
|
|
12
12
|
const [pkg,release,provenance,bom,lock,matrix]=await Promise.all([
|
|
13
13
|
read('package.json'),read('RELEASE.json'),read('PROVENANCE.json'),
|
|
14
14
|
read('SBOM.cdx.json'),readReleaseLock(root),read('TEST-MATRIX.json')]);
|
|
15
15
|
assert.equal(pkg.name,'deadbyte-mcp');
|
|
16
16
|
for(const actual of [pkg.version,release.version,provenance.version,matrix.version,bom.metadata.component.version,lock.version,lock.packages[''].version,DEADBYTE_VERSION])
|
|
17
|
-
assert.equal(actual,'0.15.
|
|
18
|
-
assert.equal(release.train,'
|
|
19
|
-
assert.equal(matrix.train,'
|
|
20
|
-
assert.equal(release.source_predecessor.version,'0.15.
|
|
21
|
-
assert.equal(release.source_predecessor.train,'
|
|
22
|
-
assert.match(release.source_predecessor.source_path,/deadbyte-mcp-0\.15\.
|
|
23
|
-
assert.equal(release.source_predecessor.manifest_status,'
|
|
17
|
+
assert.equal(actual,'0.15.71');
|
|
18
|
+
assert.equal(release.train,'R191');
|
|
19
|
+
assert.equal(matrix.train,'R191');
|
|
20
|
+
assert.equal(release.source_predecessor.version,'0.15.70');
|
|
21
|
+
assert.equal(release.source_predecessor.train,'R190');
|
|
22
|
+
assert.match(release.source_predecessor.source_path,/deadbyte-mcp-0\.15\.70\.tgz/i);
|
|
23
|
+
assert.equal(release.source_predecessor.manifest_status,'PUBLISHED_EXACT_MANIFEST_VERIFIED_NOT_ACTIVE');
|
|
24
24
|
assert.equal(release.runtime_predecessor.version,'0.15.68');
|
|
25
25
|
assert.equal(provenance.r160.source_predecessor_manifest_status,'STALE_REJECTED');
|
|
26
26
|
assert.equal(pkg.dependencies['@modelcontextprotocol/client'],'2.3.1');
|
|
@@ -9,23 +9,23 @@ import {readReleaseLock} from './helpers/release-lock.mjs';
|
|
|
9
9
|
const root=path.resolve(fileURLToPath(new URL('..',import.meta.url)));
|
|
10
10
|
const read=async rel=>JSON.parse(await readFile(path.join(root,rel),'utf8'));
|
|
11
11
|
|
|
12
|
-
test('
|
|
12
|
+
test('R190 package identity preserves R132 evidence and published R179 source predecessor',async()=>{
|
|
13
13
|
const [pkg,lock,release,prov,bom,matrix]=await Promise.all([
|
|
14
14
|
read('package.json'),readReleaseLock(root),read('RELEASE.json'),
|
|
15
15
|
read('PROVENANCE.json'),read('SBOM.cdx.json'),read('TEST-MATRIX.json')]);
|
|
16
16
|
for(const value of [pkg.version,lock.version,lock.packages[''].version,release.version,prov.version,bom.metadata.component.version,matrix.version,DEADBYTE_VERSION]){
|
|
17
17
|
assert.equal(value,pkg.version);
|
|
18
18
|
}
|
|
19
|
-
assert.equal(release.train,'
|
|
20
|
-
assert.equal(matrix.train,'
|
|
21
|
-
assert.equal(release.source_predecessor.version,'0.15.
|
|
22
|
-
assert.equal(release.source_predecessor.train,'
|
|
23
|
-
assert.match(release.source_predecessor.source_path,/deadbyte-mcp-0\.15\.
|
|
19
|
+
assert.equal(release.train,'R191');
|
|
20
|
+
assert.equal(matrix.train,'R191');
|
|
21
|
+
assert.equal(release.source_predecessor.version,'0.15.70');
|
|
22
|
+
assert.equal(release.source_predecessor.train,'R190');
|
|
23
|
+
assert.match(release.source_predecessor.source_path,/deadbyte-mcp-0\.15\.70\.tgz/i);
|
|
24
24
|
assert.equal(prov.r160.source_predecessor_manifest_status,'STALE_REJECTED');
|
|
25
25
|
assert.equal(release.r165.published_source_manifest_sha256,'4adf7d94682fbb71c4a91eb3147ecc48076bbc5d79cb67fcb875cde6a1bb4805');
|
|
26
26
|
assert.equal(release.r165.published_source_tarball_sha256,'812e97609b7c51353ed25f2202129737e43bb4fbe0778c3adec43ec065352641');
|
|
27
|
-
assert.equal(release.source_predecessor.tarball_sha256,'
|
|
28
|
-
assert.
|
|
27
|
+
assert.equal(release.source_predecessor.tarball_sha256,'1a5f5f30bf4781ed951d6d201b8a972d088b2091555d7f4cc2e2bb899ed5707e');
|
|
28
|
+
assert.notEqual(release.r179.source_predecessor_tarball_sha256,release.source_predecessor.tarball_sha256);
|
|
29
29
|
assert.equal(release.runtime_predecessor.version,'0.15.68');
|
|
30
30
|
assert.equal(prov.runtime_predecessor.release_id,release.runtime_predecessor.release_id);
|
|
31
31
|
assert.equal(prov.runtime_predecessor.manifest_sha256,release.runtime_predecessor.manifest_sha256);
|
|
@@ -26,7 +26,7 @@ test('R153 historical verifier refuses current successor package identity',async
|
|
|
26
26
|
const state={pkg:JSON.parse(await readFile(path.join(root,'package.json'),'utf8')),
|
|
27
27
|
release:JSON.parse(await readFile(path.join(root,'RELEASE.json'),'utf8')),
|
|
28
28
|
bridge:JSON.parse(await readFile(path.join(root,'bridge/package.json'),'utf8'))};
|
|
29
|
-
assert.equal(state.pkg.version,'0.15.
|
|
29
|
+
assert.equal(state.pkg.version,'0.15.71');
|
|
30
30
|
assert.throws(()=>verifyR153Lineage(state),/package version/);
|
|
31
31
|
await assert.rejects(verifyR153IdentityTree(root),/package version/);
|
|
32
32
|
});
|
|
@@ -8,7 +8,8 @@ import {fileURLToPath} from 'node:url';
|
|
|
8
8
|
import {atomicWriteActivePointer,materializeReleaseSlot,parseActivePointer,parseManifest,renderStableLauncherPs1,verifyManifestTreeExact} from '../src/release-slot.mjs';
|
|
9
9
|
|
|
10
10
|
const candidate=path.dirname(path.dirname(fileURLToPath(import.meta.url)));
|
|
11
|
-
const
|
|
11
|
+
const packageIdentity=JSON.parse(await readFile(path.join(candidate,'package.json'),'utf8'));
|
|
12
|
+
const version=packageIdentity.version;
|
|
12
13
|
const parent=path.join(os.homedir(),'.deadbyte-mcp','releases','v0.15.68-ff29dbb2fbb89862');
|
|
13
14
|
const parentDigest='ff29dbb2fbb898625592afb47835a4a62ef289429f1e0a89a83242398cd98885';
|
|
14
15
|
const sha=bytes=>createHash('sha256').update(bytes).digest('hex');
|
|
@@ -21,12 +22,11 @@ async function generation(state,label){
|
|
|
21
22
|
await writeFile(path.join(root,'GENERATION.SHA256'),manifest,{flag:'wx'});
|
|
22
23
|
return {root,manifest_sha256:sha(manifest),config_path:path.join(root,'controller-config.psd1')};
|
|
23
24
|
}
|
|
24
|
-
test('R179 isolated immutable slot activates, rolls back, and restores
|
|
25
|
+
test('R179 isolated immutable slot activates, rolls back, and restores the current successor without authority or production mutation',async()=>{
|
|
25
26
|
const stateBase=await mkdtemp(path.join(os.tmpdir(),'deadbyte-r163-isolated-'));
|
|
26
27
|
const state=path.join(stateBase,'state');
|
|
27
28
|
try{
|
|
28
|
-
|
|
29
|
-
assert.equal(pkg.version,version);
|
|
29
|
+
assert.match(version,/^0\.15\.\d+$/);
|
|
30
30
|
const parentManifest=await readFile(path.join(parent,'MANIFEST.SHA256'));
|
|
31
31
|
const nextManifest=await readFile(path.join(candidate,'MANIFEST.SHA256'));
|
|
32
32
|
assert.equal(sha(parentManifest),parentDigest);
|
|
@@ -17,7 +17,7 @@ const hashMarker=c=>c.repeat(64);
|
|
|
17
17
|
async function fixture(t,kind){
|
|
18
18
|
assert.equal(typeof envRoot,'string'); assert.ok(path.isAbsolute(envRoot));
|
|
19
19
|
assert.match(sha(await readFile(path.join(envRoot,'MANIFEST.SHA256'))),/^[0-9a-f]{64}$/);
|
|
20
|
-
assert.equal(JSON.parse(await readFile(path.join(envRoot,'package.json'),'utf8')).version,'0.15.
|
|
20
|
+
assert.equal(JSON.parse(await readFile(path.join(envRoot,'package.json'),'utf8')).version,'0.15.71');
|
|
21
21
|
const dir=await mkdtemp(path.join(os.tmpdir(),'deadbyte-r164-skew-e2e-'));
|
|
22
22
|
t.after(async()=>{await rm(dir,{recursive:true,force:true,maxRetries:10,retryDelay:150});});
|
|
23
23
|
const state=path.join(dir,'state'),bin=path.join(state,'bin');
|
|
@@ -8,7 +8,7 @@ import {DEADBYTE_VERSION} from '../src/version.mjs';
|
|
|
8
8
|
const root=path.resolve(fileURLToPath(new URL('..',import.meta.url)));
|
|
9
9
|
const read=async rel=>JSON.parse(await readFile(path.join(root,rel),'utf8'));
|
|
10
10
|
const readOptional=async rel=>{try{return await read(rel);}catch(error){if(error?.code==='ENOENT')return null;throw error;}};
|
|
11
|
-
const VERSION='0.15.
|
|
11
|
+
const VERSION='0.15.71';
|
|
12
12
|
const R166_MANIFEST='ad3c091fbbe2bac6d234548724008b67305fd483ad6542f92d5aeb1aa84f5849';
|
|
13
13
|
const R166_TARBALL='3d22412c3cebabd66bfa92ca4dbaaf3ef49acba7f92436a16485d7e4cb18c653';
|
|
14
14
|
|
|
@@ -20,20 +20,20 @@ test('current package identity retains the immutable R167/R173 activation lineag
|
|
|
20
20
|
provenance.version,matrix.version,bom.metadata.component.version,DEADBYTE_VERSION];
|
|
21
21
|
if(lock)versions.push(lock.version,lock.packages[''].version);
|
|
22
22
|
for(const version of versions)assert.equal(version,VERSION);
|
|
23
|
-
assert.equal(release.train,'
|
|
24
|
-
assert.equal(matrix.train,'
|
|
25
|
-
assert.equal(bom.serialNumber,'urn:cdx:deadbyte-mcp:
|
|
23
|
+
assert.equal(release.train,'R191');
|
|
24
|
+
assert.equal(matrix.train,'R191');
|
|
25
|
+
assert.equal(bom.serialNumber,'urn:cdx:deadbyte-mcp:r191:0.15.71');
|
|
26
26
|
assert.deepEqual(release.runtime_predecessor,{
|
|
27
27
|
version:'0.15.68',release_id:'v0.15.68-ff29dbb2fbb89862',
|
|
28
28
|
manifest_sha256:'ff29dbb2fbb898625592afb47835a4a62ef289429f1e0a89a83242398cd98885',
|
|
29
29
|
generation_manifest_sha256:'2887d4a8d54ea6fc6b28869072f72491c892dfe28178b3537936dce958147bd2'
|
|
30
30
|
});
|
|
31
|
-
assert.equal(release.source_predecessor.version,'0.15.
|
|
32
|
-
assert.equal(release.source_predecessor.train,'
|
|
33
|
-
assert.equal(release.source_predecessor.tarball_sha256,'
|
|
31
|
+
assert.equal(release.source_predecessor.version,'0.15.70');
|
|
32
|
+
assert.equal(release.source_predecessor.train,'R190');
|
|
33
|
+
assert.equal(release.source_predecessor.tarball_sha256,'1a5f5f30bf4781ed951d6d201b8a972d088b2091555d7f4cc2e2bb899ed5707e');
|
|
34
34
|
assert.equal(release.r173.source_predecessor_version,'0.15.67');
|
|
35
35
|
assert.equal(release.r167.source_predecessor_manifest_sha256,R166_MANIFEST);
|
|
36
|
-
assert.equal(provenance.source_predecessor_manifest_file_sha256,'
|
|
36
|
+
assert.equal(provenance.source_predecessor_manifest_file_sha256,'df15db35c7ddddd769650f5c86557159d10d99e9eef4dd232cae67faeb8ad6e3');
|
|
37
37
|
assert.equal(provenance.r173.source_predecessor_manifest_sha256,'4fd7bfc76b1de6401c142b607cc84e847434b28c888bc99ceb09c9e8e5adc8f8');
|
|
38
38
|
assert.equal(release.r172.source_predecessor_manifest_sha256,'962e227dace247d59aa860211388fbde461c5f9c8a7e8a878e34a14a27d355f8');
|
|
39
39
|
assert.equal(release.r172.source_predecessor_tarball_sha256,'31b194a4fbff7dbdb9f4f275b4ea49c5115f7b0bb2241c0c72d72ef690311a5d');
|
|
@@ -9,18 +9,18 @@ const root=path.resolve(fileURLToPath(new URL('..',import.meta.url)));
|
|
|
9
9
|
const read=async rel=>JSON.parse(await readFile(path.join(root,rel),'utf8'));
|
|
10
10
|
const readOptional=async rel=>{try{return await read(rel);}catch(error){if(error?.code==='ENOENT')return null;throw error;}};
|
|
11
11
|
|
|
12
|
-
test('
|
|
12
|
+
test('R190 binds the published source predecessor separately from the active runtime predecessor',async()=>{
|
|
13
13
|
const [pkg,lock,bridge,release,provenance,matrix,bom]=await Promise.all([
|
|
14
14
|
read('package.json'),readOptional('package-lock.json'),read('bridge/package.json'),read('RELEASE.json'),
|
|
15
15
|
read('PROVENANCE.json'),read('TEST-MATRIX.json'),read('SBOM.cdx.json')]);
|
|
16
16
|
const versions=[pkg.version,bridge.version,release.version,provenance.version,matrix.version,
|
|
17
17
|
bom.metadata.component.version,DEADBYTE_VERSION];
|
|
18
18
|
if(lock)versions.push(lock.version,lock.packages[''].version);
|
|
19
|
-
for(const value of versions)assert.equal(value,'0.15.
|
|
20
|
-
assert.equal(release.train,'
|
|
21
|
-
assert.equal(release.source_predecessor.version,'0.15.
|
|
22
|
-
assert.equal(release.source_predecessor.manifest_sha256,'
|
|
23
|
-
assert.equal(release.source_predecessor.tarball_sha256,'
|
|
19
|
+
for(const value of versions)assert.equal(value,'0.15.71');
|
|
20
|
+
assert.equal(release.train,'R191'); assert.equal(matrix.train,'R191');
|
|
21
|
+
assert.equal(release.source_predecessor.version,'0.15.70');
|
|
22
|
+
assert.equal(release.source_predecessor.manifest_sha256,'df15db35c7ddddd769650f5c86557159d10d99e9eef4dd232cae67faeb8ad6e3');
|
|
23
|
+
assert.equal(release.source_predecessor.tarball_sha256,'1a5f5f30bf4781ed951d6d201b8a972d088b2091555d7f4cc2e2bb899ed5707e');
|
|
24
24
|
assert.equal(release.runtime_predecessor.release_id,'v0.15.68-ff29dbb2fbb89862');
|
|
25
25
|
assert.equal(release.r173.public_tool_delta,0); assert.equal(release.r173.authority_scope_delta,0);
|
|
26
26
|
});
|
|
@@ -8,26 +8,26 @@ import {readReleaseLock} from './helpers/release-lock.mjs';
|
|
|
8
8
|
const root=path.resolve(path.dirname(fileURLToPath(import.meta.url)),'..');
|
|
9
9
|
const json=async file=>JSON.parse(await readFile(path.join(root,file),'utf8'));
|
|
10
10
|
|
|
11
|
-
test('
|
|
11
|
+
test('R190 package identity binds the exact published R179 source and live runtime predecessor',async()=>{
|
|
12
12
|
const [pkg,lock,bridge,release,provenance,matrix,sbom]=await Promise.all([
|
|
13
13
|
json('package.json'),readReleaseLock(root),json('bridge/package.json'),json('RELEASE.json'),
|
|
14
14
|
json('PROVENANCE.json'),json('TEST-MATRIX.json'),json('SBOM.cdx.json')
|
|
15
15
|
]);
|
|
16
|
-
assert.equal(pkg.version,'0.15.
|
|
17
|
-
assert.equal(lock.version,'0.15.
|
|
18
|
-
assert.equal(lock.packages[''].version,'0.15.
|
|
19
|
-
assert.equal(bridge.version,'0.15.
|
|
20
|
-
assert.equal(release.version,'0.15.
|
|
21
|
-
assert.equal(release.train,'
|
|
22
|
-
assert.equal(matrix.version,'0.15.
|
|
23
|
-
assert.equal(matrix.train,'
|
|
24
|
-
assert.equal(sbom.metadata.component.version,'0.15.
|
|
25
|
-
assert.equal(release.source_predecessor.version,'0.15.
|
|
26
|
-
assert.equal(release.source_predecessor.train,'
|
|
27
|
-
assert.equal(release.source_predecessor.manifest_sha256,'
|
|
28
|
-
assert.equal(release.source_predecessor.tarball_sha256,'
|
|
16
|
+
assert.equal(pkg.version,'0.15.71');
|
|
17
|
+
assert.equal(lock.version,'0.15.71');
|
|
18
|
+
assert.equal(lock.packages[''].version,'0.15.71');
|
|
19
|
+
assert.equal(bridge.version,'0.15.71');
|
|
20
|
+
assert.equal(release.version,'0.15.71');
|
|
21
|
+
assert.equal(release.train,'R191');
|
|
22
|
+
assert.equal(matrix.version,'0.15.71');
|
|
23
|
+
assert.equal(matrix.train,'R191');
|
|
24
|
+
assert.equal(sbom.metadata.component.version,'0.15.71');
|
|
25
|
+
assert.equal(release.source_predecessor.version,'0.15.70');
|
|
26
|
+
assert.equal(release.source_predecessor.train,'R190');
|
|
27
|
+
assert.equal(release.source_predecessor.manifest_sha256,'df15db35c7ddddd769650f5c86557159d10d99e9eef4dd232cae67faeb8ad6e3');
|
|
28
|
+
assert.equal(release.source_predecessor.tarball_sha256,'1a5f5f30bf4781ed951d6d201b8a972d088b2091555d7f4cc2e2bb899ed5707e');
|
|
29
29
|
assert.equal(release.runtime_predecessor.version,'0.15.68');
|
|
30
|
-
assert.equal(provenance.version,'0.15.
|
|
30
|
+
assert.equal(provenance.version,'0.15.71');
|
|
31
31
|
assert.equal(provenance.parent_tarball_sha256,release.source_predecessor.tarball_sha256);
|
|
32
32
|
assert.equal(provenance.source_predecessor_manifest_file_sha256,release.source_predecessor.manifest_sha256);
|
|
33
33
|
});
|
|
@@ -11,23 +11,23 @@ const parent='v0.15.68-ff29dbb2fbb89862';
|
|
|
11
11
|
const manifest='ff29dbb2fbb898625592afb47835a4a62ef289429f1e0a89a83242398cd98885';
|
|
12
12
|
const generation='2887d4a8d54ea6fc6b28869072f72491c892dfe28178b3537936dce958147bd2';
|
|
13
13
|
const tarball='1a7f1fe05a06178b8d772a140be6e0470790b743cef690c3f8f8165cc61075bf';
|
|
14
|
-
test('
|
|
14
|
+
test('R190 version, lock snapshot, bridge, matrix, SBOM and provenance identify exact 0.15.70 successor',async()=>{
|
|
15
15
|
const [pkg,lock,bridge,release,provenance,matrix,sbom]=await Promise.all([
|
|
16
16
|
read('package.json'),readReleaseLock(root),read('bridge/package.json'),read('RELEASE.json'),
|
|
17
17
|
read('PROVENANCE.json'),read('TEST-MATRIX.json'),read('SBOM.cdx.json')]);
|
|
18
18
|
for(const version of [pkg.version,lock.version,lock.packages[''].version,bridge.version,release.version,provenance.version,
|
|
19
|
-
matrix.version,sbom.metadata.component.version,DEADBYTE_VERSION])assert.equal(version,'0.15.
|
|
20
|
-
assert.equal(release.train,'
|
|
21
|
-
assert.equal(sbom.serialNumber,'urn:cdx:deadbyte-mcp:
|
|
19
|
+
matrix.version,sbom.metadata.component.version,DEADBYTE_VERSION])assert.equal(version,'0.15.71');
|
|
20
|
+
assert.equal(release.train,'R191');assert.equal(matrix.train,'R191');
|
|
21
|
+
assert.equal(sbom.serialNumber,'urn:cdx:deadbyte-mcp:r191:0.15.71');
|
|
22
22
|
assert.equal(release.parent.release_id,parent);assert.equal(release.parent.manifest_sha256,manifest);
|
|
23
23
|
assert.equal(release.parent.generation_manifest_sha256,generation);
|
|
24
|
-
assert.equal(release.source_predecessor.version,'0.15.
|
|
25
|
-
assert.equal(release.source_predecessor.manifest_sha256,
|
|
26
|
-
assert.equal(release.source_predecessor.tarball_sha256,
|
|
24
|
+
assert.equal(release.source_predecessor.version,'0.15.70');
|
|
25
|
+
assert.equal(release.source_predecessor.manifest_sha256,'df15db35c7ddddd769650f5c86557159d10d99e9eef4dd232cae67faeb8ad6e3');
|
|
26
|
+
assert.equal(release.source_predecessor.tarball_sha256,'1a5f5f30bf4781ed951d6d201b8a972d088b2091555d7f4cc2e2bb899ed5707e');
|
|
27
27
|
assert.equal(release.runtime_predecessor.release_id,parent);
|
|
28
28
|
assert.equal(provenance.runtime_predecessor.release_id,parent);
|
|
29
|
-
assert.equal(provenance.parent_tarball_sha256,
|
|
30
|
-
assert.equal(provenance.source_predecessor_tarball_sha256,
|
|
29
|
+
assert.equal(provenance.parent_tarball_sha256,release.source_predecessor.tarball_sha256);
|
|
30
|
+
assert.equal(provenance.source_predecessor_tarball_sha256,release.source_predecessor.tarball_sha256);
|
|
31
31
|
});
|
|
32
32
|
test('R179 does not misrepresent owner Autonomous enrollment, protected regression or publication as finished',async()=>{
|
|
33
33
|
const [release,prov,matrix]=await Promise.all(['RELEASE.json','PROVENANCE.json','TEST-MATRIX.json'].map(read));
|
|
@@ -43,12 +43,12 @@ test('R179 does not misrepresent owner Autonomous enrollment, protected regressi
|
|
|
43
43
|
assert.ok(matrix.required.some(r=>r.id==='r179-owner-authorized-autonomous'));
|
|
44
44
|
assert.ok(matrix.required.some(r=>r.id==='r179-protected-full'));
|
|
45
45
|
});
|
|
46
|
-
test('
|
|
46
|
+
test('R190 retains historical R179 and R173 source evidence without promoting it to current parent',async()=>{
|
|
47
47
|
const [release,prov]=await Promise.all(['RELEASE.json','PROVENANCE.json'].map(read));
|
|
48
48
|
assert.equal(release.r173.source_predecessor_version,'0.15.67');
|
|
49
49
|
assert.equal(release.r173.source_predecessor_manifest_sha256,'4fd7bfc76b1de6401c142b607cc84e847434b28c888bc99ceb09c9e8e5adc8f8');
|
|
50
50
|
assert.equal(prov.r173.source_predecessor_version,'0.15.67');
|
|
51
|
-
assert.equal(release.source_predecessor.version,'0.15.
|
|
51
|
+
assert.equal(release.source_predecessor.version,'0.15.70');
|
|
52
52
|
assert.notEqual(release.source_predecessor.tarball_sha256,release.r173.source_predecessor_tarball_sha256);
|
|
53
53
|
});
|
|
54
54
|
test('R184 ships a semantic lock snapshot matching installed package identity and dependencies',async()=>{
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import test from 'node:test';
|
|
2
|
+
import assert from 'node:assert/strict';
|
|
3
|
+
import {spawnSync} from 'node:child_process';
|
|
4
|
+
import {mkdtemp,stat,rm} from 'node:fs/promises';
|
|
5
|
+
import os from 'node:os';
|
|
6
|
+
import path from 'node:path';
|
|
7
|
+
import {fileURLToPath} from 'node:url';
|
|
8
|
+
import {inspectTrustedInstallation} from '../src/privileged-install-trust-r89.mjs';
|
|
9
|
+
|
|
10
|
+
const root=path.dirname(path.dirname(fileURLToPath(import.meta.url)));
|
|
11
|
+
|
|
12
|
+
test('R190 CLI proves R89 before touching isolated controller and fails closed on absent launcher',async t=>{
|
|
13
|
+
const stateRoot=await mkdtemp(path.join(os.tmpdir(),'deadbyte-r191-r190-cli-'));
|
|
14
|
+
t.after(()=>rm(stateRoot,{recursive:true,force:true}));
|
|
15
|
+
const installation=await inspectTrustedInstallation([
|
|
16
|
+
process.execPath,
|
|
17
|
+
path.join(root,'src','deadbyte-cli.mjs'),
|
|
18
|
+
path.join(root,'MANIFEST.SHA256'),
|
|
19
|
+
path.join(root,'src','foreground-owner-recovery-r190.mjs'),
|
|
20
|
+
path.join(root,'src','foreground-marker-cas-win32.mjs'),
|
|
21
|
+
path.join(root,'src','foreground-process-identity.mjs'),
|
|
22
|
+
path.join(root,'src','foreground-session-lock.mjs'),
|
|
23
|
+
path.join(root,'src','privileged-install-trust-r89.mjs'),
|
|
24
|
+
path.join(root,'src','canonical-json.mjs'),
|
|
25
|
+
path.join(root,'bin','deadbyte-epoch-lock-r85.node')
|
|
26
|
+
]).catch(()=>({trusted:false}));
|
|
27
|
+
const result=spawnSync(process.execPath,['src/deadbyte-cli.mjs','remote','reconcileforeground',
|
|
28
|
+
'--confirm-marker-sha256','f'.repeat(64)],{encoding:'utf8',windowsHide:true,shell:false,
|
|
29
|
+
env:{...process.env,DEADBYTE_STATE_ROOT:stateRoot}});
|
|
30
|
+
const output=String(result.stdout||'')+String(result.stderr||'');
|
|
31
|
+
assert.notEqual(result.status,0,output);
|
|
32
|
+
if(installation.trusted){
|
|
33
|
+
// R89 succeeded, but this isolated fixture has no controller launcher.
|
|
34
|
+
// The test must fail closed before foreground marker or production access.
|
|
35
|
+
assert.match(output,/ENOENT: no such file or directory, lstat .*DEADBYTE-MCP\.ps1/);
|
|
36
|
+
}else assert.match(output,/R190_PROTECTED_RECOVERY_REQUIRED/);
|
|
37
|
+
await assert.rejects(stat(path.join(stateRoot,'evidence','foreground-recovery-r190')),{code:'ENOENT'});
|
|
38
|
+
await assert.rejects(stat(path.join(stateRoot,'runtime','remote-session.json')),{code:'ENOENT'});
|
|
39
|
+
assert.doesNotMatch(output,/unsupported remote command|EXACT_ACTIVE_RELEASE_REQUIRED/);
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
test('R190 CLI requires exact marker hash and never accepts recovery confirmation on another command',()=>{
|
|
43
|
+
const missing=spawnSync(process.execPath,['src/deadbyte-cli.mjs','remote','reconcileforeground'],
|
|
44
|
+
{encoding:'utf8',windowsHide:true,shell:false});
|
|
45
|
+
assert.notEqual(missing.status,0);
|
|
46
|
+
assert.match(String(missing.stderr),/requires --confirm-marker-sha256/);
|
|
47
|
+
const confused=spawnSync(process.execPath,['src/deadbyte-cli.mjs','remote','status',
|
|
48
|
+
'--confirm-marker-sha256','f'.repeat(64)],{encoding:'utf8',windowsHide:true,shell:false});
|
|
49
|
+
assert.notEqual(confused.status,0);
|
|
50
|
+
assert.match(String(confused.stderr),/valid only with reconcileforeground/);
|
|
51
|
+
});
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import test from 'node:test';
|
|
2
|
+
import assert from 'node:assert/strict';
|
|
3
|
+
import {recoverLegacyForegroundOwnership} from '../src/foreground-owner-recovery-r190.mjs';
|
|
4
|
+
|
|
5
|
+
const H=char=>char.repeat(64);
|
|
6
|
+
const owner={schema:'deadbyte.foreground-process.v1',pid:9496,parent_pid:5272,
|
|
7
|
+
creation_utc:'2026-10-27T12:25:38.764342Z',executable_path:'C:\\Program Files\\nodejs\\node.exe',command_line_sha256:H('a')};
|
|
8
|
+
const guardian={...owner,pid:10456,parent_pid:9496,creation_utc:'2026-10-27T12:25:39.244355Z',command_line_sha256:H('b')};
|
|
9
|
+
const marker={schema:'deadbyte.remote-session.v2',owner_pid:9496,guardian_pid:10456,
|
|
10
|
+
session_nonce:H('c'),result_session_id:'b0015a97167ff20bf4e15588',started_at_utc:'2026-10-27T12:25:38.994Z',owner_identity:owner,guardian_identity:guardian};
|
|
11
|
+
const pointer={release_id:'v0.15.68-'+H('d').slice(0,16),version:'0.15.68',manifest_sha256:H('d'),
|
|
12
|
+
generation_manifest_sha256:H('e'),generation_root:'C:\\Users\\DEADBYTE\\.deadbyte-mcp\\generations\\fixture'};
|
|
13
|
+
const authority={state:'ONLINE',local_ready:true,supervisor:{running:true},authority:Object.fromEntries(
|
|
14
|
+
['process','coding','autonomous','desktop','host'].map((plane,index)=>[plane,{configured:true,armed:[true,true,false,false,true][index],reason:[true,true,false,false,true][index]?'standing-grant':'disarmed'}]))};
|
|
15
|
+
|
|
16
|
+
function harness({ownerActual={...owner,pid:9496,creation_utc:'2026-10-28T03:22:41.668663Z',executable_path:'C:\\Windows\\WidgetBoard.exe'},
|
|
17
|
+
guardianActual=null,driftPointer=false,terminalFailure=false}={}){
|
|
18
|
+
let snapshot={marker,sha256:H('f')},intent=null,terminal=null,deletes=0,pointerReads=0;
|
|
19
|
+
return {state:()=>({snapshot,intent,terminal,deletes}),run:()=>recoverLegacyForegroundOwnership({
|
|
20
|
+
expectedMarkerSha256:H('f'),readMarkerSnapshot:async()=>snapshot,
|
|
21
|
+
deleteMarkerIfHash:async expected=>{assert.equal(expected,H('f'));snapshot=null;deletes++;return 'REMOVED';},
|
|
22
|
+
readActivePointer:async()=>{pointerReads++;return driftPointer&&pointerReads>1?{...pointer,release_id:'changed'}:pointer;},
|
|
23
|
+
readAuthority:async()=>authority,
|
|
24
|
+
queryIdentity:identity=>identity.pid===owner.pid?ownerActual:guardianActual,
|
|
25
|
+
loadIntent:async()=>intent,loadTerminal:async()=>terminal,
|
|
26
|
+
publishIntent:async value=>{intent=value;return {sha256:value.intent_sha256};},
|
|
27
|
+
publishTerminal:async value=>{if(terminalFailure){terminalFailure=false;throw new Error('terminal write failed');}terminal=value;return {sha256:value.receipt_sha256};},
|
|
28
|
+
now:()=>new Date('2026-10-28T07:00:00.000Z')
|
|
29
|
+
})};
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
test('R190 explicit owner recovery CAS-removes only a PID-reused legacy marker and preserves authority vector',async()=>{
|
|
33
|
+
const h=harness(),result=await h.run(),state=h.state();
|
|
34
|
+
assert.equal(result.status,'MARKER_RECONCILED');
|
|
35
|
+
assert.equal(state.deletes,1);
|
|
36
|
+
assert.equal(state.snapshot,null);
|
|
37
|
+
assert.equal(state.intent.authority.process.armed,true);
|
|
38
|
+
assert.equal(state.intent.authority.coding.armed,true);
|
|
39
|
+
assert.equal(state.intent.authority.autonomous.armed,false);
|
|
40
|
+
assert.equal(state.intent.authority.host.armed,true);
|
|
41
|
+
assert.equal(state.terminal.marker_sha256,H('f'));
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
test('R190 refuses live matching owner and pointer drift without deleting marker',async()=>{
|
|
45
|
+
const live=harness({ownerActual:owner});
|
|
46
|
+
await assert.rejects(()=>live.run(),/owner.*active/i);
|
|
47
|
+
assert.equal(live.state().deletes,0);
|
|
48
|
+
const drift=harness({driftPointer:true});
|
|
49
|
+
await assert.rejects(()=>drift.run(),/pointer.*drift/i);
|
|
50
|
+
assert.equal(drift.state().deletes,0);
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
test('R190 resumes terminal publication after crash following native CAS delete without repeating mutation',async()=>{
|
|
54
|
+
const h=harness({terminalFailure:true});
|
|
55
|
+
await assert.rejects(()=>h.run(),/terminal write failed/);
|
|
56
|
+
assert.equal(h.state().deletes,1);
|
|
57
|
+
const result=await h.run();
|
|
58
|
+
assert.equal(result.status,'MARKER_RECONCILED_AFTER_DELETE');
|
|
59
|
+
assert.equal(h.state().deletes,1);
|
|
60
|
+
assert.ok(h.state().terminal);
|
|
61
|
+
});
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import test from 'node:test';
|
|
2
|
+
import assert from 'node:assert/strict';
|
|
3
|
+
import {readFile} from 'node:fs/promises';
|
|
4
|
+
|
|
5
|
+
const json=async file=>JSON.parse(await readFile(new URL('../'+file,import.meta.url),'utf8'));
|
|
6
|
+
test('R190 candidate identity separates published source 0.15.69 from active runtime 0.15.68',async()=>{
|
|
7
|
+
const [pkg,bridge,release,provenance,matrix,sbom,versionSource]=await Promise.all([
|
|
8
|
+
json('package.json'),json('bridge/package.json'),json('RELEASE.json'),json('PROVENANCE.json'),
|
|
9
|
+
json('TEST-MATRIX.json'),json('SBOM.cdx.json'),readFile(new URL('../src/version.mjs',import.meta.url),'utf8')]);
|
|
10
|
+
for(const value of [pkg.version,bridge.version,release.version,provenance.version,matrix.version,
|
|
11
|
+
sbom.metadata.component.version])assert.equal(value,'0.15.71');
|
|
12
|
+
assert.match(versionSource,/DEADBYTE_VERSION = '0\.15\.71'/);
|
|
13
|
+
assert.equal(release.train,'R191');assert.equal(matrix.train,'R191');
|
|
14
|
+
assert.equal(release.source_predecessor.version,'0.15.70');
|
|
15
|
+
assert.equal(release.source_predecessor.tarball_sha256,'1a5f5f30bf4781ed951d6d201b8a972d088b2091555d7f4cc2e2bb899ed5707e');
|
|
16
|
+
assert.equal(release.source_predecessor.manifest_sha256,'df15db35c7ddddd769650f5c86557159d10d99e9eef4dd232cae67faeb8ad6e3');
|
|
17
|
+
assert.equal(release.runtime_predecessor.version,'0.15.68');
|
|
18
|
+
assert.equal(release.runtime_predecessor.release_id,'v0.15.68-ff29dbb2fbb89862');
|
|
19
|
+
assert.equal(provenance.source_predecessor_version,'0.15.70');
|
|
20
|
+
assert.equal(provenance.source_predecessor_tarball_sha256,release.source_predecessor.tarball_sha256);
|
|
21
|
+
assert.match(sbom.serialNumber,/r191:0\.15\.71$/);
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
test('R190 release metadata records verified recovery without claiming publication or activation',async()=>{
|
|
25
|
+
const release=await json('RELEASE.json');
|
|
26
|
+
assert.equal(release.r190_foreground_recovery.status,'PREPUBLISH_VERIFIED');
|
|
27
|
+
assert.equal(release.r190_foreground_recovery.npm_published,false);
|
|
28
|
+
assert.equal(release.r190_foreground_recovery.production_activated,false);
|
|
29
|
+
assert.equal(release.r190_foreground_recovery.live_marker_reconciled,true);
|
|
30
|
+
assert.equal(release.r190_foreground_recovery.authority_mutation_allowed,false);
|
|
31
|
+
assert.equal(release.r190_foreground_recovery.runtime_mutation_allowed,false);
|
|
32
|
+
assert.match(release.r190_foreground_recovery.protected_full_regression,/0_FAIL$/);
|
|
33
|
+
});
|