Files
fullstack-starter/.sisyphus/plans/electrobun-prod-mode.md

36 KiB

Electrobun Desktop: Production Mode via Child Process Architecture

TL;DR

Quick Summary: Redesign the Electrobun desktop app to support production mode by spawning the TanStack Start server as a child process. Currently only dev mode works (hardcoded localhost:3000). The desktop will detect dev/prod mode, spawn the server with PORT=0 in prod, parse the actual port from stdout, and open the BrowserWindow.

Deliverables:

  • Rewritten apps/desktop/src/bun/index.ts with dev/prod mode support
  • Updated apps/desktop/electrobun.config.ts with build.copy and platform configs
  • Cross-workspace build dependency in turbo pipeline
  • Updated apps/desktop/AGENTS.md reflecting new architecture

Estimated Effort: Medium Parallel Execution: YES - 2 waves Critical Path: Tasks 1,2,3 (parallel) → Task 4 → Task 5


Context

Original Request

Redesign the Electrobun desktop app to support production mode. The desktop app should spawn the TanStack Start server as a child process, detect dev vs prod mode at runtime, use system-assigned ports for security, and handle server lifecycle (crash, quit).

Confirmed Decisions

  • Architecture: Desktop spawns server as child process via Bun.spawn
  • Server artifact: .output/server/index.mjs (not compiled binary) — Electrobun already bundles Bun
  • Port strategy: PORT=0 (system-assigned), HOST=127.0.0.1
  • Dev/Prod detection: process.env.ELECTROBUN_BUILD_ENV (see Defaults Applied below)
  • Target platforms: All (Linux, macOS, Windows)
  • DATABASE_URL: Pass-through via env var, no special handling
  • Crash handling: MVP — log error to stderr, exit process

Research Findings

Electrobun APIs (verified from source code):

  • build.copy supports paths outside the project directory (e.g., ../server/.output). Source paths are resolved relative to the project root. Destinations map into Resources/app/ in the bundle.
  • PATHS exported from electrobun/bun provides RESOURCES_FOLDER (absolute path to Resources/) and VIEWS_FOLDER (Resources/app/views/).
  • process.execPath in a bundled Electrobun app points to the bundled Bun binary.
  • Electrobun.events.on('before-quit', callback) fires before app quit. Callback receives an event with response({ allow: false }) to cancel quit.
  • ELECTROBUN_BUILD_ENV is set by the Electrobun CLI: "dev" for electrobun dev, "stable" for electrobun build --env=stable.

Server startup behavior (verified from built output):

  • .output/server/index.mjs uses Bun.serve via the h3+srvx adapter (Nitro bun preset).
  • Startup log format: ➜ Listening on: http://<address>:<port>/
  • The log uses the actual assigned address/port, not the requested one. So PORT=0 will log the real port.
  • DATABASE_URL is validated at startup via Zod (@t3-oss/env-core). Missing = immediate crash.
  • The .output/server/ directory contains index.mjs plus _libs/ with bundled dependencies.

Turbo pipeline:

  • Root turbo.json has build.dependsOn: ["^build"] which only builds workspace dependencies.
  • Desktop does NOT depend on server in package.json, so ^build won't trigger server build.
  • Need explicit cross-workspace dependency via desktop's turbo.json.

Metis Review

Identified Gaps (addressed):

  • Dev/prod detection mechanism: Switched from custom ELECTROBUN_DEV to built-in ELECTROBUN_BUILD_ENV
  • Server startup timeout: Added explicit timeout with error reporting
  • Port parsing failure: Plan includes fallback and error handling
  • Server crash during runtime: Watching subprocess.exited promise
  • cwd for spawned server: Must set to server directory for relative import resolution
  • Cross-platform considerations: ELECTROBUN_BUILD_ENV works everywhere (no cross-env needed)

Unknowns Resolved

Unknown Resolution
Does build.copy support paths outside project? YES — uses cpSync with source resolved from project root. ../server/.output works.
Runtime API for resolving bundled resource paths? PATHS.RESOURCES_FOLDER from electrobun/bun. Copied files land in Resources/app/{dest}/.
Does Nitro log actual port with PORT=0? YES — format: ➜ Listening on: http://<addr>:<port>/ via h3+srvx adapter.
How does Electrobun detect dev mode? ELECTROBUN_BUILD_ENV env var set by CLI. Values: dev, canary, stable.

Work Objectives

Core Objective

Enable the Electrobun desktop app to run in production mode by spawning the TanStack Start server as a managed child process, while preserving existing dev mode behavior.

Concrete Deliverables

  • apps/desktop/src/bun/index.ts — Complete rewrite with dual-mode support
  • apps/desktop/electrobun.config.tsbuild.copy + all-platform configs
  • apps/desktop/turbo.json — Cross-workspace build dependency
  • apps/desktop/AGENTS.md — Accurate documentation of new architecture

Definition of Done

  • bun typecheck passes from monorepo root (zero errors)
  • bun build from root succeeds: server builds first, then desktop bundles server output
  • bun dev from root still starts both apps (dev mode preserved)
  • Desktop index.ts has zero hardcoded URLs (all dynamic)

Must Have

  • Dev mode: poll external localhost:3000, open window when ready (existing behavior, refactored)
  • Prod mode: spawn server via Bun.spawn, parse port from stdout, open window
  • Server bound to 127.0.0.1 only (no network exposure)
  • PORT=0 for system-assigned port (no conflicts)
  • Server process killed on app quit (via before-quit event)
  • Server crash detection (watch exited promise, log error, exit app)
  • Startup timeout with clear error message
  • Server cwd set to its own directory (for relative import resolution)
  • DATABASE_URL passed through from parent environment

Must NOT Have (Guardrails)

  • No hardcoded port numbers (not even 3000 — use a named constant DEV_SERVER_URL)
  • No as any, @ts-ignore, or @ts-expect-error
  • No empty catch blocks — always handle or re-throw
  • No npm, npx, node — Bun only
  • No manual useMemo/useCallback (not relevant here, but per project rules)
  • No suppressed type errors — fix them properly
  • No custom env var for dev detection — use built-in ELECTROBUN_BUILD_ENV
  • No compiled server binary — use .output/server/index.mjs with bundled Bun
  • Do NOT edit apps/server/ — only apps/desktop/ files change
  • Do NOT add @furtherverse/server as a package dependency of desktop (use turbo cross-workspace dependency instead)

Verification Strategy

UNIVERSAL RULE: ZERO HUMAN INTERVENTION

ALL tasks in this plan MUST be verifiable WITHOUT any human action. Every criterion is verified by running a command or using a tool.

Test Decision

  • Infrastructure exists: NO (no test framework in this project)
  • Automated tests: NO (per project state — AGENTS.md says "No test framework configured yet")
  • Framework: None
  • Agent-Executed QA: ALWAYS (mandatory for all tasks)

Execution Strategy

Parallel Execution Waves

Wave 1 (Start Immediately — all independent, different files):
├── Task 1: Update electrobun.config.ts (build.copy + platform configs)
├── Task 2: Update turbo.json (cross-workspace build dependency)
└── Task 3: Rewrite index.ts (complete dev/prod mode implementation)

Wave 2 (After Wave 1 — needs final state of all files):
├── Task 4: Rewrite AGENTS.md (documentation reflecting new architecture)
└── Task 5: End-to-end verification (typecheck + build pipeline)

Dependency Matrix

Task Depends On Blocks Can Parallelize With
1 None 4, 5 2, 3
2 None 4, 5 1, 3
3 None 4, 5 1, 2
4 1, 2, 3 5 None
5 1, 2, 3, 4 None None (final)

Agent Dispatch Summary

Wave Tasks Recommended Agents
1 1, 2, 3 Tasks 1,2: delegate_task(category="quick"). Task 3: delegate_task(category="unspecified-high")
2 4, 5 Task 4: delegate_task(category="writing"). Task 5: delegate_task(category="quick")

TODOs

  • 1. Update apps/desktop/electrobun.config.ts — Add build.copy and platform configs

    What to do:

    • Add build.copy to include the server's Nitro build output in the desktop bundle:
      copy: {
        '../server/.output': 'server-output',
      }
      
      This copies apps/server/.output/ (the entire Nitro build output) into Resources/app/server-output/ in the Electrobun bundle. The full server entry point will be at Resources/app/server-output/server/index.mjs.
    • Add macOS platform config block (currently only linux exists):
      macOS: {
        bundleCEF: true,
      }
      
    • Add windows platform config block:
      windows: {
        bundleCEF: true,
      }
      
    • Verify the exact property names by checking Electrobun's ElectrobunConfig type definition. The linux block already uses bundleCEF: true, so follow the same pattern for other platforms. If the type doesn't support macOS/windows yet, skip those and leave a // TODO: comment explaining what's needed.
    • Preserve existing config values exactly (app name, identifier, version, bun entrypoint, linux config).

    Must NOT do:

    • Do not change the app name, identifier, or version
    • Do not change the bun entrypoint path
    • Do not remove the existing linux config
    • Do not add dependencies or scripts

    Recommended Agent Profile:

    • Category: quick
      • Reason: Single file, small config change, clear specification
    • Skills: []
      • No specialized skills needed — straightforward TypeScript config edit
    • Skills Evaluated but Omitted:
      • frontend-ui-ux: No UI work involved

    Parallelization:

    • Can Run In Parallel: YES
    • Parallel Group: Wave 1 (with Tasks 2, 3)
    • Blocks: Tasks 4, 5
    • Blocked By: None (can start immediately)

    References:

    Pattern References (existing code to follow):

    • apps/desktop/electrobun.config.ts — Current config structure. The linux.bundleCEF: true pattern should be replicated for other platforms. The build.bun.entrypoint key shows where build config lives.

    API/Type References (contracts to implement against):

    • The ElectrobunConfig type from electrobun — imported via import type { ElectrobunConfig } from 'electrobun'. Check its definition (likely in node_modules/electrobun/) to verify exact property names for copy, macOS, windows.

    External References:

    • Electrobun build.copy syntax: copies source (relative to project root) into Resources/app/{dest}/ in the bundle. Uses cpSync with dereference: true.

    WHY Each Reference Matters:

    • electrobun.config.ts: You're editing this file — need to know its current shape to preserve existing values
    • ElectrobunConfig type: Must match the type definition exactly — don't guess property names

    Acceptance Criteria:

    • build.copy key exists with '../server/.output': 'server-output' mapping
    • Platform configs added for all three platforms (or TODO comments if types don't support them)
    • Existing config values unchanged (app.name = 'Desktop', etc.)
    • File passes bun typecheck (no type errors)

    Agent-Executed QA Scenarios:

    Scenario: Config file is valid TypeScript with correct types
      Tool: Bash
      Preconditions: None
      Steps:
        1. Run: bun typecheck (from apps/desktop/)
        2. Assert: Exit code 0
        3. Assert: No errors mentioning electrobun.config.ts
      Expected Result: TypeScript compilation succeeds
      Evidence: Terminal output captured
    
    Scenario: build.copy key has correct structure
      Tool: Bash (grep)
      Preconditions: File has been edited
      Steps:
        1. Read apps/desktop/electrobun.config.ts
        2. Assert: Contains '../server/.output'
        3. Assert: Contains 'server-output'
        4. Assert: File still contains 'satisfies ElectrobunConfig'
      Expected Result: Config has copy mapping and type annotation
      Evidence: File contents
    

    Commit: YES (groups with 2)

    • Message: feat(desktop): add build.copy for server bundle and platform configs
    • Files: apps/desktop/electrobun.config.ts
    • Pre-commit: bun typecheck (from apps/desktop/)

  • 2. Update apps/desktop/turbo.json — Add cross-workspace build dependency

    What to do:

    • Add dependsOn to the existing build task to ensure the server builds before the desktop:
      {
        "tasks": {
          "build": {
            "dependsOn": ["@furtherverse/server#build"],
            "outputs": ["build/**", "artifacts/**"]
          }
        }
      }
      
    • This tells Turbo: "before running build for @furtherverse/desktop, first run build for @furtherverse/server."
    • This ensures apps/server/.output/ exists when electrobun build runs and tries to build.copy from ../server/.output.
    • Preserve the existing outputs array exactly.

    Must NOT do:

    • Do not modify the root turbo.json — only apps/desktop/turbo.json
    • Do not remove existing outputs
    • Do not add other tasks or change other config

    Recommended Agent Profile:

    • Category: quick
      • Reason: Single file, one-line JSON change
    • Skills: []
      • No specialized skills needed
    • Skills Evaluated but Omitted:
      • git-master: Commit will be grouped with Task 1

    Parallelization:

    • Can Run In Parallel: YES
    • Parallel Group: Wave 1 (with Tasks 1, 3)
    • Blocks: Tasks 4, 5
    • Blocked By: None (can start immediately)

    References:

    Pattern References (existing code to follow):

    • apps/desktop/turbo.json — Current file with build.outputs already defined. You're adding dependsOn alongside it.
    • turbo.json (root) — Shows existing turbo patterns like build.dependsOn: ["^build"]. The root already uses ^build for workspace dependencies, but since desktop doesn't list server as a package dependency, we need an explicit cross-workspace reference.

    API/Type References:

    • Turbo dependsOn syntax: "@furtherverse/server#build" means "run the build task in the @furtherverse/server workspace".

    Documentation References:

    • apps/server/package.json — The package name is @furtherverse/server (verify this is the exact name used in the dependsOn reference).

    WHY Each Reference Matters:

    • apps/desktop/turbo.json: You're editing this file — preserve existing outputs
    • apps/server/package.json: Need exact package name for the cross-workspace reference
    • Root turbo.json: Context for existing turbo patterns in this project

    Acceptance Criteria:

    • apps/desktop/turbo.json has dependsOn: ["@furtherverse/server#build"] in the build task
    • Existing outputs array is preserved
    • Valid JSON (no syntax errors)

    Agent-Executed QA Scenarios:

    Scenario: turbo.json is valid JSON with correct structure
      Tool: Bash
      Preconditions: File has been edited
      Steps:
        1. Run: bun -e "JSON.parse(require('fs').readFileSync('apps/desktop/turbo.json', 'utf8'))"
        2. Assert: Exit code 0 (valid JSON)
        3. Read the file and verify structure contains both dependsOn and outputs
      Expected Result: Valid JSON with both keys present
      Evidence: Terminal output captured
    
    Scenario: Turbo resolves the cross-workspace dependency
      Tool: Bash
      Preconditions: turbo.json updated
      Steps:
        1. Run: bunx turbo build --dry-run --filter=@furtherverse/desktop (from monorepo root)
        2. Assert: Output shows @furtherverse/server#build runs BEFORE @furtherverse/desktop#build
      Expected Result: Server build is listed as a dependency in the dry-run output
      Evidence: Terminal output showing task execution order
    

    Commit: YES (groups with 1)

    • Message: feat(desktop): add build.copy for server bundle and platform configs
    • Files: apps/desktop/turbo.json
    • Pre-commit: Valid JSON check

  • 3. Rewrite apps/desktop/src/bun/index.ts — Complete dev/prod mode implementation

    What to do:

    This is the core task. Completely rewrite index.ts to support both dev and prod modes. The new file should have this structure:

    A. Imports and Constants:

    import Electrobun, { BrowserWindow } from 'electrobun/bun'
    // Import PATHS — verify exact import syntax from electrobun/bun type definitions
    // It may be: import { PATHS } from 'electrobun/bun'
    // Or it may be on the Electrobun default export: Electrobun.PATHS
    // CHECK the type definitions in node_modules/electrobun/ before writing
    import { join, dirname } from 'path'
    
    const DEV_SERVER_URL = 'http://localhost:3000'
    const SERVER_READY_TIMEOUT_MS = 30_000
    const PORT_PATTERN = /Listening on:?\s*https?:\/\/[^\s:]+:(\d+)/
    

    B. isDev() function:

    • Check process.env.ELECTROBUN_BUILD_ENV === 'dev'
    • If ELECTROBUN_BUILD_ENV is not set, default to true (dev mode) — safe fallback
    • Return a boolean

    C. getServerEntryPath() function:

    • Use PATHS.RESOURCES_FOLDER (or equivalent) to resolve the bundled server entry
    • Path: join(PATHS.RESOURCES_FOLDER, 'app', 'server-output', 'server', 'index.mjs')
    • IMPORTANT: Verify PATHS.RESOURCES_FOLDER points to Resources/ and that build.copy destinations land in Resources/app/. If the pathing is different, adjust accordingly. The executor MUST verify by checking Electrobun's source or type definitions.

    D. waitForServer(url, timeoutMs) function (preserved from current code):

    • Polls a URL with fetch HEAD requests
    • Returns true when server responds with response.ok
    • Returns false on timeout
    • Uses Bun.sleep(100) between attempts
    • Catches fetch errors silently (server not up yet)

    E. spawnServer() function (NEW — the critical piece):

    • Returns a Promise<{ process: Subprocess; url: string }>
    • Implementation:
      1. Resolve the server entry path via getServerEntryPath()
      2. Resolve the server directory via dirname(serverEntryPath) — used as cwd
      3. Spawn with Bun.spawn:
        const serverProc = Bun.spawn([process.execPath, serverEntryPath], {
          cwd: serverDir,
          env: {
            ...process.env,
            PORT: '0',
            HOST: '127.0.0.1',
          },
          stdout: 'pipe',
          stderr: 'pipe',
        })
        
      4. Read stdout as a stream to find the port:
        • Use serverProc.stdout (a ReadableStream<Uint8Array>)
        • Create a reader, accumulate chunks into a text buffer
        • Test buffer against PORT_PATTERN regex after each chunk
        • When match found: extract port, resolve promise with { process: serverProc, url: 'http://127.0.0.1:${port}' }
      5. Implement a timeout:
        • Use setTimeout to reject the promise after SERVER_READY_TIMEOUT_MS
        • On timeout, kill the server process before rejecting
      6. Handle early exit:
        • If stdout ends (stream done) before port is found, reject with error
        • Include any stderr output in the error message for debugging

    F. main() async function:

    • Log startup message
    • Branch on isDev():
      • Dev mode:
        1. Log: "Dev mode: waiting for external server..."
        2. Call waitForServer(DEV_SERVER_URL)
        3. If timeout: log error with instructions ("Run: bun dev in apps/server"), process.exit(1)
        4. Set serverUrl = DEV_SERVER_URL
      • Prod mode:
        1. Log: "Production mode: starting embedded server..."
        2. Call spawnServer()
        3. If error: log error, process.exit(1)
        4. Store returned process and url
    • Create BrowserWindow with the resolved serverUrl:
      new BrowserWindow({
        title: 'Furtherverse',
        url: serverUrl,
        frame: { x: 100, y: 100, width: 1200, height: 800 },
        renderer: 'cef',
      })
      
    • Register lifecycle handlers:
      • Electrobun.events.on('before-quit', ...): Kill server process if it exists
      • Watch serverProcess.exited (if in prod mode): When server exits unexpectedly, log the exit code and stderr, then process.exit(1)

    G. Top-level execution:

    main().catch((error) => {
      console.error('Failed to start:', error)
      process.exit(1)
    })
    

    Critical implementation details:

    • The PORT_PATTERN regex must handle multiple log formats:
      • ➜ Listening on: http://localhost:54321/ (srvx format)
      • Listening on http://127.0.0.1:54321 (node-server format)
      • Listening on http://[::]:54321 (IPv6 format)
    • The regex /Listening on:?\s*https?:\/\/[^\s:]+:(\d+)/ captures the port from all these formats.
    • cwd MUST be set to the server directory (dirname(serverEntryPath)), not the app root. Nitro resolves internal _libs/ imports relative to its directory.
    • process.execPath in an Electrobun bundle points to the bundled Bun binary — this is what runs the server.
    • stderr: 'pipe' — capture stderr for crash diagnostics but don't block on it during startup.

    Must NOT do:

    • Do not hardcode port numbers anywhere (use PORT=0 and parse result)
    • Do not use as any or type assertions to work around issues
    • Do not use child_process module — use Bun.spawn (native Bun API)
    • Do not bind server to 0.0.0.0 — always use 127.0.0.1
    • Do not leave the waitForServer function unused in dev mode
    • Do not use synchronous I/O for stdout reading

    Recommended Agent Profile:

    • Category: unspecified-high
      • Reason: Complex async logic (stream parsing, subprocess lifecycle, timeout management), multiple code paths (dev/prod), error handling across process boundaries. This is the architectural centerpiece.
    • Skills: []
      • No specialized skills needed — pure Bun/TypeScript with Electrobun APIs
    • Skills Evaluated but Omitted:
      • frontend-ui-ux: No UI work — this is backend/process management code
      • playwright: No browser testing needed for this task

    Parallelization:

    • Can Run In Parallel: YES
    • Parallel Group: Wave 1 (with Tasks 1, 2)
    • Blocks: Tasks 4, 5
    • Blocked By: None (can start immediately — edits a different file from Tasks 1, 2)

    References:

    Pattern References (existing code to follow):

    • apps/desktop/src/bun/index.ts — Current implementation. Preserve the waitForServer polling pattern (slightly refactored). Keep the BrowserWindow config (title, frame dimensions, renderer). Keep the top-level main().catch(...) pattern.
    • apps/desktop/src/bun/index.ts:1 — Current import: import Electrobun, { BrowserWindow } from 'electrobun/bun'. Extend this to also import PATHS (verify exact export name from type definitions).

    API/Type References (contracts to implement against):

    • electrobun/bun module — Exports Electrobun (default), BrowserWindow (named), and PATHS (named — verify). Check node_modules/electrobun/ for exact type definitions.
    • Bun.spawn API — Returns Subprocess with .stdout (ReadableStream when piped), .stderr, .exited (Promise), .kill().
    • PATHS.RESOURCES_FOLDER — Absolute path to Resources/ directory in the bundle. Verify by reading the Paths.ts source in electrobun package.

    Documentation References:

    • apps/desktop/AGENTS.md — Mentions production mode architecture (aspirational, but gives intent)

    External References:

    • Electrobun lifecycle events: Electrobun.events.on('before-quit', callback) — callback can call event.response({ allow: false }) to cancel. Source: electrobun/src/bun/core/Utils.ts.
    • Electrobun PATHS: Source at electrobun/src/bun/core/Paths.ts. Contains RESOURCES_FOLDER and VIEWS_FOLDER.
    • Bun Subprocess docs: stdout is ReadableStream<Uint8Array> when stdout: 'pipe'.

    WHY Each Reference Matters:

    • Current index.ts: Preserving the waitForServer pattern, BrowserWindow config, and error handling style. You're rewriting this file, so understand what to keep vs. replace.
    • electrobun/bun types: MUST verify PATHS export name and shape before using it. Don't assume — check.
    • Bun.spawn API: Core to the entire prod mode implementation. Understand stdout stream reading.
    • Lifecycle events: before-quit is where server cleanup happens. Understand the event contract.

    Acceptance Criteria:

    • File compiles: bun typecheck passes (from apps/desktop/)
    • No hardcoded port numbers (grep for :3000 — should only appear in DEV_SERVER_URL constant)
    • isDev() function uses process.env.ELECTROBUN_BUILD_ENV
    • spawnServer() uses PORT=0, HOST=127.0.0.1, process.execPath
    • spawnServer() sets cwd to dirname(serverEntryPath)
    • before-quit handler kills server process
    • Server crash watcher exists (watches subprocess.exited)
    • Timeout handling exists in both dev and prod paths
    • All Biome rules pass: bun fix produces no changes

    Agent-Executed QA Scenarios:

    Scenario: File compiles with zero type errors
      Tool: Bash
      Preconditions: File has been rewritten
      Steps:
        1. Run: bun typecheck (from apps/desktop/)
        2. Assert: Exit code 0
        3. Assert: No errors in output
      Expected Result: Clean TypeScript compilation
      Evidence: Terminal output captured
    
    Scenario: No hardcoded ports outside DEV_SERVER_URL
      Tool: Bash (grep)
      Preconditions: File has been rewritten
      Steps:
        1. Search apps/desktop/src/bun/index.ts for literal ':3000'
        2. Assert: Only occurrence is in the DEV_SERVER_URL constant definition
        3. Search for literal '3000' — should only appear once
      Expected Result: Port 3000 only in constant, nowhere else
      Evidence: Grep output
    
    Scenario: Code passes Biome lint/format
      Tool: Bash
      Preconditions: File has been rewritten
      Steps:
        1. Run: bun fix (from apps/desktop/)
        2. Run: git diff apps/desktop/src/bun/index.ts
        3. Assert: No diff (bun fix made no changes)
      Expected Result: Code already conforms to Biome rules
      Evidence: Empty git diff
    
    Scenario: Required patterns present in source
      Tool: Bash (grep)
      Preconditions: File has been rewritten
      Steps:
        1. Grep for 'ELECTROBUN_BUILD_ENV' — Assert: found
        2. Grep for 'Bun.spawn' — Assert: found
        3. Grep for 'process.execPath' — Assert: found
        4. Grep for 'PORT.*0' — Assert: found
        5. Grep for '127.0.0.1' — Assert: found
        6. Grep for 'before-quit' — Assert: found
        7. Grep for '.exited' — Assert: found (crash watcher)
        8. Grep for 'dirname' — Assert: found (cwd for server)
      Expected Result: All required patterns present
      Evidence: Grep results for each pattern
    

    Commit: YES

    • Message: feat(desktop): implement production mode with child process server
    • Files: apps/desktop/src/bun/index.ts
    • Pre-commit: bun typecheck && bun fix

  • 4. Rewrite apps/desktop/AGENTS.md — Document new architecture

    What to do:

    • Completely rewrite AGENTS.md to reflect the actual implemented architecture
    • Document:
      • Architecture overview: Desktop spawns server as child process in prod, connects to external server in dev
      • Dev mode: How it works (polls localhost:3000, requires server running separately)
      • Prod mode: How it works (spawns server from bundle, PORT=0, parses port from stdout)
      • Environment detection: ELECTROBUN_BUILD_ENV values (dev, canary, stable)
      • Build pipeline: Server must build before desktop (turbo dependency), build.copy bundles output
      • Key files: src/bun/index.ts (main process), electrobun.config.ts (build config)
      • Environment variables: DATABASE_URL (required, passed to server), ELECTROBUN_BUILD_ENV (auto-set by CLI)
      • Server lifecycle: Spawned on start, killed on quit, crash = exit
      • Commands: bun dev, bun build, bun typecheck, bun fix
    • Follow the style and conventions of the root AGENTS.md and apps/server/AGENTS.md
    • Be factual — only document what actually exists, not aspirational features

    Must NOT do:

    • Do not document features that don't exist
    • Do not copy content from the server's AGENTS.md verbatim
    • Do not include implementation details that belong in code comments

    Recommended Agent Profile:

    • Category: writing
      • Reason: Documentation task requiring clear technical writing
    • Skills: []
      • No specialized skills needed
    • Skills Evaluated but Omitted:
      • frontend-ui-ux: Not a UI task

    Parallelization:

    • Can Run In Parallel: NO
    • Parallel Group: Wave 2
    • Blocks: Task 5
    • Blocked By: Tasks 1, 2, 3 (needs to know final state of all files)

    References:

    Pattern References (existing code to follow):

    • AGENTS.md (root) — Follow same structure: Overview, Build Commands, Code Style, Directory Structure sections
    • apps/server/AGENTS.md — Follow same style for app-specific documentation. Use this as a template for tone and detail level.

    Content References (what to document):

    • apps/desktop/src/bun/index.ts — The rewritten file (Task 3 output). Document its behavior, not its code.
    • apps/desktop/electrobun.config.ts — The updated config (Task 1 output). Document build.copy and platform configs.
    • apps/desktop/turbo.json — The updated turbo config (Task 2 output). Document the build dependency.

    WHY Each Reference Matters:

    • Root AGENTS.md: Template for documentation style
    • Server AGENTS.md: Template for app-specific docs
    • All Task 1-3 outputs: The actual implemented behavior that must be accurately documented

    Acceptance Criteria:

    • File exists and is valid Markdown
    • Documents dev mode behavior accurately
    • Documents prod mode behavior accurately
    • Documents ELECTROBUN_BUILD_ENV mechanism
    • Documents build pipeline (server → desktop dependency)
    • Documents DATABASE_URL requirement
    • Does NOT mention features that don't exist
    • Follows conventions from root AGENTS.md

    Agent-Executed QA Scenarios:

    Scenario: AGENTS.md contains all required sections
      Tool: Bash (grep)
      Preconditions: File has been rewritten
      Steps:
        1. Grep for 'dev' or 'Dev' — Assert: found (dev mode documented)
        2. Grep for 'prod' or 'Prod' or 'production' — Assert: found (prod mode documented)
        3. Grep for 'ELECTROBUN_BUILD_ENV' — Assert: found
        4. Grep for 'DATABASE_URL' — Assert: found
        5. Grep for 'child process' or 'spawn' — Assert: found (architecture documented)
        6. Grep for 'bun dev' — Assert: found (commands documented)
        7. Grep for 'bun build' — Assert: found (commands documented)
      Expected Result: All key topics are covered
      Evidence: Grep results
    
    Scenario: No aspirational/unimplemented features documented
      Tool: Bash (grep)
      Preconditions: File has been rewritten
      Steps:
        1. Grep for 'TODO' or 'planned' or 'future' or 'coming soon' — Assert: not found (or minimal)
        2. Grep for 'auto-update' — Assert: not found (not implemented)
        3. Grep for 'tray' — Assert: not found (not implemented)
      Expected Result: Only implemented features documented
      Evidence: Grep results showing no aspirational content
    

    Commit: YES

    • Message: docs(desktop): rewrite AGENTS.md to reflect production mode architecture
    • Files: apps/desktop/AGENTS.md
    • Pre-commit: None (Markdown file)

  • 5. End-to-end verification — Typecheck and build pipeline

    What to do:

    • Run full monorepo typecheck to ensure no type errors were introduced
    • Run full monorepo build to verify:
      1. Server builds first (produces .output/)
      2. Desktop builds second (copies server output into bundle)
      3. No build errors
    • Run Biome formatting/linting check on all changed files
    • Verify dev mode still works conceptually (no runtime test — just verify the code path exists)

    Must NOT do:

    • Do not fix issues in server code — only desktop code
    • Do not modify any files unless fixing issues found during verification
    • Do not skip any verification step

    Recommended Agent Profile:

    • Category: quick
      • Reason: Running commands and checking output, no creative work
    • Skills: []
      • No specialized skills needed
    • Skills Evaluated but Omitted:
      • playwright: No browser testing in this verification

    Parallelization:

    • Can Run In Parallel: NO
    • Parallel Group: Wave 2 (sequential after Task 4)
    • Blocks: None (final task)
    • Blocked By: Tasks 1, 2, 3, 4

    References:

    Documentation References:

    • AGENTS.md (root) — Build/Lint/Test commands: bun typecheck, bun fix, bun build
    • apps/desktop/package.json — Desktop-specific scripts

    WHY Each Reference Matters:

    • Root AGENTS.md: Canonical list of verification commands
    • Desktop package.json: Desktop-specific build/typecheck commands

    Acceptance Criteria:

    • bun typecheck (monorepo root) exits with code 0
    • bun build (monorepo root) exits with code 0
    • bun fix (monorepo root) produces no changes (all code formatted)
    • Build output shows server building before desktop
    • Desktop build output includes server bundle (verify in build artifacts)

    Agent-Executed QA Scenarios:

    Scenario: Monorepo typecheck passes
      Tool: Bash
      Preconditions: All tasks 1-4 completed
      Steps:
        1. Run: bun typecheck (from monorepo root)
        2. Assert: Exit code 0
        3. Assert: No error output
      Expected Result: Zero type errors across entire monorepo
      Evidence: Terminal output captured
    
    Scenario: Monorepo build succeeds with correct order
      Tool: Bash
      Preconditions: All tasks 1-4 completed
      Steps:
        1. Run: bun build (from monorepo root)
        2. Assert: Exit code 0
        3. Assert: Output shows @furtherverse/server build task runs
        4. Assert: Output shows @furtherverse/desktop build task runs AFTER server
      Expected Result: Build pipeline executes in correct order
      Evidence: Terminal output showing task order
    
    Scenario: Biome finds no issues
      Tool: Bash
      Preconditions: All tasks 1-4 completed
      Steps:
        1. Run: bun fix (from monorepo root)
        2. Run: git diff
        3. Assert: No changes (all code already formatted)
      Expected Result: All code passes Biome rules
      Evidence: Empty git diff
    
    Scenario: Desktop build artifacts include server bundle
      Tool: Bash
      Preconditions: Build succeeded
      Steps:
        1. Search desktop build output directory for server-output/ or index.mjs
        2. Assert: Server files are present in the desktop bundle
      Expected Result: Server bundle is included in desktop build output
      Evidence: File listing of build artifacts
    

    Commit: NO (verification only — no file changes unless fixing issues)


Commit Strategy

After Task(s) Message Files Verification
1, 2 feat(desktop): add build.copy for server bundle and cross-workspace build dependency electrobun.config.ts, turbo.json bun typecheck
3 feat(desktop): implement production mode with child process server src/bun/index.ts bun typecheck && bun fix
4 docs(desktop): rewrite AGENTS.md to reflect production mode architecture AGENTS.md None
5 (no commit — verification only)

Success Criteria

Verification Commands

bun typecheck          # Expected: exit 0, no errors
bun build              # Expected: exit 0, server builds before desktop
bun fix                # Expected: no changes (already formatted)

Final Checklist

  • All "Must Have" features present in index.ts
  • All "Must NOT Have" exclusions verified absent
  • All 3 verification commands pass
  • AGENTS.md accurately reflects implemented architecture
  • Server output is bundled into desktop build via build.copy
  • Turbo builds server before desktop