deadbyte-mcp 0.15.68 → 0.15.70

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (87) hide show
  1. package/MANIFEST.SHA256 +85 -38
  2. package/PROVENANCE.json +52 -24
  3. package/README.txt +44 -31
  4. package/RELEASE.json +47 -17
  5. package/SBOM.cdx.json +2 -2
  6. package/TEST-MATRIX.json +44 -2
  7. package/bridge/package.json +1 -1
  8. package/controller/deadbyte-controller.ps1 +65 -1
  9. package/controller/deadbyte-process-policy.json +4 -4
  10. package/docs/R184-package-lock.json +97 -0
  11. package/docs/R186-OWNER-CONSENT-HANDOFF.txt +87 -0
  12. package/package.json +1 -1
  13. package/scripts/install-r186-owner-trust.ps1 +59 -0
  14. package/scripts/r175-focused-runner.mjs +38 -0
  15. package/scripts/r176-log-preview.mjs +18 -0
  16. package/scripts/r177-owner-trust-inventory.ps1 +36 -0
  17. package/scripts/r178-full-regression.mjs +21 -0
  18. package/scripts/r178-npm-ci.mjs +10 -0
  19. package/scripts/r178-npm-preflight.mjs +16 -0
  20. package/scripts/r178-release-gates-readonly.mjs +9 -0
  21. package/scripts/r178-shard-regression.mjs +25 -0
  22. package/scripts/r179-predecessor-registry.mjs +24 -0
  23. package/scripts/r179-protected-inventory.ps1 +21 -0
  24. package/scripts/r186-focused-runner.mjs +22 -0
  25. package/scripts/r186-owner-anchor-inspect.mjs +6 -0
  26. package/scripts/r186-owner-consent-check.mjs +86 -0
  27. package/scripts/r186-owner-grant-cli.mjs +45 -0
  28. package/scripts/r186-owner-key-local.mjs +22 -0
  29. package/scripts/r186-owner-review-export.mjs +34 -0
  30. package/src/deadbyte-cli.mjs +130 -56
  31. package/src/foreground-owner-recovery-r190.mjs +117 -0
  32. package/src/r174-owner-webauthn.mjs +176 -0
  33. package/src/r175-foreground-boundary.mjs +28 -0
  34. package/src/r186-owner-anchor.mjs +12 -0
  35. package/src/r186-owner-consent-preflight.mjs +63 -0
  36. package/src/r186-owner-grant-broker.mjs +96 -0
  37. package/src/r186-owner-local-signing.mjs +25 -0
  38. package/src/r186-owner-request.mjs +40 -0
  39. package/src/r186-owner-review-export.mjs +49 -0
  40. package/src/remote-live-log.mjs +81 -5
  41. package/src/remote-session-guardian.mjs +23 -0
  42. package/src/version.mjs +1 -1
  43. package/test/helpers/r175-active-pointer.mjs +28 -0
  44. package/test/helpers/release-lock.mjs +12 -0
  45. package/test/r129-sdk-security-baseline.test.mjs +2 -1
  46. package/test/r130-release-security-contract.test.mjs +11 -10
  47. package/test/r132-release-identity.test.mjs +12 -11
  48. package/test/r153-prepublish-identity.test.mjs +1 -1
  49. package/test/r155-default-live-log.test.mjs +17 -16
  50. package/test/r160-package-decision-manifest.test.mjs +1 -1
  51. package/test/r160-release-contract.test.mjs +4 -5
  52. package/test/r163-isolated-slot-rollback-restore.test.mjs +8 -8
  53. package/test/r165-live-cli-skew.test.mjs +6 -6
  54. package/test/r167-release-contract.test.mjs +15 -13
  55. package/test/r168-protected-entrypoint.test.mjs +5 -2
  56. package/test/r168-release-contract.test.mjs +4 -3
  57. package/test/r168-remote-one-shot-coherence.test.mjs +2 -1
  58. package/test/r172-release-contract.test.mjs +7 -7
  59. package/test/r173-release-contract.test.mjs +18 -17
  60. package/test/r174-owner-webauthn.test.mjs +144 -0
  61. package/test/r175-cli-flags.test.mjs +23 -0
  62. package/test/r175-foreground-boundary.test.mjs +87 -0
  63. package/test/r175-guardian-owner-boundary.test.mjs +56 -0
  64. package/test/r176-pretty-live-log.test.mjs +54 -0
  65. package/test/r177-windows-absolute-live-path.test.mjs +37 -0
  66. package/test/r179-absolute-path-live-log.test.mjs +40 -0
  67. package/test/r179-release-contract.test.mjs +62 -0
  68. package/test/r186-controller-owner-grant.test.mjs +20 -0
  69. package/test/r186-owner-consent-preflight.test.mjs +98 -0
  70. package/test/r186-owner-enrollment.test.mjs +20 -0
  71. package/test/r186-owner-grant-broker.test.mjs +85 -0
  72. package/test/r186-owner-local-signing.test.mjs +30 -0
  73. package/test/r186-owner-request.test.mjs +28 -0
  74. package/test/r186-owner-review-export.test.mjs +52 -0
  75. package/test/r190-foreground-cli.test.mjs +41 -0
  76. package/test/r190-foreground-owner-recovery.test.mjs +61 -0
  77. package/test/r190-release-contract.test.mjs +33 -0
  78. package/test/r48-release-contract.test.mjs +2 -1
  79. package/test/r51-runtime-coherence.test.mjs +4 -2
  80. package/test/r58-cleanup-retention.test.mjs +5 -12
  81. package/test/r58-concurrent-recovery.test.mjs +5 -1
  82. package/test/r58-stale-owner.test.mjs +9 -3
  83. package/test/r59-tool-surface-compat.test.mjs +1 -1
  84. package/test/remote-live-log-r37.test.mjs +2 -2
  85. package/test/remote-live-log-recovery-r36.test.mjs +1 -1
  86. package/test/remote-session-cli.test.mjs +92 -126
  87. package/node_modules/which/CHANGELOG.md +0 -166
@@ -7,6 +7,7 @@ import path from 'node:path';
7
7
  import { spawn } from 'node:child_process';
8
8
  import { fileURLToPath } from 'node:url';
9
9
  import { DEADBYTE_VERSION } from '../src/version.mjs';
10
+ import {installExactActivePointer} from './helpers/r175-active-pointer.mjs';
10
11
 
11
12
  const root=path.resolve(fileURLToPath(new URL('..',import.meta.url)));
12
13
  const cliPath=path.join(root,'src','deadbyte-cli.mjs');
@@ -35,12 +36,13 @@ async function fixture({failCommand='',statusOnline=false}={}){
35
36
  const launcher=path.join(bin,'DEADBYTE-MCP.ps1');
36
37
  const ps1=`param([string]$Command)\n$ErrorActionPreference='Stop'\nfor($i=0;$i -lt 20;$i++){ try { [IO.File]::AppendAllText($env:DEADBYTE_REMOTE_TEST_TRACE,(\"$Command\" + [Environment]::NewLine),[Text.Encoding]::UTF8); break } catch { if($i -eq 19){ throw }; Start-Sleep -Milliseconds 10 } }\nif ($env:DEADBYTE_REMOTE_TEST_FAIL -and $Command -in ($env:DEADBYTE_REMOTE_TEST_FAIL -split ',')) { $runtime=Join-Path $env:DEADBYTE_STATE_ROOT 'runtime'; New-Item -ItemType Directory -Path $runtime -Force | Out-Null; $failure=@{ schema='deadbyte.controller-start-failure.v1'; command=$Command; failed_at_utc='2026-09-26T00:00:00.000Z'; message='fixture controller evidence detail' } | ConvertTo-Json -Compress; [IO.File]::WriteAllText((Join-Path $runtime 'controller-start-failure.json'),$failure,[Text.Encoding]::UTF8); Write-Error \"forced $Command failure\"; exit 9 }\nswitch ($Command) {\n 'start' { if ($env:DEADBYTE_REMOTE_TEST_SPAWN_CHILD -eq '1') { $node=(Get-Command node.exe).Source; $spawned=Start-Process -FilePath $node -ArgumentList @('-e','setTimeout(()=>process.exit(0),10000)') -PassThru -WindowStyle Hidden -RedirectStandardOutput $env:DEADBYTE_REMOTE_TEST_SPAWN_STDOUT -RedirectStandardError $env:DEADBYTE_REMOTE_TEST_SPAWN_STDERR; if ($env:DEADBYTE_REMOTE_TEST_SPAWN_PID) { [IO.File]::WriteAllText($env:DEADBYTE_REMOTE_TEST_SPAWN_PID,[string]$spawned.Id,[Text.Encoding]::UTF8) } }; if ($env:DEADBYTE_REMOTE_TEST_STARTED) { [IO.File]::WriteAllText($env:DEADBYTE_REMOTE_TEST_STARTED,'1',[Text.Encoding]::UTF8) }; Write-Host 'DEADBYTE MCP = ONLINE' }\n 'status' { if ($env:DEADBYTE_REMOTE_TEST_STATUS_ONLINE -eq '1' -or ($env:DEADBYTE_REMOTE_TEST_STARTED -and (Test-Path -LiteralPath $env:DEADBYTE_REMOTE_TEST_STARTED))) { Write-Host 'DEADBYTE MCP = ONLINE'; Write-Host 'Bridge PID : 1001 [verified]'; Write-Host 'Cloud PID : 1002 [verified]'; Write-Host 'Local ready : True'; Write-Host 'Loop supervisor: RUNNING [verified]' } else { Write-Host 'DEADBYTE MCP = OFFLINE' } }\n 'processstatus' { Write-Host 'Process plane : ARMED [standing-grant]' }\n 'codingstatus' { Write-Host 'Coding plane : ARMED [standing-grant]' }\n 'autonomousstatus' { Write-Host 'Autonomous : ARMED [standing-grant]' }\n 'desktopstatus' { Write-Host 'Desktop plane : DISARMED [disarmed]' }\n 'hoststatus' { Write-Host 'Host plane : DISARMED [disarmed]' }\n 'park' { if ($env:DEADBYTE_REMOTE_TEST_STARTED) { Remove-Item -LiteralPath $env:DEADBYTE_REMOTE_TEST_STARTED -Force -ErrorAction SilentlyContinue }; Write-Host 'DEADBYTE MCP = PARKED' }\n default { Write-Host \"$Command OK\" }\n}\nexit 0\n`;
37
38
  const structured=`function PlaneArmed([string]$name,[bool]$default) { $base=Join-Path $env:DEADBYTE_STATE_ROOT ('runtime\\' + $name); if (Test-Path -LiteralPath ($base + '.disarmed')) { return $false }; if (Test-Path -LiteralPath ($base + '.armed')) { return $true }; return $default }
38
- if ($Command -eq 'status' -and $Json) { $names=@('process','coding','autonomous','desktop','host'); $authority=@{}; foreach($name in $names){ $initial=($name -in @('process','coding','autonomous')) -or (($name -eq 'host') -and ($env:DEADBYTE_REMOTE_TEST_HOST_ARMED -eq '1')) -or (($name -eq 'desktop') -and ($env:DEADBYTE_REMOTE_TEST_DESKTOP_ARMED -eq '1')); $armed=PlaneArmed $name $initial; if($name -eq 'host' -and (($env:DEADBYTE_REMOTE_TEST_JSON_HOST_LIE_ALWAYS -eq '1') -or (($env:DEADBYTE_REMOTE_TEST_JSON_HOST_LIE -eq '1') -and (Test-Path -LiteralPath (Join-Path $env:DEADBYTE_STATE_ROOT 'runtime\\host.disarm-attempt'))))){$armed=$false}; $authority[$name]=@{ configured=$true; armed=$armed; reason=$(if($armed){'standing-grant'}else{'disarmed'}) } }; @{schema='deadbyte.controller-observation.v1';state='ONLINE';local_ready=$true;core_root='fixture';bridge=@{alive=$true;reason='verified'};core=@{alive=$true;reason='verified'};cloud=@{alive=$true;reason='verified'};supervisor=@{running=$true};authority=$authority} | ConvertTo-Json -Depth 5 -Compress; exit 0 }
39
+ if ($Command -eq 'status' -and $Json) { $names=@('process','coding','autonomous','desktop','host'); $authority=@{}; foreach($name in $names){ $initial=(($name -eq 'process') -and ($env:DEADBYTE_REMOTE_TEST_PROCESS_ARMED -eq '1')) -or (($name -eq 'coding') -and ($env:DEADBYTE_REMOTE_TEST_CODING_ARMED -eq '1')) -or (($name -eq 'autonomous') -and ($env:DEADBYTE_REMOTE_TEST_AUTONOMOUS_ARMED -eq '1')) -or (($name -eq 'host') -and ($env:DEADBYTE_REMOTE_TEST_HOST_ARMED -eq '1')) -or (($name -eq 'desktop') -and ($env:DEADBYTE_REMOTE_TEST_DESKTOP_ARMED -eq '1')); $armed=PlaneArmed $name $initial; if($name -eq 'host' -and (($env:DEADBYTE_REMOTE_TEST_JSON_HOST_LIE_ALWAYS -eq '1') -or (($env:DEADBYTE_REMOTE_TEST_JSON_HOST_LIE -eq '1') -and (Test-Path -LiteralPath (Join-Path $env:DEADBYTE_STATE_ROOT 'runtime\\host.disarm-attempt'))))){$armed=$false}; $authority[$name]=@{ configured=$true; armed=$armed; reason=$(if($armed){'standing-grant'}else{'disarmed'}) } }; @{schema='deadbyte.controller-observation.v1';state='ONLINE';local_ready=$true;core_root=$env:DEADBYTE_REMOTE_TEST_CORE_ROOT;bridge=@{alive=$true;reason='verified'};core=@{alive=$true;reason='verified'};cloud=@{alive=$true;reason='verified'};supervisor=@{running=$true};authority=$authority} | ConvertTo-Json -Depth 5 -Compress; exit 0 }
39
40
  if ($Command -eq 'machinetest' -and $env:DEADBYTE_REMOTE_PROBE_MODE) { $hostArmed=PlaneArmed 'host' ($env:DEADBYTE_REMOTE_TEST_HOST_ARMED -eq '1'); $desktopArmed=PlaneArmed 'desktop' ($env:DEADBYTE_REMOTE_TEST_DESKTOP_ARMED -eq '1'); $wantHost=$env:DEADBYTE_REMOTE_PROBE_MODE -in @('host','full'); $wantDesktop=$env:DEADBYTE_REMOTE_PROBE_MODE -in @('desktop','full'); if($hostArmed -ne $wantHost -or $desktopArmed -ne $wantDesktop){ Write-Error 'remote enforcement probe mismatch'; exit 9 } }
40
41
  if ($Command -match '^(arm|disarm)(process|coding|autonomous|desktop|host)$') { $name=$Matches[2]; $base=Join-Path $env:DEADBYTE_STATE_ROOT ('runtime\\' + $name); New-Item -ItemType Directory -Path (Split-Path $base) -Force | Out-Null; if ($Matches[1] -eq 'arm') { [IO.File]::WriteAllText(($base+'.armed'),'1'); Remove-Item -LiteralPath ($base+'.disarmed') -Force -ErrorAction SilentlyContinue } elseif ($env:DEADBYTE_REMOTE_TEST_DISARM_NOOP -ne $name) { [IO.File]::WriteAllText(($base+'.disarmed'),'1'); Remove-Item -LiteralPath ($base+'.armed') -Force -ErrorAction SilentlyContinue } else { [IO.File]::WriteAllText(($base+'.disarm-attempt'),'1') } }
41
42
  switch ($Command) {`;
42
43
  await writeFile(launcher,ps1.replace('param([string]$Command)','param([string]$Command,[switch]$Json)').replace('switch ($Command) {',()=>structured),'utf8');
43
- return {dir,state,trace,env:{...process.env,DEADBYTE_STATE_ROOT:state,DEADBYTE_REMOTE_TEST_TRACE:trace,DEADBYTE_REMOTE_TEST_FAIL:failCommand,DEADBYTE_REMOTE_TEST_STATUS_ONLINE:statusOnline?'1':'0',DEADBYTE_REMOTE_TEST_STARTED:startedMarker,DEADBYTE_REMOTE_TEST_SPAWN_STDOUT:spawnedStdout,DEADBYTE_REMOTE_TEST_SPAWN_STDERR:spawnedStderr,DEADBYTE_REMOTE_TEST_SPAWN_PID:spawnedPid},spawnedPid};
44
+ await installExactActivePointer({stateRoot:state,packageRoot:root,launcherPath:launcher});
45
+ return {dir,state,trace,env:{...process.env,DEADBYTE_STATE_ROOT:state,DEADBYTE_REMOTE_TEST_CORE_ROOT:root,DEADBYTE_REMOTE_TEST_TRACE:trace,DEADBYTE_REMOTE_TEST_FAIL:failCommand,DEADBYTE_REMOTE_TEST_STATUS_ONLINE:statusOnline?'1':'0',DEADBYTE_REMOTE_TEST_STARTED:startedMarker,DEADBYTE_REMOTE_TEST_SPAWN_STDOUT:spawnedStdout,DEADBYTE_REMOTE_TEST_SPAWN_STDERR:spawnedStderr,DEADBYTE_REMOTE_TEST_SPAWN_PID:spawnedPid},spawnedPid};
44
46
  }
45
47
 
46
48
  async function traceCommands(file,{read=readFile,retryDelayMs=10,maxAttempts=20}={}){
@@ -98,21 +100,23 @@ const winTest=isWindows?test:test.skip;
98
100
 
99
101
  winTest('bare remote reuses a fully verified ONLINE runtime without invoking blocking start',async()=>{
100
102
  const fx=await fixture({failCommand:'start',statusOnline:true});
103
+ fx.env.DEADBYTE_REMOTE_TEST_PROCESS_ARMED='1';
104
+ fx.env.DEADBYTE_REMOTE_TEST_CODING_ARMED='1';
105
+ fx.env.DEADBYTE_REMOTE_TEST_AUTONOMOUS_ARMED='1';
101
106
  let child=null;
102
107
  try{
103
- child=spawn(process.execPath,[cliPath,'remote','--operator'],{env:fx.env,stdio:['pipe','pipe','pipe'],windowsHide:true});
108
+ child=spawn(process.execPath,[cliPath,'remote'],{env:fx.env,stdio:['pipe','pipe','pipe'],windowsHide:true});
104
109
  const output=await collect(child);
105
- const ready=await waitFor(fx.trace,commands=>commands.filter(x=>x==='status').length>=3,{timeoutMs:fixtureTimeoutMs});
106
- assert.deepEqual(ready.slice(0,6),['status','status','armprocess','armcoding','armautonomous','status'],output.stderr);
107
- assert.doesNotMatch(ready.join(','),/(^|,)start(,|$)/);
110
+ const ready=await waitFor(fx.trace,commands=>commands.filter(x=>x==='status').length>=2,{timeoutMs:fixtureTimeoutMs});
111
+ assert.equal(ready.filter(x=>/^(?:arm|disarm|start|park)/.test(x)).length,0,output.stderr);
108
112
  assert.equal(child.exitCode,null,'remote session must remain foreground after reusing ONLINE runtime');
109
113
  assert.match(output.stdout,/Runtime already ONLINE; reusing verified bridge\/cloud\/supervisor/);
110
114
  const authDeadline=Date.now()+fixtureTimeoutMs;
111
115
  while(Date.now()<authDeadline&&!/\[AUTH\] process ARMED \| coding ARMED \| autonomous ARMED \| desktop DISARMED \| host DISARMED/.test(output.stdout)) await sleep(50);
112
116
  assert.match(output.stdout,/\[DISPLAY\] ASCII fallback: stdout_not_tty/);
113
117
  assert.match(output.stdout,/\[AUTH\] process ARMED \| coding ARMED \| autonomous ARMED \| desktop DISARMED \| host DISARMED/);
114
- child.kill('SIGINT');
115
- await output.closed;
118
+ child.stdin.end();
119
+ assert.equal((await output.closed).code,0,output.stderr);
116
120
  }finally{
117
121
  if(child&&child.exitCode===null) child.kill('SIGKILL');
118
122
  await safeRm(fx.dir);
@@ -130,28 +134,28 @@ winTest('R72 bare remote reaches READY with every authority disarmed and never a
130
134
  assert.match(output.stdout,/\[READY\]/,output.stderr);
131
135
  assert.match(output.stdout,/\[AUTH\] process DISARMED \| coding DISARMED \| autonomous DISARMED \| desktop DISARMED \| host DISARMED/);
132
136
  const before=await traceCommands(fx.trace);
133
- assert.deepEqual(before.slice(0,6),['status','status','disarmautonomous','disarmcoding','disarmprocess','status']);
134
- assert.equal(before.filter(x=>/^arm(?:process|coding|autonomous|host|desktop)$/.test(x)).length,0,'bare remote must issue zero ARM commands');
137
+ assert.deepEqual(before.slice(0,3),['status','status','machinetest']);
138
+ assert.equal(before.filter(x=>/^(?:arm|disarm)(?:process|coding|autonomous|host|desktop)$/.test(x)).length,0,'bare remote must issue zero authority commands');
135
139
  child.stdin.end();
136
140
  const ended=await boundedCompletion(output.closed);
137
141
  assert.equal(ended.timeout,undefined,'bare remote did not stop');
138
142
  assert.equal(ended.code,0,output.stderr);
139
- assert.equal((await traceCommands(fx.trace)).at(-1),'park');
143
+ assert.equal((await traceCommands(fx.trace)).includes('park'),false);
140
144
  }finally{
141
145
  if(child?.exitCode===null)child.kill('SIGKILL');
142
146
  await safeRm(fx.dir);
143
147
  }
144
148
  });
145
149
 
146
- winTest('bounded remote disarms a stale standing Host grant before READY',async()=>{
150
+ winTest('observation-only remote preserves a stale standing Host grant without mutation',async()=>{
147
151
  const fx=await fixture({statusOnline:true});
148
152
  fx.env.DEADBYTE_REMOTE_TEST_HOST_ARMED='1';
149
153
  let child=null;
150
154
  try{
151
155
  child=spawn(process.execPath,[cliPath,'remote'],{env:fx.env,stdio:['pipe','pipe','pipe'],windowsHide:true});
152
156
  const output=await collect(child);
153
- const commands=await waitFor(fx.trace,items=>items.filter(x=>x==='status').length>=3,{timeoutMs:fixtureTimeoutMs});
154
- assert.ok(commands.indexOf('disarmhost')>commands.indexOf('status'));
157
+ const commands=await waitFor(fx.trace,items=>items.filter(x=>x==='status').length>=2,{timeoutMs:fixtureTimeoutMs});
158
+ assert.equal(commands.some(x=>x==='disarmhost'||x==='armhost'||x==='machinetest'),false);
155
159
  const deadline=Date.now()+fixtureTimeoutMs;
156
160
  while(Date.now()<deadline&&!/\[READY\]/.test(output.stdout)) await sleep(50);
157
161
  assert.match(output.stdout,/\[READY\]/,output.stderr);
@@ -163,45 +167,31 @@ winTest('bounded remote disarms a stale standing Host grant before READY',async(
163
167
  }
164
168
  });
165
169
 
166
- winTest('remote --full verifies all five planes before READY and revokes them on disconnect',async()=>{
170
+ winTest('remote --full refuses implicit authority before controller mutation',async()=>{
167
171
  const fx=await fixture({statusOnline:true});
168
172
  let child=null;
169
173
  try{
170
174
  child=spawn(process.execPath,[cliPath,'remote','--full'],{env:fx.env,stdio:['pipe','pipe','pipe'],windowsHide:true});
171
175
  const output=await collect(child);
172
- const deadline=Date.now()+fixtureTimeoutMs;
173
- while(Date.now()<deadline&&!/\[READY\]/.test(output.stdout)) await sleep(50);
174
- assert.match(output.stdout,/\[AUTH\] process ARMED \| coding ARMED \| autonomous ARMED \| desktop ARMED \| host ARMED/,output.stderr);
175
- assert.match(output.stdout,/\[READY\]/);
176
- const before=await traceCommands(fx.trace);
177
- assert.ok(before.includes('armdesktop')&&before.includes('armhost'));
178
- child.stdin.end();
179
- assert.equal((await output.closed).code,0,output.stderr);
180
- const after=await traceCommands(fx.trace);
181
- assert.deepEqual(after.slice(-8),['disarmautonomous','disarmcoding','disarmprocess','disarmdesktop','disarmhost','status','machinetest','park']);
176
+ assert.notEqual((await output.closed).code,0);
177
+ assert.match(output.stderr,/R175_FOREGROUND_OWNER_BOUNDARY/);
178
+ assert.deepEqual(await traceCommands(fx.trace),[]);
182
179
  }finally{
183
180
  if(child?.exitCode===null) child.kill('SIGKILL');
184
181
  await safeRm(fx.dir);
185
182
  }
186
183
  });
187
184
 
188
- for(const [flag,armedPlane,disarmedPlane] of [['--desktop','desktop','host'],['--host','host','desktop']]){
189
- winTest(`remote ${flag} arms only the requested extra plane`,async()=>{
185
+ for(const flag of ['--desktop','--host']){
186
+ winTest(`remote ${flag} refuses implicit authority before controller mutation`,async()=>{
190
187
  const fx=await fixture({statusOnline:true});
191
188
  let child=null;
192
189
  try{
193
190
  child=spawn(process.execPath,[cliPath,'remote',flag],{env:fx.env,stdio:['pipe','pipe','pipe'],windowsHide:true});
194
191
  const output=await collect(child);
195
- const deadline=Date.now()+fixtureTimeoutMs;
196
- while(Date.now()<deadline&&!/\[READY\]/.test(output.stdout)) await sleep(50);
197
- assert.match(output.stdout,/\[READY\]/,output.stderr);
198
- assert.match(output.stdout,new RegExp(`${armedPlane} ARMED`));
199
- assert.match(output.stdout,new RegExp(`${disarmedPlane} DISARMED`));
200
- const commands=await traceCommands(fx.trace);
201
- assert.ok(commands.includes(`arm${armedPlane}`));
202
- assert.ok(!commands.includes(`arm${disarmedPlane}`));
203
- child.stdin.end();
204
- assert.equal((await output.closed).code,0,output.stderr);
192
+ assert.notEqual((await output.closed).code,0);
193
+ assert.match(output.stderr,/R175_FOREGROUND_OWNER_BOUNDARY/);
194
+ assert.deepEqual(await traceCommands(fx.trace),[]);
205
195
  }finally{
206
196
  if(child?.exitCode===null) child.kill('SIGKILL');
207
197
  await safeRm(fx.dir);
@@ -256,7 +246,7 @@ winTest('remote refuses READY when controller snapshot lies about Host enforceme
256
246
  }
257
247
  });
258
248
 
259
- winTest('foreground cleanup retains ownership when status lies about a no-op Host revoke',async()=>{
249
+ winTest('foreground --full rejection cannot create cleanup ownership evidence',async()=>{
260
250
  const fx=await fixture({statusOnline:true});
261
251
  fx.env.DEADBYTE_REMOTE_TEST_DISARM_NOOP='host';
262
252
  fx.env.DEADBYTE_REMOTE_TEST_JSON_HOST_LIE='1';
@@ -264,43 +254,28 @@ winTest('foreground cleanup retains ownership when status lies about a no-op Hos
264
254
  try{
265
255
  child=spawn(process.execPath,[cliPath,'remote','--full'],{env:fx.env,stdio:['pipe','pipe','pipe'],windowsHide:true});
266
256
  const output=await collect(child);
267
- const deadline=Date.now()+fixtureTimeoutMs;
268
- while(Date.now()<deadline&&!/\[READY\]/.test(output.stdout)) await sleep(50);
269
- assert.match(output.stdout,/\[READY\]/,output.stderr);
270
- child.stdin.end();
271
- assert.notEqual((await output.closed).code,0,'cleanup must fail closed');
272
- assert.match(output.stderr,/cleanup.*verification|enforcement/i);
273
- assert.equal(JSON.parse(await readFile(path.join(fx.state,'runtime','remote-session.json'),'utf8')).schema,'deadbyte.remote-session.v2');
274
- assert.ok(!(await traceCommands(fx.trace)).includes('park'));
257
+ assert.notEqual((await output.closed).code,0,'elevated foreground must fail closed');
258
+ assert.match(output.stderr,/R175_FOREGROUND_OWNER_BOUNDARY/);
259
+ await assert.rejects(()=>readFile(path.join(fx.state,'runtime','remote-session.json'),'utf8'),{code:'ENOENT'});
260
+ assert.deepEqual(await traceCommands(fx.trace),[]);
275
261
  }finally{
276
262
  if(child?.exitCode===null) child.kill('SIGKILL');
277
263
  await safeRm(fx.dir);
278
264
  }
279
265
  });
280
266
 
281
- winTest('bare remote PARKED startup does not wait for a long-lived child holding inherited pipe handles',async()=>{
267
+ winTest('observation-only remote never starts a PARKED runtime or inherited child',async()=>{
282
268
  const fx=await fixture();
283
269
  fx.env.DEADBYTE_REMOTE_TEST_SPAWN_CHILD='1';
284
270
  let child=null;
285
271
  try{
286
- const startedAt=Date.now();
287
- child=spawn(process.execPath,[cliPath,'remote','--operator'],{env:fx.env,stdio:['pipe','pipe','pipe'],windowsHide:true});
272
+ child=spawn(process.execPath,[cliPath,'remote'],{env:fx.env,stdio:['pipe','pipe','pipe'],windowsHide:true});
288
273
  const output=await collect(child);
289
- const afterStart=await waitFor(fx.trace,commands=>commands.length>=3,{timeoutMs:15000});
290
- const elapsed=Date.now()-startedAt;
291
- assert.deepEqual(afterStart.slice(0,3),['status','start','status']);
292
- assert.ok(elapsed<12000,`PARKED startup waited on inherited child pipe handles: ${elapsed}ms`);
293
- const ready=await waitFor(fx.trace,commands=>commands.filter(x=>x==='status').length>=4,{timeoutMs:fixtureTimeoutMs});
294
- assert.deepEqual(ready.slice(0,8),['status','start','status','status','armprocess','armcoding','armautonomous','status']);
295
- assert.equal(child.exitCode,null,'remote session must remain foreground after PARKED startup');
296
- assert.match(output.stdout,/\[BOOT\] Starting bridge\/runtime\.\.\./);
297
- const readyDeadline=Date.now()+fixtureTimeoutMs;
298
- while(Date.now()<readyDeadline&&!/\[OK\] ONLINE/.test(output.stdout)) await sleep(50);
299
- assert.match(output.stdout,/\[OK\] ONLINE/);
300
- child.stdin.end();
301
274
  const ended=await boundedCompletion(output.closed);
302
- assert.equal(ended.timeout,undefined,'remote session did not exit after PARKED startup proof');
303
- assert.equal(ended.code,0,output.stderr);
275
+ assert.equal(ended.timeout,undefined);
276
+ assert.notEqual(ended.code,0);
277
+ assert.match(output.stderr,/R175_FOREGROUND_RUNTIME_NOT_READY/);
278
+ assert.equal((await traceCommands(fx.trace)).includes('start'),false);
304
279
  }finally{
305
280
  if(child?.exitCode===null) child.kill('SIGKILL');
306
281
  try{const pid=Number((await readFile(fx.spawnedPid,'utf8')).trim());if(Number.isInteger(pid)&&pid>0) process.kill(pid,'SIGKILL');}catch{}
@@ -309,17 +284,17 @@ winTest('bare remote PARKED startup does not wait for a long-lived child holding
309
284
  }
310
285
  });
311
286
 
312
- winTest('bare remote uses one unified live timeline, keeps authority semantics, then disarms and parks when the foreground session ends',async()=>{
313
- const fx=await fixture();
287
+ winTest('bare remote uses one live timeline and preserves authority on disconnect',async()=>{
288
+ const fx=await fixture({statusOnline:true});
314
289
  let child=null;
315
290
  try{
316
- child=spawn(process.execPath,[cliPath,'remote','--operator'],{env:fx.env,stdio:['pipe','pipe','pipe'],windowsHide:true});
291
+ child=spawn(process.execPath,[cliPath,'remote'],{env:fx.env,stdio:['pipe','pipe','pipe'],windowsHide:true});
317
292
  const output=await collect(child);
318
- const ready=await waitFor(fx.trace,commands=>commands.filter(x=>x==='status').length>=4);
319
- assert.deepEqual(ready.slice(0,8),['status','start','status','status','armprocess','armcoding','armautonomous','status']);
293
+ const ready=await waitFor(fx.trace,commands=>commands.filter(x=>x==='status').length>=2);
294
+ assert.deepEqual(ready.slice(0,2),['status','status']);
320
295
  assert.equal(child.exitCode,null,'remote session must remain foreground after becoming ready');
321
296
  const tailDeadline=Date.now()+fixtureTimeoutMs;
322
- while(Date.now()<tailDeadline&&(!/\[AUTH\] process ARMED \| coding ARMED \| autonomous ARMED \| desktop DISARMED \| host DISARMED/.test(output.stdout)||
297
+ while(Date.now()<tailDeadline&&(!/\[AUTH\] process DISARMED \| coding DISARMED \| autonomous DISARMED \| desktop DISARMED \| host DISARMED/.test(output.stdout)||
323
298
  !/\[READY\] DEADBYTE MCP online/.test(output.stdout))) await sleep(50);
324
299
  assert.match(output.stdout,/\[DISPLAY\] ASCII fallback: stdout_not_tty/);
325
300
  assert.match(output.stdout,/\[DEVICE\] Starting DEADBYTE MCP/);
@@ -329,7 +304,7 @@ winTest('bare remote uses one unified live timeline, keeps authority semantics,
329
304
  assert.match(output.stdout,/\[BOOT\] Starting local \+ remote runtime/);
330
305
  assert.match(output.stdout,/\[OK\] ONLINE/);
331
306
  assert.match(output.stdout,/\[READY\] DEADBYTE MCP online/);
332
- assert.match(output.stdout,/\[AUTH\] process ARMED \| coding ARMED \| autonomous ARMED \| desktop DISARMED \| host DISARMED/);
307
+ assert.match(output.stdout,/\[AUTH\] process DISARMED \| coding DISARMED \| autonomous DISARMED \| desktop DISARMED \| host DISARMED/);
333
308
  assert.match(output.stdout,/\[LOG\] Live tool calls with individual START\/DONE, sanitized arguments and results\. Transport hidden; signed audit remains on disk\./);
334
309
  assert.doesNotMatch(output.stdout,/Process plane\s*:/);
335
310
  assert.doesNotMatch(output.stdout,/Coding plane\s*:/);
@@ -354,10 +329,9 @@ winTest('bare remote uses one unified live timeline, keeps authority semantics,
354
329
  ].join('\n')+'\n','utf8');
355
330
  const logDeadline=Date.now()+fixtureTimeoutMs;
356
331
  while(Date.now()<logDeadline&&!/^\[.*\] \[FAIL\] Tool call coding_file_read failed \[fedcba98\] 1\.73s$/m.test(output.stdout)) await sleep(50);
357
- assert.match(output.stdout,/\[CALL\] Received tool call \[abcdef12\] coding_tree Arguments:/,'default human view must show bounded sanitized arguments');
358
- assert.match(output.stdout,/^\[.*\] \[DONE\] Tool call coding_tree completed \[abcdef12\] 42ms$/m);
359
- assert.match(output.stdout,/^\[.*\] \[FAIL\] Tool call coding_file_read failed \[fedcba98\] 1\.73s$/m);
360
- assert.equal((output.stdout.match(/\[DONE\] Tool call coding_tree completed \[abcdef12\] 42ms/g)||[]).length,1,'duplicate terminal result must print once');
332
+ assert.match(output.stdout,/\+ PASS\s+coding_tree\s+\| \[abcdef12\]\s+42ms \| projects:src/,'default human view must show bounded path context');
333
+ assert.match(output.stdout,/X FAIL\s+coding_file_read\s+\| \[fedcba98\]\s+1\.73s \| projects:src\/deadbyte-cli\.mjs/);
334
+ assert.equal((output.stdout.match(/\+ PASS\s+coding_tree\s+\| \[abcdef12\]\s+42ms/g)||[]).length,1,'duplicate terminal result must print once');
361
335
  assert.doesNotMatch(output.stdout,/\[cursor r1:/,'full replay cursor belongs in the journal, not every human line');
362
336
  const resultJournal=JSON.parse(await readFile(path.join(fx.state,'runtime','remote-results',`${marker.result_session_id}.json`),'utf8'));
363
337
  assert.deepEqual(resultJournal.events.map(event=>event.seq),[1,2,3,4]);
@@ -375,10 +349,10 @@ winTest('bare remote uses one unified live timeline, keeps authority semantics,
375
349
  const ended=await boundedCompletion(output.closed);
376
350
  assert.equal(ended.timeout,undefined,'remote session did not exit after foreground input closed');
377
351
  const commands=await traceCommands(fx.trace);
378
- assert.deepEqual(commands.slice(-8),['disarmautonomous','disarmcoding','disarmprocess','disarmdesktop','disarmhost','status','machinetest','park']);
352
+ assert.equal(commands.some(command=>/^(?:arm|disarm|park)/.test(command)),false);
379
353
  assert.match(output.stdout,/^DEADBYTE MCP$/m);
380
354
  assert.match(output.stdout,/Live tool calls with individual START\/DONE/i);
381
- assert.match(output.stdout,/\[AUTH\] Revoking session authority/);
355
+ assert.match(output.stdout,/preserving owner standing grants/i);
382
356
  assert.doesNotMatch(output.stdout,/disarmautonomous OK|disarmcoding OK|disarmprocess OK|disarmdesktop OK|disarmhost OK|DEADBYTE MCP = PARKED/);
383
357
  assert.doesNotMatch(output.stdout,/\p{Extended_Pictographic}/u,'piped Windows fallback must remain ASCII-safe');
384
358
  }finally{
@@ -388,12 +362,12 @@ winTest('bare remote uses one unified live timeline, keeps authority semantics,
388
362
  });
389
363
 
390
364
  winTest('remote --resume replays sanitized tool-call and terminal result events after the cursor without duplicates or loss',async()=>{
391
- const fx=await fixture();
365
+ const fx=await fixture({statusOnline:true});
392
366
  let first=null,second=null;
393
367
  try{
394
368
  first=spawn(process.execPath,[cliPath,'remote'],{env:fx.env,stdio:['pipe','pipe','pipe'],windowsHide:true});
395
369
  const firstOutput=await collect(first);
396
- await waitFor(fx.trace,commands=>commands.filter(x=>x==='status').length>=4);
370
+ await waitFor(fx.trace,commands=>commands.filter(x=>x==='status').length>=2);
397
371
  const liveDeadline=Date.now()+3000;
398
372
  while(Date.now()<liveDeadline&&!/\[LOG\] Live tool calls/.test(firstOutput.stdout)) await sleep(50);
399
373
  const marker=JSON.parse(await readFile(path.join(fx.state,'runtime','remote-session.json'),'utf8'));
@@ -451,7 +425,7 @@ winTest('remote --resume replays sanitized tool-call and terminal result events
451
425
  });
452
426
 
453
427
  winTest('remote --verbose enables sanitized request detail while preserving foreground authority cleanup',async()=>{
454
- const fx=await fixture();
428
+ const fx=await fixture({statusOnline:true});
455
429
  let child=null;
456
430
  try{
457
431
  child=spawn(process.execPath,[cliPath,'remote','--verbose'],{env:fx.env,stdio:['pipe','pipe','pipe'],windowsHide:true});
@@ -474,21 +448,21 @@ winTest('remote --verbose enables sanitized request detail while preserving fore
474
448
  assert.equal(ended.timeout,undefined);
475
449
  assert.equal(ended.code,0,output.stderr);
476
450
  const commands=await traceCommands(fx.trace);
477
- assert.deepEqual(commands.slice(-8),['disarmautonomous','disarmcoding','disarmprocess','disarmdesktop','disarmhost','status','machinetest','park']);
451
+ assert.equal(commands.some(command=>/^(?:arm|disarm|park)/.test(command)),false);
478
452
  }finally{
479
453
  if(child?.exitCode===null) child.kill('SIGKILL');
480
454
  await safeRm(fx.dir);
481
455
  }
482
456
  });
483
457
 
484
- winTest('detached guardian revokes authority, parks, and removes the session marker after the owner is hard-killed',async()=>{
485
- const fx=await fixture();
458
+ winTest('detached observation guardian removes its marker without touching authority after owner death',async()=>{
459
+ const fx=await fixture({statusOnline:true});
486
460
  let child=null;
487
461
  try{
488
- child=spawn(process.execPath,[cliPath,'remote','--operator'],{env:fx.env,stdio:['pipe','pipe','pipe'],windowsHide:true});
462
+ child=spawn(process.execPath,[cliPath,'remote'],{env:fx.env,stdio:['pipe','pipe','pipe'],windowsHide:true});
489
463
  const output=await collect(child);
490
- const ready=await waitFor(fx.trace,commands=>commands.filter(x=>x==='status').length>=4);
491
- assert.deepEqual(ready.slice(0,8),['status','start','status','status','armprocess','armcoding','armautonomous','status']);
464
+ const ready=await waitFor(fx.trace,commands=>commands.filter(x=>x==='status').length>=2);
465
+ assert.deepEqual(ready.slice(0,2),['status','status']);
492
466
  const markerPath=path.join(fx.state,'runtime','remote-session.json');
493
467
  const markerDeadline=Date.now()+3000;
494
468
  let marker=null;
@@ -505,8 +479,9 @@ winTest('detached guardian revokes authority, parks, and removes the session mar
505
479
  assert.equal(killed,true,'failed to hard-kill remote owner');
506
480
  await Promise.race([output.closed,sleep(3000)]);
507
481
 
508
- const cleaned=await waitFor(fx.trace,commands=>commands.slice(-8).join(',')==='disarmautonomous,disarmcoding,disarmprocess,disarmdesktop,disarmhost,status,machinetest,park',{timeoutMs:10000});
509
- assert.deepEqual(cleaned.slice(-8),['disarmautonomous','disarmcoding','disarmprocess','disarmdesktop','disarmhost','status','machinetest','park']);
482
+ await sleep(750);
483
+ const cleaned=await traceCommands(fx.trace);
484
+ assert.equal(cleaned.some(command=>/^(?:arm|disarm|park)/.test(command)),false);
510
485
  const markerGoneDeadline=Date.now()+3000;
511
486
  let markerGone=false;
512
487
  while(Date.now()<markerGoneDeadline){
@@ -520,31 +495,24 @@ winTest('detached guardian revokes authority, parks, and removes the session mar
520
495
  }
521
496
  });
522
497
 
523
- winTest('guardian retains ownership marker when disarm exits zero but Host remains armed',async()=>{
498
+ winTest('remote --full cannot create a guardian or mutate Host authority',async()=>{
524
499
  const fx=await fixture({statusOnline:true});
525
500
  fx.env.DEADBYTE_REMOTE_TEST_DISARM_NOOP='host';
526
501
  let child=null;
527
502
  try{
528
503
  child=spawn(process.execPath,[cliPath,'remote','--full'],{env:fx.env,stdio:['pipe','pipe','pipe'],windowsHide:true});
529
504
  const output=await collect(child);
530
- const deadline=Date.now()+fixtureTimeoutMs;
531
- while(Date.now()<deadline&&!/\[READY\]/.test(output.stdout)) await sleep(50);
532
- assert.match(output.stdout,/\[READY\]/,output.stderr);
533
- child.kill('SIGKILL');
534
- await output.closed;
535
- await waitFor(fx.trace,commands=>commands.includes('disarmhost'),{timeoutMs:fixtureTimeoutMs});
536
- await sleep(750);
537
- const marker=JSON.parse(await readFile(path.join(fx.state,'runtime','remote-session.json'),'utf8'));
538
- assert.equal(marker.schema,'deadbyte.remote-session.v2');
539
- const commands=await traceCommands(fx.trace);
540
- assert.ok(!commands.includes('park'),'guardian must not park after unverified revocation');
505
+ assert.notEqual((await output.closed).code,0);
506
+ assert.match(output.stderr,/R175_FOREGROUND_OWNER_BOUNDARY/);
507
+ assert.deepEqual(await traceCommands(fx.trace),[]);
508
+ await assert.rejects(()=>readFile(path.join(fx.state,'runtime','remote-session.json'),'utf8'),{code:'ENOENT'});
541
509
  }finally{
542
510
  if(child?.exitCode===null) child.kill('SIGKILL');
543
511
  await safeRm(fx.dir);
544
512
  }
545
513
  });
546
514
 
547
- winTest('bare remote rolls back authority and parks if one plane fails to arm',async()=>{
515
+ winTest('remote --operator cannot reach authority arming or rollback',async()=>{
548
516
  const fx=await fixture({failCommand:'armcoding'});
549
517
  try{
550
518
  const child=spawn(process.execPath,[cliPath,'remote','--operator'],{env:fx.env,stdio:['ignore','pipe','pipe'],windowsHide:true});
@@ -553,8 +521,8 @@ winTest('bare remote rolls back authority and parks if one plane fails to arm',a
553
521
  assert.equal(ended.timeout,undefined,`failed startup did not terminate; stdout=${output.stdout.slice(-1200)} stderr=${output.stderr.slice(-1200)} trace=${(await traceCommands(fx.trace)).join(',')}`);
554
522
  assert.notEqual(ended.code,0);
555
523
  const commands=await traceCommands(fx.trace);
556
- assert.deepEqual(commands.slice(0,6),['status','start','status','status','armprocess','armcoding']);
557
- assert.deepEqual(commands.slice(-8),['disarmautonomous','disarmcoding','disarmprocess','disarmdesktop','disarmhost','status','machinetest','park']);
524
+ assert.deepEqual(commands,[]);
525
+ assert.match(output.stderr,/R175_FOREGROUND_OWNER_BOUNDARY/);
558
526
  assert.doesNotMatch(output.stdout,/Live logs/i);
559
527
  }finally{await safeRm(fx.dir);}
560
528
  });
@@ -571,7 +539,7 @@ winTest('bare remote retains an owner-unverified stale marker without starting o
571
539
  const ended=await boundedCompletion(output.closed);
572
540
  assert.equal(ended.timeout,undefined);
573
541
  assert.notEqual(ended.code,0);
574
- assert.match(output.stderr,/ownership(?: identity)? unverified.*marker retained/i);
542
+ assert.match(output.stderr,/R179_LEGACY_FOREGROUND_RECOVERY_REQUIRES_EXPLICIT_OWNER_COMMAND/i);
575
543
  assert.deepEqual(await traceCommands(fx.trace),[]);
576
544
  assert.equal(JSON.parse(await readFile(path.join(runtime,'remote-session.json'),'utf8')).pid,2147483000);
577
545
  }finally{
@@ -595,14 +563,14 @@ winTest('PID reuse cannot authorize removal of a marker with a different process
595
563
  const ended=await boundedCompletion(output.closed);
596
564
  assert.equal(ended.timeout,undefined);
597
565
  assert.notEqual(ended.code,0);
598
- assert.match(output.stderr,/owner identity.*marker retained/i);
566
+ assert.match(output.stderr,/R179_LEGACY_FOREGROUND_RECOVERY_REQUIRES_EXPLICIT_OWNER_COMMAND/i);
599
567
  assert.deepEqual(await traceCommands(fx.trace),[]);
600
568
  assert.equal(JSON.parse(await readFile(path.join(runtime,'remote-session.json'),'utf8')).session_nonce,marker.session_nonce);
601
569
  }finally{await safeRm(fx.dir);}
602
570
  });
603
571
 
604
572
  winTest('remote warning lines use deterministic ASCII fallback on piped Windows output',async()=>{
605
- const fx=await fixture();
573
+ const fx=await fixture({statusOnline:true});
606
574
  let child=null;
607
575
  try{
608
576
  child=spawn(process.execPath,[cliPath,'remote'],{env:fx.env,stdio:['pipe','pipe','pipe'],windowsHide:true});
@@ -625,7 +593,7 @@ winTest('remote warning lines use deterministic ASCII fallback on piped Windows
625
593
  });
626
594
 
627
595
  winTest('remote cleanup never removes a marker with changed ownership identity',async()=>{
628
- const fx=await fixture();
596
+ const fx=await fixture({statusOnline:true});
629
597
  let child=null;
630
598
  try{
631
599
  child=spawn(process.execPath,[cliPath,'remote'],{env:fx.env,stdio:['pipe','pipe','pipe'],windowsHide:true});
@@ -651,18 +619,17 @@ winTest('R58 concurrent cold remote claims have one winner and no loser cleanup'
651
619
  const fx=await fixture({statusOnline:true});const children=[],outputs=[];
652
620
  try{
653
621
  for(let i=0;i<3;i++){
654
- const child=spawn(process.execPath,[cliPath,'remote','--operator'],{env:fx.env,stdio:['pipe','pipe','pipe'],windowsHide:true});
622
+ const child=spawn(process.execPath,[cliPath,'remote'],{env:fx.env,stdio:['pipe','pipe','pipe'],windowsHide:true});
655
623
  children.push(child);outputs.push(await collect(child));
656
624
  }
657
- const commands=await waitFor(fx.trace,items=>items.filter(x=>x==='status').length>=3);
658
- assert.ok(commands.filter(x=>x==='status').length>=3,outputs.map(o=>o.stderr).join('\n'));
625
+ const commands=await waitFor(fx.trace,items=>items.filter(x=>x==='status').length>=2);
626
+ assert.ok(commands.filter(x=>x==='status').length>=2,outputs.map(o=>o.stderr).join('\n'));
659
627
  const deadline=Date.now()+5000;
660
628
  while(Date.now()<deadline&&children.filter(c=>c.exitCode===null).length>1)await sleep(50);
661
629
  const live=children.filter(c=>c.exitCode===null);
662
630
  assert.equal(live.length,1,'exactly one verified foreground may survive');
663
631
  const trace=await traceCommands(fx.trace);
664
- for(const arm of ['armprocess','armcoding','armautonomous'])assert.equal(trace.filter(x=>x===arm).length,1);
665
- assert.ok(!trace.some(x=>x.startsWith('disarm')||x==='park'),'losers must not revoke winner authority');
632
+ assert.ok(!trace.some(x=>x.startsWith('arm')||x.startsWith('disarm')||x==='park'),'observers must not mutate authority');
666
633
  for(let i=0;i<children.length;i++)if(children[i]!==live[0])assert.match(outputs[i].stderr,/remote session already (claimed|active)/,'concurrent rejection must have deterministic ownership vocabulary');
667
634
  const marker=JSON.parse(await readFile(path.join(fx.state,'runtime','remote-session.json'),'utf8'));
668
635
  assert.equal(marker.owner_pid,live[0].pid);
@@ -676,10 +643,10 @@ winTest('R58 concurrent cold remote claims have one winner and no loser cleanup'
676
643
  winTest('R58 duplicate remote verifies the real live owner and leaves its authority untouched',async()=>{
677
644
  const fx=await fixture({statusOnline:true});let first=null,second=null;
678
645
  try{
679
- first=spawn(process.execPath,[cliPath,'remote','--operator'],{env:fx.env,stdio:['pipe','pipe','pipe'],windowsHide:true});
646
+ first=spawn(process.execPath,[cliPath,'remote'],{env:fx.env,stdio:['pipe','pipe','pipe'],windowsHide:true});
680
647
  const firstOutput=await collect(first);
681
- const commands=await waitFor(fx.trace,items=>items.filter(x=>x==='status').length>=3);
682
- assert.ok(commands.filter(x=>x==='status').length>=3,firstOutput.stderr);
648
+ const commands=await waitFor(fx.trace,items=>items.filter(x=>x==='status').length>=2);
649
+ assert.ok(commands.filter(x=>x==='status').length>=2,firstOutput.stderr);
683
650
  const ownerReadyDeadline=Date.now()+fixtureTimeoutMs;
684
651
  while(Date.now()<ownerReadyDeadline&&!/\[READY\]/.test(firstOutput.stdout)) await sleep(50);
685
652
  assert.match(firstOutput.stdout,/\[READY\]/,firstOutput.stderr);
@@ -688,7 +655,7 @@ winTest('R58 duplicate remote verifies the real live owner and leaves its author
688
655
  const marker=JSON.parse(await readFile(markerPath,'utf8'));
689
656
  assert.equal(marker.owner_identity.pid,first.pid);
690
657
  assert.equal(marker.guardian_identity.parent_pid,first.pid);
691
- second=spawn(process.execPath,[cliPath,'remote','--operator'],{env:fx.env,stdio:['pipe','pipe','pipe'],windowsHide:true});
658
+ second=spawn(process.execPath,[cliPath,'remote'],{env:fx.env,stdio:['pipe','pipe','pipe'],windowsHide:true});
692
659
  const secondOutput=await collect(second);
693
660
  const stopped=await secondOutput.closed;
694
661
  assert.notEqual(stopped.code,0);
@@ -718,7 +685,7 @@ winTest('bare remote refuses unverified legacy ownership without touching author
718
685
  assert.equal(ended.timeout,undefined);
719
686
  assert.notEqual(ended.code,0);
720
687
  assert.deepEqual(await traceCommands(fx.trace),[]);
721
- assert.match(output.stderr,/remote session ownership unverified/i);
688
+ assert.match(output.stderr,/R179_LEGACY_FOREGROUND_RECOVERY_REQUIRES_EXPLICIT_OWNER_COMMAND/i);
722
689
  }finally{
723
690
  if(child?.exitCode===null) child.kill();
724
691
  await safeRm(fx.dir);
@@ -757,7 +724,7 @@ winTest('explicit remote status keeps the coherent runtime one-shot and silent',
757
724
  }finally{await safeRm(fx.dir);}
758
725
  });
759
726
 
760
- winTest('bare remote surfaces bounded controller-start-failure evidence instead of only exit code',async()=>{
727
+ winTest('bare remote reports runtime-not-ready without invoking start',async()=>{
761
728
  const fx=await fixture({failCommand:'start'});
762
729
  try{
763
730
  const child=spawn(process.execPath,[cliPath,'remote'],{env:fx.env,stdio:['ignore','pipe','pipe'],windowsHide:true});
@@ -765,12 +732,12 @@ winTest('bare remote surfaces bounded controller-start-failure evidence instead
765
732
  const ended=await boundedCompletion(output.closed);
766
733
  assert.equal(ended.timeout,undefined,'failed start did not terminate');
767
734
  assert.notEqual(ended.code,0);
768
- assert.match(output.stderr,/fixture controller evidence detail/);
769
- assert.match(output.stderr,/controller-start-failure/i);
735
+ assert.match(output.stderr,/R175_FOREGROUND_RUNTIME_NOT_READY/);
736
+ assert.equal((await traceCommands(fx.trace)).includes('start'),false);
770
737
  }finally{await safeRm(fx.dir);}
771
738
  });
772
739
 
773
- winTest('failed startup retains primary and cleanup failure plus ownership marker',async()=>{
740
+ winTest('runtime-not-ready observation does not enter cleanup authority paths',async()=>{
774
741
  const fx=await fixture({failCommand:'start,disarmautonomous'});
775
742
  try{
776
743
  const child=spawn(process.execPath,[cliPath,'remote'],{env:fx.env,stdio:['ignore','pipe','pipe'],windowsHide:true});
@@ -778,17 +745,16 @@ winTest('failed startup retains primary and cleanup failure plus ownership marke
778
745
  const ended=await boundedCompletion(output.closed);
779
746
  assert.equal(ended.timeout,undefined,'failed startup did not terminate');
780
747
  assert.notEqual(ended.code,0);
781
- assert.match(output.stderr,/remote activation failed/i);
782
- assert.match(output.stderr,/controller-start-failure/i);
783
- assert.match(output.stderr,/cleanup failed.*disarmautonomous/i);
748
+ assert.match(output.stderr,/R175_FOREGROUND_RUNTIME_NOT_READY/);
749
+ assert.equal((await traceCommands(fx.trace)).some(command=>/^(?:arm|disarm|start|park)/.test(command)),false);
784
750
  const retained=await readFile(path.join(fx.state,'runtime','remote-session.json'),'utf8').then(()=>true,()=>false);
785
- assert.equal(retained,true);
751
+ assert.equal(retained,false);
786
752
  }finally{await safeRm(fx.dir);}
787
753
  });
788
754
 
789
755
 
790
756
  winTest('remote --trace --filter selects presentation without dropping journaled events',async()=>{
791
- const fx=await fixture();
757
+ const fx=await fixture({statusOnline:true});
792
758
  let child=null;
793
759
  try{
794
760
  child=spawn(process.execPath,[cliPath,'remote','--trace','--filter','category=write,status=error,trace=goal-1234'],{
@@ -811,7 +777,7 @@ winTest('remote --trace --filter selects presentation without dropping journaled
811
777
  await writeFile(path.join(auditDir,'segment-00000001.jsonl'),audit.map(JSON.stringify).join('\n')+'\n','utf8');
812
778
  const deadline=Date.now()+4000;
813
779
  while(Date.now()<deadline&&!/machine_fs\.write.*trace=goal-1234/.test(output.stdout)) await sleep(50);
814
- assert.match(output.stdout,/Tool call machine_fs\.write failed \[filter-w\].*trace=goal-123\.\.\. span=filter-w\.\.\..*10ms.*denied/i);
780
+ assert.match(output.stdout,/machine_fs\.write.*\[filter-w\].*10ms.*trace=goal-123\.\.\. span=filter-w\.\.\..*denied/i);
815
781
  assert.doesNotMatch(output.stdout,/coding_file_read.*missing/);
816
782
  assert.doesNotMatch(output.stdout,/SECRET/);
817
783
  const journal=JSON.parse(await readFile(path.join(fx.state,'runtime','remote-results',marker.result_session_id+'.json'),'utf8'));