Skip to content

feat(desktop): developer mode display style — prompt-first UI with pinned/recent channel list, palette, and split-pane side chats - #3459

Draft
joahg wants to merge 33 commits into
block:mainfrom
joahg:joah/dev-mode-display-style
Draft

feat(desktop): developer mode display style — prompt-first UI with pinned/recent channel list, palette, and split-pane side chats#3459
joahg wants to merge 33 commits into
block:mainfrom
joahg:joah/dev-mode-display-style

Conversation

@joahg

@joahg joahg commented Jul 29, 2026

Copy link
Copy Markdown
Member

Adds a developer mode display style to the Buzz desktop app (AGNTOPS-354): a prompt-first, keyboard-driven, Amp/Claude-Code-style surface layered on the existing channel structure.

Core flow

  • One monospace composer. Tab / Shift+Tab cycles the target between plain human chat and every available agent (managed + shared relay agents).
  • Enter on a fresh prompt spawns a new channel, attaches/tags the selected agent, and sends the prompt there. Agents are not limited to a single channel.
  • Agent-directed prompts carry an @Agent prefix on the wire (plus the mention tag) so the standard UI and other clients see who a prompt is for — but dev mode lifts it out of the message body and renders it as a small “to Agent” line (in the agent's chat color) under the author name. The composer shows its Tab-cycled target the same way: a “to Agent” line at the top of the chat box (no more bordered pill), with “chat” / “working…” for the other states.
  • Prompts render as cards in the channel (borderless until ↑/↓ selects one); agent replies land in the card's thread on the same indent as the prompt. ↑/↓ (empty composer) walk cards with zero layout shift; Enter on a card opens a split-screen side chat scoped to that prompt's thread.
  • Kind-7 reactions render as chips on prompts and replies — agents react while working, so the chips double as a working/loading indicator without any architecture change.

Channel list: pinned chats, recency order, unread indicators

  • The channel list is always visible on the left; it dims while a channel is focused. The composer covers only the channel pane — full width when creating a new session or navigating the channel list.
  • Per-channel pin toggle (hover a row → pin/unpin): pinned chats render in their own pinned section at the top; all other chats sit beneath. Pins are device-local (localStorage) — the channel protocol has no pin field.
  • Both sections auto-order by last activity, newest first; new channels start unpinned near the top of the lower section.
  • Unread indicators: unread channels render with a brighter, bold name plus a dot. Dev mode tracks unread channel-level posts only — a new top-level prompt lights the dot, unopened thread replies don't. Opening a channel marks its top-level timeline read (same passive NIP-RS path the standard channel screen uses); standard UI unread behavior is unchanged.
  • Draggable channel-list width: a vertical divider on the list's right edge (pointer + keyboard, clamped 200–480px, persisted per device); the keyboard-hint footer never wraps.
  • from the fresh composer highlights/previews channels (transcript previews behind), Enter opens, Escape backs out; Escape inside a channel returns to the list.

Command palette

  • Ctrl+O anywhere (or / on an empty composer) opens an Amp-style palette: channel search across all sessions, agent configuration, appearance/profile/notifications/shortcuts settings, switch to standard UI, and a name-color picker.
  • With a channel focused or previewed, the palette offers add someone to # channel (searches people & agents; local managed agents are attached with a running harness, others added with the right role) and leave # channel (archives instead when you're the last human member, since the relay refuses to remove the last owner). While typing, matching channels rank above commands.

Window chrome & focus

  • Developer mode replaces the shared top chrome: its gray surface stretches to the top of the window, with fixed-px clearance so the title never sits under the macOS traffic lights.
  • The title is vertically aligned with the traffic lights (same 3px optical offset the standard chrome uses) and the active/previewed channel name renders in the top bar, always visible. The transcript itself carries no duplicate channel name/topic header.
  • Refocusing the window restores the last-focused input; clicks on dead space never blur the active composer.
  • Prompt-card selection is keyboard-only (↑/↓ + Enter); clicking a card or message never selects it or steals focus — message text stays drag-selectable, and a click that ends without a selection returns focus to the composer. While a card is selected the caret leaves the message box entirely (typing is disabled); the shell drives ↑/↓/Enter/Escape, and clicking the box clears the selection to resume typing.

Split-pane side chat

  • Channel and side-chat composers sit side by side; draggable divider (pointer + keyboard, clamped 25–75%, persisted per device).
  • Both composers auto-grow upward with their content (wrapped lines included), capped at 40% of the window before scrolling internally; the top border doubles as a drag handle (pointer + keyboard) that sets a persisted minimum height.
  • Inactive pane dims; ←/→ on an empty composer switches panes.
  • ⌥↑/⌥↓ steps through the visible channel list (pinned then recent) directly from the composer — the caret never leaves the textbox, works mid-draft, and from the fresh composer ⌥↑ opens the nearest channel.
  • ⌘N jumps to the fresh composer to start a new channel from anywhere in dev mode. ⌘T opens a draft side chat in the open channel: the pane opens before any thread exists; the first send posts a root message to the channel (exactly like the main composer) and the pane attaches to that new thread for follow-ups.

Channel naming

  • Channel names are generated by an agent: a new buzz-acp complete subcommand runs a bounded one-shot ACP prompt through the tagged agent's own harness (same command/model/env resolution as agent spawn), bridged by a generate_agent_completion Tauri command. The exchange is a private subprocess — it never appears in any channel.
  • Channels are created instantly under a neutral unique new-session placeholder and renamed asynchronously when the title arrives; a lingering placeholder makes naming failures visible instead of masquerading as generated titles.
  • Falls back to the mesh LLM node's OpenAI-compatible endpoint when no managed agent completion is available.

Color

  • Deterministic per-author name colors hashed from pubkey into a curated palette, with a device-local hex override settable from the palette (a synced profile color field would need schema/event changes).
  • The composer's “to Agent” line and caret wear the selected agent's chat color.
  • Conservative keyword highlighting rendered as React spans: inline code, @mentions. Mid-message mentions matching one of the message's p-tag members render as a bordered pill in that author's chat color, in that author's chat color; multi-word agent names like amp (local) are matched longest-first. A leading mention is treated as direction, not prose — stripped from the body (side-chat header preview included) and shown as the “to Agent” line.
  • #channel references: typing # in either composer autocompletes known channel names (↑/↓ select, Tab/Enter insert, Esc dismiss); accepted references serialize as plain #channel-name text (the same format the standard UI links), and known references in the transcript render as clickable links that open that channel without stealing composer focus. Multi-word names match longest-first, same as mentions.
  • URLs render as clickable Slack-style links (link icon + fetched page title when available, else the scheme-stripped URL truncated at 64 chars) opening in the system browser; a new generic fetch_page_title Tauri command (https-only, 4s timeout, 256KB cap) resolves og:/twitter:/<title> titles.

Layout containment

  • Chat content can no longer push the layout past the right edge of the window: the dev-mode shell (a flex item) now has min-w-0 so nowrap content (truncated link labels, thread-header previews) can't inflate its intrinsic width, and the transcript/side-chat scroll viewports clip residual horizontal overflow. Long unbroken words wrap; links truncate inside their pane.
  • New Playwright regression test (dev-mode-overflow.spec.ts, smoke project) sends a long unbroken message, opens the side chat, and asserts nothing extends past the window.

Markdown rendering

  • Agent markdown renders as rich formatting in dev-mode transcripts: a block-level layer (fenced code blocks, headings, bullet/numbered lists with hanging indents, blockquotes, horizontal rules) over the existing span-based inline highlighter, which now also handles bold, italic, strikethrough, and [label](url) links (labeled links render via the Slack-style link component using the written label, skipping the title fetch). Emphasis delimiters are strict so snake_case and arithmetic stay plain prose; everything renders as React nodes — never HTML — and plain human chat renders exactly as typed.

Notes

  • Display style persisted in localStorage (⌘⇧D toggles); standard UI untouched.
  • Validation: pnpm typecheck, pnpm check, pnpm test (3792 pass), cargo test -p buzz-acp (639 pass), pnpm build all green.

Linear: AGNTOPS-354

joahg and others added 2 commits July 28, 2026 22:34
Prompt-first, terminal-style UI toggled in Settings > Appearance or
Cmd/Ctrl+Shift+D. Tab cycles the composer between chat and agent modes;
Enter on a fresh prompt spawns a stream channel named from the prompt,
attaches the selected agent, and mentions it so it starts working there.
Arrow keys navigate recent session channels.

Amp-Thread-ID: https://ampcode.com/threads/T-019fabca-7cdc-777d-9ccd-ed829c1e60f0
Co-authored-by: Amp <amp@ampcode.com>
Signed-off-by: Joah Gerstenberg <joah@squareup.com>
Each prompt in a session channel renders as a card containing the
agent's threaded response. With the input empty, Up/Down selects cards;
Enter opens the selected card's thread in a 50/50 split side chat with
its own composer, so follow-up questions carry that thread's context.
Escape unwinds side chat, card selection, then channel.

Amp-Thread-ID: https://ampcode.com/threads/T-019fabca-7cdc-777d-9ccd-ed829c1e60f0
Co-authored-by: Amp <amp@ampcode.com>
Signed-off-by: Joah Gerstenberg <joah@squareup.com>
@joahg joahg changed the title feat(desktop): add developer mode display style feat(desktop): developer mode display style with prompt cards and split-screen thread side chat Jul 29, 2026
…e polish (AGNTOPS-354)

- Amp-style command palette (Ctrl+O or / on empty composer): channel search
  across all sessions, settings/agent-config entries, standard-UI switch,
  and a name-color picker
- Explicit channel navigation: up from the fresh composer slides a channel
  navigator out from the left with live preview; enter opens, escape backs
  out; escape inside a channel returns to the navigator
- LLM channel naming via the mesh LLM node's OpenAI-compatible endpoint
  when running, with async rename and slug fallback
- Split-pane side chat: draggable (and keyboard-resizable) divider with
  persisted split, inactive-pane dimming, left/right arrows switch panes
- Deterministic per-author name colors with device-local hex overrides,
  conservative keyword highlighting (inline code, URLs, mentions)
- Square corners on prompt cards, pills, and channel rows

Amp-Thread-ID: https://ampcode.com/threads/T-019fabca-7cdc-777d-9ccd-ed829c1e60f0
Co-authored-by: Amp <amp@ampcode.com>
Signed-off-by: Joah Gerstenberg <joah@squareup.com>
@joahg joahg changed the title feat(desktop): developer mode display style with prompt cards and split-screen thread side chat feat(desktop): developer mode display style — prompt-first UI with command palette, channel navigator, and split-pane side chats Jul 29, 2026
joahg and others added 2 commits July 28, 2026 23:26
…split view

Amp-Thread-ID: https://ampcode.com/threads/T-019fabca-7cdc-777d-9ccd-ed829c1e60f0
Co-authored-by: Amp <amp@ampcode.com>
Signed-off-by: Joah Gerstenberg <joah@squareup.com>
…rome polish in dev mode (AGNTOPS-354)

- Channel list is always visible, dimmed while a channel is focused;
  composer covers only the channel pane, full width only for new sessions
- Device-local channel categories with per-row assignment menu;
  uncategorized/new channels always render below all categories
- Remove the shared top chrome bar in developer mode; dev-mode gray
  reaches the window top with macOS traffic-light clearance
- Window refocus restores the last-focused input; dead-space clicks
  no longer blur the composer
- Kind-7 reactions render as chips on prompts and replies (doubles as
  the agent working indicator)
- Agent replies align with the prompt's indent; selection hint is
  absolutely positioned so up/down navigation causes no layout shift
- Composer mode pill and caret match the agent's chat name color
- Agent-directed prompts carry a visible @agent prefix

Amp-Thread-ID: https://ampcode.com/threads/T-019fabca-7cdc-777d-9ccd-ed829c1e60f0
Co-authored-by: Amp <amp@ampcode.com>
Signed-off-by: Joah Gerstenberg <joah@squareup.com>
@joahg joahg changed the title feat(desktop): developer mode display style — prompt-first UI with command palette, channel navigator, and split-pane side chats feat(desktop): developer mode display style — prompt-first UI with categorized channel workspace, palette, and split-pane side chats Jul 29, 2026
joahg and others added 12 commits July 29, 2026 00:42
Channel names now come from the tagged agent's own harness instead of a
prompt slug. buzz-acp gains a 'complete' subcommand (bounded one-shot
ACP prompt returning JSON text); the desktop bridges it through a new
generate_agent_completion Tauri command that resolves the managed
agent's effective harness command/model/env exactly like spawn. Channels
are created under a neutral unique 'new-session' placeholder and renamed
asynchronously when the title arrives, so naming failures stay visible
instead of masquerading as generated titles. Mesh inference remains the
fallback when no managed agent completion is available.

AGNTOPS-354

Amp-Thread-ID: https://ampcode.com/threads/T-019fabca-7cdc-777d-9ccd-ed829c1e60f0
Co-authored-by: Amp <amp@ampcode.com>
Signed-off-by: Joah Gerstenberg <joah@squareup.com>
…-style

* origin/main:
  fix(acp): per-runtime env defaults at spawn — isolate Hermes from configured MCP startup (block#3420)

Signed-off-by: Joah Gerstenberg <joah@squareup.com>
Both the channel and side-chat composers now size to their content —
wrapped lines included, not just hard newlines — growing upward to 40%
of the window before scrolling internally. The top border doubles as a
drag handle (pointer + keyboard) that sets a persisted minimum height.

AGNTOPS-354

Signed-off-by: Joah Gerstenberg <joah@squareup.com>
Transcript URLs were blue-underlined spans you couldn't click. They now
render as links — link icon plus the page's og:/twitter:/<title> title
when a new generic fetch_page_title command can fetch it (https-only,
4s timeout, 256KB cap), falling back to the URL without its scheme,
truncated at 64 chars. Clicks open the system browser via the opener
plugin. Trailing sentence punctuation stays in the prose instead of
breaking the href.

AGNTOPS-354

Amp-Thread-ID: https://ampcode.com/threads/T-019fabca-7cdc-777d-9ccd-ed829c1e60f0
Co-authored-by: Amp <amp@ampcode.com>
Signed-off-by: Joah Gerstenberg <joah@squareup.com>
AGNTOPS-354

Amp-Thread-ID: https://ampcode.com/threads/T-019fabca-7cdc-777d-9ccd-ed829c1e60f0
Co-authored-by: Amp <amp@ampcode.com>
Signed-off-by: Joah Gerstenberg <joah@squareup.com>
AGNTOPS-354

Amp-Thread-ID: https://ampcode.com/threads/T-019fabca-7cdc-777d-9ccd-ed829c1e60f0
Co-authored-by: Amp <amp@ampcode.com>
Signed-off-by: Joah Gerstenberg <joah@squareup.com>
…ocus

AGNTOPS-354

Amp-Thread-ID: https://ampcode.com/threads/T-019fabca-7cdc-777d-9ccd-ed829c1e60f0
Co-authored-by: Amp <amp@ampcode.com>
Signed-off-by: Joah Gerstenberg <joah@squareup.com>
AGNTOPS-354

Amp-Thread-ID: https://ampcode.com/threads/T-019fabca-7cdc-777d-9ccd-ed829c1e60f0
Co-authored-by: Amp <amp@ampcode.com>
Signed-off-by: Joah Gerstenberg <joah@squareup.com>
AGNTOPS-354

Amp-Thread-ID: https://ampcode.com/threads/T-019fabca-7cdc-777d-9ccd-ed829c1e60f0
Co-authored-by: Amp <amp@ampcode.com>
Signed-off-by: Joah Gerstenberg <joah@squareup.com>
…t search

AGNTOPS-354

Amp-Thread-ID: https://ampcode.com/threads/T-019fabca-7cdc-777d-9ccd-ed829c1e60f0
Co-authored-by: Amp <amp@ampcode.com>
Signed-off-by: Joah Gerstenberg <joah@squareup.com>
AGNTOPS-354

Amp-Thread-ID: https://ampcode.com/threads/T-019fabca-7cdc-777d-9ccd-ed829c1e60f0
Co-authored-by: Amp <amp@ampcode.com>
Signed-off-by: Joah Gerstenberg <joah@squareup.com>
Pinned chats render in their own section at the top of the channel list;
all other chats sit beneath. Both sections order by last activity, newest
first, with unread dots + bold names driven by the shared useUnreadChannels
state from AppShell.

AGNTOPS-354

Amp-Thread-ID: https://ampcode.com/threads/T-019fabca-7cdc-777d-9ccd-ed829c1e60f0
Co-authored-by: Amp <amp@ampcode.com>
Signed-off-by: Joah Gerstenberg <joah@squareup.com>
@joahg joahg changed the title feat(desktop): developer mode display style — prompt-first UI with categorized channel workspace, palette, and split-pane side chats feat(desktop): developer mode display style — prompt-first UI with pinned/recent channel list, palette, and split-pane side chats Jul 29, 2026
joahg and others added 9 commits July 29, 2026 07:11
Message @mentions whose name matches one of the event's p-tag members
(e.g. the @agent prompt prefix) render as a bordered pill in that
author's chat color — visually matching the composer's mode pill.
Multi-word agent names like "amp (local)" are matched longest-first;
unknown mentions keep the generic highlight.

AGNTOPS-354

Amp-Thread-ID: https://ampcode.com/threads/T-019fabca-7cdc-777d-9ccd-ed829c1e60f0
Co-authored-by: Amp <amp@ampcode.com>
Signed-off-by: Joah Gerstenberg <joah@squareup.com>
The channel name already renders in the app top bar; the in-transcript
duplicate wasted space and repeated the first prompt's text as topic.

AGNTOPS-354

Amp-Thread-ID: https://ampcode.com/threads/T-019fabca-7cdc-777d-9ccd-ed829c1e60f0
Co-authored-by: Amp <amp@ampcode.com>
Signed-off-by: Joah Gerstenberg <joah@squareup.com>
…elected

Selecting a card with ↑ now blurs the message box (readOnly while
inactive) so typing can't land in it; the shell owns ↑/↓/Enter/Escape
through a window listener until the selection clears. Clicking the box
clears the selection and resumes typing. Window-refocus and
click-on-message focus restoration skip the composer during card
selection so they can't silently re-arm it.

AGNTOPS-354

Amp-Thread-ID: https://ampcode.com/threads/T-019fabca-7cdc-777d-9ccd-ed829c1e60f0
Co-authored-by: Amp <amp@ampcode.com>
Signed-off-by: Joah Gerstenberg <joah@squareup.com>
⌘N jumps to the fresh composer to start a new channel. ⌘T opens a draft
side chat in the open channel: the pane opens with no thread yet, the
first send posts a root message to the channel exactly like the main
composer, and the pane attaches to that new thread for follow-ups.

AGNTOPS-354

Amp-Thread-ID: https://ampcode.com/threads/T-019fabca-7cdc-777d-9ccd-ed829c1e60f0
Co-authored-by: Amp <amp@ampcode.com>
Signed-off-by: Joah Gerstenberg <joah@squareup.com>
The gutter layout (time · name · content on one line) produced ragged
content columns. Every message now renders a header line — author name
in its chat color, timestamp, reaction chips — with the content on its
own line at the left edge, Slack-style.

AGNTOPS-354

Amp-Thread-ID: https://ampcode.com/threads/T-019fabca-7cdc-777d-9ccd-ed829c1e60f0
Co-authored-by: Amp <amp@ampcode.com>
Signed-off-by: Joah Gerstenberg <joah@squareup.com>
AGNTOPS-354

Amp-Thread-ID: https://ampcode.com/threads/T-019fabca-7cdc-777d-9ccd-ed829c1e60f0
Co-authored-by: Amp <amp@ampcode.com>
Signed-off-by: Joah Gerstenberg <joah@squareup.com>
…-style

* origin/main:
  feat(agent): fix Anthropic prompt caching with Databricks (+ MCP proxy/TLS passthrough) (block#3463)
  Fix mobile attachment and gallery polish (block#3370)

Signed-off-by: Joah Gerstenberg <joah@squareup.com>
AGNTOPS-354

Amp-Thread-ID: https://ampcode.com/threads/T-019fabca-7cdc-777d-9ccd-ed829c1e60f0
Co-authored-by: Amp <amp@ampcode.com>
Signed-off-by: Joah Gerstenberg <joah@squareup.com>
Typing # in either composer suggests known channels; Tab/Enter inserts
a plain-text #channel-name reference (the same wire format the standard
UI renders as channel links). Known references in the transcript render
as clickable links that open the channel without stealing composer
focus.

AGNTOPS-354

Amp-Thread-ID: https://ampcode.com/threads/T-019fabca-7cdc-777d-9ccd-ed829c1e60f0
Co-authored-by: Amp <amp@ampcode.com>
Signed-off-by: Joah Gerstenberg <joah@squareup.com>
joahg and others added 7 commits July 29, 2026 09:04
⌥↑/⌥↓ in the prompt composer opens the previous/next channel in the
visible list (pinned then recent) without moving focus out of the
textbox — works with a draft in progress, and from the fresh composer
⌥↑ opens the nearest channel. Suggestion popups ignore modified arrows
so the shortcut still works while autocomplete is open.

AGNTOPS-354

Amp-Thread-ID: https://ampcode.com/threads/T-019fabca-7cdc-777d-9ccd-ed829c1e60f0
Co-authored-by: Amp <amp@ampcode.com>
Signed-off-by: Joah Gerstenberg <joah@squareup.com>
The auto-grow hook shrinks the textarea to 0px on every keystroke to
measure content; that reflow made the transcript flex sibling taller
for an instant, clamping its scrollTop and drifting the chat upward.
Freeze the composer wrapper's height during the measurement so
siblings never reflow, and have the pinned-scroll observer also watch
the scroller so a pinned view stays glued to the bottom when the
composer genuinely grows (newlines, drag, split moves).

AGNTOPS-354

Amp-Thread-ID: https://ampcode.com/threads/T-019fabca-7cdc-777d-9ccd-ed829c1e60f0
Co-authored-by: Amp <amp@ampcode.com>
Signed-off-by: Joah Gerstenberg <joah@squareup.com>
Developer mode now derives its channel-list unread dots from unread
top-level posts rather than the standard thread-aware unread set.
Opening a channel marks its top-level timeline read, so unopened thread
replies do not keep or restore the channel dot. Standard UI unread and
notification behavior remains unchanged.

AGNTOPS-354

Amp-Thread-ID: https://ampcode.com/threads/T-019fabca-7cdc-777d-9ccd-ed829c1e60f0
Co-authored-by: Amp <amp@ampcode.com>
Signed-off-by: Joah Gerstenberg <joah@squareup.com>
The composer pill drops its @ sign, and a prompt's leading @name mention
no longer renders inside the message body. Dev mode lifts it out and
shows a small 'to Name' line (in the agent's chat color) under the
author name; the side-chat header preview strips it too. The wire format
is unchanged — messages still carry the @name prefix and mention tag so
the standard UI and other clients keep seeing who a prompt is for.

AGNTOPS-354

Amp-Thread-ID: https://ampcode.com/threads/T-019fabca-7cdc-777d-9ccd-ed829c1e60f0
Co-authored-by: Amp <amp@ampcode.com>
Signed-off-by: Joah Gerstenberg <joah@squareup.com>
The bordered mode pill below the composer is gone. The Tab-cycled target
now sits at the top of the chat box as the same 'to Name' direction line
messages use (agent name in its chat color); plain chat mode shows
'chat' and busy shows 'working…'. Applies to both the channel composer
and the side-chat composer.

AGNTOPS-354

Amp-Thread-ID: https://ampcode.com/threads/T-019fabca-7cdc-777d-9ccd-ed829c1e60f0
Co-authored-by: Amp <amp@ampcode.com>
Signed-off-by: Joah Gerstenberg <joah@squareup.com>
The dev-mode shell is a flex item without min-w-0, so nowrap content
(truncated link labels, thread-header previews) set a huge intrinsic
min-content width and pushed the whole shell past the right edge of the
window. Let the shell and its content row shrink, and clip any residual
horizontal overflow at the transcript / thread scroll viewports so long
messages wrap inside their pane.

Adds a Playwright regression test that sends a long unbroken message,
opens the side chat, and asserts nothing inside the shell extends past
the window and transcript content fits its viewport.

AGNTOPS-354

Amp-Thread-ID: https://ampcode.com/threads/T-019fabca-7cdc-777d-9ccd-ed829c1e60f0
Co-authored-by: Amp <amp@ampcode.com>
Signed-off-by: Joah Gerstenberg <joah@squareup.com>
Message bodies now go through a block-level markdown layer built on the
existing span-based inline highlighter: fenced code blocks, headings,
bullet/numbered lists with hanging indents, blockquotes, and horizontal
rules. Inline, the tokenizer picks up **bold**, *italic*/_italic_,
~~strikethrough~~, and [label](url) links (rendered via DevLink with the
written label instead of a fetched title). Emphasis delimiters are strict
so snake_case and arithmetic stay plain prose, and everything renders as
React nodes — never HTML.

AGNTOPS-354

Amp-Thread-ID: https://ampcode.com/threads/T-019fabca-7cdc-777d-9ccd-ed829c1e60f0
Co-authored-by: Amp <amp@ampcode.com>
Signed-off-by: Joah Gerstenberg <joah@squareup.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant