Scenarios that require human verification — either because they need a real Claude Code session, depend on macOS system behavior, or exercise interactions that Playwright/Go tests can't cover.
Run task dev:manual-test (builds testagent, seeds test data, starts dev server) before testing unless otherwise noted.
Items marked [P] have at least partial Playwright coverage in frontend/playwright/. The manual check still verifies behaviors the test can't (rendering quality, UX, real-process integration, OS-level interactions).
-
task devopens window with dark macOS titlebar, draggable, resizable - Status footer shows version and uptime, refreshes every ~5s
- Stale socket recovery:
kill -9the app process, relaunch — starts normally - Duplicate instance: launch second
ideatewhile running — shows error - Window state persistence (production binary only, not
task dev):task build && open cmd/ideate/build/bin/ideate.app, resize/move, close, relaunch — window restores position and size - StartHidden: no flash at default position before window appears at saved position
The CLI exposes only status, review, and the root daemon entry. Session start/stop, idea CRUD, and repo linking are intentionally UI + MCP only.
-
task cli -- statusreturns version and uptime JSON -
task cli -- review diff --repo . --base <ref> --head <ref>navigates to running app's review view -
task cli -- review <id>opens existing review by id (kind auto-detected) - Direct launch: close app, run
task cli -- review diff ...— app opens directly to the diff review view
- Create idea via UI: fill Name/Status/Summary, click Create, redirects to detail view [P]
- Edit idea: change status/summary, save, badge and content update
- Filesystem matches:
cat <ideas-dir>/<slug>/idea.mdshows correct YAML frontmatter + body - Same-day slug collision: create two ideas on same day, second gets time component in slug
- History panel: expand at bottom of detail, shows created/updated events with timestamps [P]
- Archived toggle: archived ideas hidden by default, "Show archived (N)" toggle reveals them [P]
- List ordering: active ideas first, most recently updated within each group
- Live reload: with the app open on the idea list or a detail view, edit
<ideas-dir>/<slug>/idea.mdfrontmatter in an external editor — the list/detail updates within ~1s without a manual refresh - Live reload — new idea dir:
mkdir <ideas-dir>/test-new-ideain a terminal — list refreshes and the new entry appears (will look incomplete untilidea.mdis created, but the watcher should pick up the dir)
- Idea detail shows backlog panel; seeded ideas (per
task seed:testdata) carry items - Add backlog item via orchestrator MCP (
add_backlog_items_by_slug): item appears in panel without manual refresh - Update item status to
in_progressvia MCP: chip / icon reflects new state - Update item status to
donevia MCP: item moves to done group (or hides, depending on view) - Delete item via MCP: removed from panel
-
depends_onandaffectsfields persist (round-trip): MCPlist_backlogreturns the fields set on add
- Start testagent session from idea via "+" button, terminal shows banner and prompt [P]
- Terminal interaction: type text, see echo response [P]
- Window resize: terminal reflows, testagent shows
[resized: WxH] - Stop session: click Stop, shows "Stopping...", terminal shows "Goodbye!", status changes to exited
- Session record persisted:
cat <ideas-dir>/<slug>/sessions/<uuid>.jsonhas correct metadata - Completed session in sidebar: gray dot, date, clicking shows metadata view [P]
- Running session reconnect: navigate away from running session and back, terminal replays buffered output
- Resume from metadata view: "Resume Session" button appears for resumable agents [P]
- Resume doesn't create duplicate sidebar entry: session count stays the same [P]
- New session via "+" creates a new sidebar entry: count increments [P]
Dormancy is now set only by the startup adoption sweep — sessions whose PID is dead on next launch flip to dormant. There is no runtime path to dormant while the app is running.
- Sessions stay
runningwhile the app is up regardless of how long they sit idle — no auto-dormancy - App quit + relaunch with PIDs gone: prior
runningsessions appear as dormant in the bar - Dormant session appears in the global session bar (chip in tabs zone) [P]
- Overflow popover lists dormant sessions alongside running ones
- Cmd+K palette: select a dormant entry → auto-resumes via the existing session UUID, terminal replays prior scrollback
- Orchestrator
list_sessionsMCP includes dormant entries withstatus: "dormant" - Orchestrator
get_session_outputon a dormant session: returns last-captured terminal output without crashing - Orchestrator
send_session_inputon a dormant session: resumes the session and submits input - Active-session deep link with a dormant target: open an idea with only dormant sessions, click an
ideate://ideas/<slug>/active-sessionlink (terminal or markdown).App.ResolveActiveSessionshould callStartIdeaSession(resume=true), the dormant session should resume, and the webview should navigate to/idea/<slug>/session/<uuid>. Unit tests cover theslug→emptyfall-through with a/dev/nullrunner; the resume happy path requires a real runner binary and is covered here.
- Force-delete a session's transcript file (
rm ~/.claude/projects/<encoded>/<uuid>.jsonl): on next sweep, session record'sstop_reasonflips toorphaned,outcomereads "claude transcript deleted" - Restore the transcript file from backup or rerun: on next sweep,
stop_reasonand the deleted-outcome marker both clear - Resume an orphan-marked session:
Outcomeclears to empty on flip back to running
These require claude CLI installed and authenticated. Start from an idea with seeded resources.
The launchd-PATH gap is the most likely first-launch failure on macOS. Exercise each tier:
-
$PATH hit: terminal-launch with
claudeon $PATH (open -a Ideatefrom a shell wherewhich claudesucceeds). Session starts. -
Curated-paths hit: launch from Finder/Dock with
claudeinstalled only at~/.local/bin/claude(or/opt/homebrew/bin/claudeetc) and a barebones launchd PATH. Session starts; no override set. -
~/.claude/local/claudehit: same as above but withclaudeonly at the Anthropic-installer location. -
IDEATE_CLAUDE_BINARYoverride: set the env var before launch (e.g.IDEATE_CLAUDE_BINARY=/tmp/claude-stub /Applications/Ideate.app/Contents/MacOS/ideate). Session uses the override even if a real claude is on $PATH. -
agents.claude.binaryconfig: set in<ideas-dir>/config.json, restart Ideate, start session — override used. Env var (if set) wins over config. -
Not-found error: rename / remove
claudefrom every searched location, start session — error toast starts withbinary not found: "claude"; looked in: $PATH, …(lists every searched path), followed by the two escape hatches (IDEATE_CLAUDE_BINARYenv,agents.claude.binaryconfig) and awhich claudehint. -
Context injection: start Claude session, ask "What idea are you working on?" — Claude mentions idea name, status, linked resources (from
--append-system-prompt) -
MCP get_idea: ask Claude to call
get_idea— returns current idea metadata -
MCP list_resources: ask Claude to call
list_resources— returns linked resources -
MCP add_resource: ask Claude to add a resource — resource appears in idea detail sidebar after refresh. Re-adding the same URL with a richer
type(e.g. promotingwebtogithub_pr) updates in place (upsert semantics). -
MCP delete_resource: ask Claude to delete a resource by URL — resource disappears from idea detail
-
MCP update_idea: ask Claude to update idea status/summary — changes reflected in UI
-
Session end hook: after Claude session ends naturally, session JSON shows
status: "completed"withendedtimestamp,session_endedevent in history -
Resume behavior: after completed Claude session, start another for same repo — Claude uses
--resume, conversation continues from prior context -
Session identity: verify
--session-id(new) and--resume(existing) flags pass the correct UUID
- Open diff:
task cli -- review diff --repo . --base <ref> --head <ref>— toolbar shows repo, range, file count [P] - File tree navigation: click files, diff panel updates with syntax highlighting
- Split/Unified toggle: click toggles between two-column and single-column view
- Resizable pane: drag border between file tree and diff panel
- No params: visit
/#/reviewdirectly — shows instruction text [P]
- Standalone via
diff start: with the daemon stopped, runtask cli -- review diff start --repo . --base <ref> --head <ref>— a new app window opens showing the diff with Submit/Cancel buttons. Click Submit — the window closes and the process exits. - In-app via MCP: with the daemon running, trigger
request_diff_reviewfrom a Claude Code session — the daemon navigates to the review view. Click Submit — the daemon stays running and the view shows "Submitted". [P] (in-app submit path) - Multiple comments on the same line: in either flow, add two comments on the same line+side; both render in the inline thread; the file tree badge shows the correct count.
- Cancel: same paths as Submit — standalone closes window, in-app stays running and shows "Cancelled". [P] (in-app cancel path)
- Pending review survives clean shutdown: with a pending review on disk linked to a running session, quit the app and relaunch — review stays
pending, session auto-resumes, agent'sget_diff_review_resultpoll continues to block until the human submits. - Pending review survives crash: same as above but
kill -9instead of clean quit — startup marks the running sessioncrash, auto-resumes it, review stayspending. - Orphaned review is cancelled on startup: with a pending review on disk whose linked session ended cleanly (StopReason=exit / cleared / compacted / user / orphaned, or session record deleted), relaunch — review flips to
cancelledandget_diff_review_resultpoll returnscancelled. - Stale review is cancelled on startup: with a pending review whose
createdis >30 days old, relaunch — review flips tocancelledregardless of session status. - Draft autosave: type a summary + add an inline comment on a pending review, wait ~1s, read
<configDir>/reviews/<id>.json—draft_bodyanddraft_commentsreflect the in-progress edits;bodyandcommentsremain empty. - Draft hydrate on reopen: with
draft_body/draft_commentspopulated on a pending record, navigate to/#/review?reviewId=<id>— summary textarea and toolbar count chip restore the drafted values. - Submit clears drafts: after a successful submit,
draft_*fields are empty in the on-disk record (the authoritativebody/commentscarry the submitted values). - Pending reviews bar: with one or more pending reviews on disk, navigate to
/— chips appear in the topbar with the kind-appropriate label (file basename for markdown,<repoLeaf> <base7>..<head7>for diff). Click → navigates to that review.
-
task cli -- review md start --path README.mdopens the Milkdown editor seeded with the file's current content; CLI prints a review ID (e.g.md-readme-<hash>) [P] (editor-render path; CLI invocation is Go-tested) - After Submit, the standalone window closes and
<configDir>/reviews/<id>.jsonshowsstatus: completewithmarkdown.marked_upreflecting the edit [P] (record state) - After Cancel, the standalone window closes and the record shows
status: cancelled[P] (record state) -
task cli -- review status <id>prints JSON withkind: "markdown",markdown.path,markdown.original,status, and (post-submit)markdown.marked_up,event,body -
task cli -- review <id>reopens an existing markdown review by ID (kind auto-detected, no--pathneeded)
(For the "any pending review blocks any new review" rule and CLI/MCP failure contract, see Concurrent Reviews.)
- After cancel on a markdown review, re-running
review md start --path <same file>returns the same deterministic ID and refreshesmarkdown.originalto the file's current content (iterate-after-cancel path) - After submit on a markdown review, re-running
review md start --path <same file>starts a fresh review (deterministic ID may be reused; checkcreatedtimestamp differs)
- In-session: agent calls
request_markdown_review(path)— daemon navigates to the markdown editor view - Agent's
get_markdown_review_result(id)blocks up to 60s and returns the marked-up content once the human submits - Skill-driven application: with
.claude/skills/request-markdown-review/, agent writes a file, requests review, then produces the next-version file by combining a textual diff + CriticMarkup scan — verbatim application for{++ ++}/{-- --}/{~~ ~> ~~}, interpretive application for{>> <<}comments andbody
- Seeded content renders with formatting (headings, paragraphs, lists, links, inline code, fenced blocks) [P]
- Top toolbar shows
+ Insert,− Delete,✎ Commentbuttons while status is pending; hidden once submitted/cancelled -
+ Inserttoggles the insertion mark on the current selection (or sets storedMarks if no selection) [P] -
− Deletetoggles the deletion mark on the current selection -
✎ Commentprompts for comment text and inserts acriticCommentatom node at the cursor (renders as{>>note<<}styled pill) - Floating selection toolbar (Crepe's bubble menu) shows only
Delete— Insert/Comment live in the top toolbar - Keymap:
⌘⇧Itoggle insertion,⌘⇧Ktoggle deletion,⌘⇧Ninsert comment - Suggesting mode (always-on in WYSIWYG): typing in the middle of a paragraph wraps the new text in an insertion mark (green underline). Source mode shows the typed text serialized as
{++...++}[P] - Suggesting mode: pressing Backspace on normal prose adds a deletion mark to the char before the cursor instead of removing it; cursor moves left past the now-struck-through char. Source mode shows
{-- --}around it [P] - Suggesting mode: pressing Backspace inside a fresh insertion (text typed in the same session) shrinks the insertion mark by one char — no
{-- --}is generated [P] - Suggesting mode: adjacent insertions (e.g. typing two separate words near each other) consolidate into a single
{++...++}mark on serialization - Suggesting mode: Backspace at the start of a paragraph defers to default Backspace behavior (joinTextblockBackward etc.) — does NOT produce a deletion mark spanning a block boundary
- Suggesting mode — substitution: select text, click
− Delete(or Backspace) to mark it as deletion, then click inside the deletion-marked range and type — the typed chars wear only the insertion mark (green underline, no red strike). Submit and verifymarked_upcollapses the adjacent del/ins into a single{~~old~>new~~}substitution [P] - Schema marks render per kind:
<ins class="cm-insertion">green underline,<del class="cm-deletion">red strikethrough,<span class="cm-comment">yellow pill,<span class="cm-substitution">orange dashed - CriticMarkup syntax inside fenced/inline code is preserved as plain text (the remark tokenizer only walks
textmdast nodes, soinlineCode/codeblocks pass through untouched) - Text selection color is high-contrast blue on the dark theme (readable, not muddled with the bg)
- Toolbar
Source/WYSIWYGbutton toggles modes; pending submit/cancel buttons stay visible across the toggle [P] - Source mode shows the full file text (including any YAML frontmatter); WYSIWYG mode hides frontmatter (Crepe would otherwise render
---as a thematic break) [P] - WYSIWYG → Source: body edits made in WYSIWYG appear in the source view; frontmatter remains intact
- Source → WYSIWYG → Source: frontmatter edits made in Source survive a WYSIWYG round-trip
- Submitting from Source mode preserves the source-edited content (frontmatter + body) in
markdown.marked_up[P]
- Reviewing an
idea.md: WYSIWYG hides frontmatter, body is editable, Submit preserves frontmatter byte-for-byte inmarked_up[P] - CriticMarkup marks added in WYSIWYG do not leak into the frontmatter region of
marked_up
- Submit with no changes →
event: APPROVE - Submit with any change (CriticMarkup mark, direct prose edit, source-mode edit) →
event: REQUEST_CHANGES - After Submit (in-app, daemon mode), the editor view shows a
Submittedstatus badge and the editor becomes read-only (toolbar mark buttons hidden) [P] (badge visibility) - After Cancel (in-app), the view shows
Cancelled, editor read-only [P] (badge visibility)
-
kill -9the daemon with a pending markdown review linked to a running session — relaunch resumes the session and the review stayspending; agent'sget_markdown_review_resultpoll keeps blocking until the human submits. (Orphaned and stale cases mirror the diff-review behavior — see the M4c crash-recovery items above.) - Markdown draft autosave: in source mode, edit body + summary on a pending markdown review, wait ~1s, read the on-disk record —
markdown.draft_marked_upanddraft_bodyreflect the edits;markdown.marked_upremains empty. - Markdown draft hydrate on reopen: with
markdown.draft_marked_uppopulated on a pending record, navigate to/#/review?reviewId=<id>and toggle to source mode — the textarea content matches the persisted draft.
Only one pending review (any kind, any path or range) is allowed at a time. While a review is pending, attempts to start a new one fail. Once the pending review reaches a terminal state (complete or cancelled), a new review can be started and the window navigates to it.
Failure contract:
- CLI (
ideate review diff start/ideate review md start) exits non-zero, printsreview already in progress: <id>to stderr, and prints nothing to stdout. The window does not navigate. - MCP (
request_diff_review/request_markdown_review) returns a tool failure that includes the in-progress review ID so the calling agent can poll the existing review (viaget_*_review_result) instead of retrying.
- Pending markdown +
review md start --path <same path>→ exits non-zero withreview already in progress: <id>, window unchanged - Pending markdown +
review md start --path <different path>→ exits non-zero, window unchanged - Pending markdown +
review diff start --repo . --base X --head Y→ exits non-zero, window unchanged - Pending diff +
review md start --path <any>→ exits non-zero, window unchanged - Pending diff +
review diff start(same range) → exits non-zero, window unchanged - Pending diff +
review diff start(different range) → exits non-zero, window unchanged - Reopen-by-ID of the pending review while it's displayed (
task cli -- review <id>) → window stays on it (this is navigation, not "start a new review"); CLI exits zero
- In-session agent calls
request_markdown_reviewwhile any review is pending → tool returns failure naming the in-progress review ID; agent polls that ID viaget_markdown_review_result(orget_diff_review_resultif the in-progress kind is diff) and unblocks normally on completion - In-session agent calls
request_diff_reviewwhile any review is pending → same: failure with in-progress ID - Cross-kind: pending markdown + agent calls
request_diff_review→ fails with the markdown review's ID; agent must poll that one first
- Submit a markdown review (status →
complete), thenreview md start --path <new>→ succeeds, window navigates to the new editor - Cancel a markdown review (status →
cancelled), thenreview diff start ...→ succeeds, window navigates to the diff viewer - Submit a diff review, then
review md start --path <any>→ succeeds, window navigates to Milkdown editor - Window focus on terminal→new transition: minimize the daemon window between submit and the new
review * start, then start the new review — window unminimizes and comes to the front - Reopen-by-ID of a different terminal review (
task cli -- review <other-id>) → window navigates, kind auto-detected
Canonical grammar lives in internal/model/urls.go and frontend/src/lib/deeplink.ts::translateIdeateURL. Note the plural: ideate://ideas/<slug> (NOT idea/). The OS-scheme variants (cold-start / hot-launch via open) have their own section below.
- MCP tool responses (e.g.
get_idea) includeideate://ideas/<slug>deep-links in text fields - Clicking a deep-link in the orchestrator terminal navigates the main view to the target idea
-
match_resource_urls: pass an array of URLs (a mix of github_pr, notion, web) → returns the slug + label of each matching idea -
list_ideasincludes inlinesummaryfield on each entry (single round trip, no follow-upget_ideaneeded) -
list_sessionsincludes inlineidea_summaryon each entry
- With
IDEATE_RSS_LOG_INTERVAL_SECset (default 60), structuredsloglines for per-session RSS appear at that cadence; no session is stopped based on RSS
- Real Claude session with a long transcript (>30 turns) triggers session-end summary: dashboard card shows the one-line intent within a few seconds of session end
- No autocompact thrash in
logs/: grep forauto-compactpatterns — should be quiet on a clean run (regression check for the env-var fix)
Spot-check after the color-token rewrite. Compare against the README's hero GIF for the canonical look.
- Dashboard: cream-on-dark background, idea-card hover states, status chip colors (active / paused / archived)
- Idea detail: sidebar, content area, header bar
- Session terminal: ANSI colors render correctly inside the host
- Diff review: green/red diff backgrounds, inline-comment chips
- Markdown review: CriticMarkup ins (green underline) + del (red strike) overlay colors
- Command palette: backdrop dim, selected row highlight
- Orchestrator drawer: pinned-state and expanded-state both readable
Run only against a real signed/notarized DMG built by the v* tag pipeline.
- Download notarized
.dmgfrom a fresh tag release → mount → drag to Applications - First launch from Applications: no Gatekeeper "unidentified developer" prompt, app opens directly
- Quarantine staple:
spctl -a -vv /Applications/Ideate.appreportsaccepted/notarized - Offline launch: disconnect from network, launch app — opens normally (notarization stapled, no online check needed)
- Concurrent cross-idea sessions: start sessions on two different ideas, verify both terminals work independently
- Single-session-per-workdir enforcement: start a session, try to start another for the same working dir — returns existing session
- Crash recovery:
kill -9app during running agent session, relaunch — stale manifests cleaned, no orphaned processes - Orphan process check: close app window,
ps aux | grep claudeshows no lingering agent processes
These are covered by automated tests and do NOT need manual verification:
Playwright E2E (task test:ui — 25 tests):
- Dashboard rendering, idea CRUD, session lifecycle with testagent, resume/new session sidebar counts, diff viewer rendering, form validation, archived toggle
Go unit/integration (go test ./...):
- Store (filesystem CRUD, history, sessions, repos, frontmatter parsing, slugs)
- Agent (coordinator lifecycle, manifest persistence, PTY session, context generation)
- MCP (tool handlers, HTTP transport, event callbacks)
- Hooks (typed handler dispatch, session finalization, history recording)
- IPC (server, socket path resolution)
- Review (diff parsing, local source)
Deep links wire open ideate://... from a shell or other app into the running Ideate window via Wails Mac.OnUrlOpen (internal/app/app_lifecycle.go::HandleOpenURL). The URL is forwarded to the frontend's deeplink event listener (frontend/src/App.tsx::useOSDeeplinkBridge) which calls handleLink — the same dispatcher used by in-terminal and markdown clicks. Tests require a built, lsregister-registered Ideate.app.
Dev rebuild dance: after each task build, run lsregister -f cmd/ideate/build/bin/ideate.app to refresh LaunchServices. Production-signed DMG install triggers it automatically.
- Cold start: with Ideate NOT running and
lsregister -f ...done,open ideate://ideas/<seeded-slug>from Terminal — Ideate launches and the visible window lands on/idea/<slug>(or its session route if a session is seeded). - Hot launch: with Ideate already running,
open ideate://orchestratorfrom Terminal — the existing window comes to front (no second process viapgrep -f ideate | wc -l → 1) and navigates to/orchestrator. - Active-session deep link with running session:
open ideate://ideas/<slug>/active-sessionon an idea with a running session — navigates to that session's/idea/<slug>/session/<uuid>. - Active-session deep link with dormant session: same URL on an idea with only dormant sessions —
ResolveActiveSessionresumes the dormant; window navigates to the session route. - Active-session deep link with no session: same URL on an idea with no sessions — falls back to
/idea/<slug>. - Unknown scheme:
open ideate://garbage— no navigation; no crash. - Markdown-link parity: from inside a markdown view, an
ideate://orchestratorlink click routes identically (in-process, viahandleLink); the OS scheme handler is not invoked.