param(
  [string]$SmokeOutput,
  [int]$SmokePort = 4318,
  [string]$SmokeToken = 'smoke-token',
  [string]$UserDataPath,
  [string]$LogsPath,
  [string]$RuntimeMode = 'auto',
  [switch]$ExpectBundledRuntime,
  [switch]$StartupOnly,
  [string]$SmokePrompt,
  [string]$SmokeSkillId,
  [switch]$PreserveUserData,
  [switch]$PrepareWorkspaceEntryFixture,
  [switch]$ExpectWorkspaceEntry,
  [switch]$ExpectRemoteBundle,
  [string]$WorkspaceProjectId = 'workspace-entry-smoke',
  [string]$WorkspaceProjectName = 'Workspace Entry Smoke',
  [string]$WorkspaceProjectDescription = 'Workspace-entry smoke fixture for desktop project isolation.',
  [string]$WorkspaceMarkerFile = 'AGENT.md',
  [string]$SmokeViewMode = 'chat',
  [string]$SmokeProjectId,
  [string]$SmokeExpertEntryId,
  [switch]$SmokeSendAfterExpertEntry,
  [ValidateSet('continue-home', 'switch-expert', 'dismiss', '')]
  [string]$SmokeSuggestionAction = '',
  [string]$ExpectedBundleSourceUrl,
  [string]$ExpectedBundleConfigVersion,
  [string]$ExpectedBundleFileName,
  [string]$ExpectedBundleSkillId,
  [string]$ExpectedReadmeMarker,
  [string]$UnexpectedReadmeMarker,
  [switch]$UseExistingCloudConfig,
  [int]$TimeoutSeconds = 180
)

$ErrorActionPreference = 'Stop'

$repoRoot = (Resolve-Path (Join-Path $PSScriptRoot '..\..')).Path
$electron = Join-Path $repoRoot 'node_modules\.pnpm\electron@34.5.8\node_modules\electron\dist\electron.exe'
$desktopApp = Join-Path $repoRoot 'apps\desktop'
$rendererUrl = Join-Path $repoRoot 'apps\desktop\dist\renderer\index.html'

if (-not (Test-Path $electron)) {
  throw "Electron executable not found at $electron"
}

if (-not $SmokeOutput) {
  $SmokeOutput = Join-Path $repoRoot '.tmp\electron-smoke-execution-policy.json'
}

if (-not $UserDataPath) {
  $UserDataPath = Join-Path $repoRoot '.tmp\smoke-user-data'
}

if (-not $LogsPath) {
  $LogsPath = Join-Path $repoRoot '.tmp\smoke-logs'
}

$SmokeOutput = [System.IO.Path]::GetFullPath($SmokeOutput)
$UserDataPath = [System.IO.Path]::GetFullPath($UserDataPath)
$LogsPath = [System.IO.Path]::GetFullPath($LogsPath)
$smokeTracePath = $SmokeOutput + '.trace.log'
$smokeStdoutPath = $SmokeOutput + '.stdout.log'
$smokeStderrPath = $SmokeOutput + '.stderr.log'
$workspaceProjectRoot = Join-Path $UserDataPath (Join-Path 'projects' $WorkspaceProjectId)

function Write-Utf8File {
  param([string]$filePath, [string]$content)
  $encoding = New-Object System.Text.UTF8Encoding $false
  [System.IO.File]::WriteAllText($filePath, $content, $encoding)
}

function Write-SmokeFailureOutput {
  param(
    [string]$Message,
    [string]$Stage = 'electron-smoke',
    [int]$ProcessId = 0,
    [string]$ExitCode = ''
  )

  if (Test-Path $SmokeOutput) {
    return
  }

  $traceTail = @()
  if (Test-Path $smokeTracePath) {
    $traceTail = @(Get-Content -LiteralPath $smokeTracePath -Tail 40 | ForEach-Object { [string]$_ })
  }

  $payload = [ordered]@{
    ok = $false
    stage = $Stage
    error = $Message
    finishedAt = (Get-Date).ToUniversalTime().ToString('o')
    smokeOutput = $SmokeOutput
    traceLogPath = if (Test-Path $smokeTracePath) { $smokeTracePath } else { $null }
    stdoutLogPath = if (Test-Path $smokeStdoutPath) { $smokeStdoutPath } else { $null }
    stderrLogPath = if (Test-Path $smokeStderrPath) { $smokeStderrPath } else { $null }
    traceTail = $traceTail
    userDataPath = $UserDataPath
    logsPath = $LogsPath
    processId = if ($ProcessId -gt 0) { $ProcessId } else { $null }
    exitCode = if ([string]::IsNullOrWhiteSpace($ExitCode)) { $null } else { $ExitCode }
    rendererPath = $rendererUrl
    rendererExists = Test-Path $rendererUrl
  }
  Write-Utf8File $SmokeOutput ($payload | ConvertTo-Json -Depth 6)
}

foreach ($pathValue in @($SmokeOutput, $UserDataPath, $LogsPath)) {
  $parent = Split-Path $pathValue -Parent
  if ($parent) {
    New-Item -ItemType Directory -Force -Path $parent | Out-Null
  }
}

if (Test-Path $SmokeOutput) {
  Remove-Item $SmokeOutput -Force
}
if (Test-Path $smokeTracePath) {
  Remove-Item $smokeTracePath -Force -ErrorAction SilentlyContinue
}
if (Test-Path $smokeStdoutPath) {
  Remove-Item $smokeStdoutPath -Force -ErrorAction SilentlyContinue
}
if (Test-Path $smokeStderrPath) {
  Remove-Item $smokeStderrPath -Force -ErrorAction SilentlyContinue
}
if (-not $PreserveUserData -and (Test-Path $UserDataPath)) {
  Remove-Item $UserDataPath -Recurse -Force -ErrorAction SilentlyContinue
}
if (Test-Path $LogsPath) {
  Remove-Item $LogsPath -Recurse -Force -ErrorAction SilentlyContinue
}
New-Item -ItemType Directory -Force -Path $UserDataPath, $LogsPath | Out-Null

if (-not (Test-Path $rendererUrl)) {
  Write-SmokeFailureOutput -Message "Renderer entry was not found. Run corepack pnpm build first. Missing: $rendererUrl" -Stage 'electron-smoke-renderer-missing'
  throw "Renderer entry was not found: $rendererUrl"
}

if ($PrepareWorkspaceEntryFixture) {
  $workspaceManifestRoot = Join-Path $UserDataPath 'manifests'
  $workspaceMemoryRoot = Join-Path $workspaceProjectRoot 'memory'
  New-Item -ItemType Directory -Force -Path $workspaceProjectRoot, $workspaceManifestRoot, $workspaceMemoryRoot | Out-Null

  $timestamp = (Get-Date).ToUniversalTime().ToString('o')
  $projectPayload = [ordered]@{
    id = $WorkspaceProjectId
    name = $WorkspaceProjectName
    description = $WorkspaceProjectDescription
    ready = $true
    boundSkillIds = @()
    updatedAt = $timestamp
    workspaceEntryEnabled = $true
  }
  $projectJsonPath = Join-Path $workspaceProjectRoot 'project.json'
  Write-Utf8File $projectJsonPath ($projectPayload | ConvertTo-Json -Depth 6)

  $markerContents = @(
    '# Workspace Entry Smoke Fixture',
    '',
    'This project exists to validate desktop project-isolated workspace execution.',
    'If asked, confirm the project root and that the request ran inside this workspace.'
  ) -join [Environment]::NewLine
  Write-Utf8File (Join-Path $workspaceProjectRoot $WorkspaceMarkerFile) $markerContents

  $readmeContents = @(
    '# Workspace Entry Smoke',
    '',
    'This fixture should be selected as the active desktop project during smoke validation.'
  ) -join [Environment]::NewLine
  Write-Utf8File (Join-Path $workspaceProjectRoot 'README.md') $readmeContents

  $activeProjectPayload = [ordered]@{
    projectId = $WorkspaceProjectId
  }
  Write-Utf8File (Join-Path $workspaceManifestRoot 'active-project.json') ($activeProjectPayload | ConvertTo-Json -Depth 3)
  Write-Host "Prepared workspace-entry fixture at $workspaceProjectRoot"
}

$env:QJCLAW_RENDERER_URL = $rendererUrl
$env:QJCLAW_SMOKE_OUTPUT = $SmokeOutput
$env:QJCLAW_SECRET_BACKEND = 'file-fallback'
if (-not $StartupOnly -and -not $UseExistingCloudConfig) {
  $env:QJCLAW_SMOKE_CLOUD_API_BASE_URL = "http://127.0.0.1:$SmokePort"
  $env:QJCLAW_SMOKE_AUTH_TOKEN = $SmokeToken
  $env:QJCLAW_SMOKE_RUNTIME_CLOUD_API_KEY = 'smoke-runtime-api-key'
  $env:QJCLAW_RUNTIME_CLOUD_HEARTBEAT_INTERVAL_MS = 1000
  $env:QJCLAW_RUNTIME_CLOUD_CONFIG_SYNC_INTERVAL_MS = 1500
  $env:QJCLAW_RUNTIME_CLOUD_EVENT_FLUSH_INTERVAL_MS = 800
  $env:QJCLAW_RUNTIME_CLOUD_EVENT_BATCH_SIZE = 3
} else {
  Remove-Item Env:QJCLAW_SMOKE_CLOUD_API_BASE_URL -ErrorAction SilentlyContinue
  Remove-Item Env:QJCLAW_SMOKE_AUTH_TOKEN -ErrorAction SilentlyContinue
  Remove-Item Env:QJCLAW_SMOKE_RUNTIME_CLOUD_API_KEY -ErrorAction SilentlyContinue
  Remove-Item Env:QJCLAW_RUNTIME_CLOUD_HEARTBEAT_INTERVAL_MS -ErrorAction SilentlyContinue
  Remove-Item Env:QJCLAW_RUNTIME_CLOUD_CONFIG_SYNC_INTERVAL_MS -ErrorAction SilentlyContinue
  Remove-Item Env:QJCLAW_RUNTIME_CLOUD_EVENT_FLUSH_INTERVAL_MS -ErrorAction SilentlyContinue
  Remove-Item Env:QJCLAW_RUNTIME_CLOUD_EVENT_BATCH_SIZE -ErrorAction SilentlyContinue
}
$env:QJCLAW_USER_DATA_PATH = $UserDataPath
$env:QJCLAW_LOGS_PATH = $LogsPath
if ($RuntimeMode) {
  $env:QJCLAW_RUNTIME_MODE = $RuntimeMode
}
if ($env:QJCLAW_SMOKE_RUNTIME_DIR) {
  $env:QJCLAW_SMOKE_RUNTIME_DIR = [System.IO.Path]::GetFullPath($env:QJCLAW_SMOKE_RUNTIME_DIR)
}
if ($PSBoundParameters.ContainsKey('SmokePrompt')) {
  $env:QJCLAW_SMOKE_PROMPT = $SmokePrompt
}
if ($PSBoundParameters.ContainsKey('SmokeSkillId')) {
  $env:QJCLAW_SMOKE_SKILL_ID = $SmokeSkillId
}
if ($PSBoundParameters.ContainsKey('SmokeViewMode')) {
  $env:QJCLAW_SMOKE_VIEW_MODE = $SmokeViewMode
}
if ($PSBoundParameters.ContainsKey('SmokeProjectId')) {
  $env:QJCLAW_SMOKE_PROJECT_ID = $SmokeProjectId
}
if ($PSBoundParameters.ContainsKey('SmokeExpertEntryId')) {
  if ([string]::IsNullOrWhiteSpace($SmokeExpertEntryId)) {
    Remove-Item Env:QJCLAW_SMOKE_EXPERT_ENTRY_ID -ErrorAction SilentlyContinue
  } else {
    $env:QJCLAW_SMOKE_EXPERT_ENTRY_ID = $SmokeExpertEntryId
  }
}
if ($SmokeSendAfterExpertEntry) {
  $env:QJCLAW_SMOKE_SEND_AFTER_EXPERT_ENTRY = '1'
} else {
  Remove-Item Env:QJCLAW_SMOKE_SEND_AFTER_EXPERT_ENTRY -ErrorAction SilentlyContinue
}
if ($PSBoundParameters.ContainsKey('SmokeSuggestionAction')) {
  if ([string]::IsNullOrWhiteSpace($SmokeSuggestionAction)) {
    Remove-Item Env:QJCLAW_SMOKE_SUGGESTION_ACTION -ErrorAction SilentlyContinue
  } else {
    $env:QJCLAW_SMOKE_SUGGESTION_ACTION = $SmokeSuggestionAction
  }
}
if ($StartupOnly) {
    $env:QJCLAW_SMOKE_STARTUP_ONLY = '1'
  } else {
    Remove-Item Env:QJCLAW_SMOKE_STARTUP_ONLY -ErrorAction SilentlyContinue
  }

try {
  Write-Host "Running Electron smoke with isolated userData at $UserDataPath"
  $electronArgs = @(
    '--disable-gpu',
    '--disable-gpu-compositing',
    '--disable-software-rasterizer',
    '--disable-gpu-sandbox',
    $desktopApp
  )
  $process = Start-Process -FilePath $electron -ArgumentList $electronArgs -PassThru -RedirectStandardOutput $smokeStdoutPath -RedirectStandardError $smokeStderrPath
  $processId = $process.Id

  function Stop-ElectronSmokeProcessTree {
    param([int]$RootProcessId)
    if ($RootProcessId -le 0) {
      return
    }

    try {
      & taskkill /PID $RootProcessId /F /T *>$null
    } catch {
      Stop-Process -Id $RootProcessId -Force -ErrorAction SilentlyContinue
    }
  }

  $deadline = (Get-Date).AddSeconds($TimeoutSeconds)
  while ((Get-Date) -lt $deadline) {
    if (Test-Path $SmokeOutput) {
      break
    }

    if (-not (Get-Process -Id $process.Id -ErrorAction SilentlyContinue)) {
      break
    }

    Start-Sleep -Milliseconds 500
  }

  $alive = Get-Process -Id $process.Id -ErrorAction SilentlyContinue
  if ($alive) {
    Wait-Process -Id $process.Id -Timeout 10 -ErrorAction SilentlyContinue
    $alive = Get-Process -Id $process.Id -ErrorAction SilentlyContinue
  }

  if ($alive) {
    if (Test-Path $SmokeOutput) {
      Write-Host 'Smoke output captured; terminating lingering Electron process.'
      Stop-ElectronSmokeProcessTree -RootProcessId $process.Id
    } else {
      Stop-ElectronSmokeProcessTree -RootProcessId $process.Id
    }
    $alive = Get-Process -Id $process.Id -ErrorAction SilentlyContinue
  }

  if (-not (Test-Path $SmokeOutput)) {
    $exitCode = ''
    if (-not $alive) {
      try {
        $exitCode = [string]$process.ExitCode
      } catch {
        $exitCode = ''
      }
    }
    $failureMessage = if ($alive) {
      "Electron smoke process did not exit within $TimeoutSeconds seconds. Trace: $smokeTracePath"
    } else {
      $exitSuffix = if ([string]::IsNullOrWhiteSpace($exitCode)) { '' } else { " Exit code: $exitCode." }
      "Smoke output file was not created before Electron exited.$exitSuffix Trace: $smokeTracePath"
    }
    $failureStage = if ($alive) { 'electron-smoke-timeout' } else { 'electron-smoke-no-output' }
    Write-SmokeFailureOutput -Message $failureMessage -Stage $failureStage -ProcessId $processId -ExitCode $exitCode
  }

  $expectBundledValue = if ($ExpectBundledRuntime) { 'true' } else { 'false' }
  $startupOnlyValue = if ($StartupOnly) { 'true' } else { 'false' }
  $expectWorkspaceEntryValue = if ($ExpectWorkspaceEntry) { 'true' } else { 'false' }
  $expectRemoteBundleValue = if ($ExpectRemoteBundle) { 'true' } else { 'false' }
  $validator = @"
const fs = require('fs');
const path = require('path');

const [
  smokeOutput,
  expectedUserData,
  expectedLogs,
  runtimeMode,
  expectBundled,
  startupOnly,
  expectWorkspaceEntry,
  expectRemoteBundle,
  workspaceProjectId,
  workspaceProjectName,
  workspaceMarkerFile,
  expectedBundleSourceUrl,
  expectedBundleConfigVersion,
  expectedBundleFileName,
  expectedBundleSkillId,
  expectedReadmeMarker,
  unexpectedReadmeMarker
] = process.argv.slice(1);
const result = JSON.parse(fs.readFileSync(smokeOutput, 'utf8'));
if (!result.ok) {
  const message = result.error || 'Unknown smoke failure.';
  throw new Error('Electron smoke failed: ' + message);
}
const settingsSave = (result.sendResult && result.sendResult.settingsSave) || {};
const expectedModelConfig = settingsSave.expertModelConfig || {};
if (startupOnly === 'true') {
  const startupOnlyResult = result.startupOnlyResult || {};
  const timeline = Array.isArray(startupOnlyResult.timeline) ? startupOnlyResult.timeline : [];
  const finalSnapshot = startupOnlyResult.finalSnapshot || {};
  const initialUi = result.initialState && result.initialState.ui ? result.initialState.ui : {};
  if (timeline.length < 2) {
    throw new Error('Startup-only smoke did not capture enough UI snapshots.');
  }
  const overlaySnapshot = timeline.find((snapshot) => {
    const ui = snapshot && snapshot.ui ? snapshot.ui : {};
    const dom = snapshot && snapshot.dom ? snapshot.dom : {};
    return ui.showStartupOverlay && dom.startupOverlayVisible && !dom.bindEntryVisible;
  });
  const fastPathReachedBindEntry = initialUi.showBindEntry && !initialUi.showStartupOverlay;
  if (!overlaySnapshot && !fastPathReachedBindEntry) {
    throw new Error('Startup-only smoke never observed the startup overlay before binding.');
  }
  const finalUi = finalSnapshot.ui || {};
  const finalDom = finalSnapshot.dom || {};
  const finalWorkspace = finalSnapshot.workspace || {};
  if (!finalUi.showBindEntry || finalUi.showStartupOverlay) {
    throw new Error('Startup-only smoke did not transition to bind-entry after shell warmup.');
  }
  if (!finalDom.bindEntryVisible || finalDom.startupOverlayVisible) {
    throw new Error('Startup-only smoke DOM did not expose the bind-entry-only state.');
  }
  if (!finalUi.shellReady || !finalWorkspace.shellReady) {
    throw new Error('Startup-only smoke reached bind-entry without shellReady=true.');
  }
  if (!finalWorkspace.bindingRequired || finalWorkspace.apiKeyConfigured) {
    throw new Error('Startup-only smoke final workspace unexpectedly reported a bound state.');
  }
  const summary = {
    ok: true,
    smokeOutput,
    runtimeMode,
    expectBundledRuntime: expectBundled === 'true',
    overlayObserved: Boolean(overlaySnapshot),
    initialOverlaySeenAt: overlaySnapshot ? (overlaySnapshot.capturedAt || null) : null,
    finalCapturedAt: finalSnapshot.capturedAt || null,
    snapshotCount: timeline.length,
    shellReady: Boolean(finalWorkspace.shellReady),
    bindingRequired: Boolean(finalWorkspace.bindingRequired),
    runtimeState: String(finalWorkspace.runtimeState || ''),
    startupPhase: String(finalWorkspace.startupPhase || '')
  };
  console.log(JSON.stringify(summary, null, 2));
  process.exit(0);
}
const sendResult = result.sendResult || {};
const streamSmoke = sendResult.streamSmoke || {};
const smokeViewMode = String(sendResult.smokeViewMode || 'chat');
const finalState = result.finalState || {};
if (smokeViewMode === 'skills') {
  if (String(finalState.viewMode || '') !== 'skills') {
    throw new Error('Skills smoke did not end on skills view: ' + String(finalState.viewMode || ''));
  }
  if (typeof sendResult.skillsPageCatalogCount !== 'number') {
    throw new Error('Skills smoke did not report skillsPageCatalogCount.');
  }
  if (typeof sendResult.workspaceSkillCount !== 'number') {
    throw new Error('Skills smoke did not report workspaceSkillCount.');
  }
} else if (smokeViewMode === 'settings') {
  if (String(finalState.viewMode || '') !== 'settings') {
    throw new Error('Settings smoke did not end on settings view: ' + String(finalState.viewMode || ''));
  }
  const settingsSave = sendResult.settingsSave || {};
  const modelConfig = finalState.modelConfig || {};
  const config = finalState.config || {};
  if (!settingsSave.expertModelConfig) {
    throw new Error('Settings smoke did not report saved expertModelConfig.');
  }
  if (String(modelConfig.image && modelConfig.image.baseUrl || '') !== 'https://ark.cn-beijing.volces.com/api/v3/images/generations') {
    throw new Error('Settings smoke did not persist image model baseUrl.');
  }
  if (String(modelConfig.video && modelConfig.video.baseUrl || '') !== 'https://ark.cn-beijing.volces.com/api/v3') {
    throw new Error('Settings smoke did not persist video model baseUrl.');
  }
  if (String(modelConfig.copywriting && modelConfig.copywriting.baseUrl || '') !== String(expectedModelConfig.copywriting && expectedModelConfig.copywriting.baseUrl || 'https://dashscope.aliyuncs.com/compatible-mode/v1')) {
    throw new Error('Settings smoke did not persist copywriting model baseUrl.');
  }
  if (String(modelConfig.copywriting && modelConfig.copywriting.modelId || '') !== String(expectedModelConfig.copywriting && expectedModelConfig.copywriting.modelId || 'qwen3.6-plus')) {
    throw new Error('Settings smoke did not persist copywriting model modelId.');
  }
  if (!Boolean(modelConfig.image && modelConfig.image.apiKeyConfigured)) {
    throw new Error('Settings smoke did not mark image model api key as configured.');
  }
  if (!Boolean(modelConfig.video && modelConfig.video.apiKeyConfigured)) {
    throw new Error('Settings smoke did not mark video model api key as configured.');
  }
  if (!Boolean(modelConfig.copywriting && modelConfig.copywriting.apiKeyConfigured)) {
    throw new Error('Settings smoke did not mark copywriting model api key as configured.');
  }
  if (String(config.runtimeMode || '') !== 'bundled-runtime') {
    throw new Error('Settings smoke unexpectedly changed runtimeMode: ' + String(config.runtimeMode || ''));
  }
} else {
  const executionPolicySource = String(streamSmoke.executionPolicySource || '');
  const sessionSwitchStreamScenario = String(sendResult.smokeScenario || '') === 'session-switch-stream';
  const homeIntentSuggestionScenario = String(sendResult.smokeScenario || '') === 'home-intent-suggestion';
  const statusLabels = Array.isArray(streamSmoke.statusLabels)
    ? streamSmoke.statusLabels.map((value) => String(value || ''))
    : [];
  const workspaceLaunchAccepted = process.env.QJCLAW_SMOKE_ACCEPT_WORKSPACE_LAUNCH === '1'
    && statusLabels.some((label) => label.includes('Launching project workspace'));
  if (sendResult.smokeExpertEntryId) {
    const finalWorkspace = finalState.workspaceSummary || {};
    const experts = finalState.experts || {};
    const expertEntry = sendResult.expertEntry || {};
    const standaloneIds = Array.isArray(experts.standaloneIds)
      ? experts.standaloneIds.map((value) => String(value || '')).sort()
      : [];
    const homeShortcutIds = Array.isArray(experts.homeShortcutIds)
      ? experts.homeShortcutIds.map((value) => String(value || '')).sort()
      : [];
    const standalonePromptAvailableIds = Array.isArray(experts.standalonePromptAvailableIds)
      ? experts.standalonePromptAvailableIds.map((value) => String(value || '')).sort()
      : [];
    const smokeExpertEntryId = String(sendResult.smokeExpertEntryId || '');
    if (!smokeExpertEntryId) {
      throw new Error('Expert-entry smoke did not report smokeExpertEntryId.');
    }
    if (standaloneIds.length !== 2) {
      throw new Error('Expert-entry smoke did not expose exactly 2 standalone experts: ' + JSON.stringify(standaloneIds));
    }
    if (homeShortcutIds.length !== 7) {
      throw new Error('Expert-entry smoke did not expose exactly 7 home shortcuts: ' + JSON.stringify(homeShortcutIds));
    }
    if (standalonePromptAvailableIds.length !== 2) {
      throw new Error('Expert-entry smoke did not expose prompt availability for both standalone experts: ' + JSON.stringify(standalonePromptAvailableIds));
    }
    if (String(expertEntry.expertId || '') !== smokeExpertEntryId) {
      throw new Error('Expert-entry smoke returned mismatched expert id: ' + String(expertEntry.expertId || ''));
    }
    if (String(expertEntry.entryMode || '') === 'standalone') {
      if (String(finalState.viewMode || '') !== 'experts') {
        throw new Error('Standalone expert entry did not switch to experts view.');
      }
      if (String(expertEntry.viewMode || '') !== 'experts') {
        throw new Error('Standalone expert action result did not report experts view.');
      }
      if (!String(expertEntry.currentProjectId || '')) {
        throw new Error('Standalone expert entry did not resolve a project id.');
      }
      if (!standaloneIds.includes(smokeExpertEntryId)) {
        throw new Error('Standalone expert id was not published in standaloneIds: ' + smokeExpertEntryId);
      }
      if (!standalonePromptAvailableIds.includes(smokeExpertEntryId)) {
        throw new Error('Standalone expert id did not report prompt availability: ' + smokeExpertEntryId);
      }
      const streamSessionId = String(streamSmoke.sessionId || sendResult.sessionId || '');
      if (streamSessionId && !streamSessionId.startsWith('project:' + String(expertEntry.currentProjectId || '') + ':')) {
        throw new Error('Standalone expert entry did not send through the expected project session. session=' + streamSessionId + ' expected=' + String(expertEntry.currentProjectId || ''));
      }
    } else if (String(expertEntry.entryMode || '') === 'home-chat-shortcut') {
      if (String(finalState.viewMode || '') !== 'chat') {
        throw new Error('Home shortcut expert entry did not switch to chat view.');
      }
      if (String(expertEntry.viewMode || '') !== 'chat') {
        throw new Error('Home shortcut expert action result did not report chat view.');
      }
      if (!homeShortcutIds.includes(smokeExpertEntryId)) {
        throw new Error('Home shortcut id was not published in homeShortcutIds: ' + smokeExpertEntryId);
      }
      if (String(finalState.ui && finalState.ui.sessionScopeProjectId || '') !== 'home-chat') {
        throw new Error('Home shortcut expert entry changed away from home-chat session scope: ' + String(finalState.ui && finalState.ui.sessionScopeProjectId || ''));
      }
      if (String(expertEntry.sessionScopeProjectId || '') !== 'home-chat') {
        throw new Error('Home shortcut expert post-action session scope mismatch: ' + String(expertEntry.sessionScopeProjectId || ''));
      }
      if (!String(expertEntry.prompt || '').trim()) {
        throw new Error('Home shortcut expert did not provide a starter prompt.');
      }
      if (Number(sendResult.messageCount || 0) !== 0) {
        throw new Error('Home shortcut expert unexpectedly sent messages: ' + Number(sendResult.messageCount || 0));
      }
      if (sendResult.homeIntentSuggestionVisible) {
        throw new Error('Home shortcut expert unexpectedly surfaced home intent suggestion state.');
      }
    } else {
      throw new Error('Expert-entry smoke returned unsupported entryMode: ' + String(expertEntry.entryMode || ''));
    }
  } else if (streamSmoke.phase !== 'completed' && !workspaceLaunchAccepted && !sessionSwitchStreamScenario && !homeIntentSuggestionScenario) {
    throw new Error('Renderer stream smoke did not complete successfully: ' + streamSmoke.phase);
  }
  if (!sendResult.smokeExpertEntryId && !homeIntentSuggestionScenario && streamSmoke.fallbackUsed) {
    throw new Error('Renderer stream smoke fell back to non-streaming sendPrompt.');
  }
  if (!sendResult.smokeExpertEntryId && !homeIntentSuggestionScenario && executionPolicySource !== 'client-config') {
    throw new Error('Unexpected stream execution policy source: ' + executionPolicySource);
  }
  if (sendResult.selectedSkillId && streamSmoke.selectedSkillId !== sendResult.selectedSkillId) {
    throw new Error('Renderer stream selectedSkillId does not match smoke selection.');
  }
  if (!sendResult.smokeExpertEntryId && !homeIntentSuggestionScenario && Number(streamSmoke.startedEventCount || 0) < 1) {
    throw new Error('Renderer stream smoke did not observe a started event.');
  }
  if (!sendResult.smokeExpertEntryId && !workspaceLaunchAccepted && !sessionSwitchStreamScenario && !homeIntentSuggestionScenario && Number(streamSmoke.deltaEventCount || 0) < 1 && !String(streamSmoke.finalContent || '')) {
    throw new Error('Renderer stream smoke did not observe a delta event or final assistant content.');
  }
  if (!sendResult.smokeExpertEntryId && !workspaceLaunchAccepted && !sessionSwitchStreamScenario && !homeIntentSuggestionScenario && Number(streamSmoke.completedEventCount || 0) < 1) {
    throw new Error('Renderer stream smoke did not observe a completed event.');
  }
  if (!sendResult.smokeExpertEntryId && !homeIntentSuggestionScenario && Number(streamSmoke.errorEventCount || 0) !== 0) {
    throw new Error('Renderer stream smoke observed unexpected error events: ' + streamSmoke.errorEventCount);
  }
  if (!sendResult.smokeExpertEntryId && !workspaceLaunchAccepted && !sessionSwitchStreamScenario && !homeIntentSuggestionScenario && !String(streamSmoke.renderedContent || streamSmoke.finalContent || '')) {
    throw new Error('Renderer stream smoke did not render assistant content.');
  }
}
if (String(sendResult.system && sendResult.system.userDataPath) !== expectedUserData) {
  throw new Error('Smoke ran against an unexpected userData path: ' + (sendResult.system && sendResult.system.userDataPath));
}
if (String(sendResult.system && sendResult.system.logsPath) !== expectedLogs) {
  throw new Error('Smoke ran against an unexpected logs path: ' + (sendResult.system && sendResult.system.logsPath));
}
const diagnosticsPath = String(sendResult.diagnostics && sendResult.diagnostics.filePath || '');
const diagnostics = diagnosticsPath && fs.existsSync(diagnosticsPath)
  ? JSON.parse(fs.readFileSync(diagnosticsPath, 'utf8'))
  : null;
const runtimeTelemetry = sendResult.runtimeTelemetryAfterWait || sendResult.runtimeTelemetryBeforeWait || {};
const acceptWorkspaceLaunch = process.env.QJCLAW_SMOKE_ACCEPT_WORKSPACE_LAUNCH === '1';
if (!sendResult.runtimeCloudFetch || sendResult.runtimeCloudFetch.state !== 'ready') {
  throw new Error('Runtime cloud config fetch did not succeed.');
}
if (!['skills', 'settings'].includes(smokeViewMode) && !['session-switch-stream', 'home-intent-suggestion'].includes(String(sendResult.smokeScenario || ''))) {
  if (!diagnosticsPath || !fs.existsSync(diagnosticsPath)) {
    throw new Error('Diagnostics snapshot was not produced by smoke.');
  }
  if (Number(runtimeTelemetry.heartbeatSuccessCount || 0) < 1) {
    throw new Error('Runtime telemetry did not record a successful heartbeat.');
  }
  if (!acceptWorkspaceLaunch && !sendResult.smokeExpertEntryId && Number(runtimeTelemetry.totalAcceptedEventCount || 0) < 3) {
    throw new Error('Runtime telemetry did not accept the expected event batch count: ' + runtimeTelemetry.totalAcceptedEventCount);
  }
  if (!acceptWorkspaceLaunch && !sendResult.smokeExpertEntryId && Number(runtimeTelemetry.configSyncSuccessCount || 0) < 1) {
    throw new Error('Runtime telemetry did not record a successful config sync.');
  }
  if (!diagnostics || !diagnostics.runtimeTelemetry) {
    throw new Error('Diagnostics snapshot did not include runtimeTelemetry.');
  }
}
if (expectBundled === 'true') {
  const runtimeStatus = sendResult.runtimeStatusFinal || sendResult.runtimeStatusAfterProbe || {};
  const runtimeHealth = sendResult.runtimeHealthFinal || sendResult.runtimeHealthAfterProbe || {};
  const initialGatewayStatus = sendResult.initialGatewayStatus || {};
  const finalGatewayStatus = sendResult.finalGatewayStatus || sendResult.status || {};
  const gatewayWasConnected = initialGatewayStatus.state === 'connected' || finalGatewayStatus.state === 'connected';
  if (runtimeStatus.activeMode !== 'bundled-runtime') {
    throw new Error('Bundled runtime did not become active. Active mode: ' + runtimeStatus.activeMode);
  }
  if (!['running', 'stopping'].includes(String(runtimeStatus.processState || ''))) {
    throw new Error('Bundled runtime did not stay available through validation. Process state: ' + runtimeStatus.processState);
  }
  if (runtimeStatus.processState === 'running' && !runtimeHealth.ok) {
    throw new Error('Bundled runtime health check did not report ok after startup.');
  }
  if (runtimeStatus.processState === 'stopping' && String(runtimeHealth.processState || '') !== 'stopping') {
    throw new Error('Bundled runtime entered stopping without matching health state. health=' + String(runtimeHealth.processState || ''));
  }
  if (!runtimeStatus.pythonReady) {
    throw new Error('Bundled runtime did not report a ready Python payload.');
  }
  if (!Array.isArray(runtimeStatus.installedPythonPackages) || runtimeStatus.installedPythonPackages.length < 12) {
    throw new Error('Bundled runtime did not report the expected Python package set.');
  }
  if (!runtimeStatus.installedPythonPackages.some((value) => { const normalized = String(value || '').toLowerCase(); return normalized === 'playwright' || normalized.startsWith('playwright=='); })) {
    throw new Error('Bundled runtime did not report Playwright in the Python package set.');
  }
  if (!runtimeStatus.installedPythonPackages.some((value) => { const normalized = String(value || '').toLowerCase(); return normalized === 'python-dotenv' || normalized.startsWith('python-dotenv=='); })) {
    throw new Error('Bundled runtime did not report python-dotenv in the Python package set.');
  }
  if (!runtimeStatus.installedPythonPackages.some((value) => { const normalized = String(value || '').toLowerCase(); return normalized === 'loguru' || normalized.startsWith('loguru=='); })) {
    throw new Error('Bundled runtime did not report loguru in the Python package set.');
  }
  if (!runtimeStatus.installedPythonPackages.some((value) => { const normalized = String(value || '').toLowerCase(); return normalized === 'pyexecjs' || normalized.startsWith('pyexecjs=='); })) {
    throw new Error('Bundled runtime did not report PyExecJS in the Python package set.');
  }
  for (const packageName of ['certifi', 'openai', 'retry', 'fastapi', 'uvicorn', 'python-multipart', 'pdfplumber']) {
    if (!runtimeStatus.installedPythonPackages.some((value) => { const normalized = String(value || '').toLowerCase(); return normalized === packageName || normalized.startsWith(packageName + '=='); })) {
      throw new Error('Bundled runtime did not report ' + packageName + ' in the Python package set.');
    }
  }
  if (!runtimeStatus.installedPythonPackages.some((value) => { const normalized = String(value || '').toLowerCase(); return normalized === 'pillow' || normalized.startsWith('pillow=='); })) {
    throw new Error('Bundled runtime did not report Pillow in the Python package set.');
  }
  if (!gatewayWasConnected) {
    throw new Error('Gateway never reported connected after bundled runtime startup. initial=' + String(initialGatewayStatus.state || '') + ' final=' + String(finalGatewayStatus.state || ''));
  }
}
let workspaceEntryValidated = false;
if (expectWorkspaceEntry === 'true' && smokeViewMode !== 'skills') {
  const latestStatusLabel = String(streamSmoke.latestStatusLabel || '');
  const statusLabels = Array.isArray(streamSmoke.statusLabels)
    ? streamSmoke.statusLabels.map((value) => String(value || ''))
    : [];
  const workspaceLaunchAccepted = acceptWorkspaceLaunch
    && statusLabels.some((label) => label.includes('Launching project workspace'));
  const assistantContent = String((sendResult.lastAssistantMessage && sendResult.lastAssistantMessage.content) || streamSmoke.finalContent || streamSmoke.renderedContent || '');
  const expectedProjectRoot = path.join(expectedUserData, 'projects', workspaceProjectId);
  const expectedSessionPrefix = 'project:' + workspaceProjectId + ':';
  const selectedSkillId = String(sendResult.selectedSkillId || streamSmoke.selectedSkillId || '');
  const reportedWorkspaceLabel = statusLabels.some((label) => label.toLowerCase().includes('workspace'));
  const reportedSkillRouteLabel = statusLabels.some((label) => label.toLowerCase().includes('routing to skill '));
  if (!reportedWorkspaceLabel && !(expectRemoteBundle === 'true' && reportedSkillRouteLabel)) {
    throw new Error('Workspace-entry smoke did not report a workspace or routed-skill status label. history=' + JSON.stringify(statusLabels) + ' latest=' + latestStatusLabel);
  }
  if (!workspaceLaunchAccepted && !assistantContent.includes('desktop project-isolated workspace')) {
    throw new Error('Workspace-entry smoke did not echo the injected isolated workspace context.');
  }
  if (!workspaceLaunchAccepted && !assistantContent.includes('Current project: ' + workspaceProjectName + ' (' + workspaceProjectId + ')')) {
    throw new Error('Workspace-entry smoke did not reference the expected project identity.');
  }
  if (!workspaceLaunchAccepted && !assistantContent.includes('Project root: ' + expectedProjectRoot)) {
    throw new Error('Workspace-entry smoke did not reference the expected project root.');
  }
  if (!workspaceLaunchAccepted && !assistantContent.includes('Keep project context isolated to this project and session.')) {
    throw new Error('Workspace-entry smoke did not preserve the project isolation instruction.');
  }
  if (selectedSkillId && expectRemoteBundle !== 'true') {
    throw new Error('Workspace-entry smoke unexpectedly selected a skill: ' + selectedSkillId);
  }
  if (!String(sendResult.sessionId || streamSmoke.sessionId || '').startsWith(expectedSessionPrefix)) {
    throw new Error('Workspace-entry smoke did not bind the session to the expected project: ' + String(sendResult.sessionId || streamSmoke.sessionId || ''));
  }
  if (!workspaceLaunchAccepted && Number(streamSmoke.deltaEventCount || 0) < 1) {
    throw new Error('Workspace-entry smoke did not emit a delta event.');
  }
  if (!workspaceLaunchAccepted && Number(sendResult.messageCount || 0) < 2) {
    throw new Error('Workspace-entry smoke did not persist the expected user/assistant message pair.');
  }
  const markerPath = path.join(expectedProjectRoot, workspaceMarkerFile);
  if (!fs.existsSync(markerPath)) {
    throw new Error('Workspace-entry smoke fixture marker file was not found: ' + markerPath);
  }
  const readmePath = path.join(expectedProjectRoot, 'README.md');
  const readmeContent = fs.existsSync(readmePath) ? fs.readFileSync(readmePath, 'utf8') : '';
  if (!workspaceLaunchAccepted && expectedReadmeMarker && !assistantContent.includes(expectedReadmeMarker)) {
    throw new Error('Workspace-entry smoke did not include the expected README marker in assistant content: ' + expectedReadmeMarker);
  }
  if (expectedReadmeMarker && !readmeContent.includes(expectedReadmeMarker)) {
    throw new Error('Workspace-entry smoke did not materialize the expected README marker on disk: ' + expectedReadmeMarker);
  }
  if (!workspaceLaunchAccepted && unexpectedReadmeMarker && assistantContent.includes(unexpectedReadmeMarker)) {
    throw new Error('Workspace-entry smoke still included the stale README marker in assistant content: ' + unexpectedReadmeMarker);
  }
  if (unexpectedReadmeMarker && readmeContent.includes(unexpectedReadmeMarker)) {
    throw new Error('Workspace-entry smoke still materialized the stale README marker on disk: ' + unexpectedReadmeMarker);
  }
  workspaceEntryValidated = true;
}
let remoteBundleValidated = false;
if (expectRemoteBundle === 'true') {
  const workspaceSummary = result.finalState && result.finalState.workspaceSummary
    ? result.finalState.workspaceSummary
    : (result.initialState && result.initialState.workspaceSummary) || {};
  const currentProjectId = String(workspaceSummary.currentProjectId || '');
  const currentProjectName = String(workspaceSummary.currentProjectName || '');
  const bundleManifestPath = path.join(expectedUserData, 'manifests', 'project-bundles.json');
  const activeProjectManifestPath = path.join(expectedUserData, 'manifests', 'active-project.json');
  const projectRootPath = path.join(expectedUserData, 'projects', workspaceProjectId);
  if (currentProjectId !== workspaceProjectId) {
    throw new Error('Remote bundle smoke did not activate the expected project: ' + currentProjectId);
  }
  if (workspaceProjectName && currentProjectName !== workspaceProjectName) {
    throw new Error('Remote bundle smoke did not expose the expected project name: ' + currentProjectName);
  }
  if (!fs.existsSync(bundleManifestPath)) {
    throw new Error('Remote bundle smoke did not produce project-bundles manifest: ' + bundleManifestPath);
  }
  if (!fs.existsSync(activeProjectManifestPath)) {
    throw new Error('Remote bundle smoke did not persist active-project manifest: ' + activeProjectManifestPath);
  }
  if (!fs.existsSync(projectRootPath)) {
    throw new Error('Remote bundle smoke did not materialize project root: ' + projectRootPath);
  }
  const bundleManifest = JSON.parse(fs.readFileSync(bundleManifestPath, 'utf8'));
  const manifestRecord = bundleManifest && bundleManifest[workspaceProjectId];
  if (!manifestRecord || typeof manifestRecord !== 'object') {
    throw new Error('Remote bundle smoke did not record manifest entry for project ' + workspaceProjectId);
  }
  const activeProjectRecord = JSON.parse(fs.readFileSync(activeProjectManifestPath, 'utf8'));
  if (String(activeProjectRecord.projectId || '') !== workspaceProjectId) {
    throw new Error('Remote bundle smoke did not persist the expected active project id: ' + String(activeProjectRecord.projectId || ''));
  }
  if (expectedBundleSourceUrl && String(manifestRecord.sourceUrl || '') !== expectedBundleSourceUrl) {
    throw new Error('Remote bundle manifest sourceUrl mismatch: ' + String(manifestRecord.sourceUrl || ''));
  }
  if (expectedBundleConfigVersion && String(manifestRecord.configVersion || '') !== expectedBundleConfigVersion) {
    throw new Error('Remote bundle manifest configVersion mismatch: ' + String(manifestRecord.configVersion || ''));
  }
  if (expectedBundleFileName && String(manifestRecord.fileName || '') !== expectedBundleFileName) {
    throw new Error('Remote bundle manifest fileName mismatch: ' + String(manifestRecord.fileName || ''));
  }
  if (expectedBundleSkillId && String(manifestRecord.sourceSkillId || '') !== expectedBundleSkillId) {
    throw new Error('Remote bundle manifest sourceSkillId mismatch: ' + String(manifestRecord.sourceSkillId || ''));
  }
  if (String(manifestRecord.projectId || '') !== workspaceProjectId) {
    throw new Error('Remote bundle manifest projectId mismatch: ' + String(manifestRecord.projectId || ''));
  }
  if (!String(manifestRecord.checksum || '')) {
    throw new Error('Remote bundle manifest did not record checksum.');
  }
  remoteBundleValidated = true;
}
const summary = {
  ok: true,
  smokeOutput,
  runtimeMode,
  userDataPath: expectedUserData,
  logsPath: expectedLogs,
  currentProjectId: String(((result.finalState && result.finalState.workspaceSummary) || (result.initialState && result.initialState.workspaceSummary) || {}).currentProjectId || ''),
  currentProjectName: String(((result.finalState && result.finalState.workspaceSummary) || (result.initialState && result.initialState.workspaceSummary) || {}).currentProjectName || ''),
  selectedSkillId: String(sendResult.selectedSkillId || ''),
  executionPolicySource: String(streamSmoke.executionPolicySource || ''),
  executionPolicyModel: String(streamSmoke.executionPolicyModel || ''),
  streamPhase: String(streamSmoke.phase || ''),
  streamStartedEventCount: Number(streamSmoke.startedEventCount || 0),
  streamDeltaEventCount: Number(streamSmoke.deltaEventCount || 0),
  streamCompletedEventCount: Number(streamSmoke.completedEventCount || 0),
  latestStatusLabel: String(streamSmoke.latestStatusLabel || ''),
  statusLabels: Array.isArray(streamSmoke.statusLabels) ? streamSmoke.statusLabels.map((value) => String(value || '')) : [],
  expectedReadmeMarker: String(expectedReadmeMarker || ''),
  unexpectedReadmeMarker: String(unexpectedReadmeMarker || ''),
  runtimeActiveMode: String(sendResult.runtimeStatusAfterProbe && sendResult.runtimeStatusAfterProbe.activeMode || ''),
  runtimeProcessState: String(sendResult.runtimeStatusAfterProbe && sendResult.runtimeStatusAfterProbe.processState || ''),
  runtimeGatewayUrl: String(sendResult.runtimeStatusAfterProbe && sendResult.runtimeStatusAfterProbe.gatewayUrl || ''),
  runtimePythonReady: Boolean(sendResult.runtimeStatusAfterProbe && sendResult.runtimeStatusAfterProbe.pythonReady),
  runtimePythonVersion: String(sendResult.runtimeStatusAfterProbe && sendResult.runtimeStatusAfterProbe.pythonVersion || ''),
  runtimePythonPackages: sendResult.runtimeStatusAfterProbe && sendResult.runtimeStatusAfterProbe.installedPythonPackages || [],
  messageCount: Number(sendResult.messageCount || 0),
  heartbeatSuccessCount: Number(runtimeTelemetry.heartbeatSuccessCount || 0),
  configSyncSuccessCount: Number(runtimeTelemetry.configSyncSuccessCount || 0),
  totalAcceptedEventCount: Number(runtimeTelemetry.totalAcceptedEventCount || 0),
  workspaceEntryValidated,
  remoteBundleValidated,
  diagnosticsPath,
};
console.log(JSON.stringify(summary, null, 2));
"@
  $summary = & node -e $validator $SmokeOutput $UserDataPath $LogsPath $RuntimeMode $expectBundledValue $startupOnlyValue $expectWorkspaceEntryValue $expectRemoteBundleValue $WorkspaceProjectId $WorkspaceProjectName $WorkspaceMarkerFile $ExpectedBundleSourceUrl $ExpectedBundleConfigVersion $ExpectedBundleFileName $ExpectedBundleSkillId $ExpectedReadmeMarker $UnexpectedReadmeMarker
  if ($LASTEXITCODE -ne 0) {
    throw 'Electron smoke validation failed.'
  }
  Write-Output $summary
}
finally {
  Remove-Item Env:QJCLAW_RENDERER_URL -ErrorAction SilentlyContinue
  Remove-Item Env:QJCLAW_SMOKE_OUTPUT -ErrorAction SilentlyContinue
  Remove-Item Env:QJCLAW_SMOKE_CLOUD_API_BASE_URL -ErrorAction SilentlyContinue
  Remove-Item Env:QJCLAW_SMOKE_AUTH_TOKEN -ErrorAction SilentlyContinue
  Remove-Item Env:QJCLAW_SMOKE_RUNTIME_CLOUD_API_KEY -ErrorAction SilentlyContinue
  Remove-Item Env:QJCLAW_RUNTIME_CLOUD_HEARTBEAT_INTERVAL_MS -ErrorAction SilentlyContinue
  Remove-Item Env:QJCLAW_RUNTIME_CLOUD_CONFIG_SYNC_INTERVAL_MS -ErrorAction SilentlyContinue
  Remove-Item Env:QJCLAW_RUNTIME_CLOUD_EVENT_FLUSH_INTERVAL_MS -ErrorAction SilentlyContinue
  Remove-Item Env:QJCLAW_RUNTIME_CLOUD_EVENT_BATCH_SIZE -ErrorAction SilentlyContinue
  Remove-Item Env:QJCLAW_USER_DATA_PATH -ErrorAction SilentlyContinue
  Remove-Item Env:QJCLAW_LOGS_PATH -ErrorAction SilentlyContinue
  Remove-Item Env:QJCLAW_RUNTIME_MODE -ErrorAction SilentlyContinue
  Remove-Item Env:QJCLAW_SMOKE_RUNTIME_DIR -ErrorAction SilentlyContinue
  Remove-Item Env:QJCLAW_SMOKE_PROMPT -ErrorAction SilentlyContinue
  Remove-Item Env:QJCLAW_SMOKE_SKILL_ID -ErrorAction SilentlyContinue
  Remove-Item Env:QJCLAW_SMOKE_VIEW_MODE -ErrorAction SilentlyContinue
  Remove-Item Env:QJCLAW_SMOKE_PROJECT_ID -ErrorAction SilentlyContinue
  Remove-Item Env:QJCLAW_SMOKE_EXPERT_ENTRY_ID -ErrorAction SilentlyContinue
  Remove-Item Env:QJCLAW_SMOKE_SEND_AFTER_EXPERT_ENTRY -ErrorAction SilentlyContinue
  Remove-Item Env:QJCLAW_SMOKE_STARTUP_ONLY -ErrorAction SilentlyContinue
  Remove-Item Env:QJCLAW_SECRET_BACKEND -ErrorAction SilentlyContinue
}

