deadbyte-mcp 0.15.74 → 0.15.76

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 (109) hide show
  1. package/MANIFEST.SHA256 +108 -42
  2. package/PROVENANCE.json +20 -22
  3. package/README.txt +23 -18
  4. package/RELEASE.json +69 -18
  5. package/SBOM.cdx.json +2 -2
  6. package/TEST-MATRIX.json +32 -2
  7. package/bin/deadbyte-desktop-uia-r199-fixed.exe +0 -0
  8. package/bin/deadbyte-desktop-uia-r199-fixed.obj +0 -0
  9. package/bin/deadbyte-desktop-uia-r199.exe +0 -0
  10. package/bin/deadbyte-desktop-uia-r199.obj +0 -0
  11. package/bin/deadbyte-fs-read-r197-fixed.exe +0 -0
  12. package/bin/deadbyte-fs-read-r198.exe +0 -0
  13. package/bin/deadbyte-fs-read-r198.obj +0 -0
  14. package/bin/deadbyte-root-relative-probe-r197.exe +0 -0
  15. package/bridge/package.json +1 -1
  16. package/controller/deadbyte-process-policy.json +4 -4
  17. package/docs/R184-package-lock.json +2 -8
  18. package/native/deadbyte-desktop-uia-r199.cpp +345 -0
  19. package/native/deadbyte-fs-read-r197.cpp +174 -0
  20. package/native/deadbyte-fs-read-r198.cpp +181 -0
  21. package/native/deadbyte-root-relative-probe-r197.cpp +97 -0
  22. package/package.json +5 -2
  23. package/scripts/build-native-desktop-uia-r199-fixed.ps1 +28 -0
  24. package/scripts/build-native-desktop-uia-r199.ps1 +28 -0
  25. package/scripts/build-native-fs-read-r197-fixed.ps1 +28 -0
  26. package/scripts/build-native-fs-read-r197.ps1 +28 -0
  27. package/scripts/build-native-fs-read-r198.ps1 +28 -0
  28. package/scripts/build-native-root-probe-r197.ps1 +28 -0
  29. package/scripts/gate-windows-r195.ps1 +114 -0
  30. package/scripts/gate-windows-r206.ps1 +116 -0
  31. package/scripts/gate-windows-r207.ps1 +104 -0
  32. package/scripts/init-desktop-policy-r199.ps1 +85 -0
  33. package/scripts/npm-release-r48.mjs +27 -0
  34. package/scripts/npm-release-readiness-r195.mjs +72 -0
  35. package/scripts/npm-release-readiness-r206.mjs +76 -0
  36. package/scripts/npm-release-readiness-r207.mjs +76 -0
  37. package/scripts/r193-owner-request-live.mjs +8 -7
  38. package/scripts/r193-owner-uac-broker.mjs +6 -5
  39. package/scripts/r205-candidate-selection.mjs +22 -0
  40. package/src/candidate-selection-evidence-r206.mjs +71 -0
  41. package/src/candidate-selection-r205.mjs +96 -0
  42. package/src/desktop-postcondition-r196.mjs +30 -0
  43. package/src/desktop-runtime-r40.mjs +44 -7
  44. package/src/desktop-uia-identity-r199.mjs +51 -0
  45. package/src/desktop-uia-r40.mjs +9 -1
  46. package/src/machine-fs-handle-read-r196.mjs +43 -0
  47. package/src/machine-fs-native-read-r197.mjs +73 -0
  48. package/src/machine-fs-native-read-r198.mjs +76 -0
  49. package/src/machine-fs-read-worker.mjs +5 -25
  50. package/src/machine-fs.mjs +31 -16
  51. package/src/r195-canonical-owner-observation.mjs +15 -0
  52. package/src/r207-owner-enrollment-io.mjs +30 -0
  53. package/src/remote-destination-journal-r202.mjs +120 -0
  54. package/src/remote-device-r44.mjs +89 -14
  55. package/src/remote-payload-boundary-r201.mjs +71 -0
  56. package/src/remote-terminal-evidence-r200.mjs +60 -0
  57. package/src/version.mjs +1 -1
  58. package/test/desktop-runtime-r40.test.mjs +111 -0
  59. package/test/desktop-uia-r40.test.mjs +15 -0
  60. package/test/fixtures/r197-junction-swap-worker.mjs +21 -0
  61. package/test/fixtures/r203-journal-writer.mjs +6 -0
  62. package/test/process-policy-refresh.test.mjs +4 -4
  63. package/test/r130-release-security-contract.test.mjs +8 -8
  64. package/test/r132-release-identity.test.mjs +7 -7
  65. package/test/r153-prepublish-identity.test.mjs +1 -1
  66. package/test/r160-release-contract.test.mjs +3 -3
  67. package/test/r165-live-cli-skew.test.mjs +1 -1
  68. package/test/r167-release-contract.test.mjs +11 -11
  69. package/test/r168-release-contract.test.mjs +4 -4
  70. package/test/r172-release-contract.test.mjs +6 -6
  71. package/test/r173-release-contract.test.mjs +16 -16
  72. package/test/r179-release-contract.test.mjs +12 -12
  73. package/test/r190-release-contract.test.mjs +10 -10
  74. package/test/r191-release-contract.test.mjs +12 -12
  75. package/test/r192-release-identity.test.mjs +9 -9
  76. package/test/r193-release-identity.test.mjs +24 -22
  77. package/test/r193-release-readiness.test.mjs +2 -2
  78. package/test/r193-windows-gate.test.mjs +10 -10
  79. package/test/r194-release-identity.test.mjs +19 -15
  80. package/test/r194-release-readiness.test.mjs +3 -3
  81. package/test/r194-windows-gate.test.mjs +9 -8
  82. package/test/r195-canonical-owner-observation.test.mjs +32 -0
  83. package/test/r195-release-readiness.test.mjs +22 -0
  84. package/test/r196-desktop-postcondition.test.mjs +53 -0
  85. package/test/r196-handle-bound-read.test.mjs +99 -0
  86. package/test/r196-remote-outcome.test.mjs +94 -0
  87. package/test/r197-namespace-race.test.mjs +41 -0
  88. package/test/r197-native-dispatch-race.test.mjs +49 -0
  89. package/test/r197-native-file-read.test.mjs +60 -0
  90. package/test/r197-native-root-open.test.mjs +61 -0
  91. package/test/r198-root-identity.test.mjs +88 -0
  92. package/test/r199-native-uia-worker.test.mjs +28 -0
  93. package/test/r199-uia-identity.test.mjs +43 -0
  94. package/test/r200-remote-reconcile.test.mjs +106 -0
  95. package/test/r201-payload-boundary.test.mjs +78 -0
  96. package/test/r201-payload-fuzz.test.mjs +37 -0
  97. package/test/r201-tool-boundary-fuzz.test.mjs +120 -0
  98. package/test/r202-destination-journal.test.mjs +92 -0
  99. package/test/r203-atomic-journal.test.mjs +86 -0
  100. package/test/r205-candidate-selection-cli.test.mjs +31 -0
  101. package/test/r205-candidate-selection.test.mjs +83 -0
  102. package/test/r206-candidate-evidence.test.mjs +64 -0
  103. package/test/r206-release-contract.test.mjs +42 -0
  104. package/test/r206-release-readiness.test.mjs +23 -0
  105. package/test/r207-owner-enrollment-io.test.mjs +37 -0
  106. package/test/r207-release-contract.test.mjs +51 -0
  107. package/test/r207-release-readiness.test.mjs +23 -0
  108. package/test/r59-tool-surface-compat.test.mjs +1 -1
  109. package/test/r64-machine-fs-large-read.test.mjs +26 -12
@@ -0,0 +1,64 @@
1
+ import test from 'node:test';
2
+ import assert from 'node:assert/strict';
3
+ import { createHash } from 'node:crypto';
4
+ import { canonicalJson } from '../src/canonical-json.mjs';
5
+ import { verifyCandidateSelectionEvidenceR206 } from '../src/candidate-selection-evidence-r206.mjs';
6
+
7
+ const H=c=>c.repeat(64);
8
+ const fixture=H('a'),environment=H('b');
9
+ const sha=value=>createHash('sha256').update(Buffer.from(canonicalJson(value),'utf8')).digest('hex');
10
+ const samples={baseline:[100,100,100,100,100,100,100],candidate:[75,75,75,75,75,75,75]};
11
+ const input=()=>({
12
+ schema:'deadbyte.candidate-selection.r205.v1',fixture_sha256:fixture,environment_sha256:environment,
13
+ baseline:{artifact_sha256:H('1'),correctness:{status:'PASS',artifact_sha256:H('1'),fixture_sha256:fixture,receipt_sha256:H('2')},
14
+ benchmark:{artifact_sha256:H('1'),fixture_sha256:fixture,environment_sha256:environment,receipt_sha256:H('3'),samples_ms:samples.baseline}},
15
+ candidates:[{candidate_id:'candidate',artifact_sha256:H('4'),
16
+ correctness:{status:'PASS',artifact_sha256:H('4'),fixture_sha256:fixture,receipt_sha256:H('5')},
17
+ benchmark:{artifact_sha256:H('4'),fixture_sha256:fixture,environment_sha256:environment,receipt_sha256:H('6'),samples_ms:samples.candidate}}]
18
+ });
19
+ const receipts=()=>new Map([
20
+ [H('2'),{verified:true,receipt_sha256:H('2'),evidence_kind:'correctness',signer_role:'independent_correctness_oracle',artifact_sha256:H('1'),fixture_sha256:fixture,status:'PASS'}],
21
+ [H('3'),{verified:true,receipt_sha256:H('3'),evidence_kind:'benchmark',signer_role:'independent_benchmark_runner',artifact_sha256:H('1'),fixture_sha256:fixture,environment_sha256:environment,samples_sha256:sha(samples.baseline)}],
22
+ [H('5'),{verified:true,receipt_sha256:H('5'),evidence_kind:'correctness',signer_role:'independent_correctness_oracle',artifact_sha256:H('4'),fixture_sha256:fixture,status:'PASS'}],
23
+ [H('6'),{verified:true,receipt_sha256:H('6'),evidence_kind:'benchmark',signer_role:'independent_benchmark_runner',artifact_sha256:H('4'),fixture_sha256:fixture,environment_sha256:environment,samples_sha256:sha(samples.candidate)}]
24
+ ]);
25
+ const verifier=map=>async receiptSha=>map.get(receiptSha)??{verified:false,receipt_sha256:receiptSha};
26
+
27
+ test('R206 independently verifies every decision-bearing receipt but never authorizes release',async()=>{
28
+ const result=await verifyCandidateSelectionEvidenceR206(input(),{verifyReceipt:verifier(receipts())});
29
+ assert.equal(result.state,'PROVISIONAL_CANDIDATE');
30
+ assert.equal(result.evidence_verified,true);
31
+ assert.equal(result.verified_receipt_count,4);
32
+ assert.equal(result.release_authorized,false);
33
+ assert.match(result.evidence_binding_sha256,/^[0-9a-f]{64}$/);
34
+ });
35
+
36
+ test('R206 rejects a forged or unverifiable receipt',async()=>{
37
+ const map=receipts();map.set(H('5'),{...map.get(H('5')),verified:false});
38
+ await assert.rejects(()=>verifyCandidateSelectionEvidenceR206(input(),{verifyReceipt:verifier(map)}),/receipt verification failed/);
39
+ });
40
+
41
+ test('R206 rejects correctness status or artifact drift after grading',async()=>{
42
+ for(const change of [{status:'FAIL'},{artifact_sha256:H('7')}]){
43
+ const map=receipts();map.set(H('5'),{...map.get(H('5')),...change});
44
+ await assert.rejects(()=>verifyCandidateSelectionEvidenceR206(input(),{verifyReceipt:verifier(map)}),/correctness receipt binding mismatch/);
45
+ }
46
+ });
47
+
48
+ test('R206 rejects benchmark sample or environment drift',async()=>{
49
+ for(const change of [{samples_sha256:H('7')},{environment_sha256:H('7')}]){
50
+ const map=receipts();map.set(H('6'),{...map.get(H('6')),...change});
51
+ await assert.rejects(()=>verifyCandidateSelectionEvidenceR206(input(),{verifyReceipt:verifier(map)}),/benchmark receipt binding mismatch/);
52
+ }
53
+ });
54
+
55
+ test('R206 rejects proposer-controlled signer roles and receipt reuse',async()=>{
56
+ const proposer=receipts();proposer.set(H('5'),{...proposer.get(H('5')),signer_role:'candidate_proposer'});
57
+ await assert.rejects(()=>verifyCandidateSelectionEvidenceR206(input(),{verifyReceipt:verifier(proposer)}),/correctness receipt binding mismatch/);
58
+ const reused=input();reused.candidates[0].correctness.receipt_sha256=H('2');
59
+ await assert.rejects(()=>verifyCandidateSelectionEvidenceR206(reused,{verifyReceipt:verifier(receipts())}),/receipt reused/);
60
+ });
61
+
62
+ test('R206 refuses a missing verifier instead of trusting hash-shaped claims',async()=>{
63
+ await assert.rejects(()=>verifyCandidateSelectionEvidenceR206(input(),{}),/independent receipt verifier required/);
64
+ });
@@ -0,0 +1,42 @@
1
+ import test from 'node:test';
2
+ import assert from 'node:assert/strict';
3
+ import { readFile } from 'node:fs/promises';
4
+ import { fileURLToPath } from 'node:url';
5
+ import path from 'node:path';
6
+
7
+ const root=path.dirname(path.dirname(fileURLToPath(import.meta.url)));
8
+ const json=async name=>JSON.parse(await readFile(path.join(root,name),'utf8'));
9
+
10
+ test('R207 retains R206 evidence while binding R206 source separately from active 0.15.75 runtime',async()=>{
11
+ const [pkg,lock,bridge,release,provenance,matrix,sbom]=await Promise.all([
12
+ json('package.json'),json('docs/R184-package-lock.json'),json('bridge/package.json'),json('RELEASE.json'),
13
+ json('PROVENANCE.json'),json('TEST-MATRIX.json'),json('SBOM.cdx.json')
14
+ ]);
15
+ assert.equal(pkg.version,'0.15.76');
16
+ assert.equal(lock.version,'0.15.76');
17
+ assert.equal(lock.packages[''].version,'0.15.76');
18
+ assert.equal(bridge.version,'0.15.76');
19
+ assert.equal(release.version,'0.15.76');assert.equal(release.train,'R207');
20
+ assert.equal(release.source_predecessor.train,'R206');
21
+ assert.equal(release.source_predecessor.manifest_sha256,'9039fff265696091f9217ba4f19526c6c74a967f9ff48b75ac6e340b5f431248');
22
+ assert.equal(release.runtime_predecessor.release_id,'v0.15.75-9039fff265696091');
23
+ assert.equal(provenance.builder,'deadbyte-r207-owner-enrollment-fix');
24
+ assert.equal(provenance.source_predecessor_train,'R206');
25
+ assert.equal(provenance.source_predecessor_manifest_sha256,'9039fff265696091f9217ba4f19526c6c74a967f9ff48b75ac6e340b5f431248');
26
+ assert.equal(matrix.train,'R207');
27
+ assert.equal(sbom.serialNumber,'urn:cdx:deadbyte-mcp:r207:0.15.76');
28
+ });
29
+
30
+ test('R206 release contract keeps public surface stable and deferred work truthful',async()=>{
31
+ const release=await json('RELEASE.json');
32
+ assert.equal(release.capabilities.compact_tool_count,43);
33
+ assert.equal(release.capabilities.full_tool_count,117);
34
+ assert.equal(release.r206.public_tool_delta,0);
35
+ assert.equal(release.r206.authority_scope_delta,0);
36
+ assert.equal(release.r206.selector_release_authority,false);
37
+ assert.deepEqual(release.r206.deferred_unverified,[
38
+ 'REMOTE_TWO_MACHINE_E2E','NATIVE_UIA_APPLICATION_EFFECT','BOUNDED_CDP_HARNESS2','REA_WINDOWS_209_TOOL_PARITY'
39
+ ]);
40
+ assert.equal(release.r206.npm_publish,'NOT_RUN');
41
+ assert.equal(release.r206.production_activation,'NOT_RUN');
42
+ });
@@ -0,0 +1,23 @@
1
+ import test from 'node:test';
2
+ import assert from 'node:assert/strict';
3
+ import {assertR206NpmPublishReady,assertR206PackedIdentity} from '../scripts/npm-release-readiness-r206.mjs';
4
+
5
+ const release={
6
+ version:'0.15.75',train:'R206',
7
+ parent:{version:'0.15.74',release_id:'v0.15.74-e84bf4eed16e9443',
8
+ manifest_sha256:'e84bf4eed16e9443fbd0bf9f0f562e26b01db778647aefcc8b12fd7f48636b47',
9
+ generation_manifest_sha256:'598f896be9ce65e6b0c618e8c8f6eaa2a9d91617a76a146bf3deb385a56d19e7'},
10
+ source_predecessor:{train:'R205',manifest_sha256:'166eebde18d53a9ec46f738383c289733f9cd37918b3572b7515d1ab9b122c92'},
11
+ r206:{scope:'EVIDENCE_BOUND_CANDIDATE_SELECTION_RELEASE_CLOSURE',public_tool_delta:0,authority_scope_delta:0,selector_release_authority:false}
12
+ };
13
+
14
+ test('R206 publication refuses an absent exact-byte closure before pack or registry effects',async()=>{
15
+ await assert.rejects(assertR206NpmPublishReady(release,{root:'D:\\candidate'}),/PUBLISH_BLOCKED R206.*closure root absent/);
16
+ });
17
+
18
+ test('R206 packed identity refuses any artifact differing from the protected closure',()=>{
19
+ assert.throws(()=>assertR206PackedIdentity({
20
+ tarball_sha256:'a'.repeat(64),tarball_integrity:'sha512-expected',tarball_shasum:'b'.repeat(40),
21
+ observedSha256:'c'.repeat(64),observedIntegrity:'sha512-other',observedShasum:'d'.repeat(40)
22
+ }),/PUBLISH_BLOCKED R206.*repacked tarball/);
23
+ });
@@ -0,0 +1,37 @@
1
+ import test from 'node:test';
2
+ import assert from 'node:assert/strict';
3
+ import {mkdtemp,mkdir,readFile,rm,symlink,writeFile} from 'node:fs/promises';
4
+ import os from 'node:os';
5
+ import path from 'node:path';
6
+ import {ensureOwnerConsentDirectory,parseOwnerEnrollmentJson} from '../src/r207-owner-enrollment-io.mjs';
7
+
8
+ test('R207 parses Windows PowerShell UTF-8 BOM supervisor ledgers',()=>{
9
+ const value=parseOwnerEnrollmentJson(Buffer.concat([Buffer.from([0xef,0xbb,0xbf]),Buffer.from('{"schema":"deadbyte.supervisor.instance.v1","pid":17}')]),'supervisor ledger');
10
+ assert.equal(value.schema,'deadbyte.supervisor.instance.v1');
11
+ assert.equal(value.pid,17);
12
+ assert.throws(()=>parseOwnerEnrollmentJson(Buffer.from('\uFEFF[]'),'supervisor ledger'),/must be an object/);
13
+ });
14
+
15
+ test('R207 initializes the bounded owner-consent state directory before no-clobber request creation',async()=>{
16
+ const base=await mkdtemp(path.join(os.tmpdir(),'deadbyte-r207-owner-'));
17
+ try{
18
+ const runtime=path.join(base,'runtime');await mkdir(runtime);
19
+ const consent=await ensureOwnerConsentDirectory(runtime);
20
+ assert.equal(consent.toLowerCase(),path.join(runtime,'owner-consent').toLowerCase());
21
+ const request=path.join(consent,'request.json');
22
+ await writeFile(request,'{}\n',{flag:'wx'});
23
+ await assert.rejects(writeFile(request,'{}\n',{flag:'wx'}),error=>error?.code==='EEXIST');
24
+ assert.equal(await readFile(request,'utf8'),'{}\n');
25
+ }finally{await rm(base,{recursive:true,force:true});}
26
+ });
27
+
28
+ test('R207 rejects an owner-consent junction or symlink escape',async t=>{
29
+ const base=await mkdtemp(path.join(os.tmpdir(),'deadbyte-r207-owner-link-'));
30
+ try{
31
+ const runtime=path.join(base,'runtime'),outside=path.join(base,'outside');
32
+ await mkdir(runtime);await mkdir(outside);
33
+ try{await symlink(outside,path.join(runtime,'owner-consent'),process.platform==='win32'?'junction':'dir');}
34
+ catch(error){if(error?.code==='EPERM'){t.skip('Windows symlink privilege unavailable');return;}throw error;}
35
+ await assert.rejects(ensureOwnerConsentDirectory(runtime),/real directory|escapes/);
36
+ }finally{await rm(base,{recursive:true,force:true});}
37
+ });
@@ -0,0 +1,51 @@
1
+ import test from 'node:test';
2
+ import assert from 'node:assert/strict';
3
+ import { readFile } from 'node:fs/promises';
4
+
5
+ const root = new URL('../', import.meta.url);
6
+ const json = async name => JSON.parse(await readFile(new URL(name, root), 'utf8'));
7
+
8
+ test('R207 identity binds exact R206 source and active 0.15.75 runtime predecessors', async () => {
9
+ const [pkg, lock, bridge, release, provenance, matrix, sbom, versionSource] = await Promise.all([
10
+ json('package.json'), json('docs/R184-package-lock.json'), json('bridge/package.json'), json('RELEASE.json'),
11
+ json('PROVENANCE.json'), json('TEST-MATRIX.json'), json('SBOM.cdx.json'),
12
+ readFile(new URL('src/version.mjs', root), 'utf8')
13
+ ]);
14
+ for (const value of [pkg.version, lock.version, lock.packages[''].version, bridge.version,
15
+ release.version, provenance.version, matrix.version, sbom.metadata.component.version]) {
16
+ assert.equal(value, '0.15.76');
17
+ }
18
+ assert.match(versionSource, /DEADBYTE_VERSION = '0\.15\.76'/);
19
+ assert.equal(release.train, 'R207');
20
+ assert.equal(matrix.train, 'R207');
21
+ assert.equal(sbom.serialNumber, 'urn:cdx:deadbyte-mcp:r207:0.15.76');
22
+ assert.deepEqual(release.source_predecessor, {
23
+ version: '0.15.75',
24
+ train: 'R206',
25
+ source_path: 'D:\\Projects\\deadbyte-mcp\\development\\deadbyte-mcp-0.15.75-r206-release-closure',
26
+ release_status: 'RELEASED',
27
+ manifest_status: 'R206_MANIFEST_VERIFIED',
28
+ manifest_sha256: '9039fff265696091f9217ba4f19526c6c74a967f9ff48b75ac6e340b5f431248',
29
+ tarball_sha256: 'c2ebb079e7725f3a2f2c7d8f98ef42059e94e51f402d99c1825f46e020237ee9'
30
+ });
31
+ assert.deepEqual(release.runtime_predecessor, {
32
+ version: '0.15.75',
33
+ release_id: 'v0.15.75-9039fff265696091',
34
+ manifest_sha256: '9039fff265696091f9217ba4f19526c6c74a967f9ff48b75ac6e340b5f431248',
35
+ generation_manifest_sha256: '83a212e01173c73f78cff2a6d26e6f3f50f476a8c0ea3cbbf592a466071909e9'
36
+ });
37
+ });
38
+
39
+ test('R207 keeps authority surface stable and fixes only protected owner-enrollment IO', async () => {
40
+ const release = await json('RELEASE.json');
41
+ assert.equal(release.capabilities.compact_tool_count, 43);
42
+ assert.equal(release.capabilities.full_tool_count, 117);
43
+ assert.equal(release.r207.scope, 'PROTECTED_OWNER_ENROLLMENT_IO_RECOVERY');
44
+ assert.equal(release.r207.public_tool_delta, 0);
45
+ assert.equal(release.r207.authority_scope_delta, 0);
46
+ assert.equal(release.r207.r70_weakened, false);
47
+ assert.equal(release.r207.r89_weakened, false);
48
+ assert.equal(release.r207.owner_private_key_access, false);
49
+ assert.equal(release.r207.owner_consent_directory_initialization, 'FAIL_CLOSED_CONTAINED_NON_REPARSE');
50
+ assert.equal(release.r207.supervisor_ledger_utf8_bom, 'ACCEPT_ONE_LEADING_BOM_ONLY');
51
+ });
@@ -0,0 +1,23 @@
1
+ import test from 'node:test';
2
+ import assert from 'node:assert/strict';
3
+ import {assertR207NpmPublishReady,assertR207PackedIdentity} from '../scripts/npm-release-readiness-r207.mjs';
4
+ const base={version:'0.15.76',train:'R207',
5
+ parent:{version:'0.15.75',release_id:'v0.15.75-9039fff265696091',manifest_sha256:'9039fff265696091f9217ba4f19526c6c74a967f9ff48b75ac6e340b5f431248',generation_manifest_sha256:'83a212e01173c73f78cff2a6d26e6f3f50f476a8c0ea3cbbf592a466071909e9'},
6
+ source_predecessor:{train:'R206',manifest_sha256:'9039fff265696091f9217ba4f19526c6c74a967f9ff48b75ac6e340b5f431248'},
7
+ r207:{scope:'PROTECTED_OWNER_ENROLLMENT_IO_RECOVERY',public_tool_delta:0,authority_scope_delta:0,owner_private_key_access:false,r70_weakened:false,r89_weakened:false}};
8
+ test('R207 refuses publish absent independently verified closure',async()=>{
9
+ await assert.rejects(assertR207NpmPublishReady(base,{root:'D:\\candidate'}),/PUBLISH_BLOCKED R207.*closure root absent/);
10
+ });
11
+ test('R207 refuses any authority increase or private-key access',async()=>{
12
+ for(const key of ['owner_private_key_access','r70_weakened','r89_weakened']){
13
+ const altered=structuredClone(base);altered.r207[key]=true;
14
+ await assert.rejects(assertR207NpmPublishReady(altered,{root:'D:\\candidate'}),/PUBLISH_BLOCKED R207.*public authority surface widened/);
15
+ }
16
+ });
17
+ test('R207 rejects mismatched parent release before reading any closure',async()=>{
18
+ const altered=structuredClone(base);altered.parent.release_id='v0.15.74-e84bf4eed16e9443';
19
+ await assert.rejects(assertR207NpmPublishReady(altered,{root:'D:\\candidate'}),/PUBLISH_BLOCKED R207.*lineage mismatch/);
20
+ });
21
+ test('R207 refuses repacked artifact identity drift',()=>{
22
+ assert.throws(()=>assertR207PackedIdentity({tarball_sha256:'a'.repeat(64),tarball_integrity:'sha512-proof',tarball_shasum:'b'.repeat(40),observedSha256:'c'.repeat(64),observedIntegrity:'sha512-another',observedShasum:'d'.repeat(40)}),/PUBLISH_BLOCKED R207.*repacked tarball/);
23
+ });
@@ -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','0.15.69','0.15.70','0.15.71','0.15.72','0.15.73','0.15.74'].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','0.15.71','0.15.72','0.15.73','0.15.74','0.15.75','0.15.76'].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');
@@ -9,6 +9,7 @@ import {tmpdir} from 'node:os';
9
9
  import path from 'node:path';
10
10
  import {loadCodingPolicy} from '../src/coding-plane.mjs';
11
11
  import {createMachineFsRuntime} from '../src/machine-fs.mjs';
12
+ import {readRangeFromHandle} from '../src/machine-fs-handle-read-r196.mjs';
12
13
  import {generateTrustKeypair} from '../src/trust.mjs';
13
14
  import {machineFsGatewayInputSchema} from '../src/compact-gateway-mcp-tools.mjs';
14
15
  import {registerMachineFsTools} from '../src/machine-fs-mcp-tools.mjs';
@@ -106,31 +107,44 @@ test('R64 large-offset read retains bounded worker cancellation',async t=>{
106
107
  await assert.rejects(()=>runtime.read({rootId:'project',relativePath:'large.bin',offset:PAGE+1,maxBytes:1,timeoutMs:100}),/timed out after 100ms/);
107
108
  });
108
109
 
109
- test('R64 default hash stream is abort-bound to read timeout',async()=>{
110
+ test('R64 default hash stream uses the retained handle and remains abort-bound',async()=>{
110
111
  const source=await readFile(new URL('../src/machine-fs.mjs',import.meta.url),'utf8');
111
- assert.ok(/createReadStream\(file,\s*\{\s*signal\s*\}\)/.test(source),'hash stream must receive the read abort signal');
112
+ const handleSource=await readFile(new URL('../src/machine-fs-handle-read-r196.mjs',import.meta.url),'utf8');
113
+ assert.ok(/readRangeFromHandle\(handle,/.test(source),'read must use opened handle');
114
+ assert.ok(/handle\.createReadStream\(\{start:0,autoClose:false,signal\}\)/.test(handleSource),
115
+ 'hash must stream through the retained FileHandle with abort signal');
112
116
  assert.ok(/abortController\.abort\(\)/.test(source),'read timeout must abort its hash stream');
113
117
  });
114
118
 
115
- test('R64 timed-out default hash stream receives abort and stops',async t=>{
119
+ test('R64 fallback FileHandle hash stream obeys abort independently of the new native backend',async t=>{
116
120
  const f=await fixture(t);
117
- await writeFile(path.join(f.project,'large.bin'),Buffer.alloc(PAGE+16));
118
- const original=fs.createReadStream;
119
- let observedAbort=false;
120
- fs.createReadStream=(_file,options={})=>{
121
+ const file=path.join(f.project,'large.bin');
122
+ await writeFile(file,Buffer.alloc(PAGE+16));
123
+ const handle=await fs.promises.open(file,'r');
124
+ const proto=Object.getPrototypeOf(handle);
125
+ const original=proto.createReadStream;
126
+ let started,called=false,observedAbort=false;
127
+ const entered=new Promise(resolve=>started=resolve);
128
+ proto.createReadStream=function(options={}){
129
+ called=true;
121
130
  const stream=new EventEmitter();
122
131
  options.signal?.addEventListener('abort',()=>{
123
132
  observedAbort=true;
124
133
  queueMicrotask(()=>stream.emit('error',new Error('aborted')));
125
134
  },{once:true});
135
+ started();
126
136
  return stream;
127
137
  };
128
- syncBuiltinESMExports();
138
+ const controller=new AbortController();
129
139
  try{
130
- await assert.rejects(()=>f.runtime.read({rootId:'project',relativePath:'large.bin',offset:PAGE+1,maxBytes:1,hashFile:true,timeoutMs:100}),/timed out after 100ms/);
131
- assert.equal(observedAbort,true,'default hash stream continued after timeout');
140
+ const operation=readRangeFromHandle(handle,{offset:PAGE+1,length:1,hashFile:true,signal:controller.signal});
141
+ await entered;
142
+ controller.abort();
143
+ await assert.rejects(operation,/aborted/);
144
+ assert.equal(called,true,'fallback handle hash was not started');
145
+ assert.equal(observedAbort,true,'fallback hash stream ignored abort');
132
146
  }finally{
133
- fs.createReadStream=original;
134
- syncBuiltinESMExports();
147
+ proto.createReadStream=original;
148
+ await handle.close();
135
149
  }
136
150
  });