deadbyte-mcp 0.15.68 → 0.15.70

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.
Files changed (87) hide show
  1. package/MANIFEST.SHA256 +85 -38
  2. package/PROVENANCE.json +52 -24
  3. package/README.txt +44 -31
  4. package/RELEASE.json +47 -17
  5. package/SBOM.cdx.json +2 -2
  6. package/TEST-MATRIX.json +44 -2
  7. package/bridge/package.json +1 -1
  8. package/controller/deadbyte-controller.ps1 +65 -1
  9. package/controller/deadbyte-process-policy.json +4 -4
  10. package/docs/R184-package-lock.json +97 -0
  11. package/docs/R186-OWNER-CONSENT-HANDOFF.txt +87 -0
  12. package/package.json +1 -1
  13. package/scripts/install-r186-owner-trust.ps1 +59 -0
  14. package/scripts/r175-focused-runner.mjs +38 -0
  15. package/scripts/r176-log-preview.mjs +18 -0
  16. package/scripts/r177-owner-trust-inventory.ps1 +36 -0
  17. package/scripts/r178-full-regression.mjs +21 -0
  18. package/scripts/r178-npm-ci.mjs +10 -0
  19. package/scripts/r178-npm-preflight.mjs +16 -0
  20. package/scripts/r178-release-gates-readonly.mjs +9 -0
  21. package/scripts/r178-shard-regression.mjs +25 -0
  22. package/scripts/r179-predecessor-registry.mjs +24 -0
  23. package/scripts/r179-protected-inventory.ps1 +21 -0
  24. package/scripts/r186-focused-runner.mjs +22 -0
  25. package/scripts/r186-owner-anchor-inspect.mjs +6 -0
  26. package/scripts/r186-owner-consent-check.mjs +86 -0
  27. package/scripts/r186-owner-grant-cli.mjs +45 -0
  28. package/scripts/r186-owner-key-local.mjs +22 -0
  29. package/scripts/r186-owner-review-export.mjs +34 -0
  30. package/src/deadbyte-cli.mjs +130 -56
  31. package/src/foreground-owner-recovery-r190.mjs +117 -0
  32. package/src/r174-owner-webauthn.mjs +176 -0
  33. package/src/r175-foreground-boundary.mjs +28 -0
  34. package/src/r186-owner-anchor.mjs +12 -0
  35. package/src/r186-owner-consent-preflight.mjs +63 -0
  36. package/src/r186-owner-grant-broker.mjs +96 -0
  37. package/src/r186-owner-local-signing.mjs +25 -0
  38. package/src/r186-owner-request.mjs +40 -0
  39. package/src/r186-owner-review-export.mjs +49 -0
  40. package/src/remote-live-log.mjs +81 -5
  41. package/src/remote-session-guardian.mjs +23 -0
  42. package/src/version.mjs +1 -1
  43. package/test/helpers/r175-active-pointer.mjs +28 -0
  44. package/test/helpers/release-lock.mjs +12 -0
  45. package/test/r129-sdk-security-baseline.test.mjs +2 -1
  46. package/test/r130-release-security-contract.test.mjs +11 -10
  47. package/test/r132-release-identity.test.mjs +12 -11
  48. package/test/r153-prepublish-identity.test.mjs +1 -1
  49. package/test/r155-default-live-log.test.mjs +17 -16
  50. package/test/r160-package-decision-manifest.test.mjs +1 -1
  51. package/test/r160-release-contract.test.mjs +4 -5
  52. package/test/r163-isolated-slot-rollback-restore.test.mjs +8 -8
  53. package/test/r165-live-cli-skew.test.mjs +6 -6
  54. package/test/r167-release-contract.test.mjs +15 -13
  55. package/test/r168-protected-entrypoint.test.mjs +5 -2
  56. package/test/r168-release-contract.test.mjs +4 -3
  57. package/test/r168-remote-one-shot-coherence.test.mjs +2 -1
  58. package/test/r172-release-contract.test.mjs +7 -7
  59. package/test/r173-release-contract.test.mjs +18 -17
  60. package/test/r174-owner-webauthn.test.mjs +144 -0
  61. package/test/r175-cli-flags.test.mjs +23 -0
  62. package/test/r175-foreground-boundary.test.mjs +87 -0
  63. package/test/r175-guardian-owner-boundary.test.mjs +56 -0
  64. package/test/r176-pretty-live-log.test.mjs +54 -0
  65. package/test/r177-windows-absolute-live-path.test.mjs +37 -0
  66. package/test/r179-absolute-path-live-log.test.mjs +40 -0
  67. package/test/r179-release-contract.test.mjs +62 -0
  68. package/test/r186-controller-owner-grant.test.mjs +20 -0
  69. package/test/r186-owner-consent-preflight.test.mjs +98 -0
  70. package/test/r186-owner-enrollment.test.mjs +20 -0
  71. package/test/r186-owner-grant-broker.test.mjs +85 -0
  72. package/test/r186-owner-local-signing.test.mjs +30 -0
  73. package/test/r186-owner-request.test.mjs +28 -0
  74. package/test/r186-owner-review-export.test.mjs +52 -0
  75. package/test/r190-foreground-cli.test.mjs +41 -0
  76. package/test/r190-foreground-owner-recovery.test.mjs +61 -0
  77. package/test/r190-release-contract.test.mjs +33 -0
  78. package/test/r48-release-contract.test.mjs +2 -1
  79. package/test/r51-runtime-coherence.test.mjs +4 -2
  80. package/test/r58-cleanup-retention.test.mjs +5 -12
  81. package/test/r58-concurrent-recovery.test.mjs +5 -1
  82. package/test/r58-stale-owner.test.mjs +9 -3
  83. package/test/r59-tool-surface-compat.test.mjs +1 -1
  84. package/test/remote-live-log-r37.test.mjs +2 -2
  85. package/test/remote-live-log-recovery-r36.test.mjs +1 -1
  86. package/test/remote-session-cli.test.mjs +92 -126
  87. package/node_modules/which/CHANGELOG.md +0 -166
@@ -0,0 +1,52 @@
1
+ import test from 'node:test';
2
+ import assert from 'node:assert/strict';
3
+ import {mkdtemp,readFile,symlink,writeFile} from 'node:fs/promises';
4
+ import {tmpdir} from 'node:os';
5
+ import path from 'node:path';
6
+ import {createHash} from 'node:crypto';
7
+ import {writeR186OwnerReviewRequest} from '../src/r186-owner-review-export.mjs';
8
+
9
+ const sha=bytes=>createHash('sha256').update(bytes).digest('hex');
10
+
11
+ async function fixture(){
12
+ const root=await mkdtemp(path.join(tmpdir(),'deadbyte-r186-review-'));
13
+ const project='D:\\Projects\\deadbyte-mcp';
14
+ const codingPath=path.join(root,'coding.json');
15
+ const autonomousPath=path.join(root,'autonomous.json');
16
+ const manifestPath=path.join(root,'MANIFEST.SHA256');
17
+ const outputPath=path.join(root,'review-request.json');
18
+ const codingBytes=Buffer.from(JSON.stringify({schema:'deadbyte.coding-policy.v1',enabled:true,
19
+ roots:{core:{path:project,read:true,write:true,exec:false}}}));
20
+ await writeFile(codingPath,codingBytes);
21
+ await writeFile(autonomousPath,JSON.stringify({schema:'deadbyte.autonomous-policy.v1',enabled:true,
22
+ root_ids:['core'],coding_policy_sha256:sha(codingBytes),lease_file:path.join(root,'grant.json')}));
23
+ await writeFile(manifestPath,'a'.repeat(64)+' package.json\n');
24
+ return {root,projectRoot:project,codingPolicyPath:codingPath,
25
+ autonomousPolicyPath:autonomousPath,manifestPath,outputPath};
26
+ }
27
+
28
+ test('R186 owner review export writes one exact bounded request without granting authority',async()=>{
29
+ const f=await fixture();
30
+ const result=await writeR186OwnerReviewRequest({...f,rootId:'core',
31
+ now:new Date('2026-10-28T04:00:00.000Z'),durationMs:300000});
32
+ const saved=JSON.parse(await readFile(f.outputPath,'utf8'));
33
+ assert.equal(saved.subject_sha256,result.subject_sha256);
34
+ assert.equal(saved.subject.project_root,f.projectRoot);
35
+ assert.equal(saved.subject.expires_at_utc,'2026-10-28T04:05:00.000Z');
36
+ assert.equal(saved.authority_granted,false);
37
+ assert.equal(saved.owner_signature_present,false);
38
+ await assert.rejects(()=>writeR186OwnerReviewRequest({...f,rootId:'core'}),/exists|EEXIST/i);
39
+ });
40
+
41
+ test('R186 owner review export rejects policy drift and reparse inputs',async()=>{
42
+ const f=await fixture();
43
+ const autonomous=JSON.parse(await readFile(f.autonomousPolicyPath,'utf8'));
44
+ autonomous.coding_policy_sha256='b'.repeat(64);
45
+ await writeFile(f.autonomousPolicyPath,JSON.stringify(autonomous));
46
+ await assert.rejects(()=>writeR186OwnerReviewRequest({...f,rootId:'core'}),/digest binding/i);
47
+
48
+ const clean=await fixture();
49
+ const link=path.join(path.dirname(clean.root),'coding-policy-junction-'+path.basename(clean.root));
50
+ await symlink(clean.root,link,'junction');
51
+ await assert.rejects(()=>writeR186OwnerReviewRequest({...clean,rootId:'core',codingPolicyPath:link}),/reparse|symlink/i);
52
+ });
@@ -0,0 +1,41 @@
1
+ import test from 'node:test';
2
+ import assert from 'node:assert/strict';
3
+ import {spawnSync} from 'node:child_process';
4
+ import path from 'node:path';
5
+ import {fileURLToPath} from 'node:url';
6
+ import {inspectTrustedInstallation} from '../src/privileged-install-trust-r89.mjs';
7
+
8
+ const root=path.dirname(path.dirname(fileURLToPath(import.meta.url)));
9
+
10
+ test('R190 CLI exposes explicit marker recovery and applies the R89 trust boundary before mutation',async()=>{
11
+ const installation=await inspectTrustedInstallation([
12
+ process.execPath,
13
+ path.join(root,'src','deadbyte-cli.mjs'),
14
+ path.join(root,'MANIFEST.SHA256'),
15
+ path.join(root,'src','foreground-owner-recovery-r190.mjs'),
16
+ path.join(root,'src','foreground-marker-cas-win32.mjs'),
17
+ path.join(root,'src','foreground-process-identity.mjs'),
18
+ path.join(root,'src','foreground-session-lock.mjs'),
19
+ path.join(root,'src','privileged-install-trust-r89.mjs'),
20
+ path.join(root,'src','canonical-json.mjs'),
21
+ path.join(root,'bin','deadbyte-epoch-lock-r85.node')
22
+ ]).catch(()=>({trusted:false}));
23
+ const result=spawnSync(process.execPath,['src/deadbyte-cli.mjs','remote','reconcileforeground',
24
+ '--confirm-marker-sha256','f'.repeat(64)],{encoding:'utf8',windowsHide:true,shell:false});
25
+ const output=String(result.stdout||'')+String(result.stderr||'');
26
+ assert.notEqual(result.status,0,output);
27
+ if(installation.trusted) assert.match(output,/marker (?:hash differs from explicit confirmation|absent without a durable R190 intent)/);
28
+ else assert.match(output,/R190_PROTECTED_RECOVERY_REQUIRED/);
29
+ assert.doesNotMatch(output,/unsupported remote command|EXACT_ACTIVE_RELEASE_REQUIRED/);
30
+ });
31
+
32
+ test('R190 CLI requires exact marker hash and never accepts recovery confirmation on another command',()=>{
33
+ const missing=spawnSync(process.execPath,['src/deadbyte-cli.mjs','remote','reconcileforeground'],
34
+ {encoding:'utf8',windowsHide:true,shell:false});
35
+ assert.notEqual(missing.status,0);
36
+ assert.match(String(missing.stderr),/requires --confirm-marker-sha256/);
37
+ const confused=spawnSync(process.execPath,['src/deadbyte-cli.mjs','remote','status',
38
+ '--confirm-marker-sha256','f'.repeat(64)],{encoding:'utf8',windowsHide:true,shell:false});
39
+ assert.notEqual(confused.status,0);
40
+ assert.match(String(confused.stderr),/valid only with reconcileforeground/);
41
+ });
@@ -0,0 +1,61 @@
1
+ import test from 'node:test';
2
+ import assert from 'node:assert/strict';
3
+ import {recoverLegacyForegroundOwnership} from '../src/foreground-owner-recovery-r190.mjs';
4
+
5
+ const H=char=>char.repeat(64);
6
+ const owner={schema:'deadbyte.foreground-process.v1',pid:9496,parent_pid:5272,
7
+ creation_utc:'2026-10-27T12:25:38.764342Z',executable_path:'C:\\Program Files\\nodejs\\node.exe',command_line_sha256:H('a')};
8
+ const guardian={...owner,pid:10456,parent_pid:9496,creation_utc:'2026-10-27T12:25:39.244355Z',command_line_sha256:H('b')};
9
+ const marker={schema:'deadbyte.remote-session.v2',owner_pid:9496,guardian_pid:10456,
10
+ session_nonce:H('c'),result_session_id:'b0015a97167ff20bf4e15588',started_at_utc:'2026-10-27T12:25:38.994Z',owner_identity:owner,guardian_identity:guardian};
11
+ const pointer={release_id:'v0.15.68-'+H('d').slice(0,16),version:'0.15.68',manifest_sha256:H('d'),
12
+ generation_manifest_sha256:H('e'),generation_root:'C:\\Users\\DEADBYTE\\.deadbyte-mcp\\generations\\fixture'};
13
+ const authority={state:'ONLINE',local_ready:true,supervisor:{running:true},authority:Object.fromEntries(
14
+ ['process','coding','autonomous','desktop','host'].map((plane,index)=>[plane,{configured:true,armed:[true,true,false,false,true][index],reason:[true,true,false,false,true][index]?'standing-grant':'disarmed'}]))};
15
+
16
+ function harness({ownerActual={...owner,pid:9496,creation_utc:'2026-10-28T03:22:41.668663Z',executable_path:'C:\\Windows\\WidgetBoard.exe'},
17
+ guardianActual=null,driftPointer=false,terminalFailure=false}={}){
18
+ let snapshot={marker,sha256:H('f')},intent=null,terminal=null,deletes=0,pointerReads=0;
19
+ return {state:()=>({snapshot,intent,terminal,deletes}),run:()=>recoverLegacyForegroundOwnership({
20
+ expectedMarkerSha256:H('f'),readMarkerSnapshot:async()=>snapshot,
21
+ deleteMarkerIfHash:async expected=>{assert.equal(expected,H('f'));snapshot=null;deletes++;return 'REMOVED';},
22
+ readActivePointer:async()=>{pointerReads++;return driftPointer&&pointerReads>1?{...pointer,release_id:'changed'}:pointer;},
23
+ readAuthority:async()=>authority,
24
+ queryIdentity:identity=>identity.pid===owner.pid?ownerActual:guardianActual,
25
+ loadIntent:async()=>intent,loadTerminal:async()=>terminal,
26
+ publishIntent:async value=>{intent=value;return {sha256:value.intent_sha256};},
27
+ publishTerminal:async value=>{if(terminalFailure){terminalFailure=false;throw new Error('terminal write failed');}terminal=value;return {sha256:value.receipt_sha256};},
28
+ now:()=>new Date('2026-10-28T07:00:00.000Z')
29
+ })};
30
+ }
31
+
32
+ test('R190 explicit owner recovery CAS-removes only a PID-reused legacy marker and preserves authority vector',async()=>{
33
+ const h=harness(),result=await h.run(),state=h.state();
34
+ assert.equal(result.status,'MARKER_RECONCILED');
35
+ assert.equal(state.deletes,1);
36
+ assert.equal(state.snapshot,null);
37
+ assert.equal(state.intent.authority.process.armed,true);
38
+ assert.equal(state.intent.authority.coding.armed,true);
39
+ assert.equal(state.intent.authority.autonomous.armed,false);
40
+ assert.equal(state.intent.authority.host.armed,true);
41
+ assert.equal(state.terminal.marker_sha256,H('f'));
42
+ });
43
+
44
+ test('R190 refuses live matching owner and pointer drift without deleting marker',async()=>{
45
+ const live=harness({ownerActual:owner});
46
+ await assert.rejects(()=>live.run(),/owner.*active/i);
47
+ assert.equal(live.state().deletes,0);
48
+ const drift=harness({driftPointer:true});
49
+ await assert.rejects(()=>drift.run(),/pointer.*drift/i);
50
+ assert.equal(drift.state().deletes,0);
51
+ });
52
+
53
+ test('R190 resumes terminal publication after crash following native CAS delete without repeating mutation',async()=>{
54
+ const h=harness({terminalFailure:true});
55
+ await assert.rejects(()=>h.run(),/terminal write failed/);
56
+ assert.equal(h.state().deletes,1);
57
+ const result=await h.run();
58
+ assert.equal(result.status,'MARKER_RECONCILED_AFTER_DELETE');
59
+ assert.equal(h.state().deletes,1);
60
+ assert.ok(h.state().terminal);
61
+ });
@@ -0,0 +1,33 @@
1
+ import test from 'node:test';
2
+ import assert from 'node:assert/strict';
3
+ import {readFile} from 'node:fs/promises';
4
+
5
+ const json=async file=>JSON.parse(await readFile(new URL('../'+file,import.meta.url),'utf8'));
6
+ test('R190 candidate identity separates published source 0.15.69 from active runtime 0.15.68',async()=>{
7
+ const [pkg,bridge,release,provenance,matrix,sbom,versionSource]=await Promise.all([
8
+ json('package.json'),json('bridge/package.json'),json('RELEASE.json'),json('PROVENANCE.json'),
9
+ json('TEST-MATRIX.json'),json('SBOM.cdx.json'),readFile(new URL('../src/version.mjs',import.meta.url),'utf8')]);
10
+ for(const value of [pkg.version,bridge.version,release.version,provenance.version,matrix.version,
11
+ sbom.metadata.component.version])assert.equal(value,'0.15.70');
12
+ assert.match(versionSource,/DEADBYTE_VERSION = '0\.15\.70'/);
13
+ assert.equal(release.train,'R190');assert.equal(matrix.train,'R190');
14
+ assert.equal(release.source_predecessor.version,'0.15.69');
15
+ assert.equal(release.source_predecessor.tarball_sha256,'578c1630608f9df816524c78abc9e1a55357fdc3cd25e142122f1a823e1d69e3');
16
+ assert.equal(release.source_predecessor.manifest_sha256,'cfede112761da00e73b626cfbce23d17e1d4b6e3a325bb675415196d31142ba0');
17
+ assert.equal(release.runtime_predecessor.version,'0.15.68');
18
+ assert.equal(release.runtime_predecessor.release_id,'v0.15.68-ff29dbb2fbb89862');
19
+ assert.equal(provenance.source_predecessor_version,'0.15.69');
20
+ assert.equal(provenance.source_predecessor_tarball_sha256,release.source_predecessor.tarball_sha256);
21
+ assert.match(sbom.serialNumber,/r190:0\.15\.70$/);
22
+ });
23
+
24
+ test('R190 release metadata records verified recovery without claiming publication or activation',async()=>{
25
+ const release=await json('RELEASE.json');
26
+ assert.equal(release.r190_foreground_recovery.status,'PREPUBLISH_VERIFIED');
27
+ assert.equal(release.r190_foreground_recovery.npm_published,false);
28
+ assert.equal(release.r190_foreground_recovery.production_activated,false);
29
+ assert.equal(release.r190_foreground_recovery.live_marker_reconciled,true);
30
+ assert.equal(release.r190_foreground_recovery.authority_mutation_allowed,false);
31
+ assert.equal(release.r190_foreground_recovery.runtime_mutation_allowed,false);
32
+ assert.match(release.r190_foreground_recovery.protected_full_regression,/0_FAIL$/);
33
+ });
@@ -2,6 +2,7 @@ import test from 'node:test';
2
2
  import assert from 'node:assert/strict';
3
3
  import { readFile } from 'node:fs/promises';
4
4
  import { DEADBYTE_VERSION } from '../src/version.mjs';
5
+ import { readReleaseLock } from './helpers/release-lock.mjs';
5
6
 
6
7
  const root = new URL('../', import.meta.url);
7
8
  const read = rel => readFile(new URL(rel, root), 'utf8');
@@ -9,7 +10,7 @@ const json = rel => read(rel).then(raw => JSON.parse(raw.replace(/^\uFEFF/,'')))
9
10
 
10
11
  test('R48 compatibility release contract remains coherent in the current successor', async () => {
11
12
  const [pkg, lock, release, provenance, matrix] = await Promise.all([
12
- json('package.json'), json('package-lock.json'), json('RELEASE.json'),
13
+ json('package.json'), readReleaseLock(new URL('.', root)), json('RELEASE.json'),
13
14
  json('PROVENANCE.json'), json('TEST-MATRIX.json')
14
15
  ]);
15
16
 
@@ -50,10 +50,12 @@ test('R51 bare remote wires coherence before live generation binding and deploy
50
50
  });
51
51
 
52
52
 
53
- test('R51 foreground authority contract explicitly verifies and cleans desktop plus host',async()=>{
53
+ test('R175 observation-only foreground retains legacy cleanup only for legacy markers, never owned grants',async()=>{
54
54
  const {readFile}=await import('node:fs/promises');
55
55
  const cli=await readFile(new URL('../src/deadbyte-cli.mjs',import.meta.url),'utf8');
56
- assert.match(cli,/assertAuthorityVector\(assertRuntimeReadyObservation\(observedAuthority/);
56
+ assert.match(cli,/if\(marker!==null\)/);
57
+ assert.match(cli,/R175_INVALID_OBSERVE_ONLY_MARKER/);
58
+ assert.match(cli,/assertRuntimeReadyObservation\(observedAuthority/);
57
59
  assert.match(cli,/SESSION_CLEANUP=.*disarmdesktop.*disarmhost/s);
58
60
  assert.match(cli,/authorityBanner\(authority\)/);
59
61
  });
@@ -6,7 +6,7 @@ import path from 'node:path';
6
6
  import { spawn } from 'node:child_process';
7
7
  import { setTimeout as delay } from 'node:timers/promises';
8
8
 
9
- test('R58 foreground cleanup failure retains ownership evidence for recovery', {timeout:60000}, async () => {
9
+ test('R175 elevated foreground rejection cannot create cleanup ownership evidence', {timeout:60000}, async () => {
10
10
  const base='D:/Projects/deadbyte-mcp/testing/0.15.35/fixtures';
11
11
  await mkdir(base,{recursive:true});
12
12
  const root=await mkdtemp(path.join(base,'cleanup-'));
@@ -34,18 +34,11 @@ exit 0
34
34
  child.stdout.on('data',b=>{stdout+=b;});child.stderr.on('data',b=>{stderr+=b;});
35
35
  const closed=new Promise(resolve=>child.once('close',code=>resolve(code)));
36
36
  try {
37
- const deadline=Date.now()+50000;
38
- while(!stdout.includes('[READY]')&&child.exitCode===null&&Date.now()<deadline) await delay(50);
39
- assert.match(stdout,/\[READY\]/,stderr);
40
- assert.match(stdout,/\[AUTH\] process ARMED \| coding ARMED \| autonomous ARMED \| desktop DISARMED \| host DISARMED/,stderr);
41
- child.stdin.end();
42
- assert.notEqual(await closed,0,'cleanup failure must not report a successful foreground exit');
43
- assert.match(stderr,/cleanup disarmprocess exit=9/);
44
- const marker=JSON.parse(await readFile(path.join(root,'runtime','remote-session.json'),'utf8'));
45
- assert.equal(marker.owner_pid,child.pid);
46
- assert.match(marker.session_nonce,/^[0-9a-f]{64}$/);
37
+ assert.notEqual(await closed,0,'elevated foreground must fail closed');
38
+ assert.match(stderr,/R175_FOREGROUND_OWNER_BOUNDARY/);
39
+ await assert.rejects(()=>readFile(path.join(root,'runtime','remote-session.json'),'utf8'),{code:'ENOENT'});
47
40
  } finally {
48
41
  if(child.exitCode===null) child.kill();
49
- // A failed cleanup intentionally leaves state as reproducible evidence.
42
+ // Rejection occurs before a session claim or controller mutation.
50
43
  }
51
44
  });
@@ -5,6 +5,9 @@ import path from 'node:path';
5
5
  import {fileURLToPath} from 'node:url';
6
6
  import {spawn} from 'node:child_process';
7
7
  import {setTimeout as delay} from 'node:timers/promises';
8
+ import {installExactActivePointer} from './helpers/r175-active-pointer.mjs';
9
+
10
+ const packageRoot=path.resolve(fileURLToPath(new URL('..',import.meta.url)));
8
11
 
9
12
  test('R75 simultaneous owner-unverified recovery attempts both retain authority and marker', {timeout:30000},async()=>{
10
13
  const base='D:/Projects/deadbyte-mcp/testing/0.15.35/fixtures';await mkdir(base,{recursive:true});
@@ -41,6 +44,7 @@ switch($Command){
41
44
  }
42
45
  exit 0
43
46
  `);
47
+ await installExactActivePointer({stateRoot:root,packageRoot,launcherPath:path.join(root,'bin','DEADBYTE-MCP.ps1')});
44
48
  const children=[],outputs=[];
45
49
  try{
46
50
  for(let i=0;i<2;i++){
@@ -52,7 +56,7 @@ exit 0
52
56
  const commands=await readFile(trace,'utf8').catch(error=>error.code==='ENOENT'?'':Promise.reject(error));
53
57
  await writeFile(path.join(root,'observation.json'),JSON.stringify({commands,exits,outputs:outputs.map(o=>({stdout:o.stdout,stderr:o.stderr})),simulation:'concurrent owner-unverified stale marker'}),{flag:'wx'});
54
58
  assert.ok(exits.every(code=>code!==0));
55
- for(const output of outputs)assert.match(output.stderr,/ownership(?: identity)? unverified.*marker retained|already claimed \(startup\/recovery lock\)/i);
59
+ for(const output of outputs)assert.match(output.stderr,/R179_LEGACY_FOREGROUND_RECOVERY_REQUIRES_EXPLICIT_OWNER_COMMAND|already claimed \(startup\/recovery lock\)/i);
56
60
  assert.equal(commands,'','neither attempt may touch authority');
57
61
  assert.equal(JSON.parse(await readFile(markerPath,'utf8')).pid,2147483000);
58
62
  }finally{
@@ -5,12 +5,16 @@ import path from 'node:path';
5
5
  import {fileURLToPath} from 'node:url';
6
6
  import {spawn} from 'node:child_process';
7
7
  import {queryForegroundIdentity} from '../src/foreground-process-identity.mjs';
8
+ import {installExactActivePointer} from './helpers/r175-active-pointer.mjs';
9
+
10
+ const packageRoot=path.resolve(fileURLToPath(new URL('..',import.meta.url)));
8
11
 
9
12
  test('R58 inconsistent owner PID binding cannot trigger stale-session authority cleanup', {timeout:20000}, async()=>{
10
13
  const base='D:/Projects/deadbyte-mcp/testing/0.15.35/fixtures';await mkdir(base,{recursive:true});
11
14
  const root=await mkdtemp(path.join(base,'owner-binding-'));await mkdir(path.join(root,'runtime'));await mkdir(path.join(root,'bin'));
12
15
  const trace=path.join(root,'trace.txt');
13
16
  await writeFile(path.join(root,'bin','DEADBYTE-MCP.ps1'),`param([string]$Command)\n[IO.File]::AppendAllText('${trace.replaceAll("'","''")}',$Command+[Environment]::NewLine)\nexit 0\n`);
17
+ await installExactActivePointer({stateRoot:root,packageRoot,launcherPath:path.join(root,'bin','DEADBYTE-MCP.ps1')});
14
18
  const identity=queryForegroundIdentity(process.pid);
15
19
  const marker={schema:'deadbyte.remote-session.v2',owner_pid:process.pid,guardian_pid:0,session_nonce:'c'.repeat(64),owner_identity:{...identity,pid:process.pid+1}};
16
20
  const markerPath=path.join(root,'runtime','remote-session.json');const bytes=JSON.stringify(marker);await writeFile(markerPath,bytes);
@@ -18,7 +22,7 @@ test('R58 inconsistent owner PID binding cannot trigger stale-session authority
18
22
  let stderr='';child.stderr.on('data',b=>{stderr+=b;});child.stdout.resume();
19
23
  const code=await new Promise(resolve=>child.once('close',resolve));
20
24
  await writeFile(path.join(root,'observation.json'),JSON.stringify({code,stderr}),{flag:'wx'});
21
- assert.notEqual(code,0);assert.match(stderr,/ownership identity|binding/i);
25
+ assert.notEqual(code,0);assert.match(stderr,/R179_LEGACY_FOREGROUND_RECOVERY_REQUIRES_EXPLICIT_OWNER_COMMAND/i);
22
26
  assert.equal(await readFile(trace,'utf8').catch(e=>e.code==='ENOENT'?'':Promise.reject(e)),'','inconsistent marker must cause no controller actions');
23
27
  assert.equal(await readFile(markerPath,'utf8'),bytes,'retain ambiguous ownership evidence');
24
28
  });
@@ -29,6 +33,7 @@ test('R58 live unrelated PID is not accepted as a remote foreground owner', {tim
29
33
  await mkdir(path.join(root,'runtime'));await mkdir(path.join(root,'bin'));
30
34
  const trace=path.join(root,'trace.txt');
31
35
  await writeFile(path.join(root,'bin','DEADBYTE-MCP.ps1'),`param([string]$Command)\n[IO.File]::AppendAllText('${trace.replaceAll("'","''")}',$Command+[Environment]::NewLine)\nexit 0\n`);
36
+ await installExactActivePointer({stateRoot:root,packageRoot,launcherPath:path.join(root,'bin','DEADBYTE-MCP.ps1')});
32
37
  // The test runner is alive but neither a remote CLI nor a guardian. This
33
38
  // represents a stale numeric PID now occupied by an unrelated process.
34
39
  const marker={schema:'deadbyte.remote-session.v2',owner_pid:process.pid,guardian_pid:process.pid,session_nonce:'a'.repeat(64),started_at_utc:'2000-01-01T00:00:00.000Z'};
@@ -41,7 +46,7 @@ test('R58 live unrelated PID is not accepted as a remote foreground owner', {tim
41
46
  // Fail-closed ambiguity is acceptable; do not kill the unrelated process or
42
47
  // mutate authority based on unverified ownership. Automatic convergence still
43
48
  // requires separately observed exact owner/guardian identity evidence.
44
- assert.match(stderr,/identity|ownership|ambiguous|unverified/i);
49
+ assert.match(stderr,/R179_LEGACY_FOREGROUND_RECOVERY_REQUIRES_EXPLICIT_OWNER_COMMAND/i);
45
50
  assert.deepEqual(JSON.parse(await readFile(path.join(root,'runtime','remote-session.json'),'utf8')),marker);
46
51
  assert.notEqual(code,0);
47
52
  });
@@ -52,6 +57,7 @@ test('R75 bound stale birth identity retains marker without touching a reused PI
52
57
  await mkdir(path.join(root,'runtime'));await mkdir(path.join(root,'bin'));
53
58
  const trace=path.join(root,'trace.txt');
54
59
  await writeFile(path.join(root,'bin','DEADBYTE-MCP.ps1'),`param([string]$Command,[switch]$Json)\n[IO.File]::AppendAllText('${trace.replaceAll("'","''")}',$Command+[Environment]::NewLine)\nif ($Command -eq 'status' -and $Json) { $off=@{configured=$true;armed=$false;reason='disarmed'}; @{schema='deadbyte.controller-observation.v1';state='OFFLINE';authority=@{process=$off;coding=$off;autonomous=$off;desktop=$off;host=$off}} | ConvertTo-Json -Depth 5 -Compress }\nexit 0\n`);
60
+ await installExactActivePointer({stateRoot:root,packageRoot,launcherPath:path.join(root,'bin','DEADBYTE-MCP.ps1')});
55
61
  const current=queryForegroundIdentity(process.pid);
56
62
  const stale={...current,creation_utc:'2000-01-01T00:00:00.000Z'};
57
63
  const marker={schema:'deadbyte.remote-session.v2',owner_pid:process.pid,guardian_pid:process.pid,session_nonce:'b'.repeat(64),owner_identity:stale,guardian_identity:stale};
@@ -62,7 +68,7 @@ test('R75 bound stale birth identity retains marker without touching a reused PI
62
68
  const code=await new Promise(resolve=>child.once('close',resolve));
63
69
  await writeFile(path.join(root,'observation.json'),JSON.stringify({code,stdout,stderr,simulation:'birth identity drift at a live unrelated numeric PID'}),{flag:'wx'});
64
70
  assert.notEqual(code,0);
65
- assert.match(stderr,/owner identity mismatch; marker retained/i);
71
+ assert.match(stderr,/R179_LEGACY_FOREGROUND_RECOVERY_REQUIRES_EXPLICIT_OWNER_COMMAND/i);
66
72
  assert.equal(await readFile(trace,'utf8').catch(error=>error.code==='ENOENT'?'':Promise.reject(error)),'','identity drift must cause no authority commands');
67
73
  assert.deepEqual(JSON.parse(await readFile(markerPath,'utf8')),marker);
68
74
  assert.equal(foregroundAlive(),true,'unrelated live process must not be killed');
@@ -83,7 +83,7 @@ test('R72 compact baseline permits only R64 read offset and authorized coding-on
83
83
  assert.deepEqual(diff.added,[]);
84
84
  assert.deepEqual(diff.removed,[]);
85
85
  const packageVersion=JSON.parse(await readFile(path.join(repoRoot,'package.json'),'utf8')).version;
86
- if(['0.15.41','0.15.42','0.15.43','0.15.44','0.15.45','0.15.46','0.15.47','0.15.48','0.15.49','0.15.50','0.15.51','0.15.52','0.15.53','0.15.54','0.15.55','0.15.56','0.15.57','0.15.58','0.15.59','0.15.60','0.15.61','0.15.62','0.15.63','0.15.64','0.15.65','0.15.66','0.15.67','0.15.68'].includes(packageVersion)){
86
+ if(['0.15.41','0.15.42','0.15.43','0.15.44','0.15.45','0.15.46','0.15.47','0.15.48','0.15.49','0.15.50','0.15.51','0.15.52','0.15.53','0.15.54','0.15.55','0.15.56','0.15.57','0.15.58','0.15.59','0.15.60','0.15.61','0.15.62','0.15.63','0.15.64','0.15.65','0.15.66','0.15.67','0.15.68','0.15.69','0.15.70'].includes(packageVersion)){
87
87
  assert.deepEqual([...diff.schema_changed].sort(),['machine_fs','native_diff_preview']);
88
88
  assert.equal(diff.unchanged.length,41);
89
89
  const prior=baseline.tools.find(tool=>tool.name==='machine_fs');
@@ -73,8 +73,8 @@ test('R40 CLI exposes explicit verbose mode while keeping default live mode comp
73
73
  const src=await readFile(new URL('../src/deadbyte-cli.mjs',import.meta.url),'utf8');
74
74
  assert.match(src,/readLiveRootPaths\(ctx\.root\)/);
75
75
  assert.match(src,/createLiveTimelineTracker\(\{verbose,displayMode,rootPaths,trace,\.\.\.defaultForegroundTimelineOptions\(\{verbose\}\)\}\)/);
76
- assert.deepEqual(live.defaultForegroundTimelineOptions({verbose:false}),{commanderStyle:true,foldHeartbeats:false});
77
- assert.deepEqual(live.defaultForegroundTimelineOptions({verbose:true}),{commanderStyle:false,foldHeartbeats:false});
76
+ assert.deepEqual(live.defaultForegroundTimelineOptions({verbose:false}),{commanderStyle:false,foldHeartbeats:true,prettyStyle:true});
77
+ assert.deepEqual(live.defaultForegroundTimelineOptions({verbose:true}),{commanderStyle:false,foldHeartbeats:false,prettyStyle:false});
78
78
  assert.match(src,/createLiveTimelineFilter\(filterExpression\)/);
79
79
  assert.match(src,/createLiveTimelineWriter\(\{stream:process\.stdout,displayMode,rewrite:false\}\)/);
80
80
  assert.match(src,/arg==='--verbose'\|\|arg==='-v'/);
@@ -9,7 +9,7 @@ test('R36 lifecycle formatter exposes device/connect/ready/cleanup states with b
9
9
  assert.equal(formatRemoteLifecycle('session_new'),'[SESSION] New foreground session');
10
10
  assert.equal(formatRemoteLifecycle('session_restored'),'♻️ [SESSION] Previous result stream replayed');
11
11
  assert.equal(formatRemoteLifecycle('ready'),'✅ [READY] DEADBYTE MCP online');
12
- assert.equal(formatRemoteLifecycle('cleanup'),'🔐 [AUTH] Revoking session authority...');
12
+ assert.equal(formatRemoteLifecycle('cleanup'),'[SESSION] Closing observation session...');
13
13
  assert.throws(()=>formatRemoteLifecycle('invented'),/unknown remote lifecycle/i);
14
14
  });
15
15