deadbyte-mcp 0.15.61 → 0.15.63
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 +81 -32
- package/PROVENANCE.json +38 -11
- package/README.txt +18 -18
- package/RELEASE.json +59 -15
- package/SBOM.cdx.json +2 -2
- package/TEST-MATRIX.json +31 -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/gate-windows-r160.ps1 +57 -0
- package/scripts/npm-release-r48.mjs +10 -0
- package/scripts/npm-release-readiness-r163.mjs +67 -0
- package/scripts/npm-release-readiness-r165.mjs +68 -0
- package/src/autonomous-supervisor.mjs +21 -4
- package/src/deadbyte-cli.mjs +90 -50
- 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/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 +12 -10
- package/test/r153-prepublish-identity.test.mjs +25 -4
- package/test/r153-staging-leaf-acl.test.mjs +5 -2
- 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 +103 -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/r164-remote-history-skew.test.mjs +13 -0
- package/test/r165-live-cli-skew.test.mjs +90 -0
- package/test/r165-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/r65-cli-invocation.test.mjs +27 -7
- 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
- package/node_modules/which/CHANGELOG.md +0 -166
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import test from 'node:test';
|
|
2
|
+
import assert from 'node:assert/strict';
|
|
3
|
+
import {mkdtemp,mkdir,rm} from 'node:fs/promises';
|
|
4
|
+
import os from 'node:os';
|
|
5
|
+
import path from 'node:path';
|
|
6
|
+
import {startAutonomousSupervisor} from '../src/autonomous-supervisor.mjs';
|
|
7
|
+
|
|
8
|
+
const goalId='a'.repeat(64);
|
|
9
|
+
async function fixture(run,status={state:'pending'},backend='openai_compatible'){
|
|
10
|
+
const dir=await mkdtemp(path.join(os.tmpdir(),'deadbyte-r156-supervisor-'));
|
|
11
|
+
await mkdir(path.join(dir,'goals',goalId),{recursive:true});
|
|
12
|
+
const policy={planner:{backend},evidence_dir:dir};
|
|
13
|
+
let statusCalls=0;
|
|
14
|
+
const runtime={policy,status:async()=>{statusCalls++;return status;},run};
|
|
15
|
+
const supervisor=startAutonomousSupervisor({runtime,policy,continuous:false,
|
|
16
|
+
idleDelayMs:750,maxCycles:3,logger:{warn(){},error(){}}});
|
|
17
|
+
return {supervisor,get statusCalls(){return statusCalls;},dispose:async()=>{await supervisor.stop();await rm(dir,{recursive:true,force:true});}};
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
test('R156 a goal waiting for retry does not count as runnable work',async()=>{
|
|
21
|
+
let calls=0;
|
|
22
|
+
const f=await fixture(async()=>{calls++;throw new Error('provider disconnected');});
|
|
23
|
+
try{
|
|
24
|
+
assert.equal(await f.supervisor.scanNow(),1,'initial work was attempted');
|
|
25
|
+
assert.equal(calls,1);
|
|
26
|
+
assert.ok(f.supervisor.failures.get(goalId)?.retry_at>Date.now(),'retry deadline recorded');
|
|
27
|
+
assert.equal(await f.supervisor.scanNow(),0,'backoff goal is not actionable');
|
|
28
|
+
assert.equal(calls,1,'provider not invoked during backoff');
|
|
29
|
+
assert.equal(f.statusCalls,1,'backoff must not issue a redundant goal-status read');
|
|
30
|
+
}finally{await f.dispose();}
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
test('R156 provider-backed Codex app server is dispatched by the outer loop',async()=>{
|
|
34
|
+
let calls=0;
|
|
35
|
+
const f=await fixture(async()=>{calls++;return {status:'succeeded'};},{state:'pending'},'codex_app_server');
|
|
36
|
+
try{
|
|
37
|
+
assert.equal(await f.supervisor.scanNow(),1);
|
|
38
|
+
assert.equal(calls,1);
|
|
39
|
+
}finally{await f.dispose();}
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
test('R156 client sampling cannot run unattended without a connected model',async()=>{
|
|
43
|
+
let calls=0;
|
|
44
|
+
const f=await fixture(async()=>{calls++;return {status:'succeeded'};},{state:'pending'},'client_sampling');
|
|
45
|
+
try{
|
|
46
|
+
assert.equal(await f.supervisor.scanNow(),0);
|
|
47
|
+
assert.equal(calls,0);
|
|
48
|
+
}finally{await f.dispose();}
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
test('R156 the supervisor never runs a paused goal that requires owner action',async()=>{
|
|
52
|
+
let calls=0;
|
|
53
|
+
const f=await fixture(async()=>{calls++;throw new Error('must not invoke');},{state:'paused',reason:'owner_approval_required'});
|
|
54
|
+
try{
|
|
55
|
+
assert.equal(await f.supervisor.scanNow(),0);
|
|
56
|
+
assert.equal(calls,0);
|
|
57
|
+
assert.equal(f.supervisor.failures.size,0);
|
|
58
|
+
}finally{await f.dispose();}
|
|
59
|
+
});
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import {readdir,readFile,writeFile} from 'node:fs/promises';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
const root='D:\\Projects\\deadbyte-mcp\\development\\deadbyte-mcp-0.15.62-r157-reference-convergence';
|
|
4
|
+
const dir=path.join(root,'src');
|
|
5
|
+
const all=(await readdir(dir,{withFileTypes:true})).filter(x=>x.isFile()&&x.name.endsWith('.mjs')).map(x=>x.name).sort();
|
|
6
|
+
const known=new Set(all);
|
|
7
|
+
const links=new Map(),externals=new Map(),incoming=new Map(all.map(name=>[name,0]));
|
|
8
|
+
for(const f of all){
|
|
9
|
+
const contents=await readFile(path.join(dir,f),'utf8');
|
|
10
|
+
const rx=/\b(?:import\s*(?:[^'"()]*?\sfrom\s*)?|export\s+[^'"()]*?\sfrom\s*|import\s*\()\s*['"]([^'"]+)['"]/g;
|
|
11
|
+
const local=new Set(),external=new Set();let match;
|
|
12
|
+
while((match=rx.exec(contents))){
|
|
13
|
+
const target=match[1];
|
|
14
|
+
if(target.startsWith('./')){
|
|
15
|
+
const base=path.posix.normalize(path.posix.join(path.posix.dirname(f),target));
|
|
16
|
+
if(known.has(base))local.add(base);
|
|
17
|
+
}else if(!target.startsWith('../'))external.add(target.split('/')[0]);
|
|
18
|
+
}
|
|
19
|
+
links.set(f,[...local].sort());
|
|
20
|
+
externals.set(f,[...external].sort());
|
|
21
|
+
for(const v of local)incoming.set(v,(incoming.get(v)||0)+1);
|
|
22
|
+
}
|
|
23
|
+
const starts=['mcp-server.mjs','deadbyte-cli.mjs','remote-live-log.mjs','autonomous-supervisor-host.mjs','autonomous-runtime.mjs','autonomous-supervisor.mjs'];
|
|
24
|
+
function traverse(start){
|
|
25
|
+
const seen=new Set(),stack=[start];
|
|
26
|
+
while(stack.length){const f=stack.pop();if(!known.has(f)||seen.has(f))continue;seen.add(f);stack.push(...(links.get(f)||[]));}
|
|
27
|
+
return [...seen].sort();
|
|
28
|
+
}
|
|
29
|
+
const groups={
|
|
30
|
+
transport:['mcp-server.mjs','deadbyte-cli.mjs','remote-bridge.mjs','remote-live-log.mjs','machine-identity.mjs','machine-ping.mjs'],
|
|
31
|
+
lifecycle:['foreground-marker-cas-win32.mjs','foreground-process-identity.mjs','remote-session-guardian.mjs','process-runtime.mjs','autonomous-local-supervisor.mjs'],
|
|
32
|
+
authority:['authority-continuity-r65.mjs','privileged-install-trust-r89.mjs','epoch-lock-r85.mjs','coding-plane.mjs','autonomous-policy.mjs'],
|
|
33
|
+
mutations:['native-diff.mjs','atomic-mutation.mjs','coding-plane.mjs','runtime-update.mjs','release-generation.mjs'],
|
|
34
|
+
automation:['autonomous-supervisor.mjs','autonomous-runtime.mjs','autonomous-progress.mjs','autonomous-loop-state.mjs','autonomous-planner.mjs'],
|
|
35
|
+
visibility:['remote-live-log.mjs','audit.mjs','observation.mjs','evidence-verify.mjs'],
|
|
36
|
+
release:['release-generation.mjs','runtime-update.mjs','doctor-installation-r58.mjs','clean-activation-recovery-r153.mjs']
|
|
37
|
+
};
|
|
38
|
+
const graph={schema:'deadbyte.r157-dependency-graph.v1',method:'regex static relative ESM import/export graph; not runtime reachability; excludes dynamic constructed imports and nested folders',module_count:all.length,edges:[...links].reduce((n,[,v])=>n+v.length,0),entrypoints:starts.map(s=>({name:s,present:known.has(s),reachable:traverse(s).length})),topIncoming:[...incoming].sort((a,b)=>b[1]-a[1]).slice(0,25).map(([name,count])=>({name,count})),groups:Object.fromEntries(Object.entries(groups).map(([k,files])=>[k,files.map(name=>({name,exists:known.has(name),imports:links.get(name)??[],referencedBy:incoming.get(name)||0}))])),files:Object.fromEntries([...links].map(([name,deps])=>[name,{deps,externals:externals.get(name)}]))};
|
|
39
|
+
await writeFile(path.join(root,'docs','R157-ARCHITECTURE-GRAPH.json'),JSON.stringify(graph,null,2)+'\n');
|
|
40
|
+
const text=['# DEADBYTE MCP R157 static dependency map','','Not a runtime reachability guarantee. Graph parses literal top-level relative ESM imports only.','',
|
|
41
|
+
'| Boundary | Runtime entry / related source | Security / compatibility rule |',
|
|
42
|
+
'|---|---|---|',
|
|
43
|
+
'| Public MCP and remote | mcp-server.mjs / deadbyte-cli.mjs / remote-live-log.mjs | machine identity and live request/result pairing |',
|
|
44
|
+
'| Session ownership | foreground-marker-cas-win32.mjs / remote-session-guardian.mjs | file identity, PID+creation identity, explicit revoke |',
|
|
45
|
+
'| Privileged epoch | authority-continuity-r65.mjs / privileged-install-trust-r89.mjs | owner-approved OS-owned root, deny untrusted ACL writes |',
|
|
46
|
+
'| Coding and native diffs | coding-plane.mjs / native-diff.mjs / atomic-mutation.mjs | bounded roots and exact SHA-256 preconditions |',
|
|
47
|
+
'| Autonomous engine | autonomous-supervisor.mjs / autonomous-runtime.mjs / autonomous-progress.mjs | bounded retries, receipts, owner approval and durable goals |',
|
|
48
|
+
'| Release generation | runtime-update.mjs / release-generation.mjs | signed exact promotion, rollback, manifest match |',
|
|
49
|
+
'','## Entrypoint reachability from literal imports'];
|
|
50
|
+
for(const e of graph.entrypoints)text.push('- '+e.name+': present='+e.present+', static reachable files='+e.reachable);
|
|
51
|
+
text.push('','## Most referenced modules within src/');
|
|
52
|
+
for(const e of graph.topIncoming)text.push('- '+e.name+' <- '+e.count+' static imports');
|
|
53
|
+
text.push('','## Explicit integration work','',
|
|
54
|
+
'- R89 protected source routing is candidate-only. Actual install provision / owner consent / revoke remains unverified.',
|
|
55
|
+
'- R155 default LiveLogs focused checks pass, but full Windows regression and startup reconciliation are not closed.',
|
|
56
|
+
'- R156/R157 autonomous supervisor is test-backed; release identity has not changed.',
|
|
57
|
+
'- A code source import does not prove an IPC, host process, or cloud boundary. Prove via isolated end-to-end test.',
|
|
58
|
+
'','## Rebuild','',
|
|
59
|
+
'node test/r157-architecture-map.mjs');
|
|
60
|
+
await writeFile(path.join(root,'docs','R157-ARCHITECTURE-DEPENDENCIES.md'),text.join('\n')+'\n');
|
|
61
|
+
console.log(JSON.stringify({status:'ok',modules:graph.module_count,edges:graph.edges,entrypoints:graph.entrypoints,output:'docs/R157-ARCHITECTURE-DEPENDENCIES.md'}));
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
import {readdir,lstat,readFile,mkdir,writeFile} from 'node:fs/promises';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
const root='D:\\Projects\\deadbyte-mcp\\references';
|
|
4
|
+
const output='D:\\Projects\\deadbyte-mcp\\development\\deadbyte-mcp-0.15.62-r157-reference-convergence\\docs';
|
|
5
|
+
const ignore=new Set(['.git','node_modules','vendor','.venv','venv','__pycache__','target','build','.next','.nuxt','coverage','.cache']);
|
|
6
|
+
const source=/\.(mjs|cjs|js|jsx|ts|tsx|py|rs|go|cpp|h|c|cs|ps1|sh)$/i;
|
|
7
|
+
const testing=/(^|\/)(test|tests|e2e|specs|fixtures|__tests__)(\/|$)|\.(test|spec)\./i;
|
|
8
|
+
const manifest=/(^|\/)(package\.json|pyproject\.toml|Cargo\.toml|go\.mod|requirements[\w.-]*\.txt|LICENSE[\w.-]*|COPYING[\w.-]*)$/i;
|
|
9
|
+
const patterns={
|
|
10
|
+
protocol:/\b(mcp|listTools|callTool|tools\/call|capabilities)\b/i,
|
|
11
|
+
sessions:/\b(session|foreground|reconnect|heartbeat|resume|ownership|handle)\b/i,
|
|
12
|
+
processes:/\b(spawn|execFile|subprocess|process\.kill|Start-Process|CreateProcess)\b/i,
|
|
13
|
+
filesystem:/\b(readFile|writeFile|edit_block|glob|directory|filesystem|file_search|path\.resolve)\b/i,
|
|
14
|
+
desktop:/\b(screenshot|uiautomation|SetCursorPos|mouse|keyboard|pyautogui|computer use|CUA)\b/i,
|
|
15
|
+
recovery:/\b(recover|rollback|retry|backoff|restart|idempotent|crash)\b/i,
|
|
16
|
+
logs:/\b(audit|logging|livelog|timeline|telemetry|trace|stream)\b/i,
|
|
17
|
+
authority:/\b(permission|authorization|grant|consent|ACL|approve|revoke)\b/i,
|
|
18
|
+
tests:/\b(assert|test\(|describe\(|pytest|unittest|benchmark|mock|fixture)\b/i,
|
|
19
|
+
agent_loop:/\b(planner|supervisor|agent.loop|critic|evaluate|goal|retry.policy)\b/i
|
|
20
|
+
};
|
|
21
|
+
const profiles=[];
|
|
22
|
+
const tops=(await readdir(root,{withFileTypes:true})).filter(x=>x.isDirectory()).map(x=>x.name).sort();
|
|
23
|
+
for(const name of tops){
|
|
24
|
+
const base=path.join(root,name);
|
|
25
|
+
const stack=[{dir:base,depth:0}],dirs=[],files=[],skips=[];
|
|
26
|
+
let truncated=false;
|
|
27
|
+
while(stack.length){
|
|
28
|
+
const {dir,depth}=stack.pop();
|
|
29
|
+
let ch;try{ch=await readdir(dir,{withFileTypes:true});}catch(e){skips.push({path:path.relative(base,dir),reason:e.code});continue;}
|
|
30
|
+
for(const x of ch){
|
|
31
|
+
const abs=path.join(dir,x.name),rel=path.relative(base,abs).replaceAll('\\','/');
|
|
32
|
+
if(x.isSymbolicLink()){skips.push({path:rel,reason:'symlink'});continue;}
|
|
33
|
+
if(x.isDirectory()){
|
|
34
|
+
dirs.push(rel);
|
|
35
|
+
if(ignore.has(x.name)){skips.push({path:rel,reason:'generated_or_dependencies'});continue;}
|
|
36
|
+
if(depth>27){truncated=true;continue;}
|
|
37
|
+
stack.push({dir:abs,depth:depth+1});
|
|
38
|
+
}else if(x.isFile()){
|
|
39
|
+
if(files.length>=55000){truncated=true;continue;}
|
|
40
|
+
try{const st=await lstat(abs);files.push({path:rel,size:st.size,source:source.test(rel),test:testing.test(rel),manifest:manifest.test(rel),ext:path.extname(rel).toLowerCase()});}catch{}
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
files.sort((a,b)=>a.path.localeCompare(b.path));
|
|
45
|
+
const found=Object.fromEntries(Object.keys(patterns).map(k=>[k,{count:0,paths:[]}]));
|
|
46
|
+
const manifests=[];
|
|
47
|
+
for(const f of files){
|
|
48
|
+
if(f.manifest&&manifests.length<100){
|
|
49
|
+
const m={path:f.path};
|
|
50
|
+
if(f.path.endsWith('package.json')&&f.size<200000){
|
|
51
|
+
try{const obj=JSON.parse(await readFile(path.join(base,f.path),'utf8'));m.name=obj.name;m.version=obj.version;m.license=obj.license;m.engines=obj.engines;m.dependencies=Object.keys(obj.dependencies||{}).slice(0,35);}catch{m.error='parse';}
|
|
52
|
+
}else if(/LICENSE|COPYING/i.test(path.basename(f.path))&&f.size<200000){
|
|
53
|
+
try{m.header=(await readFile(path.join(base,f.path),'utf8')).slice(0,320).replace(/\s+/g,' ').slice(0,180);}catch{}
|
|
54
|
+
}else if(f.size<100000){try{m.header=(await readFile(path.join(base,f.path),'utf8')).slice(0,800).replace(/\s+/g,' ').slice(0,170);}catch{}}
|
|
55
|
+
manifests.push(m);
|
|
56
|
+
}
|
|
57
|
+
if(!(f.source||f.test)||f.size>300000||f.size===0)continue;
|
|
58
|
+
let txt;try{txt=await readFile(path.join(base,f.path),'utf8');}catch{continue;}
|
|
59
|
+
for(const [key,rx] of Object.entries(patterns)){
|
|
60
|
+
if(rx.test(txt)){found[key].count++;if(found[key].paths.length<5)found[key].paths.push(f.path);}
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
const ext={};for(const f of files)if(f.source)ext[f.ext]=(ext[f.ext]||0)+1;
|
|
64
|
+
const p={name,files:files.length,directories:dirs.length,source_count:files.filter(f=>f.source).length,test_count:files.filter(f=>f.test).length,manifest_count:manifests.length,top_dirs:[...new Set(dirs.map(x=>x.split('/')[0]))].sort(),ext,manifests,found,truncated,skipped_count:skips.length,skips:skips.slice(0,85),candidate_files:files.filter(f=>f.test||/README|SECURITY|CHANGELOG|POLICY/i.test(f.path)).slice(0,100).map(f=>f.path)};
|
|
65
|
+
profiles.push(p);
|
|
66
|
+
console.log('SURVEY '+name+' files='+p.files+' source='+p.source_count+' tests='+p.test_count+' dirs='+p.directories+' skips='+p.skipped_count);
|
|
67
|
+
}
|
|
68
|
+
await mkdir(output,{recursive:true});
|
|
69
|
+
const inventory={schema:'deadbyte.reference-survey.r157.v1',root,generated_at_utc:new Date().toISOString(),method:'Read-only recursive scan of all reference subdirs except declared generated/dependency exclusions; token signals are NOT feature proof',dirs:tops,profiles};
|
|
70
|
+
await writeFile(path.join(output,'R157-REFERENCE-INVENTORY.json'),JSON.stringify(inventory,null,2)+'\n');
|
|
71
|
+
const md=['# R157 reference matrix','','Source/token inventory only, not proof of runtime behavior. No reference code was executed or imported.','',
|
|
72
|
+
'| Repository | Files | Sources | Tests | License or manifest evidence | Source signals |',
|
|
73
|
+
'|---|---:|---:|---:|---|---|'];
|
|
74
|
+
for(const p of profiles){
|
|
75
|
+
const licenses=p.manifests.filter(m=>m.license||/LICENSE|COPYING/i.test(m.path)).slice(0,3).map(m=>m.license?m.path+': '+m.license:m.path);
|
|
76
|
+
const features=Object.entries(p.found).filter(v=>v[1].count).sort((a,b)=>b[1].count-a[1].count).slice(0,5).map(v=>v[0]+'='+v[1].count);
|
|
77
|
+
md.push('| '+p.name.replaceAll('|','/')+' | '+p.files+' | '+p.source_count+' | '+p.test_count+' | '+(licenses.join('; ')||'NOT FOUND')+' | '+features.join('; ')+' |');
|
|
78
|
+
}
|
|
79
|
+
md.push('','## Per-repository source and test evidence');
|
|
80
|
+
for(const p of profiles){
|
|
81
|
+
md.push('','### '+p.name,'','Top directories: '+(p.top_dirs.join(', ')||'(none)')+'.','Scan truncated: '+p.truncated+'. Skipped '+p.skipped_count+' symlink/generated locations.');
|
|
82
|
+
md.push('Manifests: '+p.manifests.slice(0,18).map(m=>m.path+(m.license?' ['+m.license+']':'')).join(' ; '));
|
|
83
|
+
for(const [key,v] of Object.entries(p.found))if(v.count)md.push('- '+key+' matched '+v.count+' sources, examples: '+v.paths.join(' ; '));
|
|
84
|
+
if(!p.manifests.some(m=>m.license||/LICENSE|COPYING/i.test(m.path)))md.push('- LICENSE UNVERIFIED: do not reuse source code without separate review.');
|
|
85
|
+
}
|
|
86
|
+
md.push('','## Triage and boundary','',
|
|
87
|
+
'Inspect matched source plus tests before asserting any capability. Prefer algorithm redesign and benchmarked adaptation to bulk code copying.',
|
|
88
|
+
'Preserve DEADBYTE machine identity, explicit authority, native diff, signed receipts, existing tool count and rollback. No third-party runtime imports until security and license review.',
|
|
89
|
+
'R89/R155 release blockers still precede new features. Next: inspect concrete Desktop Commander implementation paths; test one candidate improvement on an isolated R157 fixture.');
|
|
90
|
+
await writeFile(path.join(output,'R157-REFERENCE-CAPABILITY-MATRIX.md'),md.join('\n')+'\n');
|
|
91
|
+
console.log('REFERENCE_SCAN_DONE repositories='+profiles.length+' total_source='+profiles.reduce((n,p)=>n+p.source_count,0)+' total_test='+profiles.reduce((n,p)=>n+p.test_count,0));
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import test from 'node:test';
|
|
2
|
+
import assert from 'node:assert/strict';
|
|
3
|
+
import os from 'node:os';
|
|
4
|
+
import path from 'node:path';
|
|
5
|
+
import {mkdtemp,mkdir,rm} from 'node:fs/promises';
|
|
6
|
+
import {startAutonomousSupervisor} from '../src/autonomous-supervisor.mjs';
|
|
7
|
+
const goal='b'.repeat(64);
|
|
8
|
+
|
|
9
|
+
test('R157 simultaneous supervisor scans share one real dispatch',async()=>{
|
|
10
|
+
const tmp=await mkdtemp(path.join(os.tmpdir(),'deadbyte-r157-singleflight-'));
|
|
11
|
+
let calls=0,statusCalls=0,release;
|
|
12
|
+
try{
|
|
13
|
+
await mkdir(path.join(tmp,'goals',goal),{recursive:true});
|
|
14
|
+
const gate=new Promise(resolve=>{release=resolve;});
|
|
15
|
+
const policy={planner:{backend:'openai_compatible'},evidence_dir:tmp};
|
|
16
|
+
const runtime={policy,
|
|
17
|
+
status:async()=>{statusCalls++;return {state:'pending'};},
|
|
18
|
+
run:async()=>{calls++;await gate;return {status:'succeeded'};}
|
|
19
|
+
};
|
|
20
|
+
const s=startAutonomousSupervisor({runtime,policy,continuous:false,logger:{warn(){},error(){}}});
|
|
21
|
+
try{
|
|
22
|
+
const first=s.scanNow(),second=s.scanNow();
|
|
23
|
+
// Allow both async scans to reach their run call.
|
|
24
|
+
await new Promise(resolve=>setTimeout(resolve,35));
|
|
25
|
+
assert.equal(calls,1,'overlapping scans must not launch two goal runs');
|
|
26
|
+
release();
|
|
27
|
+
const results=await Promise.all([first,second]);
|
|
28
|
+
assert.deepEqual(results,[1,1]);
|
|
29
|
+
assert.equal(statusCalls,1,'no redundant status read during in-flight scan');
|
|
30
|
+
}finally{release();await s.stop();}
|
|
31
|
+
}finally{await rm(tmp,{recursive:true,force:true});}
|
|
32
|
+
});
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import test from 'node:test';
|
|
2
|
+
import {readFile} from 'node:fs/promises';
|
|
3
|
+
import assert from 'node:assert/strict';
|
|
4
|
+
import {parseUpdateArgs} from '../src/runtime-update.mjs';
|
|
5
|
+
import {verifyTrustedEpochRelease} from '../src/trusted-epoch-bootstrap-r158.mjs';
|
|
6
|
+
const base=['--stage','stage','--development-root','dev','--expected-version','0.15.62','--expected-manifest','a'.repeat(64)];
|
|
7
|
+
test('R158 explicit protected Epoch root CLI rejects relative/UNC/drive-relative/empty and duplicate requests',()=>{
|
|
8
|
+
const valid='C:\\Program Files\\DEADBYTE-Epoch-Exact';
|
|
9
|
+
assert.equal(parseUpdateArgs([...base,'--authority-epoch-root',valid]).authorityEpochRoot,valid);
|
|
10
|
+
assert.equal(parseUpdateArgs(base).authorityEpochRoot,null);
|
|
11
|
+
for(const invalid of ['relative\\epoch','\\\\server\\share\\epoch','C:relative']){
|
|
12
|
+
assert.throws(()=>parseUpdateArgs([...base,'--authority-epoch-root',invalid]),/epoch root.*drive-absolute/i);
|
|
13
|
+
}
|
|
14
|
+
assert.throws(()=>parseUpdateArgs([...base,'--authority-epoch-root',valid,'--authority-epoch-root',valid]),/duplicate/i);
|
|
15
|
+
});
|
|
16
|
+
test('R158 root verification requires independently protected exact-release code',async t=>{
|
|
17
|
+
if(process.platform!=='win32'){t.skip('Windows trusted ACL');return;}
|
|
18
|
+
const fake='C:\\Users\\Public\\R158-untrusted-epoch';
|
|
19
|
+
await assert.rejects(verifyTrustedEpochRelease({epochRoot:fake,releaseRoot:fake,nodeExecutable:process.execPath}),/trusted|protected|ACL|root/i);
|
|
20
|
+
});
|
|
21
|
+
test('R158 optional live read-only protected 0.15.61 root preflight',async t=>{
|
|
22
|
+
const epochRoot=process.env.DEADBYTE_R158_TRUST_ROOT;
|
|
23
|
+
const releaseRoot=process.env.DEADBYTE_R158_RELEASE_ROOT;
|
|
24
|
+
if(!epochRoot||!releaseRoot){t.skip('requires explicit protected and release roots');return;}
|
|
25
|
+
const d=await verifyTrustedEpochRelease({epochRoot,releaseRoot,nodeExecutable:process.execPath});
|
|
26
|
+
assert.equal(d.root,epochRoot);
|
|
27
|
+
assert.ok(d.checked_files>=10);
|
|
28
|
+
});
|
|
29
|
+
test('R158 protected path flows from CLI to preflight and cannot bypass OS ACL verification',async()=>{
|
|
30
|
+
const source=await readFile(new URL('../src/runtime-update.mjs',import.meta.url),'utf8');
|
|
31
|
+
assert.match(source,/authorityEpochRoot:args\.authorityEpochRoot/);
|
|
32
|
+
assert.match(source,/verifyTrustedEpochRelease\(/);
|
|
33
|
+
assert.match(source,/control\.config\.AuthorityEpochRoot=proof\.root/);
|
|
34
|
+
});
|
|
@@ -0,0 +1,103 @@
|
|
|
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 {selectLegacyMarkerEvidence} from '../src/legacy-marker-evidence-r159.mjs';
|
|
8
|
+
const sha=x=>createHash('sha256').update(x).digest('hex');
|
|
9
|
+
const id=c=>c.repeat(64);
|
|
10
|
+
const old={release_id:'v0.15.45-OLD',version:'0.15.45',manifest_sha256:id('1'),generation_manifest_sha256:id('2')};
|
|
11
|
+
const current={release_id:'v0.15.61-CURRENT',version:'0.15.61',manifest_sha256:id('3'),generation_manifest_sha256:id('4')};
|
|
12
|
+
async function fixture(t){
|
|
13
|
+
const root=await mkdtemp(path.join(os.tmpdir(),'deadbyte-r159-locator-'));
|
|
14
|
+
const evidenceDir=path.join(root,'legacy-migration');await mkdir(evidenceDir);
|
|
15
|
+
t.after(()=>rm(root,{recursive:true,force:true}));
|
|
16
|
+
async function record(marker,predecessor,opts={}){
|
|
17
|
+
const body={schema:'deadbyte.r153-marker-reconciliation-intent.v1',state:'RECONCILIATION_CLAIMED',marker_sha256:marker,predecessor,subject_sha256:id('5')};
|
|
18
|
+
const intent=path.join(evidenceDir,'marker-'+marker+'.intent.json');
|
|
19
|
+
const intentBytes=Buffer.from(JSON.stringify(body)+'\n');
|
|
20
|
+
await writeFile(intent,intentBytes);
|
|
21
|
+
let terminalFile=null;
|
|
22
|
+
if(opts.terminal!==false){
|
|
23
|
+
terminalFile=path.join(evidenceDir,'marker-'+marker+'.terminal.json');
|
|
24
|
+
await writeFile(terminalFile,JSON.stringify({schema:'deadbyte.r153-marker-reconciliation.v1',state:'MARKER_RECONCILED',marker_sha256:marker,predecessor,intent_sha256:sha(intentBytes),subject_sha256:id('6')})+'\n');
|
|
25
|
+
}
|
|
26
|
+
return {intent,terminal:terminalFile};
|
|
27
|
+
}
|
|
28
|
+
async function locator(marker,receipt){
|
|
29
|
+
await writeFile(path.join(evidenceDir,'r153-pending-migration.json'),
|
|
30
|
+
JSON.stringify({schema:'deadbyte.r153-pending-migration.v1',marker_sha256:marker,receipt_path:receipt,receipt_sha256:sha(await readFile(receipt))})+'\n');
|
|
31
|
+
}
|
|
32
|
+
return {root,evidenceDir,record,locator};
|
|
33
|
+
}
|
|
34
|
+
test('R159 stale singleton locator does not hide current release terminal',async t=>{
|
|
35
|
+
const f=await fixture(t),o=await f.record(id('a'),old),n=await f.record(id('b'),current);
|
|
36
|
+
await f.locator(id('a'),o.terminal);
|
|
37
|
+
const v=await selectLegacyMarkerEvidence({evidenceDir:f.evidenceDir,activePointer:current});
|
|
38
|
+
assert.equal(v.kind,'completed');assert.equal(v.pending.markerSha256,id('b'));assert.equal(v.pending.terminal,n.terminal);
|
|
39
|
+
});
|
|
40
|
+
test('R159 current pending intent not masked by historic locator',async t=>{
|
|
41
|
+
const f=await fixture(t),o=await f.record(id('a'),old),n=await f.record(id('b'),current,{terminal:false});
|
|
42
|
+
await f.locator(id('a'),o.terminal);
|
|
43
|
+
const v=await selectLegacyMarkerEvidence({evidenceDir:f.evidenceDir,activePointer:current});
|
|
44
|
+
assert.equal(v.kind,'pending');assert.equal(v.pending.intent,n.intent);
|
|
45
|
+
});
|
|
46
|
+
test('R159 two matching pending intents fail closed',async t=>{
|
|
47
|
+
const f=await fixture(t);await f.record(id('a'),current,{terminal:false});await f.record(id('b'),current,{terminal:false});
|
|
48
|
+
await assert.rejects(selectLegacyMarkerEvidence({evidenceDir:f.evidenceDir,activePointer:current}),/multiple.*pending|ambiguous/i);
|
|
49
|
+
});
|
|
50
|
+
test('R159 current locator accepted after hash and subject',async t=>{
|
|
51
|
+
const f=await fixture(t),c=await f.record(id('b'),current);await f.locator(id('b'),c.terminal);
|
|
52
|
+
const v=await selectLegacyMarkerEvidence({evidenceDir:f.evidenceDir,activePointer:current});
|
|
53
|
+
assert.equal(v.kind,'located');assert.equal(v.receiptPath,c.terminal);assert.match(v.receiptSha256,/^[a-f0-9]{64}$/);
|
|
54
|
+
});
|
|
55
|
+
test('R159 tampered locator fails closed',async t=>{
|
|
56
|
+
const f=await fixture(t),c=await f.record(id('b'),current);await f.locator(id('b'),c.terminal);await writeFile(c.terminal,'{}\n');
|
|
57
|
+
await assert.rejects(selectLegacyMarkerEvidence({evidenceDir:f.evidenceDir,activePointer:current}),/hash|invalid|mismatch/i);
|
|
58
|
+
});
|
|
59
|
+
test('R159 no evidence on clean start',async t=>{
|
|
60
|
+
const f=await fixture(t);const v=await selectLegacyMarkerEvidence({evidenceDir:f.evidenceDir,activePointer:current});assert.equal(v.kind,'none');
|
|
61
|
+
});
|
|
62
|
+
test('R159 forged terminal with wrong intent hash fails closed',async t=>{
|
|
63
|
+
const f=await fixture(t),c=await f.record(id('b'),current);
|
|
64
|
+
const forged=JSON.parse(await readFile(c.terminal,'utf8'));
|
|
65
|
+
forged.intent_sha256=id('0');
|
|
66
|
+
await writeFile(c.terminal,JSON.stringify(forged)+'\n');
|
|
67
|
+
await assert.rejects(selectLegacyMarkerEvidence({evidenceDir:f.evidenceDir,activePointer:current}),/intent|mismatch/i);
|
|
68
|
+
});
|
|
69
|
+
test('R159 two unlocated completed current receipts remain ambiguous',async t=>{
|
|
70
|
+
const f=await fixture(t);await f.record(id('a'),current);await f.record(id('b'),current);
|
|
71
|
+
await assert.rejects(selectLegacyMarkerEvidence({evidenceDir:f.evidenceDir,activePointer:current}),/multiple|ambiguous/i);
|
|
72
|
+
});
|
|
73
|
+
test('R159 remote foreground code routes historical locators through subject selection',async()=>{
|
|
74
|
+
const cli=await readFile(new URL('../src/deadbyte-cli.mjs',import.meta.url),'utf8');
|
|
75
|
+
assert.match(cli,/selectLegacyMarkerEvidence\(\{evidenceDir,activePointer,\s*allowCompletedHistory:historyAllowed\|\|onlinePredecessorReady\}\)/);
|
|
76
|
+
assert.match(cli,/assertRuntimeReadyObservation\(runtime,\{coreRoot:activePointer\.release_root\}\)/);
|
|
77
|
+
assert.match(cli,/R164 predecessor changed during completed-history classification/);
|
|
78
|
+
assert.match(cli,/assertRuntimeReadyObservation\(observedAuthority\(ctx\),\{coreRoot:readback\.release_root\}\)/);
|
|
79
|
+
assert.match(cli,/R159 active release changed during marker recovery/);
|
|
80
|
+
assert.match(cli,/marker-'\+reconciled\.marker_sha256\+'\.locator\.json'/);
|
|
81
|
+
assert.doesNotMatch(cli,/publishExclusiveJson\(locatorPath,locator\)/);
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
// R159 case discovered from production: multiple independently completed
|
|
85
|
+
// terminals should not force selection of an arbitrary recovery proof.
|
|
86
|
+
test('R159 completed history requires explicit exact-release permission',async t=>{
|
|
87
|
+
const f=await fixture(t);
|
|
88
|
+
await f.record(id('a'),current);
|
|
89
|
+
await f.record(id('b'),current);
|
|
90
|
+
// Existing default must remain fail closed, even when no intents are pending.
|
|
91
|
+
await assert.rejects(selectLegacyMarkerEvidence({evidenceDir:f.evidenceDir,activePointer:current}),/ambiguous/i);
|
|
92
|
+
const allowed=await selectLegacyMarkerEvidence({evidenceDir:f.evidenceDir,activePointer:current,allowCompletedHistory:true});
|
|
93
|
+
assert.equal(allowed.kind,'completed_history_ambiguous');
|
|
94
|
+
assert.equal(allowed.count,2);
|
|
95
|
+
});
|
|
96
|
+
test('R159 CLI requires exact package identity before skipping completed history',async()=>{
|
|
97
|
+
const source=await readFile(new URL('../src/deadbyte-cli.mjs',import.meta.url),'utf8');
|
|
98
|
+
assert.match(source,/activePointer\?\.version===DEADBYTE_VERSION/);
|
|
99
|
+
assert.match(source,/activePointer\?\.manifest_sha256===currentManifestSha256/);
|
|
100
|
+
assert.match(source,/activePointer\?\.release_id===/);
|
|
101
|
+
assert.match(source,/allowCompletedHistory:historyAllowed/);
|
|
102
|
+
assert.match(source,/selected\.kind==='completed_history_ambiguous'/);
|
|
103
|
+
});
|
|
@@ -0,0 +1,100 @@
|
|
|
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 {selectLegacyMarkerEvidence} from '../src/legacy-marker-evidence-r159.mjs';
|
|
8
|
+
const sha=x=>createHash('sha256').update(x).digest('hex');
|
|
9
|
+
const id=c=>c.repeat(64);
|
|
10
|
+
const old={release_id:'v0.15.45-OLD',version:'0.15.45',manifest_sha256:id('1'),generation_manifest_sha256:id('2')};
|
|
11
|
+
const current={release_id:'v0.15.61-CURRENT',version:'0.15.61',manifest_sha256:id('3'),generation_manifest_sha256:id('4')};
|
|
12
|
+
async function fixture(t){
|
|
13
|
+
const root=await mkdtemp(path.join(os.tmpdir(),'deadbyte-r159-locator-'));
|
|
14
|
+
const evidenceDir=path.join(root,'legacy-migration');await mkdir(evidenceDir);
|
|
15
|
+
t.after(()=>rm(root,{recursive:true,force:true}));
|
|
16
|
+
async function record(marker,predecessor,opts={}){
|
|
17
|
+
const body={schema:'deadbyte.r153-marker-reconciliation-intent.v1',state:'RECONCILIATION_CLAIMED',marker_sha256:marker,predecessor,subject_sha256:id('5')};
|
|
18
|
+
const intent=path.join(evidenceDir,'marker-'+marker+'.intent.json');
|
|
19
|
+
const intentBytes=Buffer.from(JSON.stringify(body)+'\n');
|
|
20
|
+
await writeFile(intent,intentBytes);
|
|
21
|
+
let terminalFile=null;
|
|
22
|
+
if(opts.terminal!==false){
|
|
23
|
+
terminalFile=path.join(evidenceDir,'marker-'+marker+'.terminal.json');
|
|
24
|
+
await writeFile(terminalFile,JSON.stringify({schema:'deadbyte.r153-marker-reconciliation.v1',state:'MARKER_RECONCILED',marker_sha256:marker,predecessor,intent_sha256:sha(intentBytes),subject_sha256:id('6')})+'\n');
|
|
25
|
+
}
|
|
26
|
+
return {intent,terminal:terminalFile};
|
|
27
|
+
}
|
|
28
|
+
async function locator(marker,receipt){
|
|
29
|
+
await writeFile(path.join(evidenceDir,'r153-pending-migration.json'),
|
|
30
|
+
JSON.stringify({schema:'deadbyte.r153-pending-migration.v1',marker_sha256:marker,receipt_path:receipt,receipt_sha256:sha(await readFile(receipt))})+'\n');
|
|
31
|
+
}
|
|
32
|
+
return {root,evidenceDir,record,locator};
|
|
33
|
+
}
|
|
34
|
+
test('R159 stale singleton locator does not hide current release terminal',async t=>{
|
|
35
|
+
const f=await fixture(t),o=await f.record(id('a'),old),n=await f.record(id('b'),current);
|
|
36
|
+
await f.locator(id('a'),o.terminal);
|
|
37
|
+
const v=await selectLegacyMarkerEvidence({evidenceDir:f.evidenceDir,activePointer:current});
|
|
38
|
+
assert.equal(v.kind,'completed');assert.equal(v.pending.markerSha256,id('b'));assert.equal(v.pending.terminal,n.terminal);
|
|
39
|
+
});
|
|
40
|
+
test('R159 current pending intent not masked by historic locator',async t=>{
|
|
41
|
+
const f=await fixture(t),o=await f.record(id('a'),old),n=await f.record(id('b'),current,{terminal:false});
|
|
42
|
+
await f.locator(id('a'),o.terminal);
|
|
43
|
+
const v=await selectLegacyMarkerEvidence({evidenceDir:f.evidenceDir,activePointer:current});
|
|
44
|
+
assert.equal(v.kind,'pending');assert.equal(v.pending.intent,n.intent);
|
|
45
|
+
});
|
|
46
|
+
test('R159 two matching pending intents fail closed',async t=>{
|
|
47
|
+
const f=await fixture(t);await f.record(id('a'),current,{terminal:false});await f.record(id('b'),current,{terminal:false});
|
|
48
|
+
await assert.rejects(selectLegacyMarkerEvidence({evidenceDir:f.evidenceDir,activePointer:current}),/multiple.*pending|ambiguous/i);
|
|
49
|
+
});
|
|
50
|
+
test('R159 current locator accepted after hash and subject',async t=>{
|
|
51
|
+
const f=await fixture(t),c=await f.record(id('b'),current);await f.locator(id('b'),c.terminal);
|
|
52
|
+
const v=await selectLegacyMarkerEvidence({evidenceDir:f.evidenceDir,activePointer:current});
|
|
53
|
+
assert.equal(v.kind,'located');assert.equal(v.receiptPath,c.terminal);assert.match(v.receiptSha256,/^[a-f0-9]{64}$/);
|
|
54
|
+
});
|
|
55
|
+
test('R159 tampered locator fails closed',async t=>{
|
|
56
|
+
const f=await fixture(t),c=await f.record(id('b'),current);await f.locator(id('b'),c.terminal);await writeFile(c.terminal,'{}\n');
|
|
57
|
+
await assert.rejects(selectLegacyMarkerEvidence({evidenceDir:f.evidenceDir,activePointer:current}),/hash|invalid|mismatch/i);
|
|
58
|
+
});
|
|
59
|
+
test('R159 no evidence on clean start',async t=>{
|
|
60
|
+
const f=await fixture(t);const v=await selectLegacyMarkerEvidence({evidenceDir:f.evidenceDir,activePointer:current});assert.equal(v.kind,'none');
|
|
61
|
+
});
|
|
62
|
+
test('R159 forged terminal with wrong intent hash fails closed',async t=>{
|
|
63
|
+
const f=await fixture(t),c=await f.record(id('b'),current);
|
|
64
|
+
const forged=JSON.parse(await readFile(c.terminal,'utf8'));
|
|
65
|
+
forged.intent_sha256=id('0');
|
|
66
|
+
await writeFile(c.terminal,JSON.stringify(forged)+'\n');
|
|
67
|
+
await assert.rejects(selectLegacyMarkerEvidence({evidenceDir:f.evidenceDir,activePointer:current}),/intent|mismatch/i);
|
|
68
|
+
});
|
|
69
|
+
test('R159 two unlocated completed current receipts remain ambiguous',async t=>{
|
|
70
|
+
const f=await fixture(t);await f.record(id('a'),current);await f.record(id('b'),current);
|
|
71
|
+
await assert.rejects(selectLegacyMarkerEvidence({evidenceDir:f.evidenceDir,activePointer:current}),/multiple|ambiguous/i);
|
|
72
|
+
});
|
|
73
|
+
test('R159 remote foreground code routes historical locators through subject selection',async()=>{
|
|
74
|
+
const cli=await readFile(new URL('../src/deadbyte-cli.mjs',import.meta.url),'utf8');
|
|
75
|
+
assert.match(cli,/selectLegacyMarkerEvidence\(\{evidenceDir,activePointer\}\)/);
|
|
76
|
+
assert.match(cli,/R159 active release changed during marker recovery/);
|
|
77
|
+
assert.match(cli,/marker-'\+reconciled\.marker_sha256\+'\.locator\.json'/);
|
|
78
|
+
assert.doesNotMatch(cli,/publishExclusiveJson\(locatorPath,locator\)/);
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
// R159 case discovered from production: multiple independently completed
|
|
82
|
+
// terminals should not force selection of an arbitrary recovery proof.
|
|
83
|
+
test('R159 completed history requires explicit exact-release permission',async t=>{
|
|
84
|
+
const f=await fixture(t);
|
|
85
|
+
await f.record(id('a'),current);
|
|
86
|
+
await f.record(id('b'),current);
|
|
87
|
+
// Existing default must remain fail closed, even when no intents are pending.
|
|
88
|
+
await assert.rejects(selectLegacyMarkerEvidence({evidenceDir:f.evidenceDir,activePointer:current}),/ambiguous/i);
|
|
89
|
+
const allowed=await selectLegacyMarkerEvidence({evidenceDir:f.evidenceDir,activePointer:current,allowCompletedHistory:true});
|
|
90
|
+
assert.equal(allowed.kind,'completed_history_ambiguous');
|
|
91
|
+
assert.equal(allowed.count,2);
|
|
92
|
+
});
|
|
93
|
+
test('R159 CLI requires exact package identity before skipping completed history',async()=>{
|
|
94
|
+
const source=await readFile(new URL('../src/deadbyte-cli.mjs',import.meta.url),'utf8');
|
|
95
|
+
assert.match(source,/activePointer\?\.version===DEADBYTE_VERSION/);
|
|
96
|
+
assert.match(source,/activePointer\?\.manifest_sha256===currentManifestSha256/);
|
|
97
|
+
assert.match(source,/activePointer\?\.release_id===/);
|
|
98
|
+
assert.match(source,/allowCompletedHistory:historyAllowed/);
|
|
99
|
+
assert.match(source,/selected\.kind==='completed_history_ambiguous'/);
|
|
100
|
+
});
|
|
@@ -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
|
+
|