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 withPORT=0in prod, parse the actual port from stdout, and open the BrowserWindow.Deliverables:
- Rewritten
apps/desktop/src/bun/index.tswith dev/prod mode support- Updated
apps/desktop/electrobun.config.tswithbuild.copyand platform configs- Cross-workspace build dependency in turbo pipeline
- Updated
apps/desktop/AGENTS.mdreflecting new architectureEstimated 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.copysupports paths outside the project directory (e.g.,../server/.output). Source paths are resolved relative to the project root. Destinations map intoResources/app/in the bundle.PATHSexported fromelectrobun/bunprovidesRESOURCES_FOLDER(absolute path toResources/) andVIEWS_FOLDER(Resources/app/views/).process.execPathin a bundled Electrobun app points to the bundled Bun binary.Electrobun.events.on('before-quit', callback)fires before app quit. Callback receives an event withresponse({ allow: false })to cancel quit.ELECTROBUN_BUILD_ENVis set by the Electrobun CLI:"dev"forelectrobun dev,"stable"forelectrobun build --env=stable.
Server startup behavior (verified from built output):
.output/server/index.mjsusesBun.servevia theh3+srvxadapter (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=0will log the real port. DATABASE_URLis validated at startup via Zod (@t3-oss/env-core). Missing = immediate crash.- The
.output/server/directory containsindex.mjsplus_libs/with bundled dependencies.
Turbo pipeline:
- Root
turbo.jsonhasbuild.dependsOn: ["^build"]which only builds workspace dependencies. - Desktop does NOT depend on server in
package.json, so^buildwon'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_DEVto built-inELECTROBUN_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.exitedpromise cwdfor spawned server: Must set to server directory for relative import resolution- Cross-platform considerations:
ELECTROBUN_BUILD_ENVworks everywhere (nocross-envneeded)
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 supportapps/desktop/electrobun.config.ts—build.copy+ all-platform configsapps/desktop/turbo.json— Cross-workspace build dependencyapps/desktop/AGENTS.md— Accurate documentation of new architecture
Definition of Done
bun typecheckpasses from monorepo root (zero errors)bun buildfrom root succeeds: server builds first, then desktop bundles server outputbun devfrom root still starts both apps (dev mode preserved)- Desktop
index.tshas 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.1only (no network exposure) PORT=0for system-assigned port (no conflicts)- Server process killed on app quit (via
before-quitevent) - Server crash detection (watch
exitedpromise, log error, exit app) - Startup timeout with clear error message
- Server
cwdset to its own directory (for relative import resolution) DATABASE_URLpassed 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.mjswith bundled Bun - Do NOT edit
apps/server/— onlyapps/desktop/files change - Do NOT add
@furtherverse/serveras 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.mdsays "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 configsWhat to do:
- Add
build.copyto include the server's Nitro build output in the desktop bundle:This copiescopy: { '../server/.output': 'server-output', }apps/server/.output/(the entire Nitro build output) intoResources/app/server-output/in the Electrobun bundle. The full server entry point will be atResources/app/server-output/server/index.mjs. - Add
macOSplatform config block (currently onlylinuxexists):macOS: { bundleCEF: true, } - Add
windowsplatform config block:windows: { bundleCEF: true, } - Verify the exact property names by checking Electrobun's
ElectrobunConfigtype definition. Thelinuxblock already usesbundleCEF: true, so follow the same pattern for other platforms. If the type doesn't supportmacOS/windowsyet, 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
linuxconfig - 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. Thelinux.bundleCEF: truepattern should be replicated for other platforms. Thebuild.bun.entrypointkey shows where build config lives.
API/Type References (contracts to implement against):
- The
ElectrobunConfigtype fromelectrobun— imported viaimport type { ElectrobunConfig } from 'electrobun'. Check its definition (likely innode_modules/electrobun/) to verify exact property names forcopy,macOS,windows.
External References:
- Electrobun
build.copysyntax: copies source (relative to project root) intoResources/app/{dest}/in the bundle. UsescpSyncwithdereference: true.
WHY Each Reference Matters:
electrobun.config.ts: You're editing this file — need to know its current shape to preserve existing valuesElectrobunConfigtype: Must match the type definition exactly — don't guess property names
Acceptance Criteria:
build.copykey 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 contentsCommit: 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(fromapps/desktop/)
- Add
-
2. Update
apps/desktop/turbo.json— Add cross-workspace build dependencyWhat to do:
- Add
dependsOnto the existingbuildtask to ensure the server builds before the desktop:{ "tasks": { "build": { "dependsOn": ["@furtherverse/server#build"], "outputs": ["build/**", "artifacts/**"] } } } - This tells Turbo: "before running
buildfor@furtherverse/desktop, first runbuildfor@furtherverse/server." - This ensures
apps/server/.output/exists whenelectrobun buildruns and tries tobuild.copyfrom../server/.output. - Preserve the existing
outputsarray exactly.
Must NOT do:
- Do not modify the root
turbo.json— onlyapps/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 withbuild.outputsalready defined. You're addingdependsOnalongside it.turbo.json(root) — Shows existing turbo patterns likebuild.dependsOn: ["^build"]. The root already uses^buildfor workspace dependencies, but since desktop doesn't list server as a package dependency, we need an explicit cross-workspace reference.
API/Type References:
- Turbo
dependsOnsyntax:"@furtherverse/server#build"means "run thebuildtask in the@furtherverse/serverworkspace".
Documentation References:
apps/server/package.json— The package name is@furtherverse/server(verify this is the exact name used in thedependsOnreference).
WHY Each Reference Matters:
apps/desktop/turbo.json: You're editing this file — preserve existing outputsapps/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.jsonhasdependsOn: ["@furtherverse/server#build"]in the build task- Existing
outputsarray 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 orderCommit: 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
- Add
-
3. Rewrite
apps/desktop/src/bun/index.ts— Complete dev/prod mode implementationWhat to do:
This is the core task. Completely rewrite
index.tsto 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_ENVis not set, default totrue(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_FOLDERpoints toResources/and thatbuild.copydestinations land inResources/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
fetchHEAD requests - Returns
truewhen server responds withresponse.ok - Returns
falseon 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:
- Resolve the server entry path via
getServerEntryPath() - Resolve the server directory via
dirname(serverEntryPath)— used ascwd - 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', }) - Read stdout as a stream to find the port:
- Use
serverProc.stdout(aReadableStream<Uint8Array>) - Create a reader, accumulate chunks into a text buffer
- Test buffer against
PORT_PATTERNregex after each chunk - When match found: extract port, resolve promise with
{ process: serverProc, url: 'http://127.0.0.1:${port}' }
- Use
- Implement a timeout:
- Use
setTimeoutto reject the promise afterSERVER_READY_TIMEOUT_MS - On timeout, kill the server process before rejecting
- Use
- 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
- Resolve the server entry path via
F.
main()async function:- Log startup message
- Branch on
isDev():- Dev mode:
- Log: "Dev mode: waiting for external server..."
- Call
waitForServer(DEV_SERVER_URL) - If timeout: log error with instructions (
"Run: bun dev in apps/server"),process.exit(1) - Set
serverUrl = DEV_SERVER_URL
- Prod mode:
- Log: "Production mode: starting embedded server..."
- Call
spawnServer() - If error: log error,
process.exit(1) - Store returned
processandurl
- Dev mode:
- Create
BrowserWindowwith the resolvedserverUrl: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, thenprocess.exit(1)
G. Top-level execution:
main().catch((error) => { console.error('Failed to start:', error) process.exit(1) })Critical implementation details:
- The
PORT_PATTERNregex 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. cwdMUST be set to the server directory (dirname(serverEntryPath)), not the app root. Nitro resolves internal_libs/imports relative to its directory.process.execPathin 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=0and parse result) - Do not use
as anyor type assertions to work around issues - Do not use
child_processmodule — useBun.spawn(native Bun API) - Do not bind server to
0.0.0.0— always use127.0.0.1 - Do not leave the
waitForServerfunction 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 codeplaywright: 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 thewaitForServerpolling pattern (slightly refactored). Keep theBrowserWindowconfig (title, frame dimensions, renderer). Keep the top-levelmain().catch(...)pattern.apps/desktop/src/bun/index.ts:1— Current import:import Electrobun, { BrowserWindow } from 'electrobun/bun'. Extend this to also importPATHS(verify exact export name from type definitions).
API/Type References (contracts to implement against):
electrobun/bunmodule — ExportsElectrobun(default),BrowserWindow(named), andPATHS(named — verify). Checknode_modules/electrobun/for exact type definitions.Bun.spawnAPI — ReturnsSubprocesswith.stdout(ReadableStream when piped),.stderr,.exited(Promise),.kill().PATHS.RESOURCES_FOLDER— Absolute path toResources/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 callevent.response({ allow: false })to cancel. Source:electrobun/src/bun/core/Utils.ts. - Electrobun
PATHS: Source atelectrobun/src/bun/core/Paths.ts. ContainsRESOURCES_FOLDERandVIEWS_FOLDER. - Bun
Subprocessdocs:stdoutisReadableStream<Uint8Array>whenstdout: 'pipe'.
WHY Each Reference Matters:
- Current
index.ts: Preserving thewaitForServerpattern,BrowserWindowconfig, and error handling style. You're rewriting this file, so understand what to keep vs. replace. electrobun/buntypes: MUST verifyPATHSexport name and shape before using it. Don't assume — check.Bun.spawnAPI: Core to the entire prod mode implementation. Understandstdoutstream reading.- Lifecycle events:
before-quitis where server cleanup happens. Understand the event contract.
Acceptance Criteria:
- File compiles:
bun typecheckpasses (fromapps/desktop/) - No hardcoded port numbers (grep for
:3000— should only appear inDEV_SERVER_URLconstant) isDev()function usesprocess.env.ELECTROBUN_BUILD_ENVspawnServer()usesPORT=0,HOST=127.0.0.1,process.execPathspawnServer()setscwdtodirname(serverEntryPath)before-quithandler kills server process- Server crash watcher exists (watches
subprocess.exited) - Timeout handling exists in both dev and prod paths
- All Biome rules pass:
bun fixproduces 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 patternCommit: YES
- Message:
feat(desktop): implement production mode with child process server - Files:
apps/desktop/src/bun/index.ts - Pre-commit:
bun typecheck && bun fix
- Check
-
4. Rewrite
apps/desktop/AGENTS.md— Document new architectureWhat to do:
- Completely rewrite
AGENTS.mdto 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_ENVvalues (dev,canary,stable) - Build pipeline: Server must build before desktop (turbo dependency),
build.copybundles 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.mdandapps/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 sectionsapps/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_ENVmechanism - Documents build pipeline (server → desktop dependency)
- Documents
DATABASE_URLrequirement - 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 contentCommit: YES
- Message:
docs(desktop): rewrite AGENTS.md to reflect production mode architecture - Files:
apps/desktop/AGENTS.md - Pre-commit: None (Markdown file)
- Completely rewrite
-
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:
- Server builds first (produces
.output/) - Desktop builds second (copies server output into bundle)
- No build errors
- Server builds first (produces
- 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 buildapps/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 0bun build(monorepo root) exits with code 0bun 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 artifactsCommit: 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.mdaccurately reflects implemented architecture- Server output is bundled into desktop build via
build.copy - Turbo builds server before desktop