Skip to content

Claude: show replies to local slash commands - #48

Open
katulevskiy wants to merge 3 commits into
mirror/upstream-mainfrom
mirror/upstream-pr/787
Open

katulevskiy wants to merge 3 commits into
mirror/upstream-mainfrom
mirror/upstream-pr/787

Conversation

@katulevskiy

Copy link
Copy Markdown
Owner

Upstream mirror

Source: zeronsh#787
Original author: @avienor
Original revision: f4fa366e58bae5df6731420606969401d81a9946

This PR is an isolated Contribution Manager pilot mirror. Its reviews, checks and merge state are separate from upstream. An empty [skip ci] commit suppresses automatic bulk CI; the code tree matches the imported upstream revision.


Fixes zeronsh#734 (the bug half; the reset-time display it also asks for is a separate feature and is not part of this PR).

Summary

  • Claude Code answers its local commands (/usage, /context, /cost, /mcp, …) without a model turn: a single assistant frame with model: "<synthetic>" carries the full text, and no text_delta stream events precede it (reproduced with Claude Code 2.1.289). The normalizer took main-thread text only from deltas and kept only tool_use blocks from assistant frames, so these replies rendered empty.
  • Normalizer now tracks whether main-thread text was streamed since the last assistant frame or result. When it wasn't, the frame's text blocks are emitted verbatim as TextDeltas, in block order with tool calls and before AssistantMessageCompleted.
  • Points worth a close look:
    • The flag is reset on the assistant frame and on result, not in rotate_for_steer: a steer echo can rotate the message id between a message's deltas and its frame. The result reset covers turns that end on deltas without an assistant frame (as in the superseded-steers fixture), which would otherwise hide the next command's reply.
    • Only non-empty, untagged deltas set the flag; subagent traffic neither sets nor clears it.
    • Frames carrying an error code keep the current behavior (only the mapped error is shown), so a failure is not reported twice.
    • <synthetic> frames no longer emit ContextUsage (their usage is all zeros, which reset the context meter to 0%) and no longer replace last_model, which the next result uses to look up the context window. Real zero measurements from other frames are untouched.
  • docs/context-usage.md notes that local command replies are excluded from Claude's context measurement.
  • No wire, document or RPC changes; the change is confined to crates/harness/src/claude/normalize.rs.

Known limitations

  • /usage replies are plain text with single newlines; the markdown renderer folds those into one paragraph. /context (markdown) renders well. Making plain-text command output keep its line breaks is a separate change.
  • The gate is a per-message boolean: a hypothetical frame with several text blocks where only some were streamed would not emit the unstreamed ones. No such wire shape has been observed; full reconciliation would need the wire's message/block ids, which the normalizer currently discards.

Test plan

  • New tests in claude::normalize::tests (4 of them fail without the fix):
    • local_command_replies_surface_their_text
    • a_reply_ended_without_its_frame_does_not_hide_the_next_command
    • subagent_traffic_never_gates_main_thread_text
    • synthetic_replies_leave_context_usage_and_model_alone
    • regression guards: streamed_text_is_not_repeated_from_its_assistant_frame, a_steer_between_deltas_and_their_frame_does_not_repeat_text, errored_assistant_frames_keep_only_the_mapped_error
  • cargo test --locked -p zeron-harness (whole crate, including the live Claude fixtures under tests/) passes
  • cargo clippy --locked -p zeron-harness --all-targets: no new warnings (same warning count before and after, none in the touched file)
  • cargo fmt -p zeron-harness --check clean
  • Manual, Linux (Hyprland), Claude Code 2.1.289, dev build with a separate data dir: in one chat, /usage and /context were empty on main and show their full text with this branch
  • Not tested manually on macOS or Windows (the change is platform-independent)

Screenshots

Before (main): /context and /usage produce no reply.

zeron-734-before

After: /context reply rendered.

zeron-734-after-context

View with [code]smith Autofix with [code]smith
Need help on this PR? Tag @codesmith-bot with what you need. Autofix is disabled.

avienor and others added 2 commits October 4, 2026 18:01
Claude Code answers local commands (/usage, /context, /cost, /mcp...)
without a model turn: one `<synthetic>` assistant frame carries the text,
with no text deltas before it. The normalizer only took text from deltas,
so these replies rendered empty. Emit an assistant frame's text blocks
when none of its text was streamed, and keep the synthetic frame's zeroed
usage and model out of context tracking.

Fixes zeronsh#734
@contribution-manager-katulevskiy contribution-manager-katulevskiy Bot added the review:needs-triage Managed by Contribution Manager label Oct 6, 2026
@contribution-manager-katulevskiy

contribution-manager-katulevskiy Bot commented Oct 6, 2026 •

Copy link
Copy Markdown

Contribution Manager

needs triage · medium risk · untriaged

Open review workspace · Revision bead085a

Next actions:

  • Direction decision needed
  • Scope, risk, and platform triage needed
  • Independent code review needed
  • Required check: core-tests (missing)

This summary is maintained by the review app. Decisions and test evidence are recorded in the workspace; the app does not merge PRs.

@contribution-manager-katulevskiy contribution-manager-katulevskiy Bot added cm:inbox Contribution Manager: waiting for triage and removed review:needs-triage Managed by Contribution Manager labels Oct 6, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

cm:inbox Contribution Manager: waiting for triage

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants