Skip to content

spec: render inline image protocols at shell startup (#10020)#10126

Open
lonexreb wants to merge 1 commit intowarpdotdev:masterfrom
lonexreb:spec/10020-image-protocols-shell-init
Open

spec: render inline image protocols at shell startup (#10020)#10126
lonexreb wants to merge 1 commit intowarpdotdev:masterfrom
lonexreb:spec/10020-image-protocols-shell-init

Conversation

@lonexreb
Copy link
Copy Markdown
Contributor

@lonexreb lonexreb commented May 5, 2026

Summary

Spec for issue #10020 — inline image-protocol escape sequences (Kitty graphics, iTerm2 inline image) emitted from shell init files (~/.zshrc, ~/.bashrc, etc.) are silently dropped, while the same bytes typed manually after the prompt render correctly. Other terminals (iTerm2, WezTerm, Kitty, Ghostty) render images at shell startup as documented in their respective protocol specs.

Investigation

Traced the routing end-to-end:

  1. TerminalModel::handle_completed_iterm_image (terminal_model.rs:3354) → delegate!BlockList.
  2. BlockList::handle_completed_iterm_image (blocks.rs:3794) → delegate_to_block! → active block (intentionally bypasses the early-output gate).
  3. Block::handle_completed_iterm_image (block.rs:3292) → block-level delegate! (block.rs:2882).
  4. The block-level delegate! routes to header_grid when state == BlockState::BeforeExecution. The header_grid is the prompt area; its image placement is not rendered in the visible block body.
  5. Once the user types a command, the block transitions out of BeforeExecution and image bytes land in output_grid, which renders. This is why typing the same command works.

The reporter's background-subshell case (( sleep 0.2 && fastfetch ) &!) hits the same branch via a different session phase: the post-prompt block is itself in BeforeExecution waiting for the user's first keystroke, so background output emitted during the is_early_output() window is also routed to header_grid.

What's in the spec

product.md — 7 testable behavior invariants (B1–B7), 6 acceptance criteria (A1–A6), explicit non-goals, and a preserved record of the reporter's diagnostic facts (verified byte sequences, alternative approaches that were tried and ruled out).

tech.md — picks the fix site with explicit reasoning against the two alternatives, names every file and line range touched, and lays out four unit tests + six integration tests (including a negative-space test for the WarpInput hidden bootstrap block and a feature-flag-off regression guard).

Architectural choices

  • Fix at the block-level delegate! macro, not at BlockList and not at GridHandler. Rationale: BlockList does not know about header_grid vs output_grid; pushing the routing decision up would leak grid identity. GridHandler is downstream of the routing decision; pushing it down would require a redirect message back up. The block-level delegate! is exactly where the (state, grid) routing decision belongs.
  • Carve out BootstrapStage::WarpInput. Warp's own injected bootstrap-script bytes must never accidentally render an image even after the fix.
  • Add warn-level telemetry on every silent-drop path. The whole reason this bug was invisible for months is that drops were unlogged. Tightening this is the cheapest defense against the same family of bug regressing.

Test plan

  • Unit tests T1–T4 in block_test.rs — verify routing per BlockState value and per bootstrap_stage.
  • Integration tests IT1–IT6 in image_protocol_at_startup_test.rs (new file) — feed the exact byte sequences from the issue at each session phase, assert Event::ImageReceived dispatch and grid placement.
  • Manual: fastfetch in ~/.zshrc with --logo-type kitty-direct (A1), --logo-type iterm (A2), and the background-subshell variant (A3).
  • Manual: post-prompt typed fastfetch continues to render (A4 regression guard).
  • Manual: FeatureFlag::ITermImages disabled produces no render and no warn-level log noise (A5 + B7).

Open questions for maintainer review

  1. Macro naming: prefer delegate_image_completion! (sibling macro) or a boolean parameter on the existing delegate!?
  2. Telemetry level for unexpected drops: log::warn! vs log::debug! + a separate metric?
  3. The bootstrap-stage gates at blocks.rs:3789 (on_reset_grid), 3724 (precmd), and 3763 (preexec) are left alone. Confirm they are intentional and not symptoms of the same family.

Closes (spec-only) #10020 — implementation PR will follow once spec direction is confirmed.

Adds product.md + tech.md for issue warpdotdev#10020: inline image-protocol escape
sequences (Kitty graphics, iTerm2 inline image) emitted from shell init
files (~/.zshrc, etc.) are silently dropped. The same bytes typed
manually post-prompt render correctly.

Investigation traces the drop to the block-level delegate! macro
(app/src/terminal/model/block.rs:2882): when the active block is in
BlockState::BeforeExecution (which covers the entire ScriptExecution
bootstrap stage and the post-prompt early-output window), image-protocol
completions are routed to header_grid instead of output_grid. The
header_grid is the prompt area and its image placement is not rendered
in the visible block body. The reporter's background-subshell test case
(`( sleep 0.2 && fastfetch ) &!`) hits the same branch via a different
session phase, which is why deferring past the first prompt does not
help.

Spec proposes a fix at the block-level delegate (not BlockList, not
GridHandler) with explicit reasoning against the alternatives. Carves
out the WarpInput hidden bootstrap block so Warp's own bootstrap-script
bytes never accidentally render. Adds warn-level telemetry on every
silent-drop path so this class of bug regresses noisily, not silently.

Test plan covers four unit cases (BlockState routing) and six
integration cases (per-stage byte-feed end-to-end), including a
negative-space test that the WarpInput stage continues to drop and a
feature-flag-off regression guard.

Three open questions for maintainer review on macro naming, telemetry
level, and whether the existing bootstrap-stage gates at
blocks.rs:3789/3724/3763 are intentional or symptoms of the same family.
@cla-bot cla-bot Bot added the cla-signed label May 5, 2026
@oz-for-oss
Copy link
Copy Markdown
Contributor

oz-for-oss Bot commented May 5, 2026

@lonexreb

I ran into an unexpected error while working on this.

Powered by Oz

@github-actions github-actions Bot added the external-contributor Indicates that a PR has been opened by someone outside the Warp team. label May 5, 2026
@lonexreb
Copy link
Copy Markdown
Contributor Author

lonexreb commented May 5, 2026

/oz-review

@oz-for-oss
Copy link
Copy Markdown
Contributor

oz-for-oss Bot commented May 5, 2026

@lonexreb

I'm re-reviewing this spec-only pull request in response to a review request.

I completed the review and no human review was requested for this pull request.

Comment /oz-review on this pull request to retrigger a review (up to 3 times on the same pull request).

Powered by Oz

Copy link
Copy Markdown
Contributor

@oz-for-oss oz-for-oss Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Overview

Reviewed the spec-only PR for rendering inline image protocols during shell startup. The product/tech split covers the reported phases and proposes a plausible routing fix, but the telemetry requirements and implementation details need tightening before this is safe to hand to implementation.

Concerns

  • The product spec contradicts itself on whether feature-flag-off drops should warn or stay quiet.
  • The tech spec requires BootstrapStage in GridHandler logging without specifying the plumbing or alt-screen behavior.
  • The test plan does not cover the Kitty feature-flag-off path despite the cross-protocol/flag invariant.
  • Security: malformed PTY-controlled image sequences should not be able to create unbounded warn-level log volume or log image payload data.

Verdict

Found: 0 critical, 3 important, 1 suggestions

Request changes

Comment /oz-review on this pull request to retrigger a review (up to 3 times on the same pull request).

Powered by Oz

Comment thread specs/GH10020/product.md

If an image-protocol completion is reached for any reason that prevents
placement (zero dimensions, feature flag off, decode failure, missing
target grid), the event is logged at `warn` level with the protocol
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ [IMPORTANT] B6 requires warn-level logging for feature-flag-off drops, but B7 says the flag-off path should produce no telemetry noise. Align the product contract so implementers know whether disabled protocols log at debug or not at all.

Comment thread specs/GH10020/tech.md
- protocol name (`"iterm"` / `"kitty"`),
- failure reason (`"feature_flag_off"`, `"zero_dimension"`,
`"decode_failure"`),
- the active `BootstrapStage` (passed in via existing handler context
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ [IMPORTANT] GridHandler does not currently know the block BootstrapStage, and this leaves the required plumbing ambiguous. Specify where the stage is stored or passed, and how non-block callers such as alt screen should populate or omit it.

Comment thread specs/GH10020/tech.md
Asserts the image is placed in the new active block's `output_grid`.
- IT4: Existing post-prompt typed-command path continues to render
(regression guard).
- IT5: With `FeatureFlag::ITermImages` disabled, IT2's bytes produce
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ [IMPORTANT] B4 covers every protocol/feature-flag combination, but IT5 only tests iTerm disabled. Add the KittyImages/Kitty protocol flag-off case, including the no-warn expectation, so the invariant is actually enforced.

Comment thread specs/GH10020/tech.md
`bootstrap_stage()` (used by `bootstrap_block_contents`,
blocks.rs:3325), so no new accessor is needed.

### Change 4: warn-level telemetry on silent drop
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 [SUGGESTION] [SECURITY] Specify that drop telemetry never includes image payload/control data and is sampled or rate-limited for repeated malformed sequences, since PTY output can generate many failures quickly.

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

Labels

cla-signed external-contributor Indicates that a PR has been opened by someone outside the Warp team.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant