Skip to content

fix(desktop): vibrancy ON時にダイアログ/モーダル背景の透過を修正#200

Merged
MocA-Love merged 1 commit intomainfrom
worktree-fix+185-vibrancy-dialog
Apr 16, 2026
Merged

fix(desktop): vibrancy ON時にダイアログ/モーダル背景の透過を修正#200
MocA-Love merged 1 commit intomainfrom
worktree-fix+185-vibrancy-dialog

Conversation

@MocA-Love
Copy link
Copy Markdown
Owner

close #185

概要

デスクトップアプリでVibrancy(透過)をONにすると、ダイアログ/モーダルの背景まで半透明になり内容が見づらくなる問題を修正。

原因

vibrancy ON時に --background CSS変数が rgba(..., 0.6) に設定される。Dialog/AlertDialog/Sheet/Drawerは bg-background を使っているため、そのまま半透明が適用されていた。一方 --popover(0.95)は適切な不透明度だが、これらのコンポーネントでは使われていなかった。

変更内容

  • apps/desktop/src/renderer/globals.css のvibrancyセクションに、オーバーレイ系コンポーネント(data-slotセレクタ)の背景色を var(--popover) でオーバーライドするCSSルールを追加
対象コンポーネント 変更前(alpha) 変更後(alpha)
DialogContent 0.6 0.95
AlertDialogContent 0.6 0.95
SheetContent 0.6 0.95
DrawerContent 0.6 0.95

テスト方法

  1. Settings > Appearance > Vibrancy をONにする
  2. 任意のダイアログ/モーダルを開く(例: Settings自体、ファイル削除確認など)
  3. 背景が透けず、コンテンツが読みやすいことを確認
  4. Vibrancy OFFの場合に変化がないことを確認

@MocA-Love MocA-Love self-assigned this Apr 16, 2026
@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Apr 16, 2026

Warning

Rate limit exceeded

@MocA-Love has exceeded the limit for the number of commits that can be reviewed per hour. Please wait 9 minutes and 24 seconds before requesting another review.

Your organization is not enrolled in usage-based pricing. Contact your admin to enable usage-based pricing to continue reviews beyond the rate limit, or try again in 9 minutes and 24 seconds.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 69aa4918-9218-49c3-8c9d-51b2588a617d

📥 Commits

Reviewing files that changed from the base of the PR and between 58c8e6f and 31b6c7e.

📒 Files selected for processing (1)
  • apps/desktop/src/renderer/globals.css
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch worktree-fix+185-vibrancy-dialog

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@MocA-Love MocA-Love marked this pull request as ready for review April 16, 2026 02:28
@MocA-Love MocA-Love merged commit 94a53e7 into main Apr 16, 2026
6 checks passed
MocA-Love pushed a commit that referenced this pull request Apr 16, 2026
…#3467)

* Create doc

* docs(desktop): finalize v2 launch context plan

Replace initial draft with V2-greenfield architecture: structured
AgentLaunchSpec (system/user/attachments ContentPart[]), per-agent
contextPromptTemplate on ResolvedAgentConfig, Uint8Array over IPC,
vendor-aligned (AI SDK v3, Anthropic cache_control, Continue.dev
contributor metadata). CLI agents keep disk + path-ref pattern;
chat agents get structured passthrough with Files API as phase 6.

* feat(desktop/context): add v2 launch context types and fixtures

Step 1 of the v2 launch-context composition plan. Defines the core
discriminated types (LaunchSource, ContextSection, LaunchContext,
AgentLaunchSpec, ContentPart with Uint8Array data) and the canonical
multi-source + prompt-only fixtures that the composer and
buildLaunchSpec tests will share.

No runtime code yet — types and fixtures only.

* feat(desktop/context): add composer with dedup, ordering, failure tolerance

Step 2 of the v2 launch-context composition plan.

buildLaunchContext parallel-resolves sources via a contributor registry,
dedups URL/id-kinds (attachments never dedup), preserves input order
within a kind, applies the default kind group order at the end,
tolerates per-source failures (populated on failures[]), and enforces a
10s per-contributor timeout.

taskSlug derivation: first internal-task section wins, falling back to
first github-issue. 12 tests pass.

* feat(desktop/context): add six contributors and default registry

Step 3 of the v2 launch-context composition plan. One contributor per
LaunchSource kind, each with Continue.dev-style metadata (displayName,
description, requiresQuery), its own co-located test file, and a
consistent 404 -> null (non-fatal) pattern for fetch-based kinds:

- userPrompt        -- trims, returns null on empty
- attachment        -- file or image ContentPart by mediaType
- agentInstructions -- system-scoped, cacheControl: ephemeral
- githubIssue       -- title + body markdown, meta.taskSlug from slug
- githubPr          -- title + branch + body markdown
- internalTask      -- title + description, meta.taskSlug

Also adds composer.integration.test.ts covering the real registry
end-to-end against the multi-source fixture. 41 tests green.

* feat(shared): generic renderPromptTemplate + context prompt variables

Step 4 of the v2 launch-context composition plan.

- Extract renderPromptTemplate(template, Record<string, string>) as the
  generic primitive; existing renderTaskPromptTemplate is now a shim
  (same API, same behavior — callers unchanged).
- Add AGENT_CONTEXT_PROMPT_VARIABLES (userPrompt, tasks, issues, prs,
  attachments, agentInstructions) + getSupportedContextPromptVariables +
  validateContextPromptTemplate.
- Ship default context templates for markdown (codex/cursor/custom) and
  Claude (XML-wrapped user-request) — both for system + user scopes.
- Collapse runs of 3+ newlines to 2 so empty variables produce clean
  output. Empty-string values substitute in (not treated as missing).

16 tests green; no consumer breakage.

* feat(agents): add contextPromptTemplate {system, user} to agent configs

Step 5 of the v2 launch-context composition plan.

Extends the agent config surface so V2 launches can render structured
context into per-agent system/user templates:

- packages/shared/agent-definition: required contextPromptTemplateSystem
  and contextPromptTemplateUser fields on BaseAgentDefinition;
  createTerminalAgentDefinition fills defaults with the markdown
  templates from step 4.
- packages/shared/builtin-terminal-agents: Claude terminal ships the
  Claude-XML defaults; other builtins inherit markdown defaults.
- packages/shared/agent-catalog: BUILTIN_CHAT_AGENT (Claude-based
  superset-chat) ships the Claude-XML defaults.
- packages/local-db/schema/zod: add both fields to AGENT_PRESET_FIELDS,
  agentPresetOverrideSchema, agentCustomDefinitionSchema (optional).
- apps/desktop/shared/utils/agent-settings: thread through
  TERMINAL_OVERRIDE_FIELDS, CHAT_OVERRIDE_FIELDS, AgentPresetPatch,
  CustomAgentDefinitionPatch, resolveAgentConfig (both branches),
  applyCustomAgentDefinitionPatch, createOverrideEnvelopeWithPatch.
- apps/desktop/test-setup: update the mocked @superset/local-db schema
  (the Bun test workaround for drizzle-orm/sqlite-core) so tests see
  the same shape as runtime.

New tests: contextPromptTemplate resolution for claude terminal,
codex markdown defaults, superset-chat claude defaults, terminal and
chat override replacement, custom terminal fallback to markdown. 113
tests green across context + agent-settings suites.

* chore: biome format pass

* feat(desktop/context): user-prompt source takes ContentPart[] (multimodal)

Future-proofs the user-prompt LaunchSource for an eventual rich-editor
input (interleaved text, inline images, inline files). The rest of the
pipeline was already ContentPart[]-native; this removes the last narrow
string-only call site.

- LaunchSource["user-prompt"]: { text: string } -> { content: ContentPart[] }
- userPromptContributor: normalizes (drops empty text parts, trims bookend
  whitespace), returns null when nothing remains, passes file/image parts
  through untouched.
- Adds userPromptFromText(text) helper for plain-string callers so
  modal/cli/task flows don't repeat the [{ type: "text", text }] boilerplate.
- Three new tests: multimodal text+image+text preservation, whitespace-only
  content returns null, empty text parts dropped between non-empty ones.

* refactor(desktop/context): drop agent-instructions source — harnesses handle it

Agent harnesses (Claude CLI, Codex, Cursor Agent) discover their own
conventions files natively from the worktree — no injection needed from
our side. V1 confirms: zero references to AGENTS.md/CLAUDE.md as
injected context. Only the agent itself reads them.

Removing this also gets us closer to the "no Electron IPC in V2" rule —
the composer no longer needs to read files from disk.

- Drop {kind: "agent-instructions"} from LaunchSource union.
- Delete agentInstructionsContributor + its test.
- Remove readAgentInstructions from ResolveCtx; update the three
  contributor test stubs that referenced it.
- Drop "agent-instructions" from the composer KIND_ORDER and
  sourceIdentity switch.
- Drop the AGENTS.md sample from the multi-source fixture + the
  composer integration test.
- Remove "agentInstructions" from AGENT_CONTEXT_PROMPT_VARIABLES.
- System default templates are now empty strings (chat agents get no
  system context yet; future host-service-backed path can fill later).

54 tests green across context + agent-settings; 16 green in shared.

* feat(desktop/context): add buildLaunchSpec (LaunchContext -> AgentLaunchSpec)

Step 6 of the v2 launch-context composition plan.

buildLaunchSpec(ctx, agentConfig):
- Returns null for "none" agent or missing config (V1-parity semantics).
- Pre-renders per-kind markdown sub-blocks ({{tasks}}/{{issues}}/{{prs}}/
  {{attachments}}) and a flattened {{userPrompt}} text variable from the
  LaunchContext sections.
- Fills the agent's contextPromptTemplate{System,User} (from step 5)
  into ContentPart[] arrays.
- Collects non-text parts (attachment-kind files/images + inline
  non-text parts from user-prompt) into the structured attachments[]
  field — chat agents see them as proper content parts, terminal
  adapters will flatten to disk refs in step 7.

Also fixes the multi-source fixture to match what contributors actually
emit (`# Title\n\nBody` markdown bodies) so the new snapshot tests
exercise a realistic LaunchContext shape.

15 tests green (2 inline snapshots for Claude-XML + codex-markdown
rendering of the canonical multi-source fixture). 69 tests green total
across context + agent-settings.

* Lint

* refactor(agents): drop Claude XML default template — markdown is enough

V1 never wrapped prompts in XML; V1 has shipped with bare markdown/text
forever. Shipping an XML-only Claude default was speculative and added a
per-agent divergence without evidence.

- Remove DEFAULT_CLAUDE_CONTEXT_PROMPT_TEMPLATE_SYSTEM / _USER.
- Claude terminal + superset-chat ship the default markdown templates
  (same as codex/cursor/custom).
- Users can still override per-agent in settings if XML wrapping helps
  their use case — defaults stay neutral.

Also ships scripts/demo-launch-spec.ts for local template iteration:
run `bun run scripts/demo-launch-spec.ts [agent...]` to eyeball what
buildLaunchSpec produces for canonical inputs.

66 tests green across context + agent-settings; 14 green in shared.

* feat(desktop/context): preserve inline order for rich-editor user prompts

buildLaunchSpec used to flatten the user-prompt section's ContentPart[]
to a single text blob. That lost inline ordering when a rich editor
produces text + image + text — the image landed in spec.attachments
disconnected from its position.

Now:
- Split the agent's user template on {{userPrompt}}; render each half's
  other placeholders raw (no trim / no newline collapse) so whitespace
  around the placeholder is preserved.
- Splice the user-prompt section's ContentPart[] in at the split
  position, keeping [text, image, text] ordering intact.
- Merge adjacent text parts, then collapse 3+ newlines to 2 and trim
  document boundaries in a final pass.
- spec.attachments now carries only *explicit* attachment-kind sections;
  inline non-text parts from user-prompt stay inline in spec.user.
- Inline parts still appear in the {{attachments}} list so CLI agents
  reading just the flattened text get a file-path reference.

Chat agents: hand spec.user straight to the Anthropic/AI SDK user
message content[] — model sees the image between the text chunks.
Terminal adapters (step 7) will flatten file/image parts to
`![filename](.superset/attachments/...)` markdown refs at their inline
position, then write files to disk.

Demo script gets two new scenarios exercising the rich-editor flow:
text+image+text alone, and the same with a linked issue. 67 tests
green across context + agent-settings.

* feat(desktop/context): add buildAgentLaunchRequest — V2 spec to V1 launch bridge

Step 7 (scoped): hand V2's AgentLaunchSpec off to V1's battle-tested
terminal-adapter / chat-adapter without building new execution
infrastructure. Bytes-over-IPC / SuperJSON transformer work is deferred
to a follow-up.

buildAgentLaunchRequest(spec, agentConfig, { workspaceId, source }):
- Returns null for agentId "none" or disabled agents (V1 parity).
- Assigns collision-safe filenames across all binary parts (inline in
  spec.user + explicit spec.attachments). Uses the same sanitize +
  dedup algorithm V1 already uses so nothing drifts.
- Flattens spec.user to markdown text with file/image parts rendered
  as `![filename](.superset/attachments/filename)` at their inline
  positions — rich-editor ordering survives into the CLI prompt.
- Converts Uint8Array binary data to base64 data URLs at this boundary
  (V1 wire format). Internal plumbing stays on Uint8Array.
- Chat: initialPrompt = flattened text, taskSlug/model flow through.
- Terminal: command = buildPromptCommandFromAgentConfig(flattened text),
  or the non-prompt command when the prompt is empty.

10 new tests cover: "none" short-circuit, terminal command rendering,
chat initialPrompt/taskSlug, inline image path-ref correctness, explicit
attachment filename preservation, filename dedup across user + attachments,
base64 data-URL format, workspaceId/source passthrough.

77 tests green across context + agent-settings.

* feat(desktop): add useEnqueueAgentLaunch hook

Step 8 of the v2 launch-context composition plan.

Thin wrapper around V1's useWorkspaceInitStore.addPendingTerminalSetup
for the V2 submit flow. Called after host-service.workspaceCreation
resolves the real workspaceId. V1's terminal-adapter / chat-adapter
pick up the pending setup when the workspace mounts and execute the
launch — no new adapter code needed.

- buildPendingSetup(args) — pure function, rewrites launchRequest
  workspaceId to the real id and assembles the PendingTerminalSetup.
- useEnqueueAgentLaunch() — React hook wrapper that calls into the
  zustand action.
- Null launchRequest is a no-op (nothing to enqueue, e.g. agent "none").

Tests cover: null short-circuit, workspaceId rewrite, projectId
passthrough, initialCommands shape, non-workspaceId field preservation.
5 tests green; typecheck clean.

* feat(desktop/v2): wire v2 workspace launch into pending page

Step 9 of the v2 launch-context composition plan. Closes Gaps 4, 5 in
V2_WORKSPACE_MODAL_GAPS.md for the "fork" intent (plain prompt + local
host). Gaps 3 (AI branch name) and 6 (create-from-PR) remain as
follow-ups.

What runs now:
- Modal submit → pending row → pending page fires createWorkspace.
- On success, buildForkAgentLaunch runs the V2 pipeline:
    sources <- pending row (user-prompt, linked issues/PRs/tasks, attachments)
    buildLaunchContext → buildLaunchSpec → buildAgentLaunchRequest
- useEnqueueAgentLaunch stashes the V1-shaped AgentLaunchRequest in
  useWorkspaceInitStore. V1's terminal-adapter / chat-adapter pick it
  up when the workspace mounts and execute the launch — no new adapter
  code needed.

New file buildForkAgentLaunch.ts is a pure helper: builds sources from
a PendingWorkspaceRow, stubs ResolveCtx from the same row's metadata,
runs the pipeline, returns an AgentLaunchRequest or null.

Phase 1 gap: issue / PR / task bodies are not fetched over HTTP yet —
host-service lacks a body endpoint. The resolver returns empty bodies,
so agents see title + URL + task-slug metadata only. Full-body
injection is a follow-up once host-service grows getIssueContent /
getPullRequestContent / getInternalTaskContent.

13 new tests cover: empty sources → null, no-agent → null, prompt-only
terminal launch via default agent, taskSlug derivation, attachment
passthrough, source-kind ordering. 88 tests green across pending +
context + agent-settings suites.

* Lint

* docs(desktop): add v2 launch context reference doc

Post-phase-1 reference: what shipped, manual + automated test plan,
known gaps, prioritized follow-ups, and a file-layout map. Lives in
apps/desktop/docs/ per AGENTS.md rule 7 (architecture docs). The
original plan stays in plans/ since phases 2-6 are still unshipped.

* chore(debug): add [v2-launch] console logs across the launch pipeline

Temporary logs for manual testing:
- pending page: what buildForkAgentLaunch returned + enqueue inputs.
- useEnqueueAgentLaunch: stash / null-short-circuit.
- WorkspaceInitEffects: every handleTerminalSetup + dispatch branch,
  launchAgentViaOrchestrator invocation.

Grep devtools console on "[v2-launch]" to trace a full submit.
Remove or soften once the dispatch path is dialed in.

* docs(desktop): document pending-row-as-bus launch dispatch

V2 must own its own launch dispatch. V1's WorkspaceInitEffects →
orchestrator → terminal-adapter path writes panes into V1's useTabsStore,
which V2 doesn't render from, so launches dispatched through V1 land
invisibly for V2 workspaces.

Documents the replacement: pending-row-as-bus. Pending page produces
terminalLaunch / chatLaunch fields on the collection-backed pending row;
V2 workspace page mount-effect consumes them, opens a pane in the
@superset/panes store, and wires PTY via workspaceTrpc.

This mirrors the pattern V2 preset execution already uses
(useV2PresetExecution): live-query a record, open a pane, call
workspaceTrpc.terminal.ensureSession. Zero V1 primitives, zero new
host-service work, and leaves a clean migration path to host-owned
terminal launch when phase 5 ships.

Adds a blocking follow-up (#0) for the dispatch rewrite; marks
useEnqueueAgentLaunch + buildAgentLaunchRequest for removal.

* feat(desktop/v2): rewrite launch dispatch as pending-row-as-bus

The original step-8/9 wire-up stashed an AgentLaunchRequest in V1's
useWorkspaceInitStore, expecting V1's WorkspaceInitEffects to dispatch.
V1's orchestrator writes panes into useTabsStore — which V2 never
renders from — so launches landed invisibly for V2 workspaces.

This rewrite keeps V2 self-contained. After host-service.create
resolves, the pending page runs the composer pipeline and stashes a
terminalLaunch or chatLaunch on the pending row. The V2 workspace
page's new useConsumePendingLaunch mount-effect live-queries that row,
opens a pane in @superset/panes, and drives PTY via workspaceTrpc.
Same pattern as useV2PresetExecution.

Changes:
- Schema: pendingWorkspaceSchema gains optional terminalLaunch and
  chatLaunch fields, cleared to null once consumed.
- buildForkAgentLaunch returns a PendingLaunchBuild union (terminal
  with attachmentsToWrite / chat with inline initialFiles) instead of
  the V1 AgentLaunchRequest shape.
- dispatchForkLaunch: new pending-page helper that runs the composer,
  writes attachments to .superset/attachments/ via workspaceTrpc
  .filesystem.writeFile for the terminal path, and applies the launch
  field to the pending row.
- useConsumePendingLaunch: new V2-workspace-page mount effect. Reads
  row by workspaceId, opens pane in V2 store, calls workspaceTrpc
  .terminal.ensureSession with initialCommand for terminal launches,
  clears the field.
- ChatPaneData gains a transient launchConfig slot. ChatPane and
  WorkspaceChatInterface thread initialLaunchConfig +
  onConsumeLaunchConfig through. After the V2 chat runtime auto-sends
  the initial message, it clears the pane's launchConfig.
- Rip out useEnqueueAgentLaunch hook, buildAgentLaunchRequest, and
  the debug logs in WorkspaceInitEffects.

23 tests green for buildForkAgentLaunch / buildLaunchSourcesFromPending;
type-check clean in the touched surface area.

See apps/desktop/docs/V2_LAUNCH_CONTEXT.md "Dispatch architecture".

* docs(desktop): add V2_LAUNCH_TEST_PLAN.md

Structured manual test checklist for the V2 launch dispatch pipeline:
terminal + chat happy paths, pending-row lifecycle, failure paths,
source-mapping edge cases, custom agents, cross-pane behavior, V1
regression.

Paired with copy-pasteable fixtures on ~/Desktop/v2-launch-test-artifacts/
(trace.log, notes.md, sample.png, prompts.txt, README) for drag-and-
drop testing.

* chore(debug): add url probe + submit logs for v2 attachment flow

Logs the blob/data URLs we get from the PromptInput provider at
submit time, then does a fetch() probe on each URL before
storeAttachments runs. Lets us see whether the URL is already dead
when useSubmitWorkspace fires — which would confirm a pre-submit
revocation (as opposed to a race inside storeAttachments itself).

Not a fix. Remove once the root cause is nailed down.

* fix(desktop/v2): pass converted files through PromptInput onSubmit

Root cause of the "Failed to fetch" attachment toast: the
PromptInput library calls clearComposer() before invoking onSubmit,
which revokes all blob: URLs stored in the provider. Our
useSubmitWorkspace was reading attachments back from the provider
via takeFiles() after that — so it got file entries whose URLs had
just been invalidated.

The library already does the blob→data-URL conversion itself and
passes the converted files into onSubmit's message arg. Use them
directly:

- useSubmitWorkspace now takes `files: SubmitAttachment[]` as an
  explicit argument. Drops the `useProviderAttachments()` dependency.
- handlePromptSubmit receives `{text, files}` from PromptInput and
  forwards the files.
- The existing Cmd+Enter keyboard fallback calls handleCreate()
  without files (unchanged behavior for the no-attachments path; the
  PromptInput's own Enter handler takes the file-carrying path).

* refactor(desktop): use dexie for the pending-attachment store

The prior hand-rolled IDB wrapper had two transaction-lifecycle bugs:

1. storeAttachments opened a readwrite transaction, then awaited
   fetch() on each file before calling store.put() — IDB auto-commits
   when the event loop yields with no pending requests, so the first
   put() fired against a finished transaction ("The transaction has
   finished.").
2. The same file (150+ lines of raw IDB callback plumbing) is exactly
   the shape of code where this class of bug keeps reappearing as
   the flow evolves.

Swap to Dexie 4 — the de-facto IndexedDB wrapper for apps (~11.9k⭐,
actively maintained, typed, handles transaction lifecycle correctly).

- storeAttachments: resolve blobs async outside any tx, then
  bulkPut() in one shot.
- loadAttachments / clearAttachments: where("key").startsWith(prefix).
- File collapses from ~150 to ~90 lines, no raw transactions, no
  cursor dance.

Behavior is identical from the caller's side. Schema version 1;
Dexie will open the existing database transparently (same DB name).

* chore(debug): add verbose [v2-launch] logs to dispatch + consume paths

Traces:
- dispatchForkLaunch start / built / chatLaunch-applied /
  terminalLaunch-applied
- useConsumePendingLaunch tick (live-query fires) + whether
  terminalKey / chatKey are already consumed
- consumeTerminalLaunch ensureSession + addTab + clear
- consumeChatLaunch addTab + clear

Grep devtools on "[v2-launch]" through the full submit -> open-workspace
flow. Lets us pin where dispatch stalls when no pane appears.

Temporary — remove once the end-to-end flow is nailed down.

* fix(desktop/v2): replace Buffer with browser-native base64 in renderer

Electron renderer doesn't expose Node's `Buffer` global (nodeIntegration
off). The fork-launch dispatch path and buildForkAgentLaunch were both
using `Buffer.from(...).toString("base64")` / `Buffer.from(base64, "base64")`
for binary <-> base64 conversion, which ReferenceError'd at runtime.

Swap to standards-based `btoa` / `atob` + a small byte <-> binary-string
helper. Works in renderer and Bun alike.

Applies to:
- dataUrlAttachmentToBytes (buildForkAgentLaunch.ts) — decode
  attachment data URL into Uint8Array.
- toBase64DataUrl (buildForkAgentLaunch.ts) — encode chat-bound files
  for ChatLaunchConfig.initialFiles.
- writeAttachmentsToWorktree (dispatchForkLaunch.ts) — encode bytes
  for host-service filesystem.writeFile's base64 content variant.

* docs(desktop): capture v2-launch footgun backlog

Seven items we caught during manual testing and intentionally deferred:

1. Deep solve for binary transport (blob URL / base64 fragility)
2. Reload-mid-launch spawns duplicate PTY (key terminalId off pending row)
3. Silent failure in consume hook — add toast
4. joinPath assumes POSIX — breaks for Windows hosts (phase 5)
5. Dexie schema coupling with pre-existing IDB store
6. PendingTerminalLaunch.attachmentNames unused by consumer
7. Remove [v2-launch] debug logs once flow is stable

Tracked in V2_LAUNCH_CONTEXT.md "Known footguns to revisit". None
are blocking phase-1 behavior; all have notes on the proper fix.

* feat(desktop/v2): toast on silent launch-dispatch failures

Seven silent swallow points across the launch path now surface a
toast so the user knows why the agent didn't auto-launch instead of
seeing "nothing happened":

- dispatchForkLaunch: buildForkAgentLaunch throw -> "Couldn't prepare
  agent launch" (description = error message).
- dispatchForkLaunch: buildForkAgentLaunch returned null AND user
  gave meaningful input -> warning "Workspace created but no agent
  launched" with hint to enable one in settings. Silent for the
  "fresh empty workspace, no agent configured" case (expected).
- dispatchForkLaunch: host-service URL not resolved -> "Couldn't
  reach host service".
- dispatchForkLaunch: writeAttachmentsToWorktree throw -> warning
  "Attachments didn't save to the workspace; agent will launch
  without files".
- writeAttachmentsToWorktree: missing worktreePath -> throw instead
  of silent return so the outer catch's toast fires.
- consumeTerminalLaunch: defensive bail -> "Couldn't open agent
  pane" (shouldn't happen, but defensive).
- consumeTerminalLaunch: ensureSession throw -> "Couldn't start
  agent terminal" with error message.
- pending page: loadAttachments throw in fork intent -> warning
  "Couldn't load saved attachments" (non-fatal, workspace still
  creates).

All keep their [v2-launch] console.warn/log so trace survives alongside
the toast.

* lint

* fix(desktop): address PR review — real issues only

Addresses the non-stale, non-debatable feedback from review bots:

- Prototype-chain substitution in prompt templates (agent-prompt-
  template.ts + buildLaunchSpec.ts): {{toString}} and similar now
  stay intact. Use Object.hasOwn() instead of `variables[key] ??`.
- renderTaskPromptTemplate no longer picks up generic 3+-newline
  collapsing — task-flow output matches V1 exactly: own-property
  substitution + trim only.
- buildLaunchSpec.renderUserTemplate tolerates whitespace in the
  placeholder: {{ userPrompt }} / {{userPrompt}} / {{  userPrompt  }}
  all match.
- Pending page's fork dispatch fetches agent configs imperatively
  via trpcUtils.settings.getAgentPresets.fetch() instead of reading
  from a useQuery hook — eliminates the race where a not-yet-
  resolved query silently skipped the dispatch and lost the launch
  for a successful workspace create.
- Drop ContextSection.scope field. It was never read (buildLaunchSpec
  ignored it); no contributor populated anything but "user" after we
  removed agent-instructions. Cleaner type + future re-introduction
  when a real system-scope consumer lands (phase 6 host-side
  instructions injection).

Tests: 54 context-suite passing, 14 shared-suite passing; desktop
typecheck clean in touched areas.

* docs(desktop): capture body-fetching gaps observed in manual test

Claude currently sees title-only for linked issues / PRs / tasks —
no bodies. Documents the gap, what V1 did (Electron IPC to
projects.getIssueContent), why we can't reuse it for V2 (no Electron
in V2 rule), and proposes the host-service procedures + stub swap.

Also covers:
- Empty `Branch:` in PR block — pending-row schema doesn't carry
  branch; fix via getPullRequestContent body fetch.
- Sanitization helpers to extract from V1 into a shared util.
- Attach-as-file vs inline-in-prompt decision (V1 attached,
  current V2 inlines — keeping inline for phase 1).

Ordered work plan at the bottom: getIssueContent first, then PR,
then internal-task (requires scoping). Acceptance criteria shows
the expected prompt shape after the fixes land.

* Update PR notes

* lint

* feat(desktop/v2): attachment framing + PR checkout hint + gap doc rewrite

Prompt refinements from manual testing:

- buildLaunchSpec {{attachments}} block now includes a short framing
  header: "Attached files — read them to understand the request."
  Cues the agent to actually use the files rather than treating them
  as passive metadata. Only appears when there are files.

- githubPr contributor says "Branch `X` is checked out in this
  workspace — commits you make continue this PR." Confirms to the
  agent that the worktree is on the PR's branch, so it shouldn't
  create a new branch or open a new PR.

- V2_LAUNCH_CONTEXT_GAPS.md rewritten with locked design decisions:
  bodies inline in prompt (no file writes for linked context), no
  truncation, no sanitization, PR checkout is true. Work plan:
  host-service getIssueContent → getPullRequestContent → task body
  API → swap stubs. Target prompt shape included.

54 tests green; 2 snapshots updated for new PR format.

* feat(desktop/context): explicit kind labels in contributor headers

Agents shouldn't guess whether a section is a task, issue, or PR from
context clues. Each contributor now prefixes its heading with the kind:

- `# GitHub Issue #123 — Auth middleware stores tokens in plaintext`
- `# Task TASK-42 — Refactor auth middleware`
- `# PR #200 — Rewrite auth middleware`

PR phrasing also clarified: "This PR is checked out in this workspace
on branch `fix/auth-encryption`. Commits you make here will be added
to this PR."

54 tests green; 2 snapshots updated.

* feat(desktop/v2): fetch issue + PR bodies via host-service, task via cloud API

The launch prompt now includes full bodies for linked GitHub issues,
PRs, and internal tasks instead of title-only stubs.

Host-service (packages/host-service):
- getGitHubPullRequestContent: new procedure wrapping octokit.pulls.get.
  Returns body, branch, baseBranch, author, isDraft, timestamps.
  (getGitHubIssueContent already existed.)

Client (apps/desktop pending page):
- buildForkAgentLaunch accepts an optional hostServiceClient. When
  provided, the issue + PR resolvers call getGitHubIssueContent /
  getGitHubPullRequestContent for full bodies. Falls back to
  pending-row title-only if the call fails (non-fatal).
- Task resolver calls apiTrpcClient.task.byId (Superset cloud API,
  same source as the task view) for description. Falls back to
  title-only on failure.
- dispatchForkLaunch threads the host-service client through.

Contributors (already landed earlier this session):
- GitHub issue header: `# GitHub Issue #N — Title`
- PR header: `# PR #N — Title` + "This PR is checked out in this
  workspace on branch `X`. Commits you make here will be added to
  this PR."
- Task header: `# Task ID — Title`
- Attachments block: framing header cueing the agent to read the
  files.

77 tests green. Typecheck clean.

* chore(desktop): fix biome warning + stale doc comment in fork launch

- internalTask.test.ts: replace `TASK.description!` non-null
  assertion with `if (TASK.description)` guard (biome
  lint/style/noNonNullAssertion).
- buildForkAgentLaunch.ts: update stale docstring that claimed
  bodies aren't fetched yet — they are, via host-service and the
  cloud task API.

77 tests green, biome clean, typecheck clean.

* fix(host-service): shell out to gh CLI for issue/PR content (V1 parity)

host-service's octokit path needs a GitHub token from
providers.credentials.getToken("github.com") — which most users don't
have set up (requires GITHUB_TOKEN env or git credential helper config
for github.com). Result: getGitHubIssueContent / getGitHubPullRequestContent
silently 500'd, buildForkAgentLaunch fell back to title-only, and the
agent received empty bodies for linked issues/PRs.

V1's projects.getIssueContent shells out to `gh issue view` via the
user's `gh auth login` — that works out of the box.

Port the same approach:

- New packages/host-service/src/trpc/router/workspace-creation/utils/exec-gh.ts
  — promisified execFile("gh", ...) with user shell env so PATH
  resolves on macOS GUI contexts.
- getGitHubIssueContent now calls `gh issue view <n> --repo owner/name
  --json number,title,body,url,state,author,createdAt,updatedAt`.
- getGitHubPullRequestContent calls `gh pr view <n> --repo owner/name
  --json number,title,body,url,state,author,headRefName,baseRefName,isDraft,...`.
- Zod-validate the JSON output before returning.
- Normalize state to lowercase (gh returns "OPEN"/"CLOSED" uppercase).

Drops the Octokit dependency on these two procedures. Other host-service
paths that still use ctx.github() unchanged.

* fix typecheck

* clean up
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

1 participant