deadbyte-mcp 0.15.43 → 0.15.44

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 (129) hide show
  1. package/MANIFEST.SHA256 +128 -67
  2. package/PROVENANCE.json +23 -12
  3. package/README.txt +18 -18
  4. package/RELEASE.json +28 -14
  5. package/SBOM.cdx.json +14 -2
  6. package/TEST-MATRIX.json +41 -10
  7. package/bin/appcontainer-stage.obj +0 -0
  8. package/bin/bootstrap-advapi32.obj +0 -0
  9. package/bin/bootstrap-exitcode.obj +0 -0
  10. package/bin/bootstrap-kernel32.obj +0 -0
  11. package/bin/child-control-probe.obj +0 -0
  12. package/bin/child-control-stage.obj +0 -0
  13. package/bin/contained-reverse-worker.obj +0 -0
  14. package/bin/contained-transform-worker.obj +0 -0
  15. package/bin/containment-probe.obj +0 -0
  16. package/bin/deadbyte-contain.obj +0 -0
  17. package/bin/deadbyte-desktop-input-r40.obj +0 -0
  18. package/bin/deadbyte-desktop-observer-r40.obj +0 -0
  19. package/bin/deadbyte-desktop-uia-r40.obj +0 -0
  20. package/bin/deadbyte-exec.obj +0 -0
  21. package/bin/deadbyte-hardware-observer-r45.obj +0 -0
  22. package/bin/deadbyte-native-inspect-r67.exe +0 -0
  23. package/bin/deadbyte-native-inspect-r67.obj +0 -0
  24. package/bin/deadbyte-process-host.obj +0 -0
  25. package/bin/deadbyte-tunnel-host.obj +0 -0
  26. package/bridge/package.json +1 -1
  27. package/controller/deadbyte-desktop-policy.json +5 -5
  28. package/controller/deadbyte-process-policy.json +4 -4
  29. package/docs/R67-ALPHA-REFERENCE-DIFFERENTIAL.md +42 -0
  30. package/docs/R67-ALPHA-STATIC-COMPARISON.json +205 -0
  31. package/docs/R67-CANDIDATE-PARENT.json +10 -0
  32. package/docs/R67-EVIDENCE-RECONCILE-CLOSEOUT-20261011.md +64 -0
  33. package/docs/R67-FULL-REGRESSION-UNKNOWN.json +27 -0
  34. package/docs/R67-NATIVE-INSPECT-BINARY.json +11 -0
  35. package/docs/R67-NPM-EXDEV-PRESERVATION-20261011.md +39 -0
  36. package/docs/R67-OBSERVATION-TRUTH.md +82 -0
  37. package/docs/R67-REFERENCE-GAP-MATRIX.json +57 -0
  38. package/docs/R67-REGRESSION-CLOSURE-20261011.md +24 -0
  39. package/native/deadbyte-native-inspect-r67.cpp +252 -0
  40. package/package.json +6 -3
  41. package/proof/CONTAINMENT-BUILD.txt +6 -6
  42. package/scripts/build-native-inspector-r67.ps1 +28 -0
  43. package/scripts/gate-windows-r67.ps1 +56 -0
  44. package/scripts/npm-preserve-artifact-r67.mjs +99 -0
  45. package/scripts/npm-release-r48.mjs +10 -2
  46. package/scripts/npm-release-readiness-r67.mjs +26 -0
  47. package/scripts/r67-acquire-alpha-reference.mjs +95 -0
  48. package/scripts/r67-audit-nonpass-source.mjs +39 -0
  49. package/scripts/r67-compare-desktop-alpha.mjs +48 -0
  50. package/scripts/r67-diagnose-failed-shard.mjs +23 -0
  51. package/scripts/r67-durable-batch.mjs +42 -0
  52. package/scripts/r67-focused-gate.mjs +58 -0
  53. package/scripts/r67-full-regression.mjs +28 -0
  54. package/scripts/r67-independent-observation-verify.mjs +36 -0
  55. package/scripts/r67-independent-runtime-observation-verify.mjs +63 -0
  56. package/scripts/r67-nonpass-adjudicate.mjs +20 -0
  57. package/scripts/r67-publish-boundary-audit.mjs +32 -0
  58. package/scripts/r67-regression-reconcile.mjs +10 -0
  59. package/scripts/r67-regression-shard-run.mjs +70 -0
  60. package/scripts/r67-run-shard-batch.mjs +18 -0
  61. package/scripts/r67-stage-candidate.mjs +30 -0
  62. package/scripts/r67-verify-parent-ping.mjs +22 -0
  63. package/scripts/release-manifest.mjs +1 -0
  64. package/src/activation-continuity-engine-r65.mjs +2 -1
  65. package/src/autonomous-runtime.mjs +1 -1
  66. package/src/deadbyte-cli.mjs +33 -1
  67. package/src/machine-fs-smoke-client.mjs +13 -5
  68. package/src/machine-identity.mjs +59 -2
  69. package/src/machine-mcp-tools.mjs +8 -0
  70. package/src/mcp-server.mjs +11 -2
  71. package/src/native-inspector-r67.mjs +66 -0
  72. package/src/r67-diagnostic-export.mjs +118 -0
  73. package/src/r67-diagnostics-verify.mjs +82 -0
  74. package/src/r67-evidence-reconcile.mjs +128 -0
  75. package/src/r67-native-observation-authority.mjs +45 -0
  76. package/src/r67-observation-receipt.mjs +43 -0
  77. package/src/r67-regression-shards.mjs +176 -0
  78. package/src/remote-live-log.mjs +14 -10
  79. package/src/tool-audit.mjs +1 -1
  80. package/src/version.mjs +1 -1
  81. package/test/agent-runtime.test.mjs +1 -1
  82. package/test/autonomous-loop.test.mjs +11 -6
  83. package/test/autonomous-runtime.test.mjs +1 -1
  84. package/test/coding-plane.test.mjs +1 -1
  85. package/test/full-max-catalog-r46.test.mjs +1 -1
  86. package/test/output-schema.test.mjs +1 -1
  87. package/test/r51-live-log-polling.test.mjs +12 -0
  88. package/test/r53-release-contract.test.mjs +1 -1
  89. package/test/r54-release-contract.test.mjs +1 -1
  90. package/test/r55-release-contract.test.mjs +1 -1
  91. package/test/r56-release-contract.test.mjs +1 -1
  92. package/test/r57-release-contract.test.mjs +1 -1
  93. package/test/r58-bundled-bridge.test.mjs +1 -1
  94. package/test/r58-release-contract.test.mjs +2 -2
  95. package/test/r58-windows-gate.test.mjs +1 -1
  96. package/test/r59-tool-surface-compat.test.mjs +1 -1
  97. package/test/r60-release-contract.test.mjs +3 -3
  98. package/test/r61-release-contract.test.mjs +1 -1
  99. package/test/r62-release-contract.test.mjs +1 -1
  100. package/test/r63-release-contract.test.mjs +1 -1
  101. package/test/r64-release-contract.test.mjs +1 -1
  102. package/test/r65-epoch-native-activation-integration.test.mjs +4 -4
  103. package/test/r65-release-contract.test.mjs +1 -1
  104. package/test/r66-release-contract.test.mjs +2 -1
  105. package/test/r67-cli-integration.test.mjs +41 -0
  106. package/test/r67-diagnostics-cli.test.mjs +60 -0
  107. package/test/r67-diagnostics-verify.test.mjs +49 -0
  108. package/test/r67-diagnostics-zip-oracle.test.mjs +38 -0
  109. package/test/r67-diagnostics.test.mjs +60 -0
  110. package/test/r67-evidence-reconcile.test.mjs +186 -0
  111. package/test/r67-isolated-slot-rollback-restore.test.mjs +62 -0
  112. package/test/r67-live-log-secret-canary.test.mjs +44 -0
  113. package/test/r67-machine-fs-smoke-epoch.test.mjs +22 -0
  114. package/test/r67-native-inspector.test.mjs +131 -0
  115. package/test/r67-native-observation-authority.test.mjs +49 -0
  116. package/test/r67-npm-cross-volume.test.mjs +74 -0
  117. package/test/r67-npm-publish-gate.test.mjs +38 -0
  118. package/test/r67-regression-shards.test.mjs +184 -0
  119. package/test/r67-release-contract.test.mjs +44 -0
  120. package/test/r67-runtime-observation-binding.test.mjs +86 -0
  121. package/test/r67-server-observation-wiring.test.mjs +12 -0
  122. package/test/r67-signed-receipt.test.mjs +66 -0
  123. package/test/r67-skip-rationale.test.mjs +31 -0
  124. package/test/r67-tool-annotations.test.mjs +21 -0
  125. package/test/r67-windows-gate-argv.test.mjs +13 -0
  126. package/test/release-version.test.mjs +1 -1
  127. package/test/remote-live-log-r36.test.mjs +10 -0
  128. package/test/remote-live-log-r37.test.mjs +29 -0
  129. package/test/semantic-multilang.test.mjs +1 -1
@@ -0,0 +1,66 @@
1
+ import test from 'node:test';
2
+ import assert from 'node:assert/strict';
3
+ import {generateKeyPairSync,createHash} from 'node:crypto';
4
+ import {signR67Observation} from '../src/r67-observation-receipt.mjs';
5
+ import {independentlyVerifyR67Receipt} from '../scripts/r67-independent-observation-verify.mjs';
6
+ import {runNativeInspect} from '../src/native-inspector-r67.mjs';
7
+
8
+ const sha=s=>createHash('sha256').update(s).digest('hex');
9
+ const pair=generateKeyPairSync('ed25519');
10
+ const trustedPublicSpkiBase64=pair.publicKey.export({type:'spki',format:'der'}).toString('base64');
11
+ const other=generateKeyPairSync('ed25519');
12
+ const otherSpki=other.publicKey.export({type:'spki',format:'der'}).toString('base64');
13
+ const requestSha256=sha(Buffer.from(JSON.stringify(['process','4']),'utf8'));
14
+ const executableSha256='c814771dddac8bfb184250b868ddb6ab9f5e0e43613c04c3ed8abe09324ec4db';
15
+
16
+ test('R67 signs a real Windows native observation and independently verifies exact result bytes',async()=>{
17
+ const result=await runNativeInspect(['process','4']);
18
+ assert.equal(result.status,'ok');
19
+ const receipt=signR67Observation({privateKey:pair.privateKey,publicKey:pair.publicKey,operation:'process',requestSha256,executableSha256,result});
20
+ const verified=independentlyVerifyR67Receipt({receipt,result,trustedPublicSpkiBase64,requestArgs:['process','4'],expectedExecutableSha256:executableSha256});
21
+ assert.equal(verified.ok,true,verified.reason);
22
+ assert.equal(verified.signature_valid,true);
23
+ assert.equal(verified.signer_key_id,sha(Buffer.from(trustedPublicSpkiBase64,'base64')));
24
+ });
25
+
26
+ test('R67 independent verifier rejects changed result, changed receipt, alternate signer and malformed encoding',async()=>{
27
+ const result=await runNativeInspect(['process','4']);
28
+ const receipt=signR67Observation({privateKey:pair.privateKey,publicKey:pair.publicKey,operation:'process',requestSha256,executableSha256,result});
29
+ const changedResult=structuredClone(result);
30
+ changedResult.data.processes[0].name+='INJECTED';
31
+ assert.equal(independentlyVerifyR67Receipt({receipt,result:changedResult,trustedPublicSpkiBase64,requestArgs:['process','4'],expectedExecutableSha256:executableSha256}).ok,false);
32
+ const changedReceipt={...receipt,executable_sha256:sha(Buffer.from('ATTACK'))};
33
+ assert.equal(independentlyVerifyR67Receipt({receipt:changedReceipt,result,trustedPublicSpkiBase64,requestArgs:['process','4'],expectedExecutableSha256:executableSha256}).ok,false);
34
+ assert.equal(independentlyVerifyR67Receipt({receipt,result,trustedPublicSpkiBase64:otherSpki,requestArgs:['process','4'],expectedExecutableSha256:executableSha256}).ok,false);
35
+ const forged=signR67Observation({privateKey:other.privateKey,publicKey:other.publicKey,operation:'process',requestSha256,executableSha256,result});
36
+ assert.equal(independentlyVerifyR67Receipt({receipt:forged,result,trustedPublicSpkiBase64,requestArgs:['process','4'],expectedExecutableSha256:executableSha256}).ok,false);
37
+ const badSig={...receipt,attestation:{...receipt.attestation,signature_base64:'AAA='}};
38
+ assert.equal(independentlyVerifyR67Receipt({receipt:badSig,result,trustedPublicSpkiBase64,requestArgs:['process','4'],expectedExecutableSha256:executableSha256}).ok,false);
39
+ });
40
+
41
+ test('R67 signing fails closed without trusted caller signer or on mismatched public/private key and corrupt data SHA',async()=>{
42
+ const result=await runNativeInspect(['process','4']);
43
+ assert.throws(()=>signR67Observation({operation:'process',requestSha256,executableSha256,result}),/trusted runtime/);
44
+ assert.throws(()=>signR67Observation({privateKey:pair.privateKey,publicKey:other.publicKey,operation:'process',requestSha256,executableSha256,result}),/mismatch/);
45
+ const altered={...result,data_sha256:sha(Buffer.from('forged'))};
46
+ assert.throws(()=>signR67Observation({privateKey:pair.privateKey,publicKey:pair.publicKey,operation:'process',requestSha256,executableSha256,result:altered}),/digest invalid/);
47
+ });
48
+
49
+ test('R67 trusted caller signing is integrated into actual native process inspection, not implicit CLI authority',async()=>{
50
+ const {result,receipt}=await runNativeInspect(['process','4'],{trustedSigner:{privateKey:pair.privateKey,publicKey:pair.publicKey}});
51
+ assert.equal(result.status,'ok');
52
+ assert.equal(receipt.operation,'process');
53
+ const check=independentlyVerifyR67Receipt({receipt,result,trustedPublicSpkiBase64,requestArgs:['process','4'],expectedExecutableSha256:executableSha256});
54
+ assert.equal(check.ok,true,check.reason);
55
+ const untrusted=await runNativeInspect(['process','4']);
56
+ assert.equal(untrusted.status,'ok');
57
+ assert.equal(Object.hasOwn(untrusted,'attestation'),false);
58
+ });
59
+
60
+ test('R67 independent verifier requires exact request argv and trusted executable pin',async()=>{
61
+ const result=await runNativeInspect(['process','4']);
62
+ const receipt=signR67Observation({privateKey:pair.privateKey,publicKey:pair.publicKey,operation:'process',requestSha256,executableSha256,result});
63
+ assert.equal(independentlyVerifyR67Receipt({receipt,result,trustedPublicSpkiBase64,requestArgs:['process','8'],expectedExecutableSha256:executableSha256}).ok,false);
64
+ assert.equal(independentlyVerifyR67Receipt({receipt,result,trustedPublicSpkiBase64,requestArgs:['process','4'],expectedExecutableSha256:sha(Buffer.from('WRONG EXE'))}).ok,false);
65
+ assert.equal(independentlyVerifyR67Receipt({receipt,result,trustedPublicSpkiBase64}).ok,false);
66
+ });
@@ -0,0 +1,31 @@
1
+ import test from 'node:test';
2
+ import assert from 'node:assert/strict';
3
+ import {spawnSync} from 'node:child_process';
4
+ import {fileURLToPath} from 'node:url';
5
+ import path from 'node:path';
6
+ import {parseR67NonpassCases,parseR67TapSummary} from '../src/r67-regression-shards.mjs';
7
+ const root=path.dirname(path.dirname(fileURLToPath(import.meta.url)));
8
+ const historical=[
9
+ 'test/r53-release-contract.test.mjs','test/r54-release-contract.test.mjs','test/r55-release-contract.test.mjs',
10
+ 'test/r56-release-contract.test.mjs','test/r57-release-contract.test.mjs','test/r58-release-contract.test.mjs',
11
+ 'test/r58-windows-gate.test.mjs','test/r58-bundled-bridge.test.mjs','test/r60-release-contract.test.mjs',
12
+ 'test/r61-release-contract.test.mjs','test/r62-release-contract.test.mjs','test/r63-release-contract.test.mjs',
13
+ 'test/r64-release-contract.test.mjs','test/r65-release-contract.test.mjs','test/semantic-multilang.test.mjs'
14
+ ];
15
+ test('R67 real TAP from historical release and R35 fixture skips has individual source-derived reasons',()=>{
16
+ const r=spawnSync(process.execPath,['--test','--test-reporter=tap','--test-concurrency=2',...historical],{
17
+ cwd:root,encoding:'utf8',windowsHide:true,shell:false,timeout:90000,maxBuffer:16*1024*1024,
18
+ env:Object.fromEntries(Object.entries(process.env).filter(([key])=>key!=='NODE_TEST_CONTEXT'))
19
+ });
20
+ assert.equal(r.error,undefined,r.error?.message);
21
+ assert.equal(r.status,0,'selected regression exit '+r.status+' output tail '+r.stdout?.slice(-1500));
22
+ const tap=parseR67TapSummary(r.stdout);
23
+ assert.ok(tap,'valid full TAP totals; stdout-tail='+String(r.stdout).slice(-1600)+'; stderr-tail='+String(r.stderr).slice(-500));
24
+ assert.equal(tap.fail,0);
25
+ const ledger=parseR67NonpassCases(r.stdout);
26
+ assert.ok(ledger,'nonpass parser output');
27
+ assert.ok(ledger.expected_nonpass>0,'must exercise real skip path');
28
+ assert.equal(ledger.fully_classified,true,JSON.stringify(ledger.cases.filter(x=>!x.reason)));
29
+ assert.equal(ledger.cases.length,ledger.expected_nonpass);
30
+ for(const c of ledger.cases)assert.match(c.reason,/Historical|historical|fixture|R35|R\d+/);
31
+ });
@@ -0,0 +1,21 @@
1
+ import test from 'node:test';
2
+ import assert from 'node:assert/strict';
3
+ import {registerMachineTools} from '../src/machine-mcp-tools.mjs';
4
+ test('R67 MCP annotations classify pure observation distinctly from machine revoke, without granting authority',async()=>{
5
+ const catalog=new Map();
6
+ const server={registerTool:(name,config,fn)=>catalog.set(name,{config,fn})};
7
+ let granted=false;
8
+ const runtime={list:async()=>({status:'ok',machines:[]}),status:async()=>({status:'ok'}),ping:async()=>({status:'ok'}),revoke:async()=>({status:'revoked'})};
9
+ registerMachineTools(server,{runtime,assertRevokeAuthority:async()=>{if(!granted)throw new Error('LOCAL_DENY');}});
10
+ for(const name of ['machine_list','machine_status','machine_ping']){
11
+ const hint=catalog.get(name).config.annotations;
12
+ assert.equal(hint.readOnlyHint,true,name);
13
+ assert.equal(hint.destructiveHint,false,name);
14
+ assert.equal(hint.openWorldHint,false,name);
15
+ }
16
+ const rev=catalog.get('machine_revoke');
17
+ assert.deepEqual(rev.config.annotations,{readOnlyHint:false,destructiveHint:true,idempotentHint:false,openWorldHint:false});
18
+ const denial=await rev.fn({reason:'check'});
19
+ assert.ok(denial.isError||denial.structuredContent?.status==='denied','annotations cannot grant revocation');
20
+ assert.equal(granted,false);
21
+ });
@@ -0,0 +1,13 @@
1
+ import test from 'node:test';
2
+ import assert from 'node:assert/strict';
3
+ import {readFile} from 'node:fs/promises';
4
+ import path from 'node:path';
5
+ import {fileURLToPath} from 'node:url';
6
+ const root=path.dirname(path.dirname(fileURLToPath(import.meta.url)));
7
+
8
+ test('R67 Windows release gate must not shadow automatic PowerShell $Args when passing node script argv',async()=>{
9
+ const source=await readFile(path.join(root,'scripts','gate-windows-r67.ps1'),'utf8');
10
+ assert.doesNotMatch(source,/\[string\[\]\]\$Args\b/i,'PowerShell automatic $Args silently discards function parameters');
11
+ assert.match(source,/\[string\[\]\]\$NodeArgs\b/);
12
+ assert.match(source,/\& node @NodeArgs/);
13
+ });
@@ -10,7 +10,7 @@ const PARENT_RELEASE='v0.15.2-0fb46542f6a4345f';
10
10
  const PARENT_MANIFEST='0fb46542f6a4345f1c527565d06bf6e9b8e283260e47bf684fb70a60308fcf34';
11
11
  const PARENT_GENERATION='00cde7bf540cc2804d679a39040d5b07193c5f8a0d49cac3b882402f8f4e6ad8';
12
12
 
13
- test.skip('R46 historical release contract is frozen at 0.15.15 and V0.15.2 parent identity',async()=>{
13
+ test('R46 historical release contract is frozen at 0.15.15 and V0.15.2 parent identity',{skip:'Historical R46 release-version contract requires 0.15.15; current successor differs'},async()=>{
14
14
  const pkg=JSON.parse(await text('package.json'));
15
15
  const release=JSON.parse(await text('RELEASE.json'));
16
16
  const [version,controller,gate,readme,architecture,controllerReadme,deferred,parity,closure,closureVerify,windowsGate,processPolicy]=await Promise.all([
@@ -35,3 +35,13 @@ test('compact live activity shows failed result duration',()=>{
35
35
  test('compact live activity ignores unrelated audit events',()=>{
36
36
  assert.equal(formatCompactAuditEvent({event_type:'checkpoint',tool:'coding_tree'}),null);
37
37
  });
38
+
39
+ test('R67 live logs must reject terminal control injection and path escape codes',()=>{
40
+ const attacker=String.fromCharCode(27)+'[2J'+String.fromCharCode(27)+'[31mINJECTED';
41
+ const injectedTool=formatCompactAuditEvent({event_type:'tool.request',tool:'coding_file_read'+attacker,args:{root_id:'projects',path:'src/test.txt'}});
42
+ assert.equal(injectedTool,null,'tool names carrying control codes must never render');
43
+ const injectedPath=formatCompactAuditEvent({event_type:'tool.request',tool:'coding_file_read',args:{root_id:'projects',path:'src/'+attacker}});
44
+ assert.ok(injectedPath!==null);
45
+ assert.equal(injectedPath.includes(String.fromCharCode(27)),false,'terminal ESC must not be included in line');
46
+ assert.equal(injectedPath.includes('INJECTED'),false,'invalid path should be rejected rather than partially displayed');
47
+ });
@@ -235,3 +235,32 @@ test('foreground human log keeps START and DONE separate and folds heartbeat noi
235
235
  assert.match(src,/createLiveTimelineTracker\(\{verbose,displayMode,rootPaths,trace,foldHeartbeats:!verbose\}\)/);
236
236
  assert.match(src,/createLiveTimelineWriter\(\{stream:process\.stdout,displayMode,rewrite:false\}\)/);
237
237
  });
238
+
239
+ test('R67 timeline normal/verbose views reject ANSI tool names, paths and error reasons',()=>{
240
+ const esc=String.fromCharCode(27);
241
+ const tracker=createLiveTimelineTracker({clock:()=>2000,verbose:false,rootPaths:{projects:'D:\\Projects\\safe'}});
242
+ const forged={event_type:'tool.request',tool:'coding_file_write'+esc+'[2J',request_id:'bad-tool-aaaa',args:{root_id:'projects',path:'readme.txt'}};
243
+ assert.equal(tracker.observe(forged,{sequence:1}),null);
244
+ assert.deepEqual(tracker.pending(),[]);
245
+ const started=tracker.observe({event_type:'tool.request',tool:'coding_file_write',request_id:'safe-abc12345',
246
+ args:{root_id:'projects',path:'file'+esc+'[31mPWNED',content:'safe'}},{sequence:2});
247
+ assert.ok(started);
248
+ assert.equal(started.includes(esc),false);
249
+ assert.equal(started.includes('PWNED'),false);
250
+ const end=tracker.observe({event_type:'tool.result',tool:'coding_file_write',request_id:'safe-abc12345',
251
+ outcome:{is_error:true,duration_ms:3,reason:'failed'+esc+'[2JINJECT'}},{sequence:3});
252
+ assert.ok(end);
253
+ assert.equal(end.includes(esc),false);
254
+ const raw=live.formatRemoteToolCall({...forged,event_type:'tool.request'});
255
+ assert.equal(raw,null);
256
+ });
257
+
258
+ test('R67 live timeline must not expose credential canaries from an error outcome reason',()=>{
259
+ const tracker=createLiveTimelineTracker({clock:()=>1200});
260
+ tracker.observe({event_type:'tool.request',tool:'coding_command_run',request_id:'secret-r67-aaa',args:{root_id:'projects',command_id:'node_script'}},{sequence:1});
261
+ const output=tracker.observe({event_type:'tool.result',tool:'coding_command_run',request_id:'secret-r67-aaa',
262
+ outcome:{is_error:true,duration_ms:20,reason:'HTTP 401 Authorization: Bearer SECRET_TOKEN_SHOULD_NOT_APPEAR'}},{sequence:2});
263
+ assert.ok(output);
264
+ assert.doesNotMatch(output,/SECRET_TOKEN_SHOULD_NOT_APPEAR|Bearer/i);
265
+ assert.match(output,/REDACTED|SENSITIVE/);
266
+ });
@@ -50,7 +50,7 @@ const cases=[
50
50
  ];
51
51
 
52
52
  for(const entry of cases){
53
- test(`R35 ${entry.id} semantic tracer proves outline definition references and receipt-backed replace`,{skip:!(semanticConfigPath&&semanticConfigSha256),timeout:120000},async()=>{
53
+ test(`R35 ${entry.id} semantic tracer proves outline definition references and receipt-backed replace`,{skip:!(semanticConfigPath&&semanticConfigSha256) ? 'R35 external semantic backend fixture requires DEADBYTE_R35_SEMANTIC_CONFIG and matching SHA256; fixture not configured' : false,timeout:120000},async()=>{
54
54
  const f=await makeFixture(entry.id);
55
55
  try{
56
56
  await f.client.connect(f.transport);