deadbyte-mcp 0.11.1 → 0.11.3
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 +36 -20
- package/README.txt +7 -7
- package/controller/README.TXT +1 -1
- package/controller/deadbyte-controller.ps1 +4 -4
- package/docs/ARCHITECTURE.md +1 -1
- package/package.json +6 -6
- package/scripts/deferred-slot-operation-r38.mjs +124 -0
- package/scripts/deferred-slot-operation-r39.mjs +124 -0
- package/scripts/final-closure-r38.mjs +113 -0
- package/scripts/final-closure-r39.mjs +113 -0
- package/scripts/final-closure-verify-r38.mjs +208 -0
- package/scripts/final-closure-verify-r39.mjs +208 -0
- package/scripts/gate-windows-r38.ps1 +42 -0
- package/scripts/gate-windows-r39.ps1 +42 -0
- package/scripts/release-parity-r38.mjs +200 -0
- package/scripts/release-parity-r39.mjs +200 -0
- package/scripts/release-parity-tests-r38.ps1 +9 -0
- package/scripts/release-parity-tests-r39.ps1 +9 -0
- package/scripts/windows-gate-evidence-r38.mjs +52 -0
- package/scripts/windows-gate-evidence-r39.mjs +52 -0
- package/src/deadbyte-cli.mjs +7 -3
- package/src/release-generation.mjs +18 -1
- package/src/remote-live-log.mjs +9 -2
- package/src/version.mjs +1 -1
- package/test/cli-entrypoint.test.mjs +1 -1
- package/test/controller.test.mjs +11 -0
- package/test/core.test.mjs +1 -1
- package/test/production-docs.test.mjs +3 -3
- package/test/r33-closeout-regression.test.mjs +8 -8
- package/test/r33-finalization.test.mjs +14 -14
- package/test/r37-remote-tool-call-timeline.test.mjs +2 -2
- package/test/r38-finalization.test.mjs +74 -0
- package/test/r39-finalization.test.mjs +74 -0
- package/test/release-generation.test.mjs +13 -3
- package/test/release-version.test.mjs +47 -25
- package/test/remote-live-log-r37.test.mjs +1 -1
- package/test/remote-session-cli.test.mjs +46 -12
|
@@ -0,0 +1,200 @@
|
|
|
1
|
+
import { createHash } from 'node:crypto';
|
|
2
|
+
import { mkdir, readFile, realpath, writeFile } from 'node:fs/promises';
|
|
3
|
+
import { loadPowerShellDataFile } from '../src/runtime-update.mjs';
|
|
4
|
+
import os from 'node:os';
|
|
5
|
+
import path from 'node:path';
|
|
6
|
+
import { spawn } from 'node:child_process';
|
|
7
|
+
import { fileURLToPath } from 'node:url';
|
|
8
|
+
import { probeR36FullSurface } from '../src/r36-full-surface-probe.mjs';
|
|
9
|
+
import { runR36CompactParity } from '../src/remote-mcp-r36-parity-client.mjs';
|
|
10
|
+
import { probeR36CompactDisarmed } from '../src/r36-compact-disarmed-probe.mjs';
|
|
11
|
+
|
|
12
|
+
const here=path.dirname(fileURLToPath(import.meta.url));
|
|
13
|
+
const root=path.resolve(here,'..');
|
|
14
|
+
const stateRoot=path.resolve(process.env.DEADBYTE_STATE_ROOT||path.join(os.homedir(),'.deadbyte-mcp'));
|
|
15
|
+
const deferred=path.join(root,'scripts','deferred-slot-operation-r39.mjs');
|
|
16
|
+
const ps='C:\\Windows\\System32\\WindowsPowerShell\\v1.0\\powershell.exe';
|
|
17
|
+
const launcher=path.join(stateRoot,'bin','DEADBYTE-MCP.ps1');
|
|
18
|
+
const sleep=ms=>new Promise(resolve=>setTimeout(resolve,ms));
|
|
19
|
+
const sha256=bytes=>createHash('sha256').update(bytes).digest('hex');
|
|
20
|
+
|
|
21
|
+
export const R39_PREDECESSOR_RELEASE_ID='v0.11.2-058605dfccaa3035';
|
|
22
|
+
export const R39_PREDECESSOR_MANIFEST='058605dfccaa3035a57c6393d53f2421c14e5a92bd145c2334477ba24d7cab8b';
|
|
23
|
+
|
|
24
|
+
function assert(condition,message){if(!condition) throw new Error(message);}
|
|
25
|
+
function parseFrozenManifest(text){
|
|
26
|
+
const entries=[];const seen=new Set();
|
|
27
|
+
for(const [index,line] of text.split(/\r?\n/).entries()){
|
|
28
|
+
if(!line) continue;
|
|
29
|
+
const match=/^([0-9a-f]{64}) ([^\0]+)$/.exec(line);
|
|
30
|
+
assert(match,`frozen manifest line ${index+1} malformed`);
|
|
31
|
+
const rel=match[2];
|
|
32
|
+
assert(!path.posix.isAbsolute(rel)&&!rel.split('/').includes('..')&&!seen.has(rel),`unsafe/duplicate frozen manifest path: ${rel}`);
|
|
33
|
+
seen.add(rel);entries.push({sha256:match[1],path:rel});
|
|
34
|
+
}
|
|
35
|
+
return entries;
|
|
36
|
+
}
|
|
37
|
+
// Restore the mutable development stage from the immutable active release after live parity work may drift it.
|
|
38
|
+
export async function restoreFrozenStage({developmentRoot,releaseRoot,expectedManifestSha256}){
|
|
39
|
+
const dev=path.resolve(developmentRoot);const release=path.resolve(releaseRoot);
|
|
40
|
+
const manifestBytes=await readFile(path.join(release,'MANIFEST.SHA256'));
|
|
41
|
+
const manifestSha256=sha256(manifestBytes);
|
|
42
|
+
assert(manifestSha256===expectedManifestSha256,`immutable release manifest drift: ${manifestSha256}`);
|
|
43
|
+
const entries=parseFrozenManifest(manifestBytes.toString('utf8'));
|
|
44
|
+
for(const entry of entries){
|
|
45
|
+
const source=path.join(release,...entry.path.split('/'));const bytes=await readFile(source);
|
|
46
|
+
assert(sha256(bytes)===entry.sha256,`immutable release file drift: ${entry.path}`);
|
|
47
|
+
const destination=path.join(dev,...entry.path.split('/'));await mkdir(path.dirname(destination),{recursive:true});
|
|
48
|
+
await writeFile(destination,bytes);assert(sha256(await readFile(destination))===entry.sha256,`development restore verification failed: ${entry.path}`);
|
|
49
|
+
}
|
|
50
|
+
await writeFile(path.join(dev,'MANIFEST.SHA256'),manifestBytes);
|
|
51
|
+
assert(sha256(await readFile(path.join(dev,'MANIFEST.SHA256')))===expectedManifestSha256,'development manifest restore verification failed');
|
|
52
|
+
return {status:'restored',file_count:entries.length,manifest_sha256:manifestSha256};
|
|
53
|
+
}
|
|
54
|
+
function parseJsonOutput(text,label){
|
|
55
|
+
const trimmed=String(text).trim();const starts=[...trimmed.matchAll(/\{/g)].map(match=>match.index);
|
|
56
|
+
for(const start of starts){try{return JSON.parse(trimmed.slice(start));}catch{}}
|
|
57
|
+
throw new Error(`${label} did not emit JSON`);
|
|
58
|
+
}
|
|
59
|
+
function run(command,args,{timeoutMs=900000,env=process.env,cwd=root}={}){
|
|
60
|
+
return new Promise((resolve,reject)=>{
|
|
61
|
+
const child=spawn(command,args,{cwd,shell:false,windowsHide:true,env,stdio:['ignore','pipe','pipe']});
|
|
62
|
+
const out=[],err=[];const timer=setTimeout(()=>{child.kill();reject(new Error(`command timeout: ${path.basename(command)}`));},timeoutMs);
|
|
63
|
+
child.stdout.on('data',chunk=>out.push(Buffer.from(chunk)));child.stderr.on('data',chunk=>err.push(Buffer.from(chunk)));
|
|
64
|
+
child.on('error',error=>{clearTimeout(timer);reject(error);});child.on('exit',code=>{
|
|
65
|
+
clearTimeout(timer);const stdout=Buffer.concat(out).toString('utf8');const stderr=Buffer.concat(err).toString('utf8');
|
|
66
|
+
if(code!==0) reject(new Error(`command failed exit=${code}: ${stderr||stdout}`));else resolve({stdout,stderr,exitCode:code});
|
|
67
|
+
});
|
|
68
|
+
});
|
|
69
|
+
}
|
|
70
|
+
async function publicUrl(){
|
|
71
|
+
const value=(await readFile(path.join(stateRoot,'bridge','mcp-url.txt'),'utf8')).trim();const url=new URL(value);
|
|
72
|
+
if(url.protocol!=='https:') throw new Error('public MCP URL must be HTTPS');return value;
|
|
73
|
+
}
|
|
74
|
+
async function pointer(){return JSON.parse(await readFile(path.join(stateRoot,'active-release.json'),'utf8'));}
|
|
75
|
+
async function disarmAll(){
|
|
76
|
+
for(const command of ['disarmautonomous','disarmcoding','disarmprocess','disarmhost']){
|
|
77
|
+
await run(ps,['-NoLogo','-NoProfile','-ExecutionPolicy','Bypass','-File',launcher,command],{timeoutMs:60000});
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
async function waitDeferred(statePath,timeoutMs=180000){
|
|
81
|
+
const deadline=Date.now()+timeoutMs;
|
|
82
|
+
while(Date.now()<deadline){const state=JSON.parse(await readFile(statePath,'utf8'));if(state.state==='completed') return state;if(state.state==='failed') throw new Error(`deferred ${state.mode} failed: ${state.error}`);await sleep(500);}
|
|
83
|
+
throw new Error(`deferred operation timeout: ${statePath}`);
|
|
84
|
+
}
|
|
85
|
+
async function scheduleDeferred(mode,value,{bridgeRoot=null,bridgeScriptSha256=null}={}){
|
|
86
|
+
if((bridgeRoot===null)!==(bridgeScriptSha256===null)) throw new Error('deferred bridge override requires exact root + SHA pair');
|
|
87
|
+
const args=[deferred,mode,value];
|
|
88
|
+
if(bridgeRoot!==null) args.push('--bridge-root',bridgeRoot,'--bridge-script-sha256',bridgeScriptSha256);
|
|
89
|
+
const scheduled=parseJsonOutput((await run(process.execPath,args,{timeoutMs:30000})).stdout,`deferred ${mode}`);
|
|
90
|
+
assert(scheduled.status==='scheduled',`deferred ${mode} was not scheduled`);
|
|
91
|
+
assert(scheduled.execution_provider==='deadbyte_mcp'&&scheduled.rdc_operation_count===0,'deferred operation provider mismatch');
|
|
92
|
+
return {scheduled,completed:await waitDeferred(scheduled.state_path)};
|
|
93
|
+
}
|
|
94
|
+
async function publicR39Disarmed(url){
|
|
95
|
+
const result=await probeR36CompactDisarmed(url);
|
|
96
|
+
assert(result.status==='passed'&&result.tool_count<=42&&result.capability_closure===true,'R39 predecessor public compact catalog mismatch');
|
|
97
|
+
assert(result.release_id===R39_PREDECESSOR_RELEASE_ID&&result.release_manifest_sha256===R39_PREDECESSOR_MANIFEST,'R39 predecessor machine binding mismatch');
|
|
98
|
+
assert(result.coding_armed===false&&result.autonomous_armed===false,'R39 predecessor authority remained armed');
|
|
99
|
+
return result;
|
|
100
|
+
}
|
|
101
|
+
async function mutableCore(pointerValue){
|
|
102
|
+
const policy=JSON.parse(await readFile(path.join(pointerValue.generation_root,'coding-policy.json'),'utf8'));
|
|
103
|
+
const raw=policy?.roots?.core?.path??policy?.roots?.core?.real_path;
|
|
104
|
+
assert(typeof raw==='string'&&raw,'R39 generation missing coding core root');
|
|
105
|
+
const core=path.resolve(raw);const release=path.resolve(pointerValue.release_root);
|
|
106
|
+
assert(core.toLowerCase()!==release.toLowerCase(),'R39 coding core must not point at immutable active release slot');
|
|
107
|
+
return core;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
export function validateParityEvidence(value){
|
|
111
|
+
assert(value?.schema==='deadbyte.r39-parity.v1','R39 parity schema mismatch');
|
|
112
|
+
assert(value.status==='passed','R39 parity status is not passed');
|
|
113
|
+
assert(value.rdc_operation_count===0&&value.execution_provider==='deadbyte_mcp','R39 parity used non-DEADBYTE execution');
|
|
114
|
+
assert(value.candidate?.version==='0.11.3'&&/^[0-9a-f]{64}$/.test(value.candidate?.manifest_sha256??''),'R39 candidate identity missing');
|
|
115
|
+
assert(value.local_full?.status==='passed'&&value.local_full?.tool_count===76&&value.local_full?.strict_output_schemas===true,'R39 local full 76-tool proof missing');
|
|
116
|
+
assert(value.public_compact?.status==='passed'&&value.public_compact?.tool_count<=42&&value.public_compact?.capability_closure===true&&value.public_compact?.gateway_count===5,'R39 public compact closure missing');
|
|
117
|
+
assert(value.autonomous?.status==='passed'&&value.autonomous?.signed_release_verified===true,'R39 autonomous live proof missing');
|
|
118
|
+
assert(value.before_rollback_disarmed?.status==='passed'&&value.before_rollback_disarmed?.capability_closure===true&&value.before_rollback_disarmed?.coding_armed===false&&value.before_rollback_disarmed?.autonomous_armed===false,'R39 pre-rollback disarm proof missing');
|
|
119
|
+
assert(value.rollback?.state==='completed'&&value.restore?.state==='completed','R39 rollback/restore not completed');
|
|
120
|
+
assert(value.stage_restore?.status==='restored'&&value.stage_restore?.manifest_sha256===value.candidate.manifest_sha256,'R39 stage restore proof missing');
|
|
121
|
+
assert(value.predecessor?.release_id===R39_PREDECESSOR_RELEASE_ID&&value.predecessor?.manifest_sha256===R39_PREDECESSOR_MANIFEST&&value.predecessor?.version==='0.11.2','R39 exact predecessor identity mismatch');
|
|
122
|
+
assert(value.predecessor_disarmed?.status==='passed'&&value.predecessor_disarmed?.tool_count<=42&&value.predecessor_disarmed?.capability_closure===true,'R39 predecessor public proof missing');
|
|
123
|
+
assert(value.successor?.version==='0.11.3'&&value.successor?.manifest_sha256===value.candidate.manifest_sha256,'R39 exact successor restore identity mismatch');
|
|
124
|
+
assert(value.successor_disarmed?.status==='passed'&&value.successor_disarmed?.capability_closure===true&&value.successor_disarmed?.coding_armed===false&&value.successor_disarmed?.autonomous_armed===false,'R39 successor disarmed proof missing');
|
|
125
|
+
assert(value.immutable_slot?.protected===true&&value.immutable_slot?.development_root!==value.immutable_slot?.release_root,'R39 immutable-slot separation missing');
|
|
126
|
+
for(const key of ['tree_read','precondition_mutation','search','owned_process','observation','coding_loop','autonomy','interrupted_recovery','evidence_verification','compact_closure','full_surface']) assert(value.requirements?.[key]===true,`R39 parity requirement not proven: ${key}`);
|
|
127
|
+
return true;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
export async function runReleaseParity(activationReceiptPath){
|
|
131
|
+
const active=await pointer();const candidateManifest=sha256(await readFile(path.join(root,'MANIFEST.SHA256')));
|
|
132
|
+
assert(active.version==='0.11.3','R39 parity requires active 0.11.3 candidate');
|
|
133
|
+
assert(active.manifest_sha256===candidateManifest,'active release is not canonical R39 candidate');
|
|
134
|
+
const activeConfig=await loadPowerShellDataFile(active.config_path);
|
|
135
|
+
const successorBridgeRootRaw=activeConfig?.Bridge?.WorkingDirectory;
|
|
136
|
+
const successorBridgeConfiguredSha=activeConfig?.Bridge?.ScriptSha256;
|
|
137
|
+
assert(typeof successorBridgeRootRaw==='string'&&path.isAbsolute(successorBridgeRootRaw),'R39 active bridge root missing');
|
|
138
|
+
assert(typeof successorBridgeConfiguredSha==='string'&&/^[0-9a-f]{64}$/.test(successorBridgeConfiguredSha),'R39 active bridge SHA missing');
|
|
139
|
+
const successorBridgeRoot=await realpath(path.resolve(successorBridgeRootRaw));
|
|
140
|
+
const successorBridgeActualSha=sha256(await readFile(path.join(successorBridgeRoot,'src','bridge.mjs')));
|
|
141
|
+
assert(successorBridgeActualSha===successorBridgeConfiguredSha,'R39 active bridge bytes do not match generation pin');
|
|
142
|
+
const successorBridge={root:successorBridgeRoot,sha256:successorBridgeActualSha};
|
|
143
|
+
const developmentRoot=await mutableCore(active);
|
|
144
|
+
const localFull=await probeR36FullSurface({packageRoot:active.release_root,generationRoot:active.generation_root,stateRoot});
|
|
145
|
+
const url=await publicUrl();
|
|
146
|
+
let publicCompact,autonomous;
|
|
147
|
+
try{
|
|
148
|
+
publicCompact=await runR36CompactParity(url);
|
|
149
|
+
autonomous=parseJsonOutput((await run(process.execPath,[path.join(root,'src','remote-mcp-autonomous-smoke-client.mjs'),url],{timeoutMs:900000})).stdout,'R39 autonomous compact parity');
|
|
150
|
+
assert(autonomous.status==='passed'&&autonomous.signed_release_verified===true,'R39 autonomous compact parity failed');
|
|
151
|
+
}catch(error){await disarmAll().catch(()=>{});throw error;}
|
|
152
|
+
|
|
153
|
+
await disarmAll();
|
|
154
|
+
const beforeRollback=await probeR36CompactDisarmed(url);
|
|
155
|
+
const rollbackPair=await scheduleDeferred('rollback',path.resolve(activationReceiptPath));
|
|
156
|
+
const predecessor=await pointer();
|
|
157
|
+
assert(predecessor.release_id===R39_PREDECESSOR_RELEASE_ID&&predecessor.manifest_sha256===R39_PREDECESSOR_MANIFEST&&predecessor.version==='0.11.2','rollback did not reach exact sealed R39 predecessor');
|
|
158
|
+
const predecessorDisarmed=await publicR39Disarmed(url);
|
|
159
|
+
const stageRestore=await restoreFrozenStage({developmentRoot,releaseRoot:active.release_root,expectedManifestSha256:candidateManifest});
|
|
160
|
+
const restorePair=await scheduleDeferred('activate',candidateManifest,{bridgeRoot:successorBridge.root,bridgeScriptSha256:successorBridge.sha256});
|
|
161
|
+
const successor=await pointer();
|
|
162
|
+
assert(successor.version==='0.11.3'&&successor.manifest_sha256===candidateManifest,'restore did not return exact R39 candidate');
|
|
163
|
+
const successorDisarmed=await probeR36CompactDisarmed(url);
|
|
164
|
+
await disarmAll();
|
|
165
|
+
|
|
166
|
+
const evidence={
|
|
167
|
+
schema:'deadbyte.r39-parity.v1',status:'passed',completed_at_utc:new Date().toISOString(),execution_provider:'deadbyte_mcp',rdc_operation_count:0,
|
|
168
|
+
candidate:{release_id:active.release_id,version:active.version,manifest_sha256:candidateManifest},
|
|
169
|
+
immutable_slot:{protected:true,release_root:path.resolve(active.release_root),development_root:developmentRoot},
|
|
170
|
+
local_full:localFull,public_compact:publicCompact,autonomous,before_rollback_disarmed:beforeRollback,
|
|
171
|
+
rollback:{...rollbackPair.completed,scheduled_operation_id:rollbackPair.scheduled.operation_id},
|
|
172
|
+
predecessor:{release_id:predecessor.release_id,version:predecessor.version,manifest_sha256:predecessor.manifest_sha256},predecessor_disarmed:predecessorDisarmed,
|
|
173
|
+
stage_restore:stageRestore,
|
|
174
|
+
restore:{...restorePair.completed,scheduled_operation_id:restorePair.scheduled.operation_id},
|
|
175
|
+
successor:{release_id:successor.release_id,version:successor.version,manifest_sha256:successor.manifest_sha256},successor_disarmed:successorDisarmed,
|
|
176
|
+
requirements:{
|
|
177
|
+
tree_read:publicCompact.tree_entries>=0&&publicCompact.coding_stat_verified===true,
|
|
178
|
+
precondition_mutation:publicCompact.machine_fs_receipts_verified>=3,
|
|
179
|
+
search:publicCompact.coding_search_result_count>=1&&publicCompact.observation_search_result_count>=1,
|
|
180
|
+
owned_process:typeof publicCompact.process_session_id==='string'&&publicCompact.process_output_bytes>0,
|
|
181
|
+
observation:publicCompact.observation_receipts_verified>=2,
|
|
182
|
+
coding_loop:publicCompact.coding_receipts_verified===2,
|
|
183
|
+
autonomy:autonomous.signed_release_verified===true&&autonomous.observed_failure_before_replan===true,
|
|
184
|
+
interrupted_recovery:rollbackPair.completed.state==='completed'&&restorePair.completed.state==='completed',
|
|
185
|
+
evidence_verification:publicCompact.machine_fs_receipts_verified>=3&&autonomous.signed_release_verified===true,
|
|
186
|
+
compact_closure:publicCompact.capability_closure===true&&successorDisarmed.capability_closure===true,
|
|
187
|
+
full_surface:localFull.tool_count===76&&localFull.strict_output_schemas===true
|
|
188
|
+
}
|
|
189
|
+
};
|
|
190
|
+
validateParityEvidence(evidence);
|
|
191
|
+
const evidenceDir=path.join(stateRoot,'evidence','release');await mkdir(evidenceDir,{recursive:true});
|
|
192
|
+
const evidencePath=path.join(evidenceDir,`r39-parity-${candidateManifest.slice(0,16)}.json`);
|
|
193
|
+
const bytes=Buffer.from(`${JSON.stringify(evidence,null,2)}\n`,'utf8');await writeFile(evidencePath,bytes);
|
|
194
|
+
return {evidence,evidence_path:evidencePath,evidence_sha256:sha256(bytes)};
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
if(process.argv[1]&&path.resolve(process.argv[1])===fileURLToPath(import.meta.url)){
|
|
198
|
+
const receipt=process.argv[2];if(!receipt) throw new Error('usage: node scripts/release-parity-r39.mjs <activation-receipt.json>');
|
|
199
|
+
runReleaseParity(receipt).then(result=>console.log(JSON.stringify({status:'passed',...result},null,2))).catch(error=>{console.error(error instanceof Error?error.stack:String(error));process.exitCode=1;});
|
|
200
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
$ErrorActionPreference = 'Stop'
|
|
2
|
+
Set-StrictMode -Version 2.0
|
|
3
|
+
$root = Split-Path -Parent $PSScriptRoot
|
|
4
|
+
Push-Location $root
|
|
5
|
+
try {
|
|
6
|
+
& node.exe --test test\controller.test.mjs test\r38-finalization.test.mjs test\release-version.test.mjs test\r37-0110-history.test.mjs test\r37-agent-fabric.test.mjs test\remote-live-log-r37.test.mjs test\remote-session-cli.test.mjs test\r36-finalization.test.mjs test\r36-release-identity.test.mjs test\deferred-slot-operation.test.mjs
|
|
7
|
+
if ($LASTEXITCODE -ne 0) { throw "R38 release parity tests failed with exit code $LASTEXITCODE" }
|
|
8
|
+
Write-Output 'R38 RELEASE PARITY TEST PASS'
|
|
9
|
+
} finally { Pop-Location }
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
$ErrorActionPreference = 'Stop'
|
|
2
|
+
Set-StrictMode -Version 2.0
|
|
3
|
+
$root = Split-Path -Parent $PSScriptRoot
|
|
4
|
+
Push-Location $root
|
|
5
|
+
try {
|
|
6
|
+
& node.exe --test test\controller.test.mjs test\r39-finalization.test.mjs test\release-version.test.mjs test\r37-0110-history.test.mjs test\r37-agent-fabric.test.mjs test\remote-live-log-r37.test.mjs test\remote-session-cli.test.mjs test\r36-finalization.test.mjs test\r36-release-identity.test.mjs test\deferred-slot-operation.test.mjs
|
|
7
|
+
if ($LASTEXITCODE -ne 0) { throw "R39 release parity tests failed with exit code $LASTEXITCODE" }
|
|
8
|
+
Write-Output 'R39 RELEASE PARITY TEST PASS'
|
|
9
|
+
} finally { Pop-Location }
|
|
@@ -0,0 +1,52 @@
|
|
|
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
|
+
|
|
7
|
+
const here=path.dirname(fileURLToPath(import.meta.url));
|
|
8
|
+
const root=path.resolve(here,'..');
|
|
9
|
+
const sha256=bytes=>createHash('sha256').update(bytes).digest('hex');
|
|
10
|
+
const marker='DEADBYTE V0.11.2 / R38 WINDOWS BATCH GATE: PASS';
|
|
11
|
+
function insist(ok,message){if(!ok) throw new Error(message);}
|
|
12
|
+
|
|
13
|
+
export async function writeWindowsGateEvidence({
|
|
14
|
+
candidateRoot=root,
|
|
15
|
+
stateRoot=path.resolve(process.env.DEADBYTE_STATE_ROOT||path.join(os.homedir(),'.deadbyte-mcp')),
|
|
16
|
+
clock=()=>new Date()
|
|
17
|
+
}={}){
|
|
18
|
+
const candidate=path.resolve(candidateRoot);
|
|
19
|
+
const manifestBytes=await readFile(path.join(candidate,'MANIFEST.SHA256'));
|
|
20
|
+
const manifestSha256=sha256(manifestBytes);
|
|
21
|
+
const pkg=JSON.parse(await readFile(path.join(candidate,'package.json'),'utf8'));
|
|
22
|
+
insist(pkg.version==='0.11.2',`Windows gate evidence requires V0.11.2, found ${pkg.version}`);
|
|
23
|
+
const completed=clock();
|
|
24
|
+
insist(completed instanceof Date&&!Number.isNaN(completed.getTime()),'Windows gate evidence clock invalid');
|
|
25
|
+
const evidence={
|
|
26
|
+
schema:'deadbyte.r38-windows-gate.v1',status:'passed',exit_code:0,version:'0.11.2',
|
|
27
|
+
marker,completed_at_utc:completed.toISOString(),manifest_sha256:manifestSha256
|
|
28
|
+
};
|
|
29
|
+
const bytes=Buffer.from(`${JSON.stringify(evidence,null,2)}\n`,'utf8');
|
|
30
|
+
const dir=path.join(path.resolve(stateRoot),'evidence','release');
|
|
31
|
+
await mkdir(dir,{recursive:true});
|
|
32
|
+
const file=path.join(dir,`r38-windows-gate-${manifestSha256.slice(0,16)}.json`);
|
|
33
|
+
try{
|
|
34
|
+
const handle=await open(file,'wx',0o600);
|
|
35
|
+
try{await handle.writeFile(bytes);await handle.sync();}finally{await handle.close();}
|
|
36
|
+
return {status:'written',path:file,sha256:sha256(bytes),manifest_sha256:manifestSha256};
|
|
37
|
+
}catch(error){
|
|
38
|
+
if(error?.code!=='EEXIST') throw error;
|
|
39
|
+
const existingBytes=await readFile(file);
|
|
40
|
+
const existing=JSON.parse(existingBytes.toString('utf8'));
|
|
41
|
+
insist(existing.schema===evidence.schema&&existing.status==='passed'&&existing.exit_code===0,'existing Windows gate evidence invalid');
|
|
42
|
+
insist(existing.version==='0.11.2'&&existing.marker===marker&&existing.manifest_sha256===manifestSha256,'existing Windows gate evidence binding mismatch');
|
|
43
|
+
return {status:'existing',path:file,sha256:sha256(existingBytes),manifest_sha256:manifestSha256};
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
const invoked=process.argv[1]&&path.resolve(process.argv[1])===fileURLToPath(import.meta.url);
|
|
48
|
+
if(invoked){
|
|
49
|
+
writeWindowsGateEvidence()
|
|
50
|
+
.then(result=>console.log(JSON.stringify(result)))
|
|
51
|
+
.catch(error=>{console.error(error instanceof Error?error.stack:String(error));process.exitCode=1;});
|
|
52
|
+
}
|
|
@@ -0,0 +1,52 @@
|
|
|
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
|
+
|
|
7
|
+
const here=path.dirname(fileURLToPath(import.meta.url));
|
|
8
|
+
const root=path.resolve(here,'..');
|
|
9
|
+
const sha256=bytes=>createHash('sha256').update(bytes).digest('hex');
|
|
10
|
+
const marker='DEADBYTE V0.11.3 / R39 WINDOWS BATCH GATE: PASS';
|
|
11
|
+
function insist(ok,message){if(!ok) throw new Error(message);}
|
|
12
|
+
|
|
13
|
+
export async function writeWindowsGateEvidence({
|
|
14
|
+
candidateRoot=root,
|
|
15
|
+
stateRoot=path.resolve(process.env.DEADBYTE_STATE_ROOT||path.join(os.homedir(),'.deadbyte-mcp')),
|
|
16
|
+
clock=()=>new Date()
|
|
17
|
+
}={}){
|
|
18
|
+
const candidate=path.resolve(candidateRoot);
|
|
19
|
+
const manifestBytes=await readFile(path.join(candidate,'MANIFEST.SHA256'));
|
|
20
|
+
const manifestSha256=sha256(manifestBytes);
|
|
21
|
+
const pkg=JSON.parse(await readFile(path.join(candidate,'package.json'),'utf8'));
|
|
22
|
+
insist(pkg.version==='0.11.3',`Windows gate evidence requires V0.11.3, found ${pkg.version}`);
|
|
23
|
+
const completed=clock();
|
|
24
|
+
insist(completed instanceof Date&&!Number.isNaN(completed.getTime()),'Windows gate evidence clock invalid');
|
|
25
|
+
const evidence={
|
|
26
|
+
schema:'deadbyte.r39-windows-gate.v1',status:'passed',exit_code:0,version:'0.11.3',
|
|
27
|
+
marker,completed_at_utc:completed.toISOString(),manifest_sha256:manifestSha256
|
|
28
|
+
};
|
|
29
|
+
const bytes=Buffer.from(`${JSON.stringify(evidence,null,2)}\n`,'utf8');
|
|
30
|
+
const dir=path.join(path.resolve(stateRoot),'evidence','release');
|
|
31
|
+
await mkdir(dir,{recursive:true});
|
|
32
|
+
const file=path.join(dir,`r39-windows-gate-${manifestSha256.slice(0,16)}.json`);
|
|
33
|
+
try{
|
|
34
|
+
const handle=await open(file,'wx',0o600);
|
|
35
|
+
try{await handle.writeFile(bytes);await handle.sync();}finally{await handle.close();}
|
|
36
|
+
return {status:'written',path:file,sha256:sha256(bytes),manifest_sha256:manifestSha256};
|
|
37
|
+
}catch(error){
|
|
38
|
+
if(error?.code!=='EEXIST') throw error;
|
|
39
|
+
const existingBytes=await readFile(file);
|
|
40
|
+
const existing=JSON.parse(existingBytes.toString('utf8'));
|
|
41
|
+
insist(existing.schema===evidence.schema&&existing.status==='passed'&&existing.exit_code===0,'existing Windows gate evidence invalid');
|
|
42
|
+
insist(existing.version==='0.11.3'&&existing.marker===marker&&existing.manifest_sha256===manifestSha256,'existing Windows gate evidence binding mismatch');
|
|
43
|
+
return {status:'existing',path:file,sha256:sha256(existingBytes),manifest_sha256:manifestSha256};
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
const invoked=process.argv[1]&&path.resolve(process.argv[1])===fileURLToPath(import.meta.url);
|
|
48
|
+
if(invoked){
|
|
49
|
+
writeWindowsGateEvidence()
|
|
50
|
+
.then(result=>console.log(JSON.stringify(result)))
|
|
51
|
+
.catch(error=>{console.error(error instanceof Error?error.stack:String(error));process.exitCode=1;});
|
|
52
|
+
}
|
package/src/deadbyte-cli.mjs
CHANGED
|
@@ -55,9 +55,11 @@ async function remoteContext(){
|
|
|
55
55
|
const powershell=await requireRegular(path.join(systemRoot,'System32','WindowsPowerShell','v1.0','powershell.exe'),'Windows PowerShell');
|
|
56
56
|
return {root,launcher,powershell};
|
|
57
57
|
}
|
|
58
|
-
function invokeController(ctx,command,{capture=false,allowFailure=false,echoCaptured=true,timeoutMs=null}={}){
|
|
58
|
+
function invokeController(ctx,command,{capture=false,allowFailure=false,echoCaptured=true,timeoutMs=null,discardOutput=false}={}){
|
|
59
|
+
if(capture&&discardOutput) throw new Error('invokeController cannot capture and discard output together');
|
|
59
60
|
const options={
|
|
60
|
-
stdio:
|
|
61
|
+
stdio:discardOutput?['ignore','ignore','ignore']:(capture?['ignore','pipe','pipe']:'inherit'),
|
|
62
|
+
encoding:capture?'utf8':undefined,windowsHide:false,shell:false
|
|
61
63
|
};
|
|
62
64
|
if(Number.isInteger(timeoutMs)&&timeoutMs>0) options.timeout=timeoutMs;
|
|
63
65
|
const result=spawnSync(ctx.powershell,['-NoLogo','-NoProfile','-ExecutionPolicy','Bypass','-File',ctx.launcher,command],options);
|
|
@@ -291,7 +293,9 @@ async function runRemoteSession({resumeCursor=null}={}){
|
|
|
291
293
|
process.stdout.write('[BOOT] Runtime already ONLINE; reusing verified bridge/cloud/supervisor.\n');
|
|
292
294
|
}else{
|
|
293
295
|
process.stdout.write('[BOOT] Starting bridge/runtime...\n');
|
|
294
|
-
|
|
296
|
+
invokeController(ctx,'start',{discardOutput:true,timeoutMs:REMOTE_START_TIMEOUT_MS});
|
|
297
|
+
const started=invokeController(ctx,'status',{capture:true,echoCaptured:false,timeoutMs:REMOTE_STATUS_TIMEOUT_MS});
|
|
298
|
+
if(started.status!==0||!controllerRuntimeReady(started.stdout)) throw new Error('remote controller start returned without a verified ONLINE runtime');
|
|
295
299
|
startText=started.stdout||'';
|
|
296
300
|
}
|
|
297
301
|
process.stdout.write(`${formatRemoteLifecycle('bridge_connected')}\n`);
|
|
@@ -60,16 +60,33 @@ function rebindCodingPolicy(raw,{developmentRoot,stateRoot,parityScratchRoot}){
|
|
|
60
60
|
policy.roots.parity_scratch={path:parityScratchRoot,read:true,write:true,exec:false,deny_paths:[]};
|
|
61
61
|
return policy;
|
|
62
62
|
}
|
|
63
|
+
async function currentWindowsGateScript(rootPath){
|
|
64
|
+
const packagePath=path.join(rootPath,'package.json');
|
|
65
|
+
const stat=await lstat(packagePath);
|
|
66
|
+
if(!stat.isFile()||stat.isSymbolicLink()) throw new Error('windows release gate package.json invalid');
|
|
67
|
+
let pkg;
|
|
68
|
+
try{ pkg=JSON.parse(await readFile(packagePath,'utf8')); }
|
|
69
|
+
catch{ throw new Error('windows release gate package.json malformed'); }
|
|
70
|
+
const command=String(pkg?.scripts?.['gate:windows']??'').trim();
|
|
71
|
+
const match=/^powershell(?:\.exe)?\s+-NoProfile\s+-ExecutionPolicy\s+Bypass\s+-File\s+(scripts\/[A-Za-z0-9._\/-]+\.ps1)$/i.exec(command);
|
|
72
|
+
if(!match) throw new Error('windows release gate package script invalid');
|
|
73
|
+
return safeRel(match[1]);
|
|
74
|
+
}
|
|
63
75
|
async function rebindAutonomousPolicy(raw,{stateRoot,codingPolicy,codingPolicyBytes}){
|
|
64
76
|
const policy=clone(raw);
|
|
65
77
|
policy.lease_file=path.join(stateRoot,'runtime','autonomous-grant.json');
|
|
66
78
|
policy.evidence_dir=path.join(stateRoot,'evidence','autonomous');
|
|
67
79
|
policy.coding_policy_sha256=sha256(codingPolicyBytes);
|
|
68
80
|
for(const [id,profile] of Object.entries(policy.profiles??{})){
|
|
69
|
-
const rel=safeRel(String(profile.script_path).replaceAll('\\','/'));
|
|
70
81
|
const rootId=String(profile.root_id??'');
|
|
71
82
|
const rootPath=codingPolicy.roots?.[rootId]?.path;
|
|
72
83
|
if(typeof rootPath!=='string'||!path.isAbsolute(rootPath)) throw new Error(`profile root invalid: ${id}`);
|
|
84
|
+
if(id==='windows-release'){
|
|
85
|
+
if(profile.kind!=='command'||rootId!=='core'||profile.command_id!=='powershell_script') throw new Error('windows release profile shape invalid');
|
|
86
|
+
profile.script_path=await currentWindowsGateScript(rootPath);
|
|
87
|
+
profile.description='Full Windows target release gate; runs only when finish is requested.';
|
|
88
|
+
}
|
|
89
|
+
const rel=safeRel(String(profile.script_path).replaceAll('\\','/'));
|
|
73
90
|
const file=path.resolve(rootPath,...rel.split('/'));
|
|
74
91
|
const boundary=path.relative(rootPath,file);
|
|
75
92
|
if(boundary.startsWith(`..${path.sep}`)||path.isAbsolute(boundary)) throw new Error(`profile path escapes coding root: ${id}`);
|
package/src/remote-live-log.mjs
CHANGED
|
@@ -34,14 +34,21 @@ function summarizeArgs(args){
|
|
|
34
34
|
}
|
|
35
35
|
|
|
36
36
|
const TOOL_COLUMN_WIDTH=32;
|
|
37
|
+
const DISPLAY_UTC_OFFSET_MINUTES=7*60;
|
|
38
|
+
const DISPLAY_UTC_OFFSET_LABEL='+07:00';
|
|
39
|
+
|
|
40
|
+
function displayTimestamp(ms){
|
|
41
|
+
const shifted=new Date(ms+DISPLAY_UTC_OFFSET_MINUTES*60*1000).toISOString();
|
|
42
|
+
return `${shifted.slice(0,-1)}${DISPLAY_UTC_OFFSET_LABEL}`;
|
|
43
|
+
}
|
|
37
44
|
|
|
38
45
|
function eventTimestamp(event,clock){
|
|
39
46
|
for(const value of [event?.created_at_utc,event?.observed_at_utc]){
|
|
40
47
|
const parsed=Date.parse(value);
|
|
41
|
-
if(typeof value==='string'&&Number.isFinite(parsed)) return
|
|
48
|
+
if(typeof value==='string'&&Number.isFinite(parsed)) return displayTimestamp(parsed);
|
|
42
49
|
}
|
|
43
50
|
const value=Number(clock());
|
|
44
|
-
return
|
|
51
|
+
return displayTimestamp(Number.isFinite(value)?value:Date.now());
|
|
45
52
|
}
|
|
46
53
|
|
|
47
54
|
export function formatRemoteToolCall(event,{clock=()=>Date.now()}={}){
|
package/src/version.mjs
CHANGED
|
@@ -21,7 +21,7 @@ test('npx CLI reports the canonical package version through --version, -v and ve
|
|
|
21
21
|
for(const arg of ['--version','-v','version']){
|
|
22
22
|
const result=spawnSync(process.execPath,[cliPath,arg],{cwd:root,encoding:'utf8',windowsHide:true});
|
|
23
23
|
assert.equal(result.status,0,result.stderr);
|
|
24
|
-
assert.equal(result.stdout.trim(),'0.11.
|
|
24
|
+
assert.equal(result.stdout.trim(),'0.11.3');
|
|
25
25
|
}
|
|
26
26
|
});
|
|
27
27
|
|
package/test/controller.test.mjs
CHANGED
|
@@ -215,3 +215,14 @@ test('R34 supervisor lifecycle never arms privileged planes', async () => {
|
|
|
215
215
|
assert.match(block,/Disarm-Process/);
|
|
216
216
|
assert.match(block,/Disarm-Host/);
|
|
217
217
|
});
|
|
218
|
+
|
|
219
|
+
test('supervisor recovery never assigns PowerShell automatic variable $Host', async () => {
|
|
220
|
+
const source=await readFile(controller,'utf8');
|
|
221
|
+
const start=source.indexOf('function Recover-AutonomousSupervisor');
|
|
222
|
+
const end=source.indexOf('function Show-State',start);
|
|
223
|
+
assert.ok(start>=0&&end>start,'supervisor recovery block missing');
|
|
224
|
+
const block=source.slice(start,end);
|
|
225
|
+
assert.doesNotMatch(block,/\$host\s*=/i,'PowerShell $Host is read-only and names are case-insensitive');
|
|
226
|
+
assert.match(block,/\$hostStatus\s*=\s*Get-HostArmStatus\s+\$Cfg/);
|
|
227
|
+
assert.match(block,/Host=\$hostStatus/);
|
|
228
|
+
});
|
package/test/core.test.mjs
CHANGED
|
@@ -105,7 +105,7 @@ test('proof execution creates deterministic output and signed verifiable receipt
|
|
|
105
105
|
assert.equal(result.receipt.request.schema, 'deadbyte.request.v1');
|
|
106
106
|
assert.match(result.receipt.request.nonce, /^[0-9a-f]{64}$/);
|
|
107
107
|
assert.match(result.receipt.request.sha256, /^[0-9a-f]{64}$/);
|
|
108
|
-
assert.equal(result.receipt.runtime.package_version, '0.11.
|
|
108
|
+
assert.equal(result.receipt.runtime.package_version, '0.11.3');
|
|
109
109
|
assert.match(result.receipt.runtime.mcp_ingress_adapter, /^(?:none|tunnel-stdio-probe-compat-v1)$/);
|
|
110
110
|
assert.match(result.receipt.runtime.mcp_ingress_adapter_sha256, /^[0-9a-f]{64}$/);
|
|
111
111
|
assert.match(result.receipt.runtime.mcp_ingress_mode_source_sha256, /^[0-9a-f]{64}$/);
|
|
@@ -9,9 +9,9 @@ const readme=()=>readFile(path.join(root,'README.txt'),'utf8');
|
|
|
9
9
|
|
|
10
10
|
test('production README documents one-command stdio configs for major MCP clients',async()=>{
|
|
11
11
|
const text=await readme();
|
|
12
|
-
assert.match(text,/npx -y deadbyte-mcp@0\.11\.
|
|
12
|
+
assert.match(text,/npx -y deadbyte-mcp@0\.11\.3 mcp/);
|
|
13
13
|
assert.match(text,/CLAUDE DESKTOP/);
|
|
14
|
-
assert.match(text,/"mcpServers"[\s\S]*?"command"\s*:\s*"npx"[\s\S]*?"-y"[\s\S]*?"deadbyte-mcp@0\.11\.
|
|
14
|
+
assert.match(text,/"mcpServers"[\s\S]*?"command"\s*:\s*"npx"[\s\S]*?"-y"[\s\S]*?"deadbyte-mcp@0\.11\.3"[\s\S]*?"mcp"/);
|
|
15
15
|
assert.match(text,/CURSOR/);
|
|
16
16
|
assert.match(text,/\.cursor\/mcp\.json/);
|
|
17
17
|
assert.match(text,/VS CODE/);
|
|
@@ -20,7 +20,7 @@ test('production README documents one-command stdio configs for major MCP client
|
|
|
20
20
|
assert.match(text,/CODEX/);
|
|
21
21
|
assert.match(text,/\[mcp_servers\.deadbyte\]/);
|
|
22
22
|
assert.match(text,/command\s*=\s*"npx"/);
|
|
23
|
-
assert.match(text,/args\s*=\s*\["-y",\s*"deadbyte-mcp@0\.11\.
|
|
23
|
+
assert.match(text,/args\s*=\s*\["-y",\s*"deadbyte-mcp@0\.11\.3",\s*"mcp"\]/);
|
|
24
24
|
});
|
|
25
25
|
|
|
26
26
|
test('production README records tracer bullet, semantic scope, remote UX, safety and document extension',async()=>{
|
|
@@ -5,7 +5,7 @@ import { mkdtemp, mkdir, readFile, rm, writeFile, readdir } from 'node:fs/promis
|
|
|
5
5
|
import os from 'node:os';
|
|
6
6
|
import path from 'node:path';
|
|
7
7
|
import { spawn } from 'node:child_process';
|
|
8
|
-
import * as parity from '../scripts/release-parity.mjs';
|
|
8
|
+
import * as parity from '../scripts/release-parity-r39.mjs';
|
|
9
9
|
import { createToolAudit } from '../src/tool-audit.mjs';
|
|
10
10
|
import { verifyAuditChain } from '../src/audit-verify.mjs';
|
|
11
11
|
import { generateTrustKeypair } from '../src/trust.mjs';
|
|
@@ -86,12 +86,12 @@ test('audit restart reconciles an orphan request with an explicit interrupted re
|
|
|
86
86
|
}finally{await rm(stateRoot,{recursive:true,force:true});}
|
|
87
87
|
});
|
|
88
88
|
|
|
89
|
-
test('Windows
|
|
89
|
+
test('Windows R39 gate emits manifest-bound machine-readable evidence outside the candidate tree', async()=>{
|
|
90
90
|
const stateRoot=await temp('deadbyte-r33-windows-evidence-');
|
|
91
91
|
try{
|
|
92
|
-
const gate=await readFile(path.join(root,'scripts','gate-windows.ps1'),'utf8');
|
|
93
|
-
assert.match(gate,/windows-gate-evidence\.mjs/,'gate-windows.ps1 must invoke the evidence writer');
|
|
94
|
-
const result=await run(process.execPath,[path.join(root,'scripts','windows-gate-evidence.mjs')],{
|
|
92
|
+
const gate=await readFile(path.join(root,'scripts','gate-windows-r39.ps1'),'utf8');
|
|
93
|
+
assert.match(gate,/windows-gate-evidence-r39\.mjs/,'gate-windows-r39.ps1 must invoke the evidence writer');
|
|
94
|
+
const result=await run(process.execPath,[path.join(root,'scripts','windows-gate-evidence-r39.mjs')],{
|
|
95
95
|
cwd:root,env:{...process.env,DEADBYTE_STATE_ROOT:stateRoot}
|
|
96
96
|
});
|
|
97
97
|
assert.equal(result.code,0,result.stderr||result.stdout);
|
|
@@ -99,12 +99,12 @@ test('Windows R37 gate emits manifest-bound machine-readable evidence outside th
|
|
|
99
99
|
assert.equal(summary.status,'written');
|
|
100
100
|
const evidence=JSON.parse(await readFile(summary.path,'utf8'));
|
|
101
101
|
const manifestSha=sha256(await readFile(path.join(root,'MANIFEST.SHA256')));
|
|
102
|
-
assert.equal(evidence.schema,'deadbyte.
|
|
102
|
+
assert.equal(evidence.schema,'deadbyte.r39-windows-gate.v1');
|
|
103
103
|
assert.equal(evidence.status,'passed');
|
|
104
104
|
assert.equal(evidence.exit_code,0);
|
|
105
|
-
assert.equal(evidence.version,'0.11.
|
|
105
|
+
assert.equal(evidence.version,'0.11.3');
|
|
106
106
|
assert.equal(evidence.manifest_sha256,manifestSha);
|
|
107
|
-
assert.equal(evidence.marker,'DEADBYTE V0.11.
|
|
107
|
+
assert.equal(evidence.marker,'DEADBYTE V0.11.3 / R39 WINDOWS BATCH GATE: PASS');
|
|
108
108
|
assert.ok(summary.path.startsWith(path.join(stateRoot,'evidence','release')));
|
|
109
109
|
}finally{await rm(stateRoot,{recursive:true,force:true});}
|
|
110
110
|
});
|
|
@@ -2,8 +2,8 @@ import test from 'node:test';
|
|
|
2
2
|
import assert from 'node:assert/strict';
|
|
3
3
|
import { readFile } from 'node:fs/promises';
|
|
4
4
|
import { R34_EXPECTED_TOOLS } from '../src/remote-mcp-r34-parity-client.mjs';
|
|
5
|
-
import { validateParityEvidence } from '../scripts/release-parity.mjs';
|
|
6
|
-
import { verifyFinalClosureObject } from '../scripts/final-closure-verify.mjs';
|
|
5
|
+
import { validateParityEvidence } from '../scripts/release-parity-r39.mjs';
|
|
6
|
+
import { verifyFinalClosureObject } from '../scripts/final-closure-verify-r39.mjs';
|
|
7
7
|
|
|
8
8
|
const root = new URL('../', import.meta.url);
|
|
9
9
|
const text = rel => readFile(new URL(rel, root), 'utf8');
|
|
@@ -19,28 +19,28 @@ test('R34 public parity catalog is exact, unique, and includes production coding
|
|
|
19
19
|
|
|
20
20
|
test('release parity and final verifier are package entry points', async () => {
|
|
21
21
|
const pkg = JSON.parse(await text('package.json'));
|
|
22
|
-
assert.equal(pkg.scripts['release:parity'], 'node scripts/release-parity.mjs');
|
|
23
|
-
assert.equal(pkg.scripts['release:final:write'], 'node scripts/final-closure.mjs');
|
|
24
|
-
assert.equal(pkg.scripts['release:final:verify'], 'node scripts/final-closure-verify.mjs');
|
|
22
|
+
assert.equal(pkg.scripts['release:parity'], 'node scripts/release-parity-r39.mjs');
|
|
23
|
+
assert.equal(pkg.scripts['release:final:write'], 'node scripts/final-closure-r39.mjs');
|
|
24
|
+
assert.equal(pkg.scripts['release:final:verify'], 'node scripts/final-closure-verify-r39.mjs');
|
|
25
25
|
assert.equal(typeof validateParityEvidence, 'function');
|
|
26
26
|
assert.equal(typeof verifyFinalClosureObject, 'function');
|
|
27
27
|
});
|
|
28
28
|
|
|
29
|
-
test('
|
|
30
|
-
const verifier = await text('scripts/final-closure-verify.mjs');
|
|
31
|
-
const writer = await text('scripts/final-closure.mjs');
|
|
29
|
+
test('R39 final closure verifier is implementation-independent from production helpers', async () => {
|
|
30
|
+
const verifier = await text('scripts/final-closure-verify-r39.mjs');
|
|
31
|
+
const writer = await text('scripts/final-closure-r39.mjs');
|
|
32
32
|
assert.doesNotMatch(verifier, /from ['"]\.\.\/src\//);
|
|
33
33
|
assert.match(verifier, /DEADBYTE-R27-SLOT-UPDATE-V1/);
|
|
34
34
|
assert.match(verifier, /DEADBYTE-MACHINE-PING-V1/);
|
|
35
|
-
assert.match(verifier, /DEADBYTE-
|
|
36
|
-
assert.match(writer, /pointer\.version==='0\.11\.
|
|
37
|
-
assert.match(verifier, /envelope\.body\.version==='0\.11\.
|
|
38
|
-
assert.match(verifier, /release_label==='
|
|
39
|
-
assert.match(verifier, /
|
|
35
|
+
assert.match(verifier, /DEADBYTE-R39-FINAL-CLOSURE-V1/);
|
|
36
|
+
assert.match(writer, /pointer\.version==='0\.11\.3'/);
|
|
37
|
+
assert.match(verifier, /envelope\.body\.version==='0\.11\.3'/);
|
|
38
|
+
assert.match(verifier, /release_label==='R39 FINAL MACHINE BASELINE'/);
|
|
39
|
+
assert.match(verifier, /058605dfccaa3035a57c6393d53f2421c14e5a92bd145c2334477ba24d7cab8b/);
|
|
40
40
|
});
|
|
41
41
|
|
|
42
42
|
test('final closure verifier canonicalizes traversal order before exact-tree comparison', async () => {
|
|
43
|
-
const verifier = await text('scripts/final-closure-verify.mjs');
|
|
43
|
+
const verifier = await text('scripts/final-closure-verify-r39.mjs');
|
|
44
44
|
assert.match(verifier, /const actual=\(await walk\(root\)\)\.filter\(rel=>rel!==manifestName\)\.sort\(\(a,b\)=>a\.localeCompare\(b,'en'\)\);/);
|
|
45
45
|
assert.match(verifier, /const expected=\[\.\.\.entries\.keys\(\)\]\.sort\(\(a,b\)=>a\.localeCompare\(b,'en'\)\);/);
|
|
46
46
|
});
|
|
@@ -24,7 +24,7 @@ test('R37 unified timeline renders one sanitized tool-call line with ISO timesta
|
|
|
24
24
|
}
|
|
25
25
|
};
|
|
26
26
|
const line=formatRemoteToolCall(event);
|
|
27
|
-
assert.match(line,/^2026-09-
|
|
27
|
+
assert.match(line,/^2026-09-18T23:24:30\.125\+07:00 \| coding_file_write\s+\| Arguments: \{/);
|
|
28
28
|
assert.match(line,/"root_id":"projects"/);
|
|
29
29
|
assert.match(line,/"path":"src\/example\.mjs"/);
|
|
30
30
|
assert.match(line,/"reason":"payload"/);
|
|
@@ -37,7 +37,7 @@ test('R37 live tracker uses the tool-call line for request and keeps correlated
|
|
|
37
37
|
const tracker=createLiveTimelineTracker({clock:()=>now});
|
|
38
38
|
const call=tracker.observe({event_type:'tool.request',request_id:'abcdef1234567890',tool:'coding_tree',
|
|
39
39
|
created_at_utc:'2026-09-18T16:24:30.125Z',args:{root_id:'projects',path:'src'}});
|
|
40
|
-
assert.match(call,/^2026-09-
|
|
40
|
+
assert.match(call,/^2026-09-18T23:24:30\.125\+07:00 \| coding_tree\s+\| Arguments: \{"path":"src","root_id":"projects"\}$/);
|
|
41
41
|
now+=250;
|
|
42
42
|
assert.equal(tracker.observe({event_type:'tool.result',request_id:'abcdef1234567890',tool:'coding_tree',
|
|
43
43
|
outcome:{is_error:false,duration_ms:250}}),'✓ [abcdef12] OK coding_tree 250ms');
|