Skip to content

feat: restore TUI provider cost footer baseline (APP-4952) - #14220

Merged
ianhodge merged 10 commits into
masterfrom
factory/app-4952-tui-cost-footer-restore
Jul 31, 2026
Merged

feat: restore TUI provider cost footer baseline (APP-4952)#14220
ianhodge merged 10 commits into
masterfrom
factory/app-4952-tui-cost-footer-restore

Conversation

@warp-agent-staging

@warp-agent-staging warp-agent-staging Bot commented Jul 23, 2026

Copy link
Copy Markdown
Contributor

Summary

Restores the TUI provider-cost footer from the server-authoritative nullable total_provider_cost_in_cents snapshot. Restored conversations now retain a known historical baseline, while legacy/null payloads explicitly render Cost unavailable instead of $0.00; follow-up request deltas only accumulate when a baseline is known.

Implementation

  • Added the optional provider-cost field to persistence and both GraphQL usage metadata paths, preserving absent legacy values as None and distinguishing known zero.
  • Seeded both AIConversation restore constructors and the cloud conversation conversion path from persisted/server usage metadata.
  • Kept new-conversation behavior and per-request accumulation intact, with unknown restored cost remaining unavailable after deltas.
  • Updated TUI usage projection/rendering and added restore, cloud conversion, serde, accumulation, and display regression coverage.
  • Fixed restore-constructor ownership so the client builds cleanly after reading the optional baseline.

Verification

  • cargo check -p warp --all-targets passed on the XL runner.
  • cargo check -p warp_tui --all-targets, debug warp-tui-oss build, and release warp-tui-dev build passed.
  • Focused warp regression suite: 5 passed (known baseline, empty-task restore, legacy unknown baseline, cloud conversion, and baseline-plus-live-delta).
  • Focused warp_tui usage suite: 4 passed ($ formatting, credits mode, mode toggle, and Cost unavailable).
  • Persistence tests: 23 passed. GraphQL tests: 3 passed.
  • Final cargo fmt --all -- --check, cargo clippy -p warp --all-targets --tests -- -D warnings, and cargo clippy -p warp_tui --all-targets --tests -- -D warnings passed.
  • ./script/presubmit passed formatting, inline-module, clippy, clang-format, and WGSL checks. Its workspace nextest phase remains red only on unrelated headless-environment integration assumptions (X11 recording foreground visibility, SSH password prompts, remote-server 501 fixtures, notebook/UI integration fixtures, and one TUI startup-state test); no APP-4952 regression failed.

Live TUI verification

The release TUI launched cleanly in a real XTerm PTY with the staging channel shim and inherited API-key login. Captured truthful screenshots show Conversations / No conversations found with Could not load cloud conversations. Showing local conversations only. and a fresh-session footer of 2.8 credits; no $0.00 was rendered. The requested restored historical-dollar baseline, baseline-plus-follow-up, and Cost unavailable legacy/null states could not be captured because this service-account principal has no cloud conversation history (staging metadata listing returns 422 for the service-account UUID) and the local conversation list is empty after the harmless fresh prompt. Verification run: https://oz.staging.warp.dev/runs/019f91cc-ee9e-72d8-9368-1227f0b2dcec

Originating thread: https://linear.app/warpdotdev/issue/APP-4952/tui-cost-footer-shows-dollar00-after-conversation-restoration-ignores

Conversation: https://staging.warp.dev/conversation/09f38106-ab47-4561-8d43-d91f4c1a86c3
Run: https://oz.staging.warp.dev/runs/019f91cc-ee9e-72d8-9368-1227f0b2dcec

This PR was generated with Oz.

@cla-bot cla-bot Bot added the cla-signed label Jul 23, 2026
@warp-agent-staging warp-agent-staging Bot changed the title Spec: Restore TUI cost footer baseline (APP-4952) feat: restore TUI provider cost footer baseline (APP-4952) Jul 23, 2026
@ianhodge

Copy link
Copy Markdown
Member

Review feedback implemented + local E2E verification (APP-4952)

All findings from a full cross-repo review have been implemented and validated locally end-to-end (local warp-server built from warpdotdev/warp-server#13244 + TUI built from this branch, deterministic llm-mock with non-zero provider cost).

Changes in response to review (commits 28900ea, 6a910bf):

  • Added the spec-mandated regression test restored_usage_totals_preserve_server_provider_cost_and_add_follow_up: restore with known 3.2¢ baseline + 1.2¢ follow-up == 4.4¢, covering BOTH restore constructors (the += accumulation path).
  • has_usage is now derived from metadata contents (credits, cost, token rows, context-window usage, summarized flag) instead of blob presence — restored zero-usage conversations no longer show a misleading "Cost unavailable"/0.0 footer entry. Includes view-level tests (no footer totals until usage exists; zero-credits + unknown-cost renders "Cost unavailable", never "$0.00").
  • Stream-snapshot reconciliation (spec behavior Update docs link #4): proto pin bumped to warp-proto-apis@47d07d8 where field 12 is google.protobuf.FloatValue → prost generates true Option<f32> presence. When StreamFinished carries Some(cumulative), it replaces local accumulation for that turn (no double-count, recovers unknown legacy baselines); when None, per-request accumulation is unchanged. set_server_metadata has a monotonic guard so stale GraphQL snapshots can never regress or re-add. Tests: server-snapshot-wins ("never 5.6¢"), unknown-baseline recovery, stale-snapshot no-regression, absent-field fallback.
  • Shared-session hosts forward the cumulative in synthetic StreamFinished events to viewers.

Validation: ./script/format clean; cargo clippy -p warp -p warp_tui -p warp_graphql -p persistence --all-targets -- -D warnings clean; focused warp tests 159/159 pass; warp_tui/graphql/persistence 636/636 pass. (Full workspace clippy/nextest left to CI.)

E2E testing plan & results (local, llm-mock):

  1. New conversation, 3 turns → footer accumulated $0.01 → $0.05 ✅
  2. Exit + same-profile --resume → footer shows $0.05 baseline before any prompt (the original APP-4952 regression) ✅
  3. Follow-up prompt → $0.06 = baseline + one turn (no double-count, not increment-only) ✅
  4. Cross-device simulation: restore on a fresh data profile with zero local persistence → footer $0.06 seeded purely from server data ✅
  5. Server: GraphQL totalProviderCostInCents = 6 after 4 turns; pre-fix legacy conversation still null (treated as unknown, never fabricated) ✅
  6. Zero-usage evidence: a brand-new conversation shows no footer usage entry at all until usage exists ✅

Artifacts: asciinema recordings + footer captures at each checkpoint (terminal-text capture; no screen video available in the test environment).

Deploy order: warpdotdev/warp-server#13244 must deploy before this ships — the GraphQL fragment addition here hard-fails against servers without the new schema field (this also affects devs on older local servers).

Related: warpdotdev/warp-server#13244, warpdotdev/warp-proto-apis#349

Co-Authored-By: Oz oz-agent@warp.dev

@ianhodge

Copy link
Copy Markdown
Member

Update: descoped to GraphQL-only + re-verified E2E (local + cloud video)

Scope change: the StreamFinished cumulative-cost field is deferred to upcoming stream-level cost work — warpdotdev/warp-proto-apis#349 is closed (branch retained as the starting point, including the FloatValue presence fix for Rust codegen). This PR pair now ships the restore fix GraphQL-only:

  • warp-server (535c358da3): proto pin restored to exactly develop's; no cumulative cost on the wire (a test asserts the StreamFinished payload has no such field, and a comment marks stream exposure as intentionally deferred). Persistence, GraphQL exposure, baseline-poisoning fix, required providerCostKnown, and errored-turn cost recording all unchanged.
  • warp client (1fa96c003): proto pin restored to its pre-stream rev; stream reconciliation and shared-session forwarding removed. Live sessions accumulate per-request cost_in_cents on top of the GraphQL-seeded baseline; the monotonic guard remains so stale GraphQL snapshots can never regress or double-count. Regression test (3.2¢ + 1.2¢ = 4.4¢, both restore constructors) and content-derived has_usage unchanged.

E2E round 3 (local, llm-mock): all pass on the reworked heads — $0.22 accumulated over 3 turns; same-profile restore shows $0.22 before prompting; follow-up → $0.30 (baseline + one increment, no double-count); fresh-profile (cross-device) restore shows $0.30 seeded purely from server data; GraphQL returned totalProviderCostInCents: 30; StreamFinished confirmed to carry no cumulative field.

E2E round 4 (cloud agent, computer use, video-recorded): independently reproduced from scratch in an isolated cloud environment (own Postgres/Redis/Temporal + fake-GCS for transcripts), running the TUI in a visible terminal on video with per-checkpoint screenshots. All 6 checkpoints pass: non-zero first-turn cost, monotonic accumulation (4.5→13.5¢), resume token, same-profile restore baseline $0.14 pre-prompt, follow-up $0.18 with no double-count, and fresh-profile restore $0.18 pre-prompt matching GraphQL (totalProviderCostInCents: 18). Run: https://staging.warp.dev/conversation/ae45fb66-46f3-433e-bb97-1dfc734fea1e

Deploy order still applies: server before client.

Co-Authored-By: Oz oz-agent@warp.dev

@ianhodge

Copy link
Copy Markdown
Member

/oz-review

@oz-for-oss

oz-for-oss Bot commented Jul 30, 2026

Copy link
Copy Markdown
Contributor

@ianhodge

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

You can view the conversation on Warp.

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

oz-agent and others added 7 commits July 30, 2026 15:25
Define the client-only restore baseline, cloud fallback, and validation contract for APP-4952.\n\nCo-Authored-By: Oz <oz-agent@warp.dev>
Co-Authored-By: Oz <oz-agent@warp.dev>
Co-Authored-By: Oz <oz-agent@warp.dev>
Co-Authored-By: Oz <oz-agent@warp.dev>
…regression test

- Add the spec-mandated regression test for the ticket's exact failing
  sequence: a restored 3.2-cent server baseline plus a 1.2-cent follow-up
  must display 4.4 cents, covering both restore constructors.
- Derive has_usage from the usage metadata's contents instead of its mere
  presence. Local persistence always writes a metadata blob, so restored
  zero-usage conversations previously surfaced a misleading footer entry
  ("Cost unavailable" / 0.0 credits) that used to be hidden.
- Make set_server_metadata consistent: usage evidence comes from the
  snapshot's contents, and an absent provider-cost field never erases a
  known baseline.
- Add view-level TUI tests: the footer usage entry stays hidden until a
  conversation reports usage, and unknown historical cost renders
  'Cost unavailable' (never $0.00) even with zero credits.

Co-Authored-By: Oz <oz-agent@warp.dev>
Bump warp_multi_agent_api to the proto rev that models StreamFinished's
total_provider_cost_in_cents as google.protobuf.FloatValue, giving the
Rust binding explicit presence (Option<f32>) so an absent field is
distinguishable from a known zero.

- StreamFinished reconciliation: when a response reports the server's
  cumulative provider cost, that snapshot is authoritative for the turn.
  It replaces local accumulation (the snapshot already includes the
  response's per-request costs, so nothing is counted twice) and recovers
  a restored legacy conversation's unknown baseline. Without the field
  (older server), per-request costs accumulate onto a known baseline as
  before.
- set_server_metadata ordering guard: asynchronous GraphQL metadata
  snapshots may only seed or advance the displayed total, never regress
  it or re-add costs the stream already counted.
- Shared-session hosts now forward the cumulative provider cost in the
  synthetic StreamFinished events they replay to viewers.
- Tests: snapshot-supersedes-local (never 5.6 cents), unknown-baseline
  recovery via stream snapshot, stale-GraphQL-snapshot no-regression, and
  absent-field fallback to local accumulation (existing regression test).

Co-Authored-By: Oz <oz-agent@warp.dev>
The cumulative provider-cost stream contract is deferred to separate work.
Keep APP-4952 focused on restoring the server-authoritative GraphQL
baseline and accumulating the existing per-request live deltas.

- Restore warp_multi_agent_api to the branch's pre-stream pin.
- Remove StreamFinished cumulative-cost reconciliation and synthetic
  shared-session forwarding.
- Remove the stream-snapshot tests while retaining the exact 3.2 + 1.2
  restore regression, absent-field fallback, and stale GraphQL snapshot
  no-regression coverage.
- Keep the monotonic GraphQL metadata guard so asynchronous snapshots may
  advance but never regress a locally accumulated total.
- Align the checked-in APP-4952 spec with the GraphQL-only scope.

Co-Authored-By: Oz <oz-agent@warp.dev>

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

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.

Overview

This PR carries server-authoritative provider cost through persistence/GraphQL restore paths and updates the TUI usage footer to distinguish unknown historical cost from a known cumulative cost.

Concerns

  • usage_metadata_indicates_usage still treats Some(0.0) provider cost as no usage. That hides a known zero-cost baseline instead of displaying $0.00, so restored/server snapshot cases can still conflate real zero with an absent historical baseline.
  • The provided PR context does not include an attached TUI transcript, screenshot, or snapshot for the restored known-baseline, baseline-plus-follow-up, or legacy Cost unavailable states; the description says those specific states could not be captured. Because those are the user-visible states changed by this PR, please attach TUI verification for them before merge.

Verdict

Found: 0 critical, 2 important, 0 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 app/src/ai/agent/conversation.rs Outdated
Comment on lines +206 to +208
|| metadata
.total_provider_cost_in_cents
.is_some_and(|cost| cost != 0.0)

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] Some(0.0) is a known provider-cost baseline, but this treats it as no usage, so a restored/server snapshot with cumulative zero hides the footer instead of switching from unknown to known $0.00.

Suggested change
|| metadata
.total_provider_cost_in_cents
.is_some_and(|cost| cost != 0.0)
|| metadata.total_provider_cost_in_cents.is_some()

Master moved selected_conversation_usage_totals into
terminal_session_view/statusline.rs during the footer refactor; port the
has_usage gate there, drop the duplicate in terminal_session_view.rs, and
convert the zero-credits view test to the FooterSegments::ordered API.

Co-Authored-By: Oz <oz-agent@warp.dev>
@ianhodge
ianhodge force-pushed the factory/app-4952-tui-cost-footer-restore branch from 1fa96c0 to 5b79f4d Compare July 30, 2026 19:37
ianhodge and others added 2 commits July 30, 2026 15:37
Some(0.0) is only recorded once a turn has completed accounting, so a
restored or server snapshot with a cumulative zero must surface the footer
as a truthful $0.00 instead of being hidden as though no usage occurred.
None remains the only unknown/hidden state.

Co-Authored-By: Oz <oz-agent@warp.dev>
@ianhodge

Copy link
Copy Markdown
Member

/oz-review

@oz-for-oss

oz-for-oss Bot commented Jul 30, 2026

Copy link
Copy Markdown
Contributor

@ianhodge

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

You can view the conversation on Warp.

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

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

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.

Overview

This PR threads the server-authoritative nullable provider-cost baseline through persistence, GraphQL usage metadata conversion, AIConversation restore/live usage state, and the headless TUI footer so restored conversations can distinguish known cumulative cost from unknown historical cost.

Concerns

  • This is a user-facing headless TUI output change, but the PR does not attach acceptable TUI evidence for the target restored states. The body says the restored historical-dollar baseline, baseline-plus-follow-up, and legacy/null Cost unavailable states could not be captured, and the linked run is not an attached terminal transcript, render_to_lines/TuiBuffer snapshot diff, or ./script/run-tui capture demonstrating those states. Please attach TUI evidence showing the restored known baseline before a follow-up, baseline plus live delta after a follow-up, and the legacy/null Cost unavailable state.

Verdict

Found: 0 critical, 1 important, 0 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

@ianhodge
ianhodge enabled auto-merge (squash) July 30, 2026 21:04
@ianhodge
ianhodge merged commit 8b7055e into master Jul 31, 2026
27 checks passed
@ianhodge
ianhodge deleted the factory/app-4952-tui-cost-footer-restore branch July 31, 2026 21:42
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants