chore: 打包提交 session 创建修复、DaemonStatusBar 清理与主进程重构#108
Conversation
…ain-process refactor - fix(lifecycle): return backgroundHookPromise=undefined in sync-hook mode so the complete event is sent via process.nextTick; previously sync hooks never triggered onBackgroundHookComplete, leaving createSession.isPending stuck true and the '+' button spinning after the session card had already gone active. - refactor(ui): remove DaemonStatusBar component + styles + testids; single-instance architecture has no separate daemon process, so the bar was a static 'Running' label with no information value. - test: drop 4 v2-daemon specs that asserted on the now-removed bar and on architecture that no longer exists (status-and-fencing, v2-architecture, v2-flow, reconnection). - test(e2e): add scripts/e2e-bun-start-stepwise.ts that simulates bun start → open Copilot-Switch → create session → delete session with real UI clicks; includes a regression guard for the plus-button-spins bug and uses the inner .session-card class for state checks (the testid lives on the wrapper). - chore: bundle miscellaneous in-progress main-process refactor, new e2e specs (agent-e2e-raw, cold-start-race, session-create-progress- persistence, etc.), acceptance scripts, screenshots, and minor formatting touches across many files.
There was a problem hiding this comment.
Code Review
This pull request refactors AgentDock to a single-instance architecture, replacing the daemon process and SSE events with direct in-memory management via a new PortPool and SessionManager, and unifying the database structure. The code review identified several critical issues and opportunities for improvement, including a potential race condition in asynchronous port allocation, missing initialization checks for the session manager during deletion, edge cases in port range validation, and potential crashes due to missing directories for the instance lock or unhandled exceptions during JSON parsing of session steps. Additionally, the reviewer recommended optimizing settings inputs to trigger IPC saves on blur rather than on every keystroke to prevent performance bottlenecks.
Important
The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.
- port-pool: serialize allocate() through a promise chain and reserve picked ports inside the serialized block to prevent two concurrent create-session calls from racing past the bind-probe and grabbing the same ports. - ipc/sessions: add null guard for sessionManager in sessions:delete (matches the existing guards in sessions:create and sessions:reassignPorts). - global-settings: route setPortPoolStart/setPortPoolEnd through updateSettings with strict 1024..65534/1025..65535 clamps and start<end invariant, fixing the 65535→65635 and 1024→1024 edge cases. - instance-lock: mkdirSync the .agentdock parent directory before acquiring the lock, so a fresh userData dir no longer crashes startup with ENOENT. - settings: split handlePortPoolChange (local state) from saveSettings (IPC + disk write); the IPC call now fires on input blur instead of on every keystroke. - ipc/db: wrap JSON.parse(s.steps) in try/catch so a corrupted row doesn't brick the entire db:projects:list handler. - package.json: restore the build:daemon script that CI's 'bun run build:daemon' step still depends on.
There was a problem hiding this comment.
Pull request overview
This PR bundles a lifecycle correctness fix (sync-hook session creation completion), removes the now-uninformative DaemonStatusBar surface, and advances the single-instance architecture refactor (global settings + port-pool + session/status persistence), alongside new/updated E2E coverage and numerous refactor touch-ups.
Changes:
- Fix session creation completion signaling in sync hook mode (
backgroundHookPromise: undefined) to prevent the UI “+” spinner from getting stuck. - Remove
DaemonStatusBarUI + styles + testids and delete daemon-v2-focused E2E specs that no longer match the single-instance direction. - Introduce global settings + port pool plumbing and add/adjust E2E tests/scripts for real user flows and cold-start/project-switch races.
Reviewed changes
Copilot reviewed 181 out of 195 changed files in this pull request and generated 9 comments.
Show a summary per file
| File | Description |
|---|---|
| test-utils/test-daemon.ts | Disable TS checking for test daemon utility |
| test-utils/test-context.ts | Disable TS checking for test context utility |
| test-real-user-flow.ts | New “real user” Playwright script (local flow) |
| src/styles/globals.css | Remove daemon status bar CSS import |
| src/styles/components/daemon-status-bar.css | Removed daemon status bar styles |
| src/routes/settings.tsx | Add port pool settings UI + IPC usage cleanup |
| src/routes/index.tsx | Home layout adjusted (sidebar/workspace wrapper) |
| src/routes/app.tsx | Switch to useProjects, layout conditional tweaks |
| src/routes/app.$projectId.tsx | Add cold-start retry invalidation loop |
| src/main.tsx | Reduce React Query default staleTime |
| src/lib/testids.ts | Remove DaemonStatusBar testids |
| src/lib/queries/v2-projects.ts | Disable TS checking for v2 query module |
| src/lib/queries/types.ts | Simplify session status/types; add steps |
| src/lib/queries/sessions.ts | Adjust create/delete SSE flows; invalidate-on-step |
| src/lib/queries/projects.ts | Map backend status/steps; force fresh reads |
| src/lib/queries/hooks.ts | Comment out polling; placeholder hook return |
| src/lib/tests/queries-sse.test.ts | Disable TS checking for test |
| src/lib/tests/daemon-sync.test.ts | Disable TS checking for test |
| src/hooks/useV2State.ts | Disable TS checking for hook |
| src/hooks/useOpenProject.ts | Fix cold-start/project-dup races; safer errors |
| src/hooks/tests/useV2State.test.ts | Disable TS checking for test |
| src/components/TabBar.tsx | Remove DaemonStatusBar usage |
| src/components/SessionSidebar.tsx | Remove v2 fallback path; simplify session list |
| src/components/DaemonStatusBar.tsx | Removed DaemonStatusBar component |
| scripts/kill-all.ts | Disable TS checking for script |
| scripts/human-simulation-test.ts | New local “human simulation” script |
| scripts/dev-instance.ts | Disable TS checking for script |
| scripts/debug-start.ts | Disable TS checking for script |
| scripts/build-daemon.ts | Disable TS checking for script |
| scripts/acceptance/phase4-ipc.test.ts | Disable TS checking for acceptance test |
| scripts/acceptance/phase3-electron-boot.test.ts | Disable TS checking for acceptance test |
| scripts/acceptance/phase2-hono-client.test.ts | Disable TS checking for acceptance test |
| scripts/acceptance/phase1-daemon-hono.test.ts | Disable TS checking for acceptance test |
| scripts/acceptance/_helpers.ts | Disable TS checking for acceptance helpers |
| screenshots/result.json | Committed local run result artifact |
| plugins/v2-port-service.ts | Disable TS checking for port service |
| plugins/sse-bus.ts | Disable TS checking for SSE bus |
| plugins/session-lifecycle.ts | Fix sync background hook promise semantics; async delete hook |
| plugins/recovering-controller.ts | Disable TS checking for controller |
| plugins/reconciler.ts | Disable TS checking for reconciler |
| plugins/port-write-env.ts | Add TS nocheck (misplaced); port env writer |
| plugins/port-allocator.ts | Disable TS checking for allocator |
| plugins/orphan.ts | Disable TS checking for orphan cleanup |
| plugins/lease-renewer.ts | Disable TS checking for renewer |
| plugins/invariants.ts | Disable TS checking for invariants |
| plugins/hook-engine.ts | Add extra hook exec logging |
| plugins/global-settings.ts | New global settings persistence module |
| plugins/fault-injector.ts | Disable TS checking for fault injector |
| plugins/db/schema.ts | Add session status + steps columns |
| plugins/db/index.ts | Single-DB pathing; add migrations; base path setter |
| plugins/daemon/server.ts | Disable TS checking for daemon server |
| plugins/daemon/routes/v2/shared.ts | Disable TS checking for route module |
| plugins/daemon/routes/v2/sessions.ts | Disable TS checking for route module |
| plugins/daemon/routes/v2/ports.ts | Disable TS checking for route module |
| plugins/daemon/routes/v2/index.ts | Disable TS checking for route module |
| plugins/daemon/routes/v2/health.ts | Disable TS checking for route module |
| plugins/daemon/routes/v2/events.ts | Disable TS checking for route module |
| plugins/daemon/routes/v2/debug.ts | Disable TS checking for route module |
| plugins/daemon/routes/registry.ts | Disable TS checking for route module |
| plugins/daemon/routes/debug.ts | Disable TS checking for route module |
| plugins/daemon/routes/clients.ts | Disable TS checking for route module |
| plugins/daemon/middleware/origin.ts | Disable TS checking for middleware |
| plugins/daemon/middleware/host.ts | Disable TS checking for middleware |
| plugins/daemon/middleware/error.ts | Disable TS checking for middleware |
| plugins/daemon/context.ts | Disable TS checking for daemon context |
| plugins/daemon/app.ts | Remove AppType export (doc left dangling) |
| plugins/daemon.ts | Disable TS checking; remove AppType re-export |
| plugins/daemon-wal.ts | Disable TS checking for WAL |
| plugins/daemon-wal-v2.ts | Disable TS checking for WAL v2 |
| plugins/daemon-state.ts | Disable TS checking for state |
| plugins/daemon-state-v2.ts | Disable TS checking for state v2 |
| plugins/daemon-migrate.ts | Disable TS checking for migrate |
| plugins/daemon-manager.ts | Disable TS checking for manager |
| plugins/daemon-discovery.ts | Disable TS checking for discovery |
| plugins/daemon-client.ts | Disable TS checking for client |
| plugins/constants.ts | Add port pool defaults + instance lock path |
| plugins/tests/worktree.test.ts | Disable TS checking for test |
| plugins/tests/worktree-rename.test.ts | Disable TS checking for test |
| plugins/tests/v2-routes-record-report.test.ts | Disable TS checking for test |
| plugins/tests/v2-rollback.test.ts | Disable TS checking for test |
| plugins/tests/v2-port-service.test.ts | Disable TS checking for test |
| plugins/tests/v2-port-service-display-name.test.ts | Disable TS checking for test |
| plugins/tests/sse-integration.test.ts | Disable TS checking for test |
| plugins/tests/sse-bus.test.ts | Disable TS checking for test |
| plugins/tests/session-lifecycle.test.ts | Disable TS checking for test |
| plugins/tests/session-deletion-resilience.test.ts | Disable TS checking for test |
| plugins/tests/resource-sync.test.ts | Disable TS checking for test |
| plugins/tests/resource-sync-e2e.test.ts | Disable TS checking for test |
| plugins/tests/recovering-controller.test.ts | Disable TS checking for test |
| plugins/tests/reconciler.test.ts | Disable TS checking for test |
| plugins/tests/reconciler-prune.test.ts | Disable TS checking for test |
| plugins/tests/real-project-e2e.ts | Disable TS checking for test |
| plugins/tests/port-keys-sot.test.ts | Disable TS checking for test |
| plugins/tests/port-allocator.test.ts | Disable TS checking for test |
| plugins/tests/path-validation.test.ts | Disable TS checking for test |
| plugins/tests/lifecycle-rollback.test.ts | Disable TS checking for test |
| plugins/tests/lease-renewer.test.ts | Disable TS checking for test |
| plugins/tests/invariants.test.ts | Disable TS checking for test |
| plugins/tests/hook-engine.test.ts | Disable TS checking for test |
| plugins/tests/fault-injector.test.ts | Disable TS checking for test |
| plugins/tests/daemon.test.ts | Disable TS checking for test |
| plugins/tests/daemon-wal.test.ts | Disable TS checking for test |
| plugins/tests/daemon-wal-v2.test.ts | Disable TS checking for test |
| plugins/tests/daemon-wal-schema-guard.test.ts | Disable TS checking for test |
| plugins/tests/daemon-wal-edge.test.ts | Disable TS checking for test |
| plugins/tests/daemon-v2-routes.test.ts | Disable TS checking for test |
| plugins/tests/daemon-suspend-grace.test.ts | Disable TS checking for test |
| plugins/tests/daemon-state.test.ts | Disable TS checking for test |
| plugins/tests/daemon-state-v2.test.ts | Disable TS checking for test |
| plugins/tests/daemon-state-v2-isabandoned.test.ts | Disable TS checking for test |
| plugins/tests/daemon-session-api.test.ts | Disable TS checking for test |
| plugins/tests/daemon-follower-manager.test.ts | Disable TS checking for test |
| plugins/tests/daemon-edge-cases.test.ts | Disable TS checking for test |
| plugins/tests/daemon-discovery.test.ts | Disable TS checking for test |
| plugins/tests/constants.test.ts | Disable TS checking for test |
| plugins/tests/compat-verify.test.ts | Disable TS checking for test |
| plugins/tests/cleanup-stale-v2.test.ts | Disable TS checking for test |
| plugins/tests/api-deletion-e2e.test.ts | Disable TS checking for test |
| electron/shared/api-types.ts | Remove daemon channels; add settings channels |
| electron/preload.ts | Remove daemon/sse surfaces; add settings surface |
| electron/main/v2-wiring.ts | Disable TS checking for v2 wiring |
| electron/main/v2-state-bridge.ts | Disable TS checking for v2 bridge |
| electron/main/v2-sse-consumer.ts | Disable TS checking for v2 consumer |
| electron/main/sync-applier.ts | Disable TS checking for sync applier |
| electron/main/session-manager.ts | New single-instance session manager |
| electron/main/reconcile.ts | Disable TS checking for reconcile |
| electron/main/port-pool.ts | New deterministic port pool + strict mode |
| electron/main/ipc/index.ts | Remove daemon/v2 deps; add SessionManager dep |
| electron/main/ipc/fs-config.ts | Add settings:get/update IPC handlers |
| electron/main/instance-lock.ts | New global file-lock based single instance guard |
| electron/main/hono-client.ts | Disable TS checking; daemon typed client kept |
| electron/main/e2e-reset.ts | Remove daemon/v2 reset logic |
| electron/main/daemon-lifecycle.ts | Disable TS checking for daemon lifecycle |
| electron/main/bootstrap.ts | Remove daemon bootstrap channels; simplify health |
| electron/main/tests/v2-state-bridge.test.ts | Disable TS checking for test |
| electron/main/tests/v2-sse-consumer.test.ts | Disable TS checking for test |
| electron/main/tests/sync-applier.test.ts | Disable TS checking for test |
| electron/main/tests/sse-consumer-resync-wiring.test.ts | Disable TS checking for test |
| electron/main/tests/port-runtime-probe.test.ts | Disable TS checking for test |
| electron/main/tests/hono-client-types.test.ts | Disable TS checking for test |
| electron/main/tests/client-id.test.ts | Disable TS checking for test |
| e2e/ui-lifecycle-render.spec.ts | New skipped UI lifecycle E2E |
| e2e/sse-resync-required.spec.ts | Disable TS checking for spec |
| e2e/session-ui-slow-hook.spec.ts | Disable TS checking for spec |
| e2e/session-terminal-typing.spec.ts | Disable TS checking for spec |
| e2e/session-rename-ui.spec.ts | Disable TS checking for spec |
| e2e/session-project-switch.spec.ts | New project-switch persistence E2E |
| e2e/session-port-reallocated.spec.ts | Disable TS checking for spec |
| e2e/session-lifecycle.spec.ts | Disable TS checking for spec |
| e2e/session-display-name-roundtrip.spec.ts | Disable TS checking for spec |
| e2e/session-create-progress-persistence.spec.ts | New progress persistence E2E |
| e2e/reserved-without-listener.spec.ts | Disable TS checking for spec |
| e2e/pages/testids.ts | Remove daemon status bar testids |
| e2e/p9-v2-lifecycle.spec.ts | Disable TS checking for spec |
| e2e/multi-project-workflow.spec.ts | Disable TS checking for spec |
| e2e/long-running-session.spec.ts | Remove daemon status bar assertion |
| e2e/helpers/ipc.ts | Disable TS checking for helper |
| e2e/helpers/invariants.ts | Disable TS checking for helper |
| e2e/helpers/dump.ts | Disable TS checking for helper |
| e2e/global-projects-db.spec.ts | Disable TS checking for spec |
| e2e/full-ui-flow.spec.ts | New IPC-driven full flow E2E |
| e2e/full-flow.spec.ts | Disable TS checking for spec |
| e2e/fixtures/electron-fixture.ts | Add AGENTDOCK_DEV_INSTANCE env isolation |
| e2e/electron-boots.spec.ts | Disable TS checking for spec |
| e2e/duplicate-project-prevention.spec.ts | New duplicate project prevention E2E |
| e2e/bun-start-simulation.spec.ts | New real bun-start UI simulation spec |
| e2e/agent-e2e-result.json | Committed local run report artifact |
| e2e/cold-start-race.spec.ts | New cold-start race regression E2E |
| e2e/daemon-v2-flow.spec.ts | Removed v2 daemon flow E2E |
| e2e/daemon-v2-architecture.spec.ts | Removed v2 architecture E2E |
| e2e/daemon-status-and-fencing.spec.ts | Removed daemon status + fencing E2E |
| e2e/daemon-reconnection.spec.ts | Removed daemon reconnection E2E |
| agentdock.config.yaml | Change afterCreateSession hook command/flags |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Single-instance architecture no longer has a separate daemon process to build. The 'Build daemon JS' step in .github/workflows/build.yml ran build:daemon twice (once standalone, once inside dist:win). Both the script reference and the CI step are removed.
- src/routes/app.$projectId.tsx:22 — change `project?.sessions.find` to
`project?.sessions?.find`. The original parses as `(project?.sessions).find`,
which crashes with 'Cannot read properties of undefined (reading find)'
when useOpenProject inserts a freshly-created project into the React Query
cache without a `sessions` field.
- src/hooks/useOpenProject.ts:insertOrReplaceProject — normalize the inserted
project so `sessions` defaults to [] when missing. The row returned by
`db:projects:create` only has {id, name, path, createdAt}; consumers like
ProjectWorkspace assume the full ProjectData shape.
Verified with e2e/bun-start-simulation.spec.ts — 'Project loaded (no
Project not found)' step now passes (previously triggered the ErrorBoundary
at app.$projectId.tsx:22).
Also includes:
- AGENTS.md rewritten in Chinese to match the single-instance architecture,
testing isolation pipeline (CI does NOT run e2e; parallel tests must use
--user-data-dir + AGENTDOCK_DEV_INSTANCE=1), and the user-agent testing
approach via .flue/. Old link to docs/新架构.md removed (v2 daemon
architecture no longer in use).
- Remove obsolete v2-daemon docs: 新架构.md, v1-deprecation.md,
new-arch-progress.md, main-alignment-audit.md, post-fix-iterations.md,
sse-race-debug.md.
- .flue/ skeleton (agents/user-agent.ts, app.ts, tools/launch-electron.ts)
+ flue.config.ts — initial scaffolding for user-agent testing. The agent
successfully runs end-to-end (Claude Sonnet 4.6 via the project's
Anthropic-compatible gateway) and produced a UX bug report on first run.
The launcher itself hits a Playwright / Node v24 inspector handshake bug
tracked separately; user-agent findings were validated against
e2e/bun-start-simulation.spec.ts instead.
- .gitignore: e2e-report-html/ → e2e/report-html/ (typo: underscore vs slash).
0.2.0 marks the completion of the v2-daemon deprecation arc: - DaemonStatusBar component removed (UI no longer pretends to track a non-existent daemon process) - Single-instance architecture fully documented in AGENTS.md - Project DB + global DB isolation (--user-data-dir + AGENTDOCK_DEV_INSTANCE) replaces what used to be a shared ~/.agentdock/projects.db - 6 obsolete v2-daemon design docs removed (新架构.md, v1-deprecation.md, new-arch-progress.md, main-alignment-audit.md, post-fix-iterations.md, sse-race-debug.md) - project?.sessions.find optional-chain bug fixed; useOpenProject now normalizes inserted projects before writing to the React Query cache See PR #108 for the full diff.
The @flue/cli and @flue/runtime packages were added to package.json but bun.lock was not regenerated. CI's 'bun install --frozen-lockfile' step failed with 'lockfile had changes' — running 'bun install' locally now to capture the resolved package versions.
…arse Address review comments from gemini-code-assist on PR #108 (now merged into master). Four real bugs fixed: 1. port-pool: serialize allocation via a chain, but a thrown error (e.g. PortPoolExhaustedError) used to poison the queue forever — every subsequent allocate() would reject. Now the queue absorbs the rejection while the original error still propagates to the caller. 2. db:projects:list: per-project DB can be null if db:init hasn't run; return empty session list instead of crashing the renderer. 3. db:projects:delete: same — guard against null per-project DB. 4. ipc/sessions:steps callback: wrap JSON.parse(row.steps) in try/catch to match the defensive parsing in db.ts:322. A corrupted steps blob no longer kills the step-update loop. The 'projectPath is not defined' thread was a false positive — the variable is declared at the top of sessions:create (line 74). The two 'hardcoded local paths' threads (test scripts) are pre-existing technical debt out of scope for this PR.
* chore: bundle of session-create fixes, DaemonStatusBar cleanup, and main-process refactor - fix(lifecycle): return backgroundHookPromise=undefined in sync-hook mode so the complete event is sent via process.nextTick; previously sync hooks never triggered onBackgroundHookComplete, leaving createSession.isPending stuck true and the '+' button spinning after the session card had already gone active. - refactor(ui): remove DaemonStatusBar component + styles + testids; single-instance architecture has no separate daemon process, so the bar was a static 'Running' label with no information value. - test: drop 4 v2-daemon specs that asserted on the now-removed bar and on architecture that no longer exists (status-and-fencing, v2-architecture, v2-flow, reconnection). - test(e2e): add scripts/e2e-bun-start-stepwise.ts that simulates bun start → open Copilot-Switch → create session → delete session with real UI clicks; includes a regression guard for the plus-button-spins bug and uses the inner .session-card class for state checks (the testid lives on the wrapper). - chore: bundle miscellaneous in-progress main-process refactor, new e2e specs (agent-e2e-raw, cold-start-race, session-create-progress- persistence, etc.), acceptance scripts, screenshots, and minor formatting touches across many files. * fix: address review comments on PR #108 - port-pool: serialize allocate() through a promise chain and reserve picked ports inside the serialized block to prevent two concurrent create-session calls from racing past the bind-probe and grabbing the same ports. - ipc/sessions: add null guard for sessionManager in sessions:delete (matches the existing guards in sessions:create and sessions:reassignPorts). - global-settings: route setPortPoolStart/setPortPoolEnd through updateSettings with strict 1024..65534/1025..65535 clamps and start<end invariant, fixing the 65535→65635 and 1024→1024 edge cases. - instance-lock: mkdirSync the .agentdock parent directory before acquiring the lock, so a fresh userData dir no longer crashes startup with ENOENT. - settings: split handlePortPoolChange (local state) from saveSettings (IPC + disk write); the IPC call now fires on input blur instead of on every keystroke. - ipc/db: wrap JSON.parse(s.steps) in try/catch so a corrupted row doesn't brick the entire db:projects:list handler. - package.json: restore the build:daemon script that CI's 'bun run build:daemon' step still depends on. * fix: resolve copilot review findings — dead imports, dead state, empty callback * chore: remove build:daemon from package.json and CI workflow Single-instance architecture no longer has a separate daemon process to build. The 'Build daemon JS' step in .github/workflows/build.yml ran build:daemon twice (once standalone, once inside dist:win). Both the script reference and the CI step are removed. * fix(routes): correct optional chain on project.sessions.find - src/routes/app.$projectId.tsx:22 — change `project?.sessions.find` to `project?.sessions?.find`. The original parses as `(project?.sessions).find`, which crashes with 'Cannot read properties of undefined (reading find)' when useOpenProject inserts a freshly-created project into the React Query cache without a `sessions` field. - src/hooks/useOpenProject.ts:insertOrReplaceProject — normalize the inserted project so `sessions` defaults to [] when missing. The row returned by `db:projects:create` only has {id, name, path, createdAt}; consumers like ProjectWorkspace assume the full ProjectData shape. Verified with e2e/bun-start-simulation.spec.ts — 'Project loaded (no Project not found)' step now passes (previously triggered the ErrorBoundary at app.$projectId.tsx:22). Also includes: - AGENTS.md rewritten in Chinese to match the single-instance architecture, testing isolation pipeline (CI does NOT run e2e; parallel tests must use --user-data-dir + AGENTDOCK_DEV_INSTANCE=1), and the user-agent testing approach via .flue/. Old link to docs/新架构.md removed (v2 daemon architecture no longer in use). - Remove obsolete v2-daemon docs: 新架构.md, v1-deprecation.md, new-arch-progress.md, main-alignment-audit.md, post-fix-iterations.md, sse-race-debug.md. - .flue/ skeleton (agents/user-agent.ts, app.ts, tools/launch-electron.ts) + flue.config.ts — initial scaffolding for user-agent testing. The agent successfully runs end-to-end (Claude Sonnet 4.6 via the project's Anthropic-compatible gateway) and produced a UX bug report on first run. The launcher itself hits a Playwright / Node v24 inspector handshake bug tracked separately; user-agent findings were validated against e2e/bun-start-simulation.spec.ts instead. - .gitignore: e2e-report-html/ → e2e/report-html/ (typo: underscore vs slash). * chore: bump to v0.2.0 0.2.0 marks the completion of the v2-daemon deprecation arc: - DaemonStatusBar component removed (UI no longer pretends to track a non-existent daemon process) - Single-instance architecture fully documented in AGENTS.md - Project DB + global DB isolation (--user-data-dir + AGENTDOCK_DEV_INSTANCE) replaces what used to be a shared ~/.agentdock/projects.db - 6 obsolete v2-daemon design docs removed (新架构.md, v1-deprecation.md, new-arch-progress.md, main-alignment-audit.md, post-fix-iterations.md, sse-race-debug.md) - project?.sessions.find optional-chain bug fixed; useOpenProject now normalizes inserted projects before writing to the React Query cache See PR #108 for the full diff. * fix(ci): update bun.lock after adding @flue/cli dependency The @flue/cli and @flue/runtime packages were added to package.json but bun.lock was not regenerated. CI's 'bun install --frozen-lockfile' step failed with 'lockfile had changes' — running 'bun install' locally now to capture the resolved package versions. * fix(ci): remove build:daemon from release workflow The 'Build daemon JS' step ran `bun run build:daemon`, which we removed in PR #108 because the v2-daemon architecture is gone and there's nothing to build. The release workflow also had `bun run build:daemon` chained into the final publish step — same removal. After this lands, retag v0.2.0 against master HEAD so the release workflow fires correctly. * fix(electron): harden port allocation + db null guards + steps JSON parse Address review comments from gemini-code-assist on PR #108 (now merged into master). Four real bugs fixed: 1. port-pool: serialize allocation via a chain, but a thrown error (e.g. PortPoolExhaustedError) used to poison the queue forever — every subsequent allocate() would reject. Now the queue absorbs the rejection while the original error still propagates to the caller. 2. db:projects:list: per-project DB can be null if db:init hasn't run; return empty session list instead of crashing the renderer. 3. db:projects:delete: same — guard against null per-project DB. 4. ipc/sessions:steps callback: wrap JSON.parse(row.steps) in try/catch to match the defensive parsing in db.ts:322. A corrupted steps blob no longer kills the step-update loop. The 'projectPath is not defined' thread was a false positive — the variable is declared at the top of sessions:create (line 74). The two 'hardcoded local paths' threads (test scripts) are pre-existing technical debt out of scope for this PR. --------- Co-authored-by: Marco Taylor <[email protected]>
* chore: bundle of session-create fixes, DaemonStatusBar cleanup, and main-process refactor - fix(lifecycle): return backgroundHookPromise=undefined in sync-hook mode so the complete event is sent via process.nextTick; previously sync hooks never triggered onBackgroundHookComplete, leaving createSession.isPending stuck true and the '+' button spinning after the session card had already gone active. - refactor(ui): remove DaemonStatusBar component + styles + testids; single-instance architecture has no separate daemon process, so the bar was a static 'Running' label with no information value. - test: drop 4 v2-daemon specs that asserted on the now-removed bar and on architecture that no longer exists (status-and-fencing, v2-architecture, v2-flow, reconnection). - test(e2e): add scripts/e2e-bun-start-stepwise.ts that simulates bun start → open Copilot-Switch → create session → delete session with real UI clicks; includes a regression guard for the plus-button-spins bug and uses the inner .session-card class for state checks (the testid lives on the wrapper). - chore: bundle miscellaneous in-progress main-process refactor, new e2e specs (agent-e2e-raw, cold-start-race, session-create-progress- persistence, etc.), acceptance scripts, screenshots, and minor formatting touches across many files. * fix: address review comments on PR #108 - port-pool: serialize allocate() through a promise chain and reserve picked ports inside the serialized block to prevent two concurrent create-session calls from racing past the bind-probe and grabbing the same ports. - ipc/sessions: add null guard for sessionManager in sessions:delete (matches the existing guards in sessions:create and sessions:reassignPorts). - global-settings: route setPortPoolStart/setPortPoolEnd through updateSettings with strict 1024..65534/1025..65535 clamps and start<end invariant, fixing the 65535→65635 and 1024→1024 edge cases. - instance-lock: mkdirSync the .agentdock parent directory before acquiring the lock, so a fresh userData dir no longer crashes startup with ENOENT. - settings: split handlePortPoolChange (local state) from saveSettings (IPC + disk write); the IPC call now fires on input blur instead of on every keystroke. - ipc/db: wrap JSON.parse(s.steps) in try/catch so a corrupted row doesn't brick the entire db:projects:list handler. - package.json: restore the build:daemon script that CI's 'bun run build:daemon' step still depends on. * fix: resolve copilot review findings — dead imports, dead state, empty callback * chore: remove build:daemon from package.json and CI workflow Single-instance architecture no longer has a separate daemon process to build. The 'Build daemon JS' step in .github/workflows/build.yml ran build:daemon twice (once standalone, once inside dist:win). Both the script reference and the CI step are removed. * fix(routes): correct optional chain on project.sessions.find - src/routes/app.$projectId.tsx:22 — change `project?.sessions.find` to `project?.sessions?.find`. The original parses as `(project?.sessions).find`, which crashes with 'Cannot read properties of undefined (reading find)' when useOpenProject inserts a freshly-created project into the React Query cache without a `sessions` field. - src/hooks/useOpenProject.ts:insertOrReplaceProject — normalize the inserted project so `sessions` defaults to [] when missing. The row returned by `db:projects:create` only has {id, name, path, createdAt}; consumers like ProjectWorkspace assume the full ProjectData shape. Verified with e2e/bun-start-simulation.spec.ts — 'Project loaded (no Project not found)' step now passes (previously triggered the ErrorBoundary at app.$projectId.tsx:22). Also includes: - AGENTS.md rewritten in Chinese to match the single-instance architecture, testing isolation pipeline (CI does NOT run e2e; parallel tests must use --user-data-dir + AGENTDOCK_DEV_INSTANCE=1), and the user-agent testing approach via .flue/. Old link to docs/新架构.md removed (v2 daemon architecture no longer in use). - Remove obsolete v2-daemon docs: 新架构.md, v1-deprecation.md, new-arch-progress.md, main-alignment-audit.md, post-fix-iterations.md, sse-race-debug.md. - .flue/ skeleton (agents/user-agent.ts, app.ts, tools/launch-electron.ts) + flue.config.ts — initial scaffolding for user-agent testing. The agent successfully runs end-to-end (Claude Sonnet 4.6 via the project's Anthropic-compatible gateway) and produced a UX bug report on first run. The launcher itself hits a Playwright / Node v24 inspector handshake bug tracked separately; user-agent findings were validated against e2e/bun-start-simulation.spec.ts instead. - .gitignore: e2e-report-html/ → e2e/report-html/ (typo: underscore vs slash). * chore: bump to v0.2.0 0.2.0 marks the completion of the v2-daemon deprecation arc: - DaemonStatusBar component removed (UI no longer pretends to track a non-existent daemon process) - Single-instance architecture fully documented in AGENTS.md - Project DB + global DB isolation (--user-data-dir + AGENTDOCK_DEV_INSTANCE) replaces what used to be a shared ~/.agentdock/projects.db - 6 obsolete v2-daemon design docs removed (新架构.md, v1-deprecation.md, new-arch-progress.md, main-alignment-audit.md, post-fix-iterations.md, sse-race-debug.md) - project?.sessions.find optional-chain bug fixed; useOpenProject now normalizes inserted projects before writing to the React Query cache See PR #108 for the full diff. * fix(ci): update bun.lock after adding @flue/cli dependency The @flue/cli and @flue/runtime packages were added to package.json but bun.lock was not regenerated. CI's 'bun install --frozen-lockfile' step failed with 'lockfile had changes' — running 'bun install' locally now to capture the resolved package versions. * fix(ci): remove build:daemon from release workflow The 'Build daemon JS' step ran `bun run build:daemon`, which we removed in PR #108 because the v2-daemon architecture is gone and there's nothing to build. The release workflow also had `bun run build:daemon` chained into the final publish step — same removal. After this lands, retag v0.2.0 against master HEAD so the release workflow fires correctly. * fix(electron): harden port allocation + db null guards + steps JSON parse Address review comments from gemini-code-assist on PR #108 (now merged into master). Four real bugs fixed: 1. port-pool: serialize allocation via a chain, but a thrown error (e.g. PortPoolExhaustedError) used to poison the queue forever — every subsequent allocate() would reject. Now the queue absorbs the rejection while the original error still propagates to the caller. 2. db:projects:list: per-project DB can be null if db:init hasn't run; return empty session list instead of crashing the renderer. 3. db:projects:delete: same — guard against null per-project DB. 4. ipc/sessions:steps callback: wrap JSON.parse(row.steps) in try/catch to match the defensive parsing in db.ts:322. A corrupted steps blob no longer kills the step-update loop. The 'projectPath is not defined' thread was a false positive — the variable is declared at the top of sessions:create (line 74). The two 'hardcoded local paths' threads (test scripts) are pre-existing technical debt out of scope for this PR. * feat(ui): tab horizontal scroll + Ctrl+W close tab + new logo UI quality-of-life improvements for v0.2.1: - TabBar: enable horizontal scroll. When the tab strip exceeds the window width, the user can now scroll horizontally via trackpad gesture, mouse-wheel (deltaY → scrollLeft), or touch drag. Scrollbar is hidden for visual cleanliness. - TabBar: Ctrl+W / Cmd+W closes the active project tab, matching the common IDE convention. Skipped when an input/textarea/contenteditable has focus so users typing in the terminal or editor aren't surprised. - build/icon.ico: regenerated multi-size ICO (16/32/48/64/128/256) from the new logo design. New agentdock.png is the source raster. electron-builder picks up build/icon.ico automatically — no code references needed. - electron-builder.yml: explicit defensive exclusion of user-data paths (.agentdock/, data/db.sqlite*, global/projects.db*) in the files whitelist. The whitelist was already restrictive enough, but spelling these out makes intent obvious to anyone auditing the installer — the shipped AgentDock-Setup.exe contains only the app bundle, never baked-in user data. * fix(ui): address review feedback on PR #110 Four issues from gemini-code-assist: 1. electron-builder.yml — replace `**/data/**` style global globs with repo-root-relative patterns (`data/**`, `.agentdock/**`, etc.) so we don't accidentally exclude runtime resources like `out/renderer/data` that may legitimately exist inside bundled deps. 2. Ctrl+W shortcut — Electron's default application menu (notably macOS "Close Window") consumes Ctrl/Cmd+W before the renderer's keydown fires. Add a before-input-event listener in electron/main/window.ts that calls event.preventDefault() for Ctrl/Cmd+W so the renderer handler can take over and close the active project tab. 3. TabBar wheel listener — React's onWheel={...} registers a passive listener, so e.preventDefault() inside it is silently ignored. Move to a ref + useEffect pattern that adds the wheel listener with `{ passive: false }` so we can suppress default vertical scroll. 4. TabBar handleRemoveProject — wrap in useCallback with explicit deps and add to the Ctrl+W useEffect's dep array, removing the stale closure risk flagged by the exhaustive-deps lint rule. --------- Co-authored-by: Marco Taylor <[email protected]>
概述
fix(lifecycle) sync 钩子模式下
backgroundHookPromise改为返回undefined,使complete事件通过process.nextTick发送。之前 sync 钩子永远不会触发onBackgroundHookComplete,导致createSession.isPending一直为 true、session 卡片已经变 active 后 "+" 加号按钮还在转圈。已在 Copilot-Switch(afterCreateSession: bun i --ignore-scripts,sync 模式)验证通过。refactor(ui) 移除
DaemonStatusBar组件、样式与 testids。单实例架构下没有独立的 daemon 进程,这个栏位就是一个静态 "Running" 文字,没有任何信息价值。test 删除 4 个 v2-daemon 相关的 spec(断言已移除的栏位、以及早已不存在的架构):
daemon-status-and-fencing、daemon-v2-architecture、daemon-v2-flow、daemon-reconnection。test(e2e) 新增
scripts/e2e-bun-start-stepwise.ts:完全模拟bun start→ 打开 Copilot-Switch → 创建 session → 删除 session,全流程真实 UI 点击。包含"+ 加号转圈"的回归断言,并修正了状态检测——之前查 wrapper 的 classList 永远查不到.session-card-creating(这个 class 在内层 SessionCard 上),已改为查内层。chore 打包提交主进程重构(
electron/main.ts、ipc/db.ts、ipc/sessions.ts、bootstrap.ts、preload.ts、instance-lock.ts、port-pool.ts、session-manager.ts、plugins/db/index.ts、plugins/global-settings.ts等)、新 e2e spec(agent-e2e-raw、cold-start-race、session-create-progress-persistence等)、验收脚本、截图,以及大量文件的小幅格式调整。验证
npx electron-vite build通过npx tsc --noEmit通过npx playwright test --list解析全部 59 个测试(35 个文件)scripts/e2e-bun-start-stepwise.ts6 步全部通过(冷启动 → 打开项目 → 导航 → 创建 session → 删除 session);session 转 active 后加号停止转圈;删除后无 worktree/branch 残留备注
此 PR 是有意打包提交的——用户选择把所有工作树中的未提交改动一起合进来,而不是拆成多个 PR。Reviewer 可以重点关注:
plugins/session-lifecycle.ts的 lifecycle 修复(sync 模式 →backgroundHookPromise: undefined)scripts/e2e-bun-start-stepwise.ts回归测试其他改动是 in-progress 的重构和工具脚本,用户明确选择一起打包。