deadbyte-mcp 0.15.65 → 0.15.67

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 (69) hide show
  1. package/MANIFEST.SHA256 +68 -21
  2. package/PROVENANCE.json +35 -9
  3. package/README.txt +31 -18
  4. package/RELEASE.json +43 -7
  5. package/SBOM.cdx.json +2 -2
  6. package/TEST-MATRIX.json +45 -2
  7. package/bridge/package.json +1 -1
  8. package/controller/deadbyte-process-policy.json +4 -4
  9. package/docs/R168-ACTIVATION-RECONCILIATION-BLOCKER-20261026.md +39 -0
  10. package/docs/R168-CRASH-JOURNAL-PROTECTED-PREFLIGHT-20261026.md +46 -0
  11. package/docs/R168-FIVE-PLANE-FENCE-20261026.md +41 -0
  12. package/docs/R168-NATIVE-EXCLUSION-TERMINAL-CONTRACT-20261026.md +53 -0
  13. package/docs/R168-NPX-REMOTE-SKEW-HOTFIX-20261026.md +41 -0
  14. package/docs/R168-READONLY-AUDIT-AND-RESUME-20261026.md +56 -0
  15. package/docs/R168-SIGNED-RECONCILIATION-PRECOMMIT-20261026.md +45 -0
  16. package/docs/R169-NPX-ACTIVE-CONTROLLER-DELEGATION-20261026.md +57 -0
  17. package/node_modules/which/CHANGELOG.md +166 -0
  18. package/package.json +5 -2
  19. package/scripts/gate-windows-r168.ps1 +75 -0
  20. package/scripts/npm-release-r48.mjs +23 -3
  21. package/scripts/npm-release-readiness-r168.mjs +70 -0
  22. package/scripts/r168-audit-readonly.mjs +32 -0
  23. package/scripts/r168-npm-registry-readonly.mjs +19 -0
  24. package/scripts/r168-protected-preflight.mjs +65 -0
  25. package/scripts/r168-release-scan.mjs +28 -0
  26. package/scripts/r168-source-full-test-runner.mjs +46 -0
  27. package/scripts/r168-subject-readonly.mjs +49 -0
  28. package/scripts/r172-protected-preflight.mjs +42 -0
  29. package/src/activation-continuity-abort-r172.mjs +108 -0
  30. package/src/activation-lock-r65.mjs +5 -1
  31. package/src/activation-preflight-r65.mjs +2 -1
  32. package/src/activation-reconciliation-audit-r168.mjs +200 -0
  33. package/src/activation-reconciliation-consent-r168.mjs +235 -0
  34. package/src/activation-reconciliation-fence-r168.mjs +87 -0
  35. package/src/activation-reconciliation-journal-r168.mjs +100 -0
  36. package/src/activation-reconciliation-receipt-store-r168.mjs +42 -0
  37. package/src/activation-reconciliation-terminal-r168.mjs +111 -0
  38. package/src/active-controller-delegation-r169.mjs +57 -0
  39. package/src/deadbyte-cli.mjs +38 -0
  40. package/src/npm-publication-resume-r168.mjs +25 -0
  41. package/src/remote-one-shot-coherence-r168.mjs +43 -0
  42. package/src/version.mjs +1 -1
  43. package/test/r130-release-security-contract.test.mjs +7 -7
  44. package/test/r132-release-identity.test.mjs +8 -8
  45. package/test/r153-prepublish-identity.test.mjs +1 -1
  46. package/test/r163-isolated-slot-rollback-restore.test.mjs +2 -2
  47. package/test/r165-live-cli-skew.test.mjs +7 -7
  48. package/test/r167-release-contract.test.mjs +11 -10
  49. package/test/r168-combined-targeted-runner.mjs +14 -0
  50. package/test/r168-crash-journal.test.mjs +149 -0
  51. package/test/r168-five-plane-fence-runner.mjs +11 -0
  52. package/test/r168-five-plane-fence.test.mjs +128 -0
  53. package/test/r168-focused-suite.test.mjs +4 -0
  54. package/test/r168-native-five-plane-runner.mjs +11 -0
  55. package/test/r168-native-five-plane-scratch.test.mjs +49 -0
  56. package/test/r168-native-r85-protected-scratch.test.mjs +44 -0
  57. package/test/r168-npm-resume-identity.test.mjs +49 -0
  58. package/test/r168-owner-reconciliation-contract.test.mjs +250 -0
  59. package/test/r168-protected-entrypoint.test.mjs +53 -0
  60. package/test/r168-reconciliation-audit.test.mjs +42 -0
  61. package/test/r168-release-contract.test.mjs +46 -0
  62. package/test/r168-remote-one-shot-coherence.test.mjs +66 -0
  63. package/test/r169-delegate-active.test.mjs +36 -0
  64. package/test/r169-delegate-cli-integration.test.mjs +93 -0
  65. package/test/r169-targeted-runner.mjs +16 -0
  66. package/test/r172-activation-recovery.test.mjs +95 -0
  67. package/test/r172-release-contract.test.mjs +35 -0
  68. package/test/r59-tool-surface-compat.test.mjs +1 -1
  69. package/test/r63-one-shot-coherence.test.mjs +10 -5
@@ -0,0 +1,75 @@
1
+ #requires -Version 5.1
2
+ [CmdletBinding()]
3
+ param(
4
+ [ValidateSet('preflight','focused','full','manifest','doctor','all')][string]$Stage='preflight',
5
+ [string]$ExpectedDevelopmentRoot
6
+ )
7
+ Set-StrictMode -Version Latest
8
+ $ErrorActionPreference='Stop'
9
+ $repo=Split-Path -Parent $PSScriptRoot
10
+ if([string]::IsNullOrWhiteSpace($ExpectedDevelopmentRoot)){$ExpectedDevelopmentRoot=$repo}
11
+ $ExpectedDevelopmentRoot=[IO.Path]::GetFullPath($ExpectedDevelopmentRoot)
12
+ $state=Join-Path $env:USERPROFILE '.deadbyte-mcp'
13
+ $parentId='v0.15.61-055d1ff0aa99e60b'
14
+ $parentSha='055d1ff0aa99e60bbf9e9f136b02ebd4fffb8391712560f7607cff30af94dcdf'
15
+ $parentGen='f940e794daac2dda097a66301c4b85c4b29a2df7c669e549503c05a2a55ff6c4'
16
+ $sourceManifest='3316654646cc870584a7c969c2032af6c795229ff8c65e5a8b794f97c2458c41'
17
+ $nativeNode='C:\Program Files\nodejs\node.exe'
18
+ $node=if(Test-Path -LiteralPath $nativeNode -PathType Leaf){$nativeNode}else{(Get-Command node -ErrorAction Stop).Source}
19
+ function Sha([string]$file){
20
+ $sha=[Security.Cryptography.SHA256]::Create()
21
+ try{$stream=[IO.File]::OpenRead($file);try{return ([BitConverter]::ToString($sha.ComputeHash($stream))).Replace('-','').ToLowerInvariant()}finally{$stream.Dispose()}}finally{$sha.Dispose()}
22
+ }
23
+ function Run-Node([string]$label,[string[]]$nodeArgs){
24
+ if($null -eq $nodeArgs -or $nodeArgs.Count -lt 1){throw "R168_${label}_NO_NODE_ARGUMENTS"}
25
+ Write-Output "R168_STAGE_START=$label"
26
+ Write-Output "R168_STAGE_ARGS=$($nodeArgs.Count)"
27
+ $global:LASTEXITCODE=0
28
+ & $node @nodeArgs
29
+ $exit=$global:LASTEXITCODE
30
+ if($exit -ne 0){throw "R168_${label}_FAILED exit=$exit"}
31
+ Write-Output "R168_STAGE_PASS=$label"
32
+ }
33
+ Push-Location $repo
34
+ $previousExpectedDevelopmentRoot=$env:DEADBYTE_EXPECTED_DEVELOPMENT_ROOT
35
+ try{
36
+ $env:DEADBYTE_EXPECTED_DEVELOPMENT_ROOT=$ExpectedDevelopmentRoot
37
+ $pkg=Get-Content -LiteralPath 'package.json' -Raw | ConvertFrom-Json
38
+ $release=Get-Content -LiteralPath 'RELEASE.json' -Raw | ConvertFrom-Json
39
+ if($pkg.version -ne '0.15.66' -or $release.version -ne '0.15.66' -or $release.train -ne 'R168'){throw 'R168_CANDIDATE_VERSION_DRIFT'}
40
+ if($release.source_predecessor.version -ne '0.15.65' -or $release.source_predecessor.train -ne 'R167' -or $release.r168.source_predecessor_manifest_sha256 -ne $sourceManifest){throw 'R168_SOURCE_PREDECESSOR_IDENTITY_DRIFT'}
41
+ if($release.runtime_predecessor.release_id -ne $parentId -or $release.runtime_predecessor.manifest_sha256 -ne $parentSha -or $release.runtime_predecessor.generation_manifest_sha256 -ne $parentGen){throw 'R168_RUNTIME_PREDECESSOR_IDENTITY_DRIFT'}
42
+ $active=Get-Content -LiteralPath (Join-Path $state 'active-release.json') -Raw | ConvertFrom-Json
43
+ if($active.version -ne '0.15.61' -or $active.release_id -ne $parentId -or $active.manifest_sha256 -ne $parentSha -or $active.generation_manifest_sha256 -ne $parentGen){throw 'R168_ACTIVE_RUNTIME_DRIFT'}
44
+ if((Sha (Join-Path $active.release_root 'MANIFEST.SHA256')) -ne $parentSha){throw 'R168_ACTIVE_MANIFEST_MISMATCH'}
45
+ Write-Output 'R168_PREDECESSOR_PREFLIGHT=PASS'
46
+ if($Stage -eq 'preflight'){return}
47
+ if($Stage -in @('focused','all')){
48
+ Run-Node -label 'focused' -nodeArgs @('--test','--test-reporter=tap','--test-concurrency=1','test/r167-activation-path.test.mjs','test/r168-release-contract.test.mjs','test/r168-remote-one-shot-coherence.test.mjs','test/r160-release-contract.test.mjs','test/r158-epoch-bootstrap.test.mjs','test/r65-activation-orchestration.test.mjs','test/r89-privileged-install-boundary.test.mjs','test/r119-recovery-crash-after-commit.test.mjs','test/r59-tool-surface-compat.test.mjs')
49
+ if($Stage -eq 'focused'){return}
50
+ }
51
+ if($Stage -in @('full','all')){
52
+ $previousCrossVolume=$env:DEADBYTE_TEST_CROSS_VOLUME_ROOT
53
+ $tempDrive=[IO.Path]::GetPathRoot([IO.Path]::GetFullPath($env:TEMP))
54
+ $scratchDrive=if($tempDrive -ieq 'D:\'){'C:\'}else{'D:\'}
55
+ if(-not (Test-Path -LiteralPath $scratchDrive -PathType Container)){throw 'R168_CROSS_VOLUME_DRIVE_UNAVAILABLE'}
56
+ $crossVolume=Join-Path $scratchDrive 'DEADBYTE-R168-CrossVolume'
57
+ try{
58
+ [IO.Directory]::CreateDirectory($crossVolume)|Out-Null
59
+ $env:DEADBYTE_TEST_CROSS_VOLUME_ROOT=$crossVolume
60
+ Run-Node -label 'full' -nodeArgs @('--test','--test-reporter=tap','--test-concurrency=4','test/*.test.mjs')
61
+ }finally{$env:DEADBYTE_TEST_CROSS_VOLUME_ROOT=$previousCrossVolume}
62
+ if($Stage -eq 'full'){return}
63
+ }
64
+ if($Stage -in @('manifest','all')){
65
+ Run-Node -label 'manifest' -nodeArgs @('scripts/release-manifest.mjs','verify')
66
+ if($Stage -eq 'manifest'){return}
67
+ }
68
+ if($Stage -in @('doctor','all')){
69
+ $previous=$env:DEADBYTE_STATE_ROOT
70
+ $isolated=Join-Path $env:TEMP ('deadbyte-r168-doctor-'+[guid]::NewGuid().ToString('N'))
71
+ try{[IO.Directory]::CreateDirectory($isolated)|Out-Null;$env:DEADBYTE_STATE_ROOT=$isolated;Run-Node -label 'isolated_doctor' -nodeArgs @('src/deadbyte-cli.mjs','doctor')}
72
+ finally{$env:DEADBYTE_STATE_ROOT=$previous;Remove-Item -LiteralPath $isolated -Recurse -Force -ErrorAction SilentlyContinue}
73
+ }
74
+ Write-Output 'R168_WINDOWS_CANDIDATE_GATE=PASS_PREPUBLICATION'
75
+ }finally{$env:DEADBYTE_EXPECTED_DEVELOPMENT_ROOT=$previousExpectedDevelopmentRoot;Pop-Location}
@@ -11,6 +11,8 @@ import {assertR163NpmPublishReady} from './npm-release-readiness-r163.mjs';
11
11
  import {assertR165NpmPublishReady} from './npm-release-readiness-r165.mjs';
12
12
  import {assertR166NpmPublishReady} from './npm-release-readiness-r166.mjs';
13
13
  import {assertR167NpmPublishReady,assertR167PackedIdentity} from './npm-release-readiness-r167.mjs';
14
+ import {assertR168NpmPublishReady,assertR168PackedIdentity} from './npm-release-readiness-r168.mjs';
15
+ import {resumePackRecord} from '../src/npm-publication-resume-r168.mjs';
14
16
  import { existsSync } from 'node:fs';
15
17
  import { resolveStateRoot } from '../src/state-root.mjs';
16
18
  import path from 'node:path';
@@ -116,6 +118,11 @@ async function main() {
116
118
  if (!publishOnly) throw new Error('PUBLISH_BLOCKED R167 requires --publish-only; activation is separate');
117
119
  r167Readiness=await assertR167NpmPublishReady(release,{root});
118
120
  }
121
+ let r168Readiness=null;
122
+ if (!isVerifyOnly && version==='0.15.66') {
123
+ if (!publishOnly) throw new Error('PUBLISH_BLOCKED R168 requires --publish-only; activation is separate');
124
+ r168Readiness=await assertR168NpmPublishReady(release,{root});
125
+ }
119
126
  if (release.parent?.release_id !== provenance.parent_release_id ||
120
127
  release.parent?.manifest_sha256 !== provenance.parent_manifest_sha256 ||
121
128
  release.parent?.generation_manifest_sha256 !== provenance.parent_generation_manifest_sha256) {
@@ -145,8 +152,14 @@ async function main() {
145
152
  if(resuming&&!existsSync(preservedTarball))throw new Error('immutable npm publication claim exists but exact local tarball is missing; manual artifact reconciliation required');
146
153
  if(!resuming&&preservedTarball&&existsSync(preservedTarball))throw new Error('unclaimed preserved npm tarball exists; inspect artifact evidence before another pack');
147
154
  if(!resuming)await assertVacantPackDestination({root,filename:`deadbyte-mcp-${version}.tgz`});
155
+ const resumeClaim=resuming?JSON.parse(await readFile(path.join(publicationDir,'claim.json'),'utf8')):null;
156
+ const preservedRecord=resuming?resumePackRecord({
157
+ filename:path.basename(preservedTarball),localBytes:await readFile(preservedTarball),
158
+ claim:resumeClaim,version,
159
+ manifestSha256:createHash('sha256').update(await readFile(path.join(root,'MANIFEST.SHA256'))).digest('hex')
160
+ }):null;
148
161
  const packed=resuming?null:parseJson(runNpm(['pack', '--json'], { timeout: 180000 }));
149
- const packRecord=resuming?{filename:path.basename(preservedTarball)}:(Array.isArray(packed) ? packed[0] : packed?.[pkg.name]);
162
+ const packRecord=resuming?preservedRecord:(Array.isArray(packed) ? packed[0] : packed?.[pkg.name]);
150
163
  const filename = packRecord?.filename;
151
164
  if (!filename) throw new Error('npm pack did not return a tarball filename');
152
165
  const tarball = resuming?preservedTarball:path.join(root, filename);
@@ -157,8 +170,11 @@ async function main() {
157
170
  try {
158
171
  console.log('[4/7] Verifying packed artifact file surface against MANIFEST.SHA256 and packaging contract...');
159
172
  const manifestText = await readFile(path.join(root, 'MANIFEST.SHA256'), 'utf8');
160
- const claimData = resuming ? JSON.parse(await readFile(path.join(publicationDir, 'claim.json'), 'utf8')) : null;
161
- const manifestSha256 = resuming ? claimData.manifest_sha256 : createHash('sha256').update(manifestText).digest('hex');
173
+ const claimData = resumeClaim;
174
+ const currentManifestSha256 = createHash('sha256').update(manifestText).digest('hex');
175
+ if(resuming&&claimData.manifest_sha256!==currentManifestSha256)
176
+ throw new Error('immutable npm resume claim manifest changed during artifact verification');
177
+ const manifestSha256 = resuming ? claimData.manifest_sha256 : currentManifestSha256;
162
178
  const manifestEntries = new Set();
163
179
  for (const line of manifestText.split(/\r?\n/)) {
164
180
  if (!line) continue;
@@ -242,6 +258,10 @@ async function main() {
242
258
  ...r167Readiness,observedSha256:createHash('sha256').update(localBytes).digest('hex'),
243
259
  observedIntegrity:localIntegrity,observedShasum:packRecord.shasum
244
260
  });
261
+ if(r168Readiness)assertR168PackedIdentity({
262
+ ...r168Readiness,observedSha256:createHash('sha256').update(localBytes).digest('hex'),
263
+ observedIntegrity:localIntegrity,observedShasum:packRecord.shasum
264
+ });
245
265
 
246
266
  console.log('[6/7] Publishing artifact to npm registry...');
247
267
  publishAttempted = true;
@@ -0,0 +1,70 @@
1
+ import {createHash} from 'node:crypto';
2
+ import {lstat,readFile} from 'node:fs/promises';
3
+ import path from 'node:path';
4
+
5
+ const sha256=bytes=>createHash('sha256').update(bytes).digest('hex');
6
+ const hex=value=>typeof value==='string'&&/^[0-9a-f]{64}$/.test(value);
7
+ const required=(value,message)=>{if(!value)throw new Error(`PUBLISH_BLOCKED R168 ${message}`);};
8
+
9
+ async function checkedFile(file,expected,{maxBytes=32*1024*1024}={}){
10
+ required(hex(expected),'evidence digest missing');
11
+ const info=await lstat(file);
12
+ required(info.isFile()&&!info.isSymbolicLink()&&info.size>0&&info.size<maxBytes,'evidence file invalid');
13
+ const bytes=await readFile(file);
14
+ required(sha256(bytes)===expected,'evidence digest differs');
15
+ return bytes;
16
+ }
17
+
18
+ export function assertR168PackedIdentity({
19
+ tarball_sha256,tarball_integrity,tarball_shasum,
20
+ observedSha256,observedIntegrity,observedShasum
21
+ }={}){
22
+ required(hex(tarball_sha256)&&typeof tarball_integrity==='string'&&tarball_integrity.startsWith('sha512-')&&
23
+ typeof tarball_shasum==='string'&&/^[0-9a-f]{40}$/.test(tarball_shasum),'verified tarball identity invalid');
24
+ required(observedSha256===tarball_sha256&&observedIntegrity===tarball_integrity&&observedShasum===tarball_shasum,
25
+ 'repacked tarball differs from verified closure bytes');
26
+ return true;
27
+ }
28
+
29
+ export async function assertR168NpmPublishReady(release,{root}={}){
30
+ required(release?.version==='0.15.66'&&release?.train==='R168'&&
31
+ release?.r168?.scope==='REMOTE_VERSION_SKEW_FAIL_CLOSED_AND_READONLY_RECONCILIATION',
32
+ 'candidate metadata invalid');
33
+ required(release?.parent?.release_id==='v0.15.61-055d1ff0aa99e60b'&&
34
+ release?.source_predecessor?.version==='0.15.65'&&release?.source_predecessor?.train==='R167',
35
+ 'lineage mismatch');
36
+ const evidence=process.env.DEADBYTE_R168_CLOSURE_ROOT;
37
+ required(root&&path.isAbsolute(root)&&evidence&&path.isAbsolute(evidence),'closure root absent');
38
+ const allowed=path.resolve(root,'..','..','testing','r168-final-gate-fix1');
39
+ required(path.resolve(evidence).toLowerCase()===allowed.toLowerCase(),'closure root not in scoped testing folder');
40
+ const closure=JSON.parse(await readFile(path.join(evidence,'closure.json'),'utf8'));
41
+ required(closure?.schema==='deadbyte.r168-release-closure.v1'&&closure.status==='PASS'&&closure.version==='0.15.66',
42
+ 'closure status mismatch');
43
+ required(closure.production_release_id===release.parent.release_id&&hex(closure.manifest_sha256),'predecessor or manifest invalid');
44
+ required(closure.manifest_sha256===sha256(await readFile(path.join(root,'MANIFEST.SHA256'))),'final manifest drift');
45
+
46
+ const receiptBytes=await checkedFile(path.join(evidence,'protected-install-receipt.json'),closure.protected_receipt_sha256);
47
+ const receipt=JSON.parse(receiptBytes.toString('utf8'));
48
+ required(receipt?.status==='PASS'&&receipt.package_version==='0.15.66'&&
49
+ receipt.source_manifest_sha256===closure.manifest_sha256&&receipt.protected_manifest_sha256===closure.manifest_sha256,
50
+ 'protected exact-byte receipt mismatch');
51
+
52
+ const focused=(await checkedFile(path.join(evidence,'windows-focused.stdout.log'),closure.focused_stdout_sha256)).toString('utf8');
53
+ const full=(await checkedFile(path.join(evidence,'windows-full.stdout.log'),closure.full_stdout_sha256)).toString('utf8');
54
+ const verify=(await checkedFile(path.join(evidence,'npm-verify.stdout.log'),closure.verify_stdout_sha256)).toString('utf8');
55
+ required(focused.includes('R168_STAGE_PASS=focused')&&focused.includes('# fail 0'),'focused protected gate incomplete');
56
+ required(full.includes('R168_STAGE_PASS=full')&&full.includes('# fail 0'),'full protected gate incomplete');
57
+ const summaries=[...full.matchAll(/^# tests (\d+)\s*\r?\n# suites (\d+)\s*\r?\n# pass (\d+)\s*\r?\n# fail (\d+)\s*\r?\n# cancelled (\d+)\s*\r?\n# skipped (\d+)/gm)];
58
+ required(summaries.length>0,'full TAP summary absent');
59
+ const final=summaries.at(-1),tests=Number(final[1]),passes=Number(final[3]),fails=Number(final[4]),skips=Number(final[6]);
60
+ required(tests>=1975&&passes>=1925&&fails===0&&skips<=65&&passes+skips===tests,'full regression count mismatch');
61
+ required(verify.includes('VERIFY-ONLY PIPELINE PASS')&&verify.includes('Artifact isolation gate PASS')&&verify.includes('VERIFIED_TARBALL '),
62
+ 'npm exact-artifact verification incomplete');
63
+ const line=verify.split(/\r?\n/).find(value=>value.startsWith('VERIFIED_TARBALL '));
64
+ let artifact;try{artifact=JSON.parse(line.slice('VERIFIED_TARBALL '.length));}catch{required(false,'tarball evidence malformed');}
65
+ required(artifact.filename==='deadbyte-mcp-0.15.66.tgz'&&artifact.sha256===closure.tarball_sha256&&
66
+ artifact.integrity===closure.tarball_integrity&&artifact.shasum===closure.tarball_shasum&&artifact.bytes>0,
67
+ 'tarball identity mismatch');
68
+ return Object.freeze({status:'R168_RELEASE_READY_EVIDENCE_BOUND',manifest_sha256:closure.manifest_sha256,
69
+ tarball_sha256:artifact.sha256,tarball_integrity:artifact.integrity,tarball_shasum:artifact.shasum,tests,passes,skips});
70
+ }
@@ -0,0 +1,32 @@
1
+ // Standalone R168 *read-only* diagnostic. Never signs/releases activation locks.
2
+ import os from 'node:os';
3
+ import path from 'node:path';
4
+ import {createHash} from 'node:crypto';
5
+ import {readFile} from 'node:fs/promises';
6
+ import {inspectR168UnknownWithoutMutation} from '../src/activation-reconciliation-audit-r168.mjs';
7
+
8
+ const auditModule=new URL('../src/activation-reconciliation-audit-r168.mjs',import.meta.url);
9
+ const expectedModuleHash='467268f6f195e9359b819e35c8b418c81e1cb40c9ba04b990fd2656e354c65a5';
10
+ const state=path.join(os.homedir(),'.deadbyte-mcp');
11
+ try{
12
+ const actualModuleHash=createHash('sha256').update(await readFile(auditModule)).digest('hex');
13
+ if(actualModuleHash!==expectedModuleHash)throw new Error('R168 audit module source hash changed');
14
+ const result=await inspectR168UnknownWithoutMutation({
15
+ stateRoot:state,
16
+ expectedOperationId:'c84d7ffa4eee80a2f467acda3ddd9332',
17
+ expectedSubjectSha256:'fba80e0128da9657dcbfcaa27d3a4dc8d9d8939146e81a097293fa5fcb6ae8fc',
18
+ expectedOutcomeFilename:'r65-activation-outcome-1792993876612-cc7c498632c7757d.json',
19
+ expectedContinuityId:'r65-ef42bc84451ce024c4dee736d4047b36',
20
+ expectedPredecessorReleaseId:'v0.15.61-055d1ff0aa99e60b',
21
+ expectedPredecessorManifestSha256:'055d1ff0aa99e60bbf9e9f136b02ebd4fffb8391712560f7607cff30af94dcdf',
22
+ expectedPredecessorGenerationSha256:'f940e794daac2dda097a66301c4b85c4b29a2df7c669e549503c05a2a55ff6c4',
23
+ expectedCandidateManifestSha256:'ad3c091fbbe2bac6d234548724008b67305fd483ad6542f92d5aeb1aa84f5849'
24
+ });
25
+ console.log(JSON.stringify(result,null,2));
26
+ console.log('R168_NO_MUTATION_DIAGNOSTIC=OBSERVED_NOT_SIGNED');
27
+ console.log('R168_ACTIVATION_CLAIM=UNCHANGED');
28
+ console.log('R168_PRODUCTION_ACTIVATION=NOT_RUN');
29
+ }catch(error){
30
+ console.error('R168_READONLY_AUDIT=BLOCKED '+String(error?.message??error));
31
+ process.exitCode=1;
32
+ }
@@ -0,0 +1,19 @@
1
+ // R168 read-only exact registry preflight. No npm publish here.
2
+ import {spawnSync} from 'node:child_process';
3
+ import path from 'node:path';
4
+ const cli=path.join(path.dirname(process.execPath),'node_modules','npm','bin','npm-cli.js');
5
+ const calls=[
6
+ ['view','deadbyte-mcp@0.15.66','version','--json'],
7
+ ['view','deadbyte-mcp','dist-tags.latest','--json'],
8
+ ['whoami']
9
+ ];
10
+ for(const args of calls){
11
+ const r=spawnSync(process.execPath,[cli,...args],{
12
+ shell:false,windowsHide:true,encoding:'utf8',timeout:60000,maxBuffer:1024*1024
13
+ });
14
+ console.log(JSON.stringify({args:args.slice(0,3),exit:r.status,
15
+ stdout:String(r.stdout??'').trim().slice(0,250),
16
+ stderr:String(r.stderr??'').trim().slice(0,250)}));
17
+ if(r.error)throw r.error;
18
+ if(r.status!==0&&args[0]!=='view')process.exitCode=1;
19
+ }
@@ -0,0 +1,65 @@
1
+ // R168 privileged preflight; read-only. Never invokes native CAS or changes grants.
2
+ // This code must be part of a signed manifest in a protected Program Files install.
3
+ import {createHash} from 'node:crypto';
4
+ import {readFile} from 'node:fs/promises';
5
+ import path from 'node:path';
6
+ import {fileURLToPath} from 'node:url';
7
+ import {parseManifest,verifyManifestTreeExact} from '../src/release-slot.mjs';
8
+ import {inspectTrustedInstallation,privilegedEpochInstallFiles} from '../src/privileged-install-trust-r89.mjs';
9
+
10
+ const root=path.dirname(path.dirname(fileURLToPath(import.meta.url)));
11
+ const version='0.15.66';
12
+ const expectedTrain='R168';
13
+ const mandatory=['scripts/r168-protected-preflight.mjs',
14
+ 'src/activation-reconciliation-journal-r168.mjs',
15
+ 'src/activation-reconciliation-consent-r168.mjs',
16
+ 'src/activation-reconciliation-terminal-r168.mjs',
17
+ 'src/activation-reconciliation-fence-r168.mjs',
18
+ 'src/activation-reconciliation-audit-r168.mjs',
19
+ 'src/activation-reconciliation-receipt-store-r168.mjs'];
20
+ function denied(reason){throw new Error('R168_'+reason);}
21
+ function actualProtectedRoot(){
22
+ const trusted='C:\\Program Files';
23
+ const rel=path.win32.relative(trusted,root);
24
+ if(!rel||rel==='..'||rel.startsWith('..'+path.win32.sep)||path.win32.isAbsolute(rel))
25
+ denied('ROOT_UNPROTECTED');
26
+ return root;
27
+ }
28
+ async function main(){
29
+ if(process.platform!=='win32'||process.arch!=='x64')
30
+ denied('WINDOWS_X64_REQUIRED');
31
+ if(process.argv.length!==2)denied('UNKNOWN_OPTION');
32
+ const verifiedRoot=actualProtectedRoot();
33
+ const packageInfo=JSON.parse(await readFile(path.join(verifiedRoot,'package.json'),'utf8'));
34
+ const release=JSON.parse(await readFile(path.join(verifiedRoot,'RELEASE.json'),'utf8'));
35
+ if(packageInfo.name!=='deadbyte-mcp'||packageInfo.version!==version||
36
+ release.name!=='deadbyte-mcp'||release.version!==version||release.train!==expectedTrain)
37
+ denied('VERSION_DRIFT');
38
+ const manifest=await readFile(path.join(verifiedRoot,'MANIFEST.SHA256'));
39
+ const entries=parseManifest(manifest.toString('utf8'));
40
+ for(const name of mandatory){
41
+ if(!entries.has(name))denied('R168_SOURCE_NOT_IN_MANIFEST');
42
+ }
43
+ // The installed package is checked against its complete manifest, not a
44
+ // handpicked critical-file subset or a writable development source root.
45
+ await verifyManifestTreeExact(verifiedRoot,entries,{allowedExtraPaths:['.npmignore','package-lock.json']});
46
+ const important=[
47
+ ...privilegedEpochInstallFiles(verifiedRoot,process.execPath),
48
+ ...mandatory.map(rel=>path.win32.join(verifiedRoot,...rel.split('/'))),
49
+ path.win32.join(verifiedRoot,'MANIFEST.SHA256'),
50
+ path.win32.join(verifiedRoot,'RELEASE.json'),
51
+ path.win32.join(verifiedRoot,'package.json')
52
+ ];
53
+ // OS ACL trust is checked *after* content verification, under the protected
54
+ // root. No user-controlled environment may override this inventory.
55
+ await inspectTrustedInstallation(important);
56
+ const hash=createHash('sha256').update(manifest).digest('hex');
57
+ process.stdout.write(JSON.stringify({status:'R168_PROTECTED_PREFLIGHT_READONLY_PASS',
58
+ version,train:expectedTrain,manifest_sha256:hash,files:entries.size,
59
+ terminalization_authorized:false,activation_authorized:false})+'\n');
60
+ }
61
+ try{await main();}
62
+ catch(error){
63
+ process.stderr.write('R168_PROTECTED_PREFLIGHT=BLOCKED '+String(error?.message??error)+'\n');
64
+ process.exitCode=1;
65
+ }
@@ -0,0 +1,28 @@
1
+ // R168 bounded read-only scan of current version-bound source.
2
+ import {readdir,readFile,lstat} from 'node:fs/promises';
3
+ import {fileURLToPath} from 'node:url';
4
+ import path from 'node:path';
5
+ const root=fileURLToPath(new URL('..',import.meta.url));
6
+ let count=0, results=[];
7
+ const scopes=['test','src','scripts','controller','bridge'];
8
+ for(const scope of scopes){
9
+ const visit=async(dir,depth)=>{
10
+ if(depth>14)throw Error('scan depth exceeded');
11
+ const entries=await readdir(dir,{withFileTypes:true});
12
+ if(entries.length>3000)throw Error('inventory exceeds budget');
13
+ for(const e of entries){
14
+ if(e.isSymbolicLink())throw Error('symlink in bounded scan');
15
+ const file=path.join(dir,e.name),rel=path.relative(root,file).replaceAll('\\','/');
16
+ if(e.isDirectory()) {if(!['node_modules','.git','tmp'].includes(e.name))await visit(file,depth+1);continue}
17
+ if(!e.isFile()||!/(\.mjs|\.ps1|\.json)$/.test(e.name))continue;
18
+ const stats=await lstat(file);if(stats.size>300000)continue;
19
+ const text=await readFile(file,'utf8');
20
+ const lines=text.split(/\r?\n/);
21
+ const matches=lines.map((s,j)=>({line:j+1,s})).filter(q=>/0\.15\.65|R167.*version|R167_CANDIDATE_VERSION_DRIFT|r167:0\.15\.65|R167_STAGE_PASS=/.test(q.s));
22
+ if(matches.length)results.push({file:rel,lines:matches.slice(0,7)});
23
+ count++;
24
+ }
25
+ };
26
+ await visit(path.join(root,scope),0);
27
+ }
28
+ console.log(JSON.stringify({scannedFiles:count,matchedFiles:results.length,results},null,2));
@@ -0,0 +1,46 @@
1
+ // R168 Windows unprotected SOURCE gate. No publication, activation or claims.
2
+ import {spawnSync} from 'node:child_process';
3
+ import {createHash} from 'node:crypto';
4
+ import {closeSync,fsyncSync,openSync} from 'node:fs';
5
+ import {mkdir,readFile,writeFile} from 'node:fs/promises';
6
+ import {fileURLToPath} from 'node:url';
7
+ import path from 'node:path';
8
+
9
+ const root=path.resolve(fileURLToPath(new URL('..',import.meta.url)));
10
+ const outDir=path.resolve(root,'..','..','testing','r168-source-full-20261026-fix1');
11
+ const manifestBytes=await readFile(path.join(root,'MANIFEST.SHA256'));
12
+ const manifestHash=createHash('sha256').update(manifestBytes).digest('hex');
13
+ const fileCount=manifestBytes.toString('utf8').trimEnd().split(/\r?\n/).length;
14
+ const digest=b=>createHash('sha256').update(b).digest('hex');
15
+ const release=JSON.parse(await readFile(path.join(root,'RELEASE.json'),'utf8'));
16
+ if(process.platform!=='win32'||release.version!=='0.15.66'||release.train!=='R168')
17
+ throw new Error('R168 Windows source release identity mismatch');
18
+ const manifestCheck=spawnSync(process.execPath,[path.join(root,'scripts','release-manifest.mjs'),'verify'],{cwd:root,shell:false,windowsHide:true,encoding:'utf8',timeout:180000});
19
+ if(manifestCheck.status!==0)throw new Error('R168 manifest verify failed '+String(manifestCheck.stderr??manifestCheck.stdout).slice(0,1200));
20
+ await mkdir(outDir,{recursive:true});
21
+ const stdoutPath=path.join(outDir,'windows-full.stdout.log');
22
+ const stderrPath=path.join(outDir,'windows-full.stderr.log');
23
+ let o=null,e=null;
24
+ try{
25
+ o=openSync(stdoutPath,'wx');e=openSync(stderrPath,'wx');
26
+ const cross=path.join(root,'..','..','testing','r168-crossvolume-scratch');
27
+ await mkdir(cross,{recursive:true});
28
+ const result=spawnSync(process.execPath,
29
+ ['--test','--test-reporter=tap','--test-concurrency=4','test/*.test.mjs'],{
30
+ cwd:root,shell:false,windowsHide:true,stdio:['ignore',o,e],
31
+ timeout:480000,env:{...process.env,DEADBYTE_TEST_CROSS_VOLUME_ROOT:cross}
32
+ });
33
+ fsyncSync(o);fsyncSync(e);
34
+ closeSync(o);closeSync(e);o=e=null;
35
+ const full=await readFile(stdoutPath,'utf8');
36
+ const match=[...full.matchAll(/^# tests (\d+)\s*\r?\n# suites (\d+)\s*\r?\n# pass (\d+)\s*\r?\n# fail (\d+)\s*\r?\n# cancelled (\d+)\s*\r?\n# skipped (\d+)/gm)].at(-1);
37
+ const summary={status:result.status===0?'SOURCE_FULL_PASS':'SOURCE_FULL_FAIL',
38
+ exit_code:result.status,terminated_signal:result.signal??null,
39
+ files_checked:fileCount,manifest_sha256:manifestHash,
40
+ tests:match?Number(match[1]):null,passes:match?Number(match[3]):null,
41
+ failures:match?Number(match[4]):null,skipped:match?Number(match[6]):null,
42
+ stdout_sha256:digest(await readFile(stdoutPath)),stderr_sha256:digest(await readFile(stderrPath))};
43
+ console.log(JSON.stringify(summary,null,2));
44
+ if(result.error)throw result.error;
45
+ if(result.status!==0)process.exitCode=1;
46
+ }catch(error){if(o!==null)closeSync(o);if(e!==null)closeSync(e);throw error;}
@@ -0,0 +1,49 @@
1
+ // R168 read-only subject preparation. No signing keys or activation mutation.
2
+ import {readFile} from 'node:fs/promises';
3
+ import {createHash} from 'node:crypto';
4
+ import os from 'node:os';
5
+ import path from 'node:path';
6
+ import {readPinnedEpochLockSnapshot} from '../src/foreground-marker-cas-win32.mjs';
7
+ import {inspectR168UnknownWithoutMutation} from '../src/activation-reconciliation-audit-r168.mjs';
8
+ import {createR168ReconciliationSubject,makeR168ConsentChallenge} from '../src/activation-reconciliation-consent-r168.mjs';
9
+
10
+ const sha=b=>createHash('sha256').update(b).digest('hex');
11
+ const sourceExpected={
12
+ 'activation-reconciliation-audit-r168.mjs':'467268f6f195e9359b819e35c8b418c81e1cb40c9ba04b990fd2656e354c65a5',
13
+ 'activation-reconciliation-consent-r168.mjs':'a82b05e1c521cc77f3398985d6faa1beb0fe8f65595f50bed474227550d462c5'
14
+ };
15
+ const stateRoot=path.join(os.homedir(),'.deadbyte-mcp');
16
+ const lockPath=path.join(stateRoot,'evidence','updates','r65-activation.lock');
17
+ try {
18
+ if(process.platform!=='win32')throw new Error('Windows-only R168 subject');
19
+ for(const [name,expected] of Object.entries(sourceExpected)){
20
+ const bytes=await readFile(new URL('../src/'+name,import.meta.url));
21
+ if(sha(bytes)!==expected)throw new Error('R168 source identity changed: '+name);
22
+ }
23
+ const pinnedBefore=readPinnedEpochLockSnapshot(lockPath);
24
+ if(!pinnedBefore)throw new Error('old claim missing');
25
+ const observation=await inspectR168UnknownWithoutMutation({
26
+ stateRoot,
27
+ expectedOperationId:'c84d7ffa4eee80a2f467acda3ddd9332',
28
+ expectedSubjectSha256:'fba80e0128da9657dcbfcaa27d3a4dc8d9d8939146e81a097293fa5fcb6ae8fc',
29
+ expectedOutcomeFilename:'r65-activation-outcome-1792993876612-cc7c498632c7757d.json',
30
+ expectedContinuityId:'r65-ef42bc84451ce024c4dee736d4047b36',
31
+ expectedPredecessorReleaseId:'v0.15.61-055d1ff0aa99e60b',
32
+ expectedPredecessorManifestSha256:'055d1ff0aa99e60bbf9e9f136b02ebd4fffb8391712560f7607cff30af94dcdf',
33
+ expectedPredecessorGenerationSha256:'f940e794daac2dda097a66301c4b85c4b29a2df7c669e549503c05a2a55ff6c4',
34
+ expectedCandidateManifestSha256:'ad3c091fbbe2bac6d234548724008b67305fd483ad6542f92d5aeb1aa84f5849'
35
+ });
36
+ const pinnedAfter=readPinnedEpochLockSnapshot(lockPath);
37
+ if(!pinnedAfter||pinnedAfter.sha256!==pinnedBefore.sha256||pinnedAfter.fileId!==pinnedBefore.fileId)
38
+ throw new Error('R168 lock changed during subject derivation');
39
+ const subject=createR168ReconciliationSubject({observation,claimSnapshot:pinnedBefore,stateRoot});
40
+ console.log(JSON.stringify({status:'R168_SUBJECT_READY_READONLY',claim_operation_id:subject.claim_operation_id,
41
+ observation_sha256:observation.observation_sha256,subject_sha256:subject.subject_sha256,
42
+ native_claim_sha256:pinnedBefore.sha256,native_claim_file_id:pinnedBefore.fileId,
43
+ verified_signed_events:observation.signed_event_count,
44
+ approval_challenge:makeR168ConsentChallenge(subject),
45
+ owner_signature:'NOT_PRESENT',terminalization_committed:false,activation_authorized:false},null,2));
46
+ }catch(error){
47
+ console.error('R168_SUBJECT_READONLY_BLOCKED '+String(error?.message??error));
48
+ process.exitCode=1;
49
+ }
@@ -0,0 +1,42 @@
1
+ // R172 privileged preflight; read-only. Never invokes native CAS or changes grants.
2
+ import {createHash} from 'node:crypto';
3
+ import {readFile} from 'node:fs/promises';
4
+ import path from 'node:path';
5
+ import {fileURLToPath} from 'node:url';
6
+ import {parseManifest,verifyManifestTreeExact} from '../src/release-slot.mjs';
7
+ import {inspectTrustedInstallation,privilegedEpochInstallFiles} from '../src/privileged-install-trust-r89.mjs';
8
+
9
+ const root=path.dirname(path.dirname(fileURLToPath(import.meta.url)));
10
+ const version='0.15.67',expectedTrain='R172';
11
+ const mandatory=['scripts/r172-protected-preflight.mjs','src/activation-continuity-abort-r172.mjs',
12
+ 'src/activation-lock-r65.mjs','src/activation-preflight-r65.mjs',
13
+ 'src/activation-reconciliation-journal-r168.mjs','src/activation-reconciliation-terminal-r168.mjs',
14
+ 'src/activation-reconciliation-fence-r168.mjs','src/activation-reconciliation-audit-r168.mjs'];
15
+ function denied(reason){throw new Error('R172_'+reason);}
16
+ function actualProtectedRoot(){
17
+ const trusted='C:\\Program Files',rel=path.win32.relative(trusted,root);
18
+ if(!rel||rel==='..'||rel.startsWith('..'+path.win32.sep)||path.win32.isAbsolute(rel))denied('ROOT_UNPROTECTED');
19
+ return root;
20
+ }
21
+ async function main(){
22
+ if(process.platform!=='win32'||process.arch!=='x64')denied('WINDOWS_X64_REQUIRED');
23
+ if(process.argv.length!==2)denied('UNKNOWN_OPTION');
24
+ const verifiedRoot=actualProtectedRoot();
25
+ const pkg=JSON.parse(await readFile(path.join(verifiedRoot,'package.json'),'utf8'));
26
+ const release=JSON.parse(await readFile(path.join(verifiedRoot,'RELEASE.json'),'utf8'));
27
+ if(pkg.name!=='deadbyte-mcp'||pkg.version!==version||release.name!=='deadbyte-mcp'||
28
+ release.version!==version||release.train!==expectedTrain)denied('VERSION_DRIFT');
29
+ const manifest=await readFile(path.join(verifiedRoot,'MANIFEST.SHA256'));
30
+ const entries=parseManifest(manifest.toString('utf8'));
31
+ for(const name of mandatory)if(!entries.has(name))denied('SOURCE_NOT_IN_MANIFEST');
32
+ await verifyManifestTreeExact(verifiedRoot,entries,{allowedExtraPaths:['.npmignore','package-lock.json']});
33
+ const important=[...privilegedEpochInstallFiles(verifiedRoot,process.execPath),
34
+ ...mandatory.map(rel=>path.win32.join(verifiedRoot,...rel.split('/'))),
35
+ path.win32.join(verifiedRoot,'MANIFEST.SHA256'),path.win32.join(verifiedRoot,'RELEASE.json'),
36
+ path.win32.join(verifiedRoot,'package.json')];
37
+ await inspectTrustedInstallation(important);
38
+ const hash=createHash('sha256').update(manifest).digest('hex');
39
+ process.stdout.write(JSON.stringify({status:'R172_PROTECTED_PREFLIGHT_READONLY_PASS',version,train:expectedTrain,
40
+ manifest_sha256:hash,files:entries.size,terminalization_authorized:false,activation_authorized:false})+'\n');
41
+ }
42
+ try{await main();}catch(error){process.stderr.write('R172_PROTECTED_PREFLIGHT=BLOCKED '+String(error?.message??error)+'\n');process.exitCode=1;}
@@ -0,0 +1,108 @@
1
+ import {createHash} from 'node:crypto';
2
+ import {lstat, readFile, readdir} from 'node:fs/promises';
3
+ import path from 'node:path';
4
+ import {createAuthorityEpochStore} from './authority-continuity-r65.mjs';
5
+
6
+ const PLANES = Object.freeze(['process', 'coding', 'autonomous', 'desktop', 'host']);
7
+ const GRANT_FIELD = Object.freeze({
8
+ process: 'grant_file', coding: 'lease_file', autonomous: 'lease_file',
9
+ desktop: 'grant_file', host: 'arm_file'
10
+ });
11
+ const HEX64 = /^[0-9a-f]{64}$/u;
12
+ const CONTINUITY = /^r65-[0-9a-f]{32}$/u;
13
+ const sha = bytes => createHash('sha256').update(bytes).digest('hex');
14
+
15
+ function parse(bytes, label) {
16
+ let text = bytes.toString('utf8');
17
+ if (text.charCodeAt(0) === 0xfeff) text = text.slice(1);
18
+ try { return JSON.parse(text); }
19
+ catch { throw new Error(`R172 ${label} JSON invalid`); }
20
+ }
21
+
22
+ function inside(root, target) {
23
+ const relative = path.relative(path.resolve(root), path.resolve(target));
24
+ return relative === '' || (relative !== '..' && !relative.startsWith(`..${path.sep}`) && !path.isAbsolute(relative));
25
+ }
26
+
27
+ async function optionalBytes(file) {
28
+ try { return await readFile(file); }
29
+ catch (error) { if (error?.code === 'ENOENT') return null; throw error; }
30
+ }
31
+
32
+ export async function verifyR172PredispatchContinuityAbort({outcome, lock, stateRoot} = {}) {
33
+ if (!outcome || !lock || typeof stateRoot !== 'string' || !path.isAbsolute(stateRoot)) return false;
34
+ if (outcome.schema !== 'deadbyte.r65-activation-outcome.v1' ||
35
+ outcome.claim_operation_id !== lock.operation_id ||
36
+ outcome.preflight_subject_sha256 !== lock.subject_sha256 ||
37
+ outcome.outcome?.state !== 'ACTIVATION_OUTCOME_UNKNOWN' ||
38
+ outcome.outcome?.preflight !== 'AUTHORITY_UNVERIFIED' ||
39
+ outcome.update_id !== null || outcome.state_path !== null ||
40
+ !CONTINUITY.test(String(outcome.continuity_update_id ?? '')) ||
41
+ !HEX64.test(String(outcome.candidate_manifest_sha256 ?? '')) ||
42
+ typeof outcome.candidate_version !== 'string' || typeof outcome.predecessor_release_id !== 'string') return false;
43
+
44
+ const created = Date.parse(lock.created_at_utc), observed = Date.parse(outcome.observed_at_utc);
45
+ if (!Number.isFinite(created) || !Number.isFinite(observed) || observed < created || observed - created > 120_000) return false;
46
+ const state = path.resolve(stateRoot), pointerBytes = await readFile(path.join(state, 'active-release.json'));
47
+ const pointer = parse(pointerBytes, 'active pointer');
48
+ if (pointer.release_id !== outcome.predecessor_release_id ||
49
+ !HEX64.test(String(pointer.manifest_sha256 ?? '')) ||
50
+ !HEX64.test(String(pointer.generation_manifest_sha256 ?? ''))) return false;
51
+ const generation = path.resolve(String(pointer.generation_root ?? ''));
52
+ if (!inside(path.join(state, 'generations'), generation)) return false;
53
+
54
+ const updates = path.join(state, 'evidence', 'updates');
55
+ const candidatePrefix = `r27-${outcome.candidate_manifest_sha256.slice(0, 16)}-`;
56
+ const updateEntries = await readdir(updates, {withFileTypes: true});
57
+ if (updateEntries.some(entry => entry.name.startsWith(candidatePrefix))) return false;
58
+
59
+ const runtime = path.join(state, 'runtime');
60
+ const store = createAuthorityEpochStore({runtimeDir: runtime});
61
+ const revoked = new Map();
62
+ for (const plane of PLANES) {
63
+ const audit = await store.auditHistory({plane});
64
+ if (!['PASS', 'EMPTY'].includes(audit.status)) return false;
65
+ const eventsDir = path.join(runtime, 'authority-epochs', plane, 'events');
66
+ let entries;
67
+ try { entries = await readdir(eventsDir, {withFileTypes: true}); }
68
+ catch (error) { if (error?.code === 'ENOENT' && audit.status === 'EMPTY') entries = []; else throw error; }
69
+ if (entries.length !== audit.verified_events || entries.length > 10_000) return false;
70
+ for (const entry of entries) {
71
+ if (!entry.isFile() || entry.isSymbolicLink()) return false;
72
+ const match = /^(\d{16})-([0-9a-f]{64})\.json$/u.exec(entry.name);
73
+ if (!match) return false;
74
+ const bytes = await readFile(path.join(eventsDir, entry.name));
75
+ if (sha(bytes) !== match[2]) return false;
76
+ const event = parse(bytes, `${plane} epoch event`);
77
+ if (event.update_id !== outcome.continuity_update_id) continue;
78
+ const when = Date.parse(event.recorded_at_utc);
79
+ if (event.plane !== plane || event.action !== 'transaction_revoke' ||
80
+ !Number.isFinite(when) || when < created || when > observed || revoked.has(plane)) return false;
81
+ revoked.set(plane, event);
82
+ }
83
+ }
84
+ if (revoked.size < 1) return false;
85
+
86
+ for (const [plane, event] of revoked) {
87
+ const policyFile = path.join(generation, `${plane}-policy.json`);
88
+ const policyBytes = await readFile(policyFile), policy = parse(policyBytes, `${plane} policy`);
89
+ const grantFile = path.resolve(String(policy?.[GRANT_FIELD[plane]] ?? ''));
90
+ if (!inside(runtime, grantFile)) return false;
91
+ const planeStore = createAuthorityEpochStore({runtimeDir: runtime, policyBytes});
92
+ const head = await planeStore.readHead({plane});
93
+ if (!head || head.seq <= event.seq || head.previous_event_sha256 === null) return false;
94
+ if (head.action === 'user_revoke') {
95
+ if (await optionalBytes(grantFile) !== null) return false;
96
+ continue;
97
+ }
98
+ if (!['grant', 'transaction_restore'].includes(head.action)) return false;
99
+ const grantBytes = await optionalBytes(grantFile);
100
+ if (!grantBytes) return false;
101
+ const grant = parse(grantBytes, `${plane} grant`);
102
+ const verified = await planeStore.verifyGrant({
103
+ plane, grantBytes, grantInstanceNonce: grant.instance_nonce, policyBytes
104
+ });
105
+ if (verified.trusted !== true || verified.event_sha256 !== head.event_sha256) return false;
106
+ }
107
+ return true;
108
+ }
@@ -2,6 +2,7 @@ import {createPublicKey,randomBytes} from 'node:crypto';
2
2
  import {mkdir,open,readFile,readdir,unlink} from 'node:fs/promises';
3
3
  import path from 'node:path';
4
4
  import {verifySlotUpdateEnvelope} from './runtime-update.mjs';
5
+ import {verifyR172PredispatchContinuityAbort} from './activation-continuity-abort-r172.mjs';
5
6
 
6
7
  const HEX64=/^[0-9a-f]{64}$/;
7
8
  const OP=/^[0-9a-f]{32}$/;
@@ -130,8 +131,11 @@ async function findTerminalOutcome(updatesDir,lock,stateRoot){
130
131
  catch{continue;}
131
132
  if(value?.claim_operation_id!==lock.operation_id)continue;
132
133
  if(value?.preflight_subject_sha256!==lock.subject_sha256){foreign=true;continue;}
134
+ let continuityAbort=false;
135
+ try{continuityAbort=await verifyR172PredispatchContinuityAbort({outcome:value,lock,stateRoot});}
136
+ catch{continuityAbort=false;}
133
137
  if(isSafePreMutationReject(value,lock)||await isSafeTerminalActivation(value,lock,stateRoot)||
134
- await isSafeSignedPreconditionReject(value,lock,stateRoot))matched=true;
138
+ await isSafeSignedPreconditionReject(value,lock,stateRoot)||continuityAbort)matched=true;
135
139
  else unknown=true;
136
140
  }
137
141
  return {matched,foreign,unknown};