deadbyte-mcp 0.15.60 → 0.15.62
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 +90 -42
- package/PROVENANCE.json +33 -14
- package/README.txt +18 -18
- package/RELEASE.json +45 -16
- package/SBOM.cdx.json +2 -2
- package/TEST-MATRIX.json +24 -2
- package/bridge/package.json +1 -1
- package/controller/deadbyte-controller.ps1 +78 -1
- package/controller/deadbyte-process-policy.json +4 -4
- package/docs/R156-LOOP-AND-EPOCH-ROUTING.md +86 -0
- package/docs/R157-ARCHITECTURE-DEPENDENCIES.md +58 -0
- package/docs/R157-ARCHITECTURE-GRAPH.json +2874 -0
- package/docs/R157-ENGINEERING-HANDOFF.md +71 -0
- package/docs/R157-FILE-DELTA.json +80 -0
- package/docs/R157-FOCUSED-VERIFICATION.log +0 -0
- package/docs/R157-REFERENCE-CAPABILITY-MATRIX.md +207 -0
- package/docs/R157-REFERENCE-DECISIONS.md +46 -0
- package/docs/R157-REFERENCE-INVENTORY.json +2849 -0
- package/docs/R158-COMBINED-REGRESSION.log +0 -0
- package/docs/R158-DELTA.json +80 -0
- package/docs/R158-EPOCH-BOOTSTRAP-GREEN.log +0 -0
- package/docs/R158-EPOCH-BOOTSTRAP-RED.log +0 -0
- package/docs/R158-EPOCH-IDENTITY-RED.log +0 -0
- package/docs/R158-FOCUSED-GREEN.log +0 -0
- package/docs/R158-PROTECTED-EPOCH-VERIFIED.md +43 -0
- package/docs/R158-PROTECTED-EXACT-SOURCE-LIVE.log +0 -0
- package/docs/R158-UPDATER-REGRESSION.log +0 -0
- package/docs/R159-COMPLETED-HISTORY-BACKUP/deadbyte-cli.mjs.before +965 -0
- package/docs/R159-COMPLETED-HISTORY-BACKUP/legacy-marker-evidence-r159.mjs.before +102 -0
- package/docs/R159-COMPLETED-HISTORY-BACKUP/r159-legacy-marker-evidence.test.mjs.before +79 -0
- package/docs/R159-COMPLETED-HISTORY-PATCH.json +26 -0
- package/docs/R159-MARKER-LOCATOR-CHECKPOINT.md +46 -0
- package/docs/R160-ENGINEERING-HANDOFF.md +32 -0
- package/docs/R160-REFERENCE-CAPABILITY-MATRIX.json +165 -0
- package/package.json +2 -2
- package/scripts/bootstrap-uac-migration-r153.ps1 +17 -5
- package/scripts/deploy-r48.mjs +7 -6
- package/scripts/gate-windows-r160.ps1 +57 -0
- package/scripts/issue-uac-migration-r153.mjs +12 -5
- package/scripts/npm-release-r48.mjs +5 -0
- package/scripts/npm-release-readiness-r163.mjs +67 -0
- package/scripts/r153-prepublish-identity.mjs +3 -3
- package/scripts/request-uac-migration-r153.ps1 +17 -4
- package/scripts/verify-protected-stage-r153.mjs +1 -0
- package/src/autonomous-supervisor.mjs +21 -4
- package/src/clean-activation-recovery-r153.mjs +1 -1
- package/src/deadbyte-cli.mjs +83 -60
- package/src/host-command-pins-r154.mjs +34 -0
- package/src/legacy-marker-evidence-r159.mjs +128 -0
- package/src/package-identity-r160.mjs +61 -0
- package/src/release-generation.mjs +13 -0
- package/src/release-slot.mjs +2 -1
- package/src/remote-live-log.mjs +30 -2
- package/src/runtime-update.mjs +18 -6
- package/src/trusted-epoch-bootstrap-r158.mjs +42 -0
- package/src/uac-approval-contract-r153.mjs +6 -3
- package/src/version.mjs +1 -1
- package/src/windows-powershell-environment.mjs +5 -2
- package/test/r106-trusted-powershell-spawn.test.mjs +1 -0
- package/test/r130-release-security-contract.test.mjs +9 -9
- package/test/r132-release-identity.test.mjs +8 -10
- package/test/r153-clean-activation-recovery.test.mjs +2 -2
- package/test/r153-prepublish-identity.test.mjs +26 -5
- package/test/r153-r65-signed-precondition-recovery.test.mjs +2 -2
- package/test/r153-staging-leaf-acl.test.mjs +24 -10
- package/test/r153-uac-approval-contract.test.mjs +5 -3
- package/test/r153-uac-launch-parameter-contract.test.mjs +1 -1
- package/test/r154-multipin-recovery.test.mjs +45 -0
- package/test/r155-commander-live-log.test.mjs +44 -0
- package/test/r155-default-live-log.test.mjs +89 -0
- package/test/r156-epoch-route-smoke.ps1 +48 -0
- package/test/r156-supervisor-pacing.test.mjs +59 -0
- package/test/r157-architecture-map.mjs +61 -0
- package/test/r157-reference-survey.mjs +91 -0
- package/test/r157-supervisor-singleflight.test.mjs +32 -0
- package/test/r158-epoch-bootstrap.test.mjs +34 -0
- package/test/r159-legacy-marker-evidence.test.mjs +100 -0
- package/test/r159-legacy-marker-evidence.test.mjs.before-assertion-fix-20261025-120246.bak +100 -0
- package/test/r160-marker-proof.test.mjs +87 -0
- package/test/r160-package-decision-manifest.test.mjs +67 -0
- package/test/r160-release-contract.test.mjs +30 -0
- package/test/r160-stable-launcher.test.mjs +10 -0
- package/test/r163-isolated-slot-rollback-restore.test.mjs +58 -0
- package/test/r163-publish-fence.test.mjs +13 -0
- package/test/r48-release-contract.test.mjs +7 -1
- package/test/r58-launcher-shell.test.mjs +4 -1
- package/test/r59-tool-surface-compat.test.mjs +1 -1
- package/test/release-generation.test.mjs +16 -0
- package/test/remote-live-log-r37.test.mjs +5 -3
- package/test/remote-session-cli.test.mjs +11 -11
- package/test/stabilization-state-root.test.mjs +2 -1
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
import test from 'node:test';
|
|
2
|
+
import assert from 'node:assert/strict';
|
|
3
|
+
import {mkdtemp,mkdir,writeFile,readFile,rm} from 'node:fs/promises';
|
|
4
|
+
import path from 'node:path';
|
|
5
|
+
import os from 'node:os';
|
|
6
|
+
import {createHash} from 'node:crypto';
|
|
7
|
+
import {
|
|
8
|
+
selectLegacyMarkerEvidence,
|
|
9
|
+
verifyLocatedLegacyMarkerReceipt
|
|
10
|
+
} from '../src/legacy-marker-evidence-r159.mjs';
|
|
11
|
+
import {createMarkerReconciliationReceipt} from '../src/clean-activation-recovery-r153.mjs';
|
|
12
|
+
import {canonicalJson} from '../src/canonical-json.mjs';
|
|
13
|
+
|
|
14
|
+
const digest=bytes=>createHash('sha256').update(bytes).digest('hex');
|
|
15
|
+
const id=value=>value.repeat(64);
|
|
16
|
+
const pointer=Object.freeze({
|
|
17
|
+
release_id:'v0.15.61-CURRENT',version:'0.15.61',
|
|
18
|
+
manifest_sha256:id('1'),generation_manifest_sha256:id('2')
|
|
19
|
+
});
|
|
20
|
+
const policySha256=Object.freeze({
|
|
21
|
+
process:id('3'),coding:id('4'),autonomous:id('5'),desktop:id('6'),host:id('7')
|
|
22
|
+
});
|
|
23
|
+
const authority=Object.freeze({process:false,coding:false,autonomous:false,desktop:false,host:false});
|
|
24
|
+
const proofFor=(markerSha256,policies=policySha256)=>digest(Buffer.from(canonicalJson({
|
|
25
|
+
pointer,policy_sha256:policies,marker_sha256:markerSha256,authority
|
|
26
|
+
}),'utf8'));
|
|
27
|
+
|
|
28
|
+
async function locatedFixture(t,{tamper}={}){
|
|
29
|
+
const root=await mkdtemp(path.join(os.tmpdir(),'deadbyte-r160-located-'));
|
|
30
|
+
const evidenceDir=path.join(root,'legacy-migration');
|
|
31
|
+
await mkdir(evidenceDir);
|
|
32
|
+
t.after(()=>rm(root,{recursive:true,force:true}));
|
|
33
|
+
const markerSha256=id('a'),intentSha256=id('b');
|
|
34
|
+
const receipt=createMarkerReconciliationReceipt({
|
|
35
|
+
pointer,policySha256,markerSha256,
|
|
36
|
+
proofSha256:proofFor(markerSha256),
|
|
37
|
+
observedAtUtc:'2026-10-25T00:00:00.000Z',intentSha256
|
|
38
|
+
});
|
|
39
|
+
const terminal=tamper?tamper({...receipt}):receipt;
|
|
40
|
+
const terminalPath=path.join(evidenceDir,`marker-${markerSha256}.terminal.json`);
|
|
41
|
+
const terminalBytes=Buffer.from(`${JSON.stringify(terminal)}\n`,'utf8');
|
|
42
|
+
await writeFile(terminalPath,terminalBytes);
|
|
43
|
+
await writeFile(path.join(evidenceDir,'r153-pending-migration.json'),`${JSON.stringify({
|
|
44
|
+
schema:'deadbyte.r153-pending-migration.v1',marker_sha256:markerSha256,
|
|
45
|
+
receipt_path:terminalPath,receipt_sha256:digest(terminalBytes)
|
|
46
|
+
})}\n`);
|
|
47
|
+
return {evidenceDir,terminalPath};
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
test('R160 located receipt is accepted only after R153 proof binding verification',async t=>{
|
|
51
|
+
const fixture=await locatedFixture(t);
|
|
52
|
+
const selected=await selectLegacyMarkerEvidence({evidenceDir:fixture.evidenceDir,activePointer:pointer});
|
|
53
|
+
const verified=await verifyLocatedLegacyMarkerReceipt({located:selected,pointer,policySha256});
|
|
54
|
+
assert.equal(verified.state,'MARKER_RECONCILED');
|
|
55
|
+
assert.equal(verified.receipt_path,fixture.terminalPath);
|
|
56
|
+
assert.match(verified.receipt_sha256,/^[0-9a-f]{64}$/);
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
test('R160 consistent locator and terminal hashes cannot hide a wrong proof subject',async t=>{
|
|
60
|
+
const fixture=await locatedFixture(t,{tamper:receipt=>({...receipt,proof_sha256:id('f')})});
|
|
61
|
+
const selected=await selectLegacyMarkerEvidence({evidenceDir:fixture.evidenceDir,activePointer:pointer});
|
|
62
|
+
assert.equal(selected.kind,'located','R159 selector alone accepts the internally consistent bytes');
|
|
63
|
+
await assert.rejects(
|
|
64
|
+
verifyLocatedLegacyMarkerReceipt({located:selected,pointer,policySha256}),
|
|
65
|
+
/proof binding mismatch|subject mismatch|predecessor drift/i
|
|
66
|
+
);
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
test('R160 located receipt verification rejects policy drift after selection',async t=>{
|
|
70
|
+
const fixture=await locatedFixture(t);
|
|
71
|
+
const selected=await selectLegacyMarkerEvidence({evidenceDir:fixture.evidenceDir,activePointer:pointer});
|
|
72
|
+
await assert.rejects(
|
|
73
|
+
verifyLocatedLegacyMarkerReceipt({
|
|
74
|
+
located:selected,pointer,policySha256:{...policySha256,coding:id('c')}
|
|
75
|
+
}),
|
|
76
|
+
/proof binding mismatch|predecessor drift/i
|
|
77
|
+
);
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
test('R160 CLI rechecks active pointer after located proof verification',async()=>{
|
|
81
|
+
const source=await readFile(new URL('../src/deadbyte-cli.mjs',import.meta.url),'utf8');
|
|
82
|
+
assert.match(source,/R160 active release changed after located marker verification/);
|
|
83
|
+
const verifyAt=source.indexOf('verifyLocatedLegacyMarkerReceipt({located:selected,pointer:readback,policySha256})');
|
|
84
|
+
const finalReadAt=source.indexOf('await readActiveRuntimeBinding(ctx.root)',verifyAt);
|
|
85
|
+
assert.ok(verifyAt>0&&finalReadAt>verifyAt,'final pointer read-back must follow proof verification');
|
|
86
|
+
});
|
|
87
|
+
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import test from 'node:test';
|
|
2
|
+
import assert from 'node:assert/strict';
|
|
3
|
+
import {mkdtemp,mkdir,writeFile,readFile,rm} from 'node:fs/promises';
|
|
4
|
+
import path from 'node:path';
|
|
5
|
+
import os from 'node:os';
|
|
6
|
+
import {createHash} from 'node:crypto';
|
|
7
|
+
import {
|
|
8
|
+
R160_STARTUP_DECISION_FILES,
|
|
9
|
+
verifyPackageDecisionManifest
|
|
10
|
+
} from '../src/package-identity-r160.mjs';
|
|
11
|
+
|
|
12
|
+
const sha=bytes=>createHash('sha256').update(bytes).digest('hex');
|
|
13
|
+
|
|
14
|
+
async function fixture(t){
|
|
15
|
+
const root=await mkdtemp(path.join(os.tmpdir(),'deadbyte-r160-package-'));
|
|
16
|
+
t.after(()=>rm(root,{recursive:true,force:true}));
|
|
17
|
+
const bodies=new Map();
|
|
18
|
+
for(const rel of R160_STARTUP_DECISION_FILES){
|
|
19
|
+
const bytes=Buffer.from(rel==='package.json'
|
|
20
|
+
? `${JSON.stringify({name:'deadbyte-mcp',version:'0.15.62'})}\n`
|
|
21
|
+
: `fixture:${rel}\n`,'utf8');
|
|
22
|
+
const file=path.join(root,...rel.split('/'));
|
|
23
|
+
await mkdir(path.dirname(file),{recursive:true});
|
|
24
|
+
await writeFile(file,bytes);
|
|
25
|
+
bodies.set(rel,bytes);
|
|
26
|
+
}
|
|
27
|
+
const manifest=Buffer.from([...bodies].map(([rel,bytes])=>`${sha(bytes)} ${rel}`).join('\n')+'\n','utf8');
|
|
28
|
+
await writeFile(path.join(root,'MANIFEST.SHA256'),manifest);
|
|
29
|
+
return {root,bodies,manifest};
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
test('R160 startup decision manifest binds every decision file and package identity',async t=>{
|
|
33
|
+
const f=await fixture(t);
|
|
34
|
+
const result=await verifyPackageDecisionManifest({root:f.root,expectedVersion:'0.15.62'});
|
|
35
|
+
assert.equal(result.files,R160_STARTUP_DECISION_FILES.length);
|
|
36
|
+
assert.equal(result.manifest_sha256,sha(f.manifest));
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
test('R160 startup decision manifest rejects changed bytes before foreground claim',async t=>{
|
|
40
|
+
const f=await fixture(t);
|
|
41
|
+
await writeFile(path.join(f.root,'src','deadbyte-cli.mjs'),'changed\n');
|
|
42
|
+
await assert.rejects(
|
|
43
|
+
verifyPackageDecisionManifest({root:f.root,expectedVersion:'0.15.62'}),
|
|
44
|
+
/startup decision file hash mismatch: src\/deadbyte-cli\.mjs/
|
|
45
|
+
);
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
test('R160 startup decision manifest rejects a newly required recovery module omitted from manifest',async t=>{
|
|
49
|
+
const f=await fixture(t);
|
|
50
|
+
const lines=(await readFile(path.join(f.root,'MANIFEST.SHA256'),'utf8')).split(/\r?\n/)
|
|
51
|
+
.filter(line=>!line.endsWith(' src/legacy-marker-evidence-r159.mjs'));
|
|
52
|
+
await writeFile(path.join(f.root,'MANIFEST.SHA256'),`${lines.join('\n')}\n`);
|
|
53
|
+
await assert.rejects(
|
|
54
|
+
verifyPackageDecisionManifest({root:f.root,expectedVersion:'0.15.62'}),
|
|
55
|
+
/startup decision manifest missing: src\/legacy-marker-evidence-r159\.mjs/
|
|
56
|
+
);
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
test('R160 remote startup verifies decision bytes before evidence recovery and foreground claim',async()=>{
|
|
60
|
+
const source=await readFile(new URL('../src/deadbyte-cli.mjs',import.meta.url),'utf8');
|
|
61
|
+
const verifyAt=source.indexOf('await verifyPackageDecisionManifest({root:CURRENT_PACKAGE_ROOT,expectedVersion:DEADBYTE_VERSION})');
|
|
62
|
+
const recoverAt=source.indexOf('const migrationContext=await recoverRemoteSession(ctx,{displayMode})');
|
|
63
|
+
const claimAt=source.indexOf('marker=await claimRemoteSession(ctx)');
|
|
64
|
+
assert.ok(verifyAt>0&&verifyAt<recoverAt&&recoverAt<claimAt,
|
|
65
|
+
'package decision manifest must be verified before recovery and claim');
|
|
66
|
+
});
|
|
67
|
+
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import test from 'node:test';
|
|
2
|
+
import assert from 'node:assert/strict';
|
|
3
|
+
import {readFile} from 'node:fs/promises';
|
|
4
|
+
import path from 'node:path';
|
|
5
|
+
import {fileURLToPath} from 'node:url';
|
|
6
|
+
import {DEADBYTE_VERSION} from '../src/version.mjs';
|
|
7
|
+
|
|
8
|
+
const root=path.resolve(fileURLToPath(new URL('..',import.meta.url)));
|
|
9
|
+
const read=async rel=>JSON.parse(await readFile(path.join(root,rel),'utf8'));
|
|
10
|
+
|
|
11
|
+
test('R160 package identity separates source and live runtime predecessors',async()=>{
|
|
12
|
+
const [pkg,lock,bridge,release,provenance,matrix,bom]=await Promise.all([
|
|
13
|
+
read('package.json'),read('package-lock.json'),read('bridge/package.json'),read('RELEASE.json'),
|
|
14
|
+
read('PROVENANCE.json'),read('TEST-MATRIX.json'),read('SBOM.cdx.json')]);
|
|
15
|
+
for(const version of [pkg.version,lock.version,lock.packages[''].version,bridge.version,release.version,
|
|
16
|
+
provenance.version,matrix.version,bom.metadata.component.version,DEADBYTE_VERSION]) assert.equal(version,'0.15.62');
|
|
17
|
+
assert.equal(release.train,'R160');
|
|
18
|
+
assert.equal(matrix.train,'R160');
|
|
19
|
+
assert.equal(release.runtime_predecessor.version,'0.15.61');
|
|
20
|
+
assert.equal(release.runtime_predecessor.release_id,'v0.15.61-055d1ff0aa99e60b');
|
|
21
|
+
assert.equal(release.runtime_predecessor.manifest_sha256,'055d1ff0aa99e60bbf9e9f136b02ebd4fffb8391712560f7607cff30af94dcdf');
|
|
22
|
+
assert.equal(release.runtime_predecessor.generation_manifest_sha256,'f940e794daac2dda097a66301c4b85c4b29a2df7c669e549503c05a2a55ff6c4');
|
|
23
|
+
assert.equal(release.source_predecessor.train,'R159');
|
|
24
|
+
assert.match(release.source_predecessor.source_path,/deadbyte-mcp-0\.15\.62-r159-marker-reconcile/i);
|
|
25
|
+
assert.equal(release.r160.public_tool_delta,0);
|
|
26
|
+
assert.equal(release.r160.authority_scope_delta,0);
|
|
27
|
+
assert.equal(release.r160.production_activation,'NOT_RUN');
|
|
28
|
+
assert.equal(release.r160.npm_publish,'NOT_RUN');
|
|
29
|
+
assert.equal(provenance.r160.source_bytes_copied_from_references,false);
|
|
30
|
+
});
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import test from 'node:test';
|
|
2
|
+
import assert from 'node:assert/strict';
|
|
3
|
+
import {renderStableLauncherCmd} from '../src/release-slot.mjs';
|
|
4
|
+
|
|
5
|
+
test('R160 stable CMD launcher selects the native Windows PowerShell explicitly', () => {
|
|
6
|
+
const cmd=renderStableLauncherCmd();
|
|
7
|
+
assert.match(cmd,/if not defined SystemRoot exit \/b 1/i);
|
|
8
|
+
assert.match(cmd,/"%SystemRoot%\\System32\\WindowsPowerShell\\v1\.0\\powershell\.exe"/i);
|
|
9
|
+
assert.doesNotMatch(cmd,/(?:^|\r?\n)powershell\.exe\s/im);
|
|
10
|
+
});
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import test from 'node:test';
|
|
2
|
+
import assert from 'node:assert/strict';
|
|
3
|
+
import {createHash} from 'node:crypto';
|
|
4
|
+
import {mkdtemp,mkdir,readFile,rm,writeFile,lstat} from 'node:fs/promises';
|
|
5
|
+
import os from 'node:os';
|
|
6
|
+
import path from 'node:path';
|
|
7
|
+
import {fileURLToPath} from 'node:url';
|
|
8
|
+
import {atomicWriteActivePointer,materializeReleaseSlot,parseActivePointer,parseManifest,renderStableLauncherPs1,verifyManifestTreeExact} from '../src/release-slot.mjs';
|
|
9
|
+
|
|
10
|
+
const candidate=path.dirname(path.dirname(fileURLToPath(import.meta.url)));
|
|
11
|
+
const version='0.15.62';
|
|
12
|
+
const parent=path.join(os.homedir(),'.deadbyte-mcp','releases','v0.15.61-055d1ff0aa99e60b');
|
|
13
|
+
const parentDigest='055d1ff0aa99e60bbf9e9f136b02ebd4fffb8391712560f7607cff30af94dcdf';
|
|
14
|
+
const sha=bytes=>createHash('sha256').update(bytes).digest('hex');
|
|
15
|
+
async function generation(state,label){
|
|
16
|
+
const root=path.join(state,'generations',label);
|
|
17
|
+
await mkdir(root,{recursive:true});
|
|
18
|
+
const config=Buffer.from(`@{ Label = '${label}' }\r\n`,'utf8');
|
|
19
|
+
await writeFile(path.join(root,'controller-config.psd1'),config,{flag:'wx'});
|
|
20
|
+
const manifest=Buffer.from(`${sha(config)} controller-config.psd1\n`,'utf8');
|
|
21
|
+
await writeFile(path.join(root,'GENERATION.SHA256'),manifest,{flag:'wx'});
|
|
22
|
+
return {root,manifest_sha256:sha(manifest),config_path:path.join(root,'controller-config.psd1')};
|
|
23
|
+
}
|
|
24
|
+
test('R163 isolated immutable slot activates, rolls back, and restores 0.15.62 without authority or production mutation',async()=>{
|
|
25
|
+
const stateBase=await mkdtemp(path.join(os.tmpdir(),'deadbyte-r163-isolated-'));
|
|
26
|
+
const state=path.join(stateBase,'state');
|
|
27
|
+
try{
|
|
28
|
+
const pkg=JSON.parse(await readFile(path.join(candidate,'package.json'),'utf8'));
|
|
29
|
+
assert.equal(pkg.version,version);
|
|
30
|
+
const parentManifest=await readFile(path.join(parent,'MANIFEST.SHA256'));
|
|
31
|
+
const nextManifest=await readFile(path.join(candidate,'MANIFEST.SHA256'));
|
|
32
|
+
assert.equal(sha(parentManifest),parentDigest);
|
|
33
|
+
const nextDigest=sha(nextManifest);
|
|
34
|
+
const parentSlot=await materializeReleaseSlot({source:parent,stateRoot:state,version:'0.15.61',manifestSha256:parentDigest});
|
|
35
|
+
const nextSlot=await materializeReleaseSlot({source:candidate,stateRoot:state,version,manifestSha256:nextDigest});
|
|
36
|
+
const pGen=await generation(state,'r163-parent');
|
|
37
|
+
const nGen=await generation(state,'r163-next');
|
|
38
|
+
const launcherSha=sha(Buffer.from(renderStableLauncherPs1(),'utf8'));
|
|
39
|
+
const pointerPath=path.join(state,'active-release.json');
|
|
40
|
+
const ptr=(slot,ver,g)=>({schema:'deadbyte.active-release.v1',release_id:slot.release_id,version:ver,
|
|
41
|
+
manifest_sha256:slot.manifest_sha256,release_root:slot.release_root,generation_root:g.root,
|
|
42
|
+
generation_manifest_sha256:g.manifest_sha256,launcher_sha256:launcherSha,config_path:g.config_path});
|
|
43
|
+
const activated=await atomicWriteActivePointer(pointerPath,ptr(nextSlot,version,nGen));
|
|
44
|
+
assert.equal(activated.release_id,nextSlot.release_id);
|
|
45
|
+
await verifyManifestTreeExact(nextSlot.release_root,parseManifest(nextManifest.toString('utf8')));
|
|
46
|
+
const rolled=await atomicWriteActivePointer(pointerPath,ptr(parentSlot,'0.15.61',pGen));
|
|
47
|
+
assert.equal(rolled.release_id,parentSlot.release_id);
|
|
48
|
+
assert.equal(rolled.manifest_sha256,parentDigest);
|
|
49
|
+
await verifyManifestTreeExact(parentSlot.release_root,parseManifest(parentManifest.toString('utf8')));
|
|
50
|
+
const restored=await atomicWriteActivePointer(pointerPath,ptr(nextSlot,version,nGen));
|
|
51
|
+
assert.deepEqual(restored,activated);
|
|
52
|
+
assert.deepEqual(parseActivePointer(await readFile(pointerPath,'utf8')),activated);
|
|
53
|
+
await verifyManifestTreeExact(nextSlot.release_root,parseManifest(nextManifest.toString('utf8')));
|
|
54
|
+
for(const n of ['process-grant.json','coding-grant.json','autonomous-grant.json','desktop-grant.json','host-grant.json']){
|
|
55
|
+
await assert.rejects(()=>lstat(path.join(state,'runtime',n)),{code:'ENOENT'});
|
|
56
|
+
}
|
|
57
|
+
}finally{await rm(stateBase,{recursive:true,force:true});}
|
|
58
|
+
});
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import test from 'node:test';
|
|
2
|
+
import assert from 'node:assert/strict';
|
|
3
|
+
import path from 'node:path';
|
|
4
|
+
import {fileURLToPath} from 'node:url';
|
|
5
|
+
import {assertR163NpmPublishReady} from '../scripts/npm-release-readiness-r163.mjs';
|
|
6
|
+
const root=path.dirname(path.dirname(fileURLToPath(import.meta.url)));
|
|
7
|
+
const release={name:'deadbyte-mcp',version:'0.15.62',train:'R160',r163:{publication_mode:'PUBLISH_ONLY'},parent:{release_id:'v0.15.61-055d1ff0aa99e60b',manifest_sha256:'055d1ff0aa99e60bbf9e9f136b02ebd4fffb8391712560f7607cff30af94dcdf',generation_manifest_sha256:'f940e794daac2dda097a66301c4b85c4b29a2df7c669e549503c05a2a55ff6c4'},runtime_predecessor:{release_id:'v0.15.61-055d1ff0aa99e60b'}};
|
|
8
|
+
test('R163 publish gate fails closed with missing evidence',async()=>{
|
|
9
|
+
await assert.rejects(()=>assertR163NpmPublishReady(release,{root,evidenceRoot:null}),/PUBLISH_BLOCKED R163/);
|
|
10
|
+
});
|
|
11
|
+
test('R163 publish gate rejects changed parent identity before evidence read',async()=>{
|
|
12
|
+
await assert.rejects(()=>assertR163NpmPublishReady({...release,parent:{...release.parent,release_id:'v0.15.60-wrong'}},{root}),/predecessor lineage mismatch/);
|
|
13
|
+
});
|
|
@@ -30,5 +30,11 @@ test('R48 compatibility release contract remains coherent in the current success
|
|
|
30
30
|
assert.equal(provenance.parent_release_id, release.parent.release_id);
|
|
31
31
|
assert.equal(provenance.parent_manifest_sha256, release.parent.manifest_sha256);
|
|
32
32
|
assert.equal(provenance.parent_generation_manifest_sha256, release.parent.generation_manifest_sha256);
|
|
33
|
-
|
|
33
|
+
if(release.train==='R160'){
|
|
34
|
+
assert.equal(release.source_predecessor.train,'R159');
|
|
35
|
+
assert.equal(release.source_predecessor.manifest_status,'STALE_REJECTED');
|
|
36
|
+
assert.equal(provenance.r160.source_predecessor_manifest_status,'STALE_REJECTED');
|
|
37
|
+
}else{
|
|
38
|
+
assert.equal(provenance.source_predecessor_tarball_sha256, release.source_predecessor.tarball_sha256);
|
|
39
|
+
}
|
|
34
40
|
});
|
|
@@ -4,6 +4,7 @@ import { mkdir, mkdtemp, writeFile } from 'node:fs/promises';
|
|
|
4
4
|
import { spawnSync } from 'node:child_process';
|
|
5
5
|
import path from 'node:path';
|
|
6
6
|
import { renderStableLauncherPs1 } from '../src/release-slot.mjs';
|
|
7
|
+
import { windowsPowerShellEnvironment } from '../src/runtime-update.mjs';
|
|
7
8
|
|
|
8
9
|
test('R58 launcher dispatch uses native Windows PowerShell even when entered from pwsh', async () => {
|
|
9
10
|
const base = 'D:/Projects/deadbyte-mcp/testing/0.15.35/fixtures';
|
|
@@ -20,7 +21,9 @@ test('R58 launcher dispatch uses native Windows PowerShell even when entered fro
|
|
|
20
21
|
const native = path.join(process.env.SystemRoot, 'System32', 'WindowsPowerShell', 'v1.0', 'powershell.exe');
|
|
21
22
|
const modern = path.join(process.env.USERPROFILE, '.cache', 'codex-runtimes', 'codex-primary-runtime', 'dependencies', 'native', 'powershell', 'pwsh.exe');
|
|
22
23
|
for (const executable of [native, modern]) {
|
|
23
|
-
const observed = spawnSync(executable, ['-NoProfile', '-File', dispatch], {
|
|
24
|
+
const observed = spawnSync(executable, ['-NoProfile', '-File', dispatch], {
|
|
25
|
+
env: windowsPowerShellEnvironment(process.env), encoding: 'utf8', timeout: 20000
|
|
26
|
+
});
|
|
24
27
|
assert.ifError(observed.error);
|
|
25
28
|
assert.equal(observed.status, 7, observed.stderr);
|
|
26
29
|
assert.match(observed.stdout, /FIXTURE:status/);
|
|
@@ -83,7 +83,7 @@ test('R72 compact baseline permits only R64 read offset and authorized coding-on
|
|
|
83
83
|
assert.deepEqual(diff.added,[]);
|
|
84
84
|
assert.deepEqual(diff.removed,[]);
|
|
85
85
|
const packageVersion=JSON.parse(await readFile(path.join(repoRoot,'package.json'),'utf8')).version;
|
|
86
|
-
if(['0.15.41','0.15.42','0.15.43','0.15.44','0.15.45','0.15.46','0.15.47','0.15.48','0.15.49','0.15.50','0.15.51','0.15.52','0.15.53','0.15.54','0.15.55','0.15.56','0.15.57','0.15.58','0.15.59','0.15.60'].includes(packageVersion)){
|
|
86
|
+
if(['0.15.41','0.15.42','0.15.43','0.15.44','0.15.45','0.15.46','0.15.47','0.15.48','0.15.49','0.15.50','0.15.51','0.15.52','0.15.53','0.15.54','0.15.55','0.15.56','0.15.57','0.15.58','0.15.59','0.15.60','0.15.61','0.15.62'].includes(packageVersion)){
|
|
87
87
|
assert.deepEqual([...diff.schema_changed].sort(),['machine_fs','native_diff_preview']);
|
|
88
88
|
assert.equal(diff.unchanged.length,41);
|
|
89
89
|
const prior=baseline.tools.find(tool=>tool.name==='machine_fs');
|
|
@@ -156,6 +156,22 @@ test('R27 generation bundle rebinds authority and mutable state outside the rele
|
|
|
156
156
|
const second=await build({stateRoot:state,releaseRoot:release,developmentRoot:development,workspaceRoot:workspace,releaseId:'v0.8.3-aaaaaaaaaaaaaaaa',currentConfig:changedConfig,codingPolicy:coding,autonomousPolicy:autonomous,hostPolicy:host,processPolicy,desktopPolicy});
|
|
157
157
|
assert.notEqual(second.generation_root,result.generation_root);
|
|
158
158
|
assert.notEqual(second.generation_seed_sha256,result.generation_seed_sha256);
|
|
159
|
+
// R158 regression: changing the trusted Epoch source changes generation identity.
|
|
160
|
+
const epochA=structuredClone(currentConfig);
|
|
161
|
+
epochA.AuthorityEpochRoot='C:\\Program Files\\DEADBYTE-R158-Epoch-A';
|
|
162
|
+
const generationA=await build({stateRoot:state,releaseRoot:release,developmentRoot:development,workspaceRoot:workspace,releaseId:'v0.8.3-aaaaaaaaaaaaaaaa',currentConfig:epochA,codingPolicy:coding,autonomousPolicy:autonomous,hostPolicy:host,processPolicy,desktopPolicy});
|
|
163
|
+
assert.notEqual(generationA.generation_root,result.generation_root);
|
|
164
|
+
assert.match(await readFile(generationA.config_path,'utf8'),/AuthorityEpochRoot = 'C:\\Program Files\\DEADBYTE-R158-Epoch-A'/);
|
|
165
|
+
const epochB=structuredClone(epochA);
|
|
166
|
+
epochB.AuthorityEpochRoot='C:\\Program Files\\DEADBYTE-R158-Epoch-B';
|
|
167
|
+
const generationB=await build({stateRoot:state,releaseRoot:release,developmentRoot:development,workspaceRoot:workspace,releaseId:'v0.8.3-aaaaaaaaaaaaaaaa',currentConfig:epochB,codingPolicy:coding,autonomousPolicy:autonomous,hostPolicy:host,processPolicy,desktopPolicy});
|
|
168
|
+
assert.notEqual(generationB.generation_root,generationA.generation_root);
|
|
169
|
+
assert.notEqual(generationB.generation_seed_sha256,generationA.generation_seed_sha256);
|
|
170
|
+
for(const invalidRoot of ['relative\\epoch','\\\\server\\share\\epoch','C:relative','']){
|
|
171
|
+
const invalid=structuredClone(currentConfig);
|
|
172
|
+
invalid.AuthorityEpochRoot=invalidRoot;
|
|
173
|
+
await assert.rejects(build({stateRoot:state,releaseRoot:release,developmentRoot:development,workspaceRoot:workspace,releaseId:'v0.8.3-aaaaaaaaaaaaaaaa',currentConfig:invalid,codingPolicy:coding,autonomousPolicy:autonomous,hostPolicy:host,processPolicy,desktopPolicy}),/AuthorityEpochRoot.*drive-absolute/i);
|
|
174
|
+
}
|
|
159
175
|
const endpointChanged=structuredClone(currentConfig);
|
|
160
176
|
endpointChanged.McpUrlFileSha256='2'.repeat(64);
|
|
161
177
|
const third=await build({stateRoot:state,releaseRoot:release,developmentRoot:development,workspaceRoot:workspace,releaseId:'v0.8.3-aaaaaaaaaaaaaaaa',currentConfig:endpointChanged,codingPolicy:coding,autonomousPolicy:autonomous,hostPolicy:host,processPolicy,desktopPolicy});
|
|
@@ -72,13 +72,15 @@ test('R40 CLI exposes explicit verbose mode while keeping default live mode comp
|
|
|
72
72
|
const {readFile}=await import('node:fs/promises');
|
|
73
73
|
const src=await readFile(new URL('../src/deadbyte-cli.mjs',import.meta.url),'utf8');
|
|
74
74
|
assert.match(src,/readLiveRootPaths\(ctx\.root\)/);
|
|
75
|
-
assert.match(src,/createLiveTimelineTracker\(\{verbose,displayMode,rootPaths,trace
|
|
75
|
+
assert.match(src,/createLiveTimelineTracker\(\{verbose,displayMode,rootPaths,trace,\.\.\.defaultForegroundTimelineOptions\(\{verbose\}\)\}\)/);
|
|
76
|
+
assert.deepEqual(live.defaultForegroundTimelineOptions({verbose:false}),{commanderStyle:true,foldHeartbeats:false});
|
|
77
|
+
assert.deepEqual(live.defaultForegroundTimelineOptions({verbose:true}),{commanderStyle:false,foldHeartbeats:false});
|
|
76
78
|
assert.match(src,/createLiveTimelineFilter\(filterExpression\)/);
|
|
77
79
|
assert.match(src,/createLiveTimelineWriter\(\{stream:process\.stdout,displayMode,rewrite:false\}\)/);
|
|
78
80
|
assert.match(src,/arg==='--verbose'\|\|arg==='-v'/);
|
|
79
81
|
assert.match(src,/arg==='--trace'/);
|
|
80
82
|
assert.match(src,/arg==='--filter'/);
|
|
81
|
-
assert.match(src,/Transport
|
|
83
|
+
assert.match(src,/Transport hidden; signed audit remains on disk/);
|
|
82
84
|
assert.doesNotMatch(src,/\[cursor \$\{recorded\.cursor\}\]/);
|
|
83
85
|
});
|
|
84
86
|
|
|
@@ -232,7 +234,7 @@ test('human mutation span labels START and preserves terminal audit status',()=>
|
|
|
232
234
|
test('foreground human log keeps START and DONE separate and folds heartbeat noise',async()=>{
|
|
233
235
|
const {readFile}=await import('node:fs/promises');
|
|
234
236
|
const src=await readFile(new URL('../src/deadbyte-cli.mjs',import.meta.url),'utf8');
|
|
235
|
-
assert.match(src,/createLiveTimelineTracker\(\{verbose,displayMode,rootPaths,trace
|
|
237
|
+
assert.match(src,/createLiveTimelineTracker\(\{verbose,displayMode,rootPaths,trace,\.\.\.defaultForegroundTimelineOptions\(\{verbose\}\)\}\)/);
|
|
236
238
|
assert.match(src,/createLiveTimelineWriter\(\{stream:process\.stdout,displayMode,rewrite:false\}\)/);
|
|
237
239
|
});
|
|
238
240
|
|
|
@@ -330,7 +330,7 @@ winTest('bare remote uses one unified live timeline, keeps authority semantics,
|
|
|
330
330
|
assert.match(output.stdout,/\[OK\] ONLINE/);
|
|
331
331
|
assert.match(output.stdout,/\[READY\] DEADBYTE MCP online/);
|
|
332
332
|
assert.match(output.stdout,/\[AUTH\] process ARMED \| coding ARMED \| autonomous ARMED \| desktop DISARMED \| host DISARMED/);
|
|
333
|
-
assert.match(output.stdout,/\[LOG\]
|
|
333
|
+
assert.match(output.stdout,/\[LOG\] Live tool calls with individual START\/DONE, sanitized arguments and results\. Transport hidden; signed audit remains on disk\./);
|
|
334
334
|
assert.doesNotMatch(output.stdout,/Process plane\s*:/);
|
|
335
335
|
assert.doesNotMatch(output.stdout,/Coding plane\s*:/);
|
|
336
336
|
const auditDir=path.join(fx.state,'audit');
|
|
@@ -353,11 +353,11 @@ winTest('bare remote uses one unified live timeline, keeps authority semantics,
|
|
|
353
353
|
'[TOOL OUT] 2026-09-16T00:00:00.002Z #000002 coding_file_read {"is_error":true}'
|
|
354
354
|
].join('\n')+'\n','utf8');
|
|
355
355
|
const logDeadline=Date.now()+fixtureTimeoutMs;
|
|
356
|
-
while(Date.now()<logDeadline
|
|
357
|
-
assert.
|
|
358
|
-
assert.match(output.stdout
|
|
359
|
-
assert.match(output.stdout
|
|
360
|
-
assert.equal((output.stdout.match(/\[abcdef12\]
|
|
356
|
+
while(Date.now()<logDeadline&&!/^\[.*\] \[FAIL\] Tool call coding_file_read failed \[fedcba98\] 1\.73s$/m.test(output.stdout)) await sleep(50);
|
|
357
|
+
assert.match(output.stdout,/\[CALL\] Received tool call \[abcdef12\] coding_tree Arguments:/,'default human view must show bounded sanitized arguments');
|
|
358
|
+
assert.match(output.stdout,/^\[.*\] \[DONE\] Tool call coding_tree completed \[abcdef12\] 42ms$/m);
|
|
359
|
+
assert.match(output.stdout,/^\[.*\] \[FAIL\] Tool call coding_file_read failed \[fedcba98\] 1\.73s$/m);
|
|
360
|
+
assert.equal((output.stdout.match(/\[DONE\] Tool call coding_tree completed \[abcdef12\] 42ms/g)||[]).length,1,'duplicate terminal result must print once');
|
|
361
361
|
assert.doesNotMatch(output.stdout,/\[cursor r1:/,'full replay cursor belongs in the journal, not every human line');
|
|
362
362
|
const resultJournal=JSON.parse(await readFile(path.join(fx.state,'runtime','remote-results',`${marker.result_session_id}.json`),'utf8'));
|
|
363
363
|
assert.deepEqual(resultJournal.events.map(event=>event.seq),[1,2,3,4]);
|
|
@@ -377,7 +377,7 @@ winTest('bare remote uses one unified live timeline, keeps authority semantics,
|
|
|
377
377
|
const commands=await traceCommands(fx.trace);
|
|
378
378
|
assert.deepEqual(commands.slice(-8),['disarmautonomous','disarmcoding','disarmprocess','disarmdesktop','disarmhost','status','machinetest','park']);
|
|
379
379
|
assert.match(output.stdout,/^DEADBYTE MCP$/m);
|
|
380
|
-
assert.match(output.stdout,/
|
|
380
|
+
assert.match(output.stdout,/Live tool calls with individual START\/DONE/i);
|
|
381
381
|
assert.match(output.stdout,/\[AUTH\] Revoking session authority/);
|
|
382
382
|
assert.doesNotMatch(output.stdout,/disarmautonomous OK|disarmcoding OK|disarmprocess OK|disarmdesktop OK|disarmhost OK|DEADBYTE MCP = PARKED/);
|
|
383
383
|
assert.doesNotMatch(output.stdout,/\p{Extended_Pictographic}/u,'piped Windows fallback must remain ASCII-safe');
|
|
@@ -395,7 +395,7 @@ winTest('remote --resume replays sanitized tool-call and terminal result events
|
|
|
395
395
|
const firstOutput=await collect(first);
|
|
396
396
|
await waitFor(fx.trace,commands=>commands.filter(x=>x==='status').length>=4);
|
|
397
397
|
const liveDeadline=Date.now()+3000;
|
|
398
|
-
while(Date.now()<liveDeadline&&!/\[LOG\]
|
|
398
|
+
while(Date.now()<liveDeadline&&!/\[LOG\] Live tool calls/.test(firstOutput.stdout)) await sleep(50);
|
|
399
399
|
const marker=JSON.parse(await readFile(path.join(fx.state,'runtime','remote-session.json'),'utf8'));
|
|
400
400
|
assert.match(String(marker.result_session_id||''),/^[0-9a-f]{24}$/);
|
|
401
401
|
const auditDir=path.join(fx.state,'audit');await mkdir(auditDir,{recursive:true});
|
|
@@ -428,7 +428,7 @@ winTest('remote --resume replays sanitized tool-call and terminal result events
|
|
|
428
428
|
deadline=Date.now()+5000;
|
|
429
429
|
while(Date.now()<deadline&&(!/coding_file_read\s+\| Arguments: .*\| REPLAY #3/.test(secondOutput.stdout)||
|
|
430
430
|
!/\[REPLAY\] #4 coding_file_read OK/.test(secondOutput.stdout)||
|
|
431
|
-
!/\[LOG\]
|
|
431
|
+
!/\[LOG\] Live tool calls/.test(secondOutput.stdout))) await sleep(50);
|
|
432
432
|
assert.match(secondOutput.stdout,/^2026-09-18T23:30:01\.000\+07:00 \| coding_file_read\s+\| Arguments: .*"path":"src\/deadbyte-cli\.mjs".*\| REPLAY #3$/m);
|
|
433
433
|
assert.match(secondOutput.stdout,/\[REPLAY\] #4 coding_file_read OK/);
|
|
434
434
|
assert.match(secondOutput.stdout,/\[SESSION\] Previous result stream replayed/);
|
|
@@ -438,7 +438,7 @@ winTest('remote --resume replays sanitized tool-call and terminal result events
|
|
|
438
438
|
assert.equal((secondOutput.stdout.match(/\[REPLAY\] #4 coding_file_read OK/g)||[]).length,1,'terminal replay duplicated');
|
|
439
439
|
const callReplayIndex=secondOutput.stdout.indexOf('| coding_file_read');
|
|
440
440
|
const resultReplayIndex=secondOutput.stdout.indexOf('[REPLAY] #4 coding_file_read OK');
|
|
441
|
-
const liveIndex=secondOutput.stdout.indexOf('[LOG]
|
|
441
|
+
const liveIndex=secondOutput.stdout.indexOf('[LOG] Live tool calls');
|
|
442
442
|
assert.ok(callReplayIndex>=0&&resultReplayIndex>callReplayIndex&&liveIndex>resultReplayIndex,'resume ordering must be call -> result -> live tail');
|
|
443
443
|
second.stdin.end();
|
|
444
444
|
const secondEnded=await boundedCompletion(secondOutput.closed);
|
|
@@ -811,7 +811,7 @@ winTest('remote --trace --filter selects presentation without dropping journaled
|
|
|
811
811
|
await writeFile(path.join(auditDir,'segment-00000001.jsonl'),audit.map(JSON.stringify).join('\n')+'\n','utf8');
|
|
812
812
|
const deadline=Date.now()+4000;
|
|
813
813
|
while(Date.now()<deadline&&!/machine_fs\.write.*trace=goal-1234/.test(output.stdout)) await sleep(50);
|
|
814
|
-
assert.match(output.stdout,/machine_fs\.write.*trace=goal-123\.\.\. span=filter-w\.\.\..*10ms.*
|
|
814
|
+
assert.match(output.stdout,/Tool call machine_fs\.write failed \[filter-w\].*trace=goal-123\.\.\. span=filter-w\.\.\..*10ms.*denied/i);
|
|
815
815
|
assert.doesNotMatch(output.stdout,/coding_file_read.*missing/);
|
|
816
816
|
assert.doesNotMatch(output.stdout,/SECRET/);
|
|
817
817
|
const journal=JSON.parse(await readFile(path.join(fx.state,'runtime','remote-results',marker.result_session_id+'.json'),'utf8'));
|
|
@@ -54,7 +54,8 @@ test('CMD launcher transports explicit root with spaces to nested Node child',as
|
|
|
54
54
|
await writeFile(path.join(isolated,'bin','DEADBYTE-MCP.ps1'),`& '${process.execPath.replaceAll("'","''")}' -e 'console.log(process.env.DEADBYTE_STATE_ROOT)'\nexit $LASTEXITCODE\n`);
|
|
55
55
|
const cmd=path.join(fixture,'launcher.cmd');
|
|
56
56
|
await writeFile(cmd,renderStableLauncherCmd());
|
|
57
|
-
const
|
|
57
|
+
const nativeCmd=path.join(process.env.SystemRoot,'System32','cmd.exe');
|
|
58
|
+
const r=spawnSync(nativeCmd,['/d','/c',cmd,'status'],{encoding:'utf8',timeout:20000,env:windowsPowerShellEnvironment({...process.env,DEADBYTE_STATE_ROOT:isolated,USERPROFILE:fixture})});
|
|
58
59
|
assert.equal(r.status,0,r.stderr);
|
|
59
60
|
assert.equal(r.stdout.trim(),isolated);
|
|
60
61
|
});
|