Skip to content

Drop pending_server_requests from the reconnect snapshot - #131

Merged
marmeladema merged 1 commit into
mainfrom
claude/drop-pending-server-requests
Jul 26, 2026
Merged

Drop pending_server_requests from the reconnect snapshot#131
marmeladema merged 1 commit into
mainfrom
claude/drop-pending-server-requests

Conversation

@marmeladema

@marmeladema marmeladema commented Jul 26, 2026

Copy link
Copy Markdown
Owner

The symmetric slice to #130 (which dropped pending_approval). After both, LiveTurnSnapshot carries no precomputed "pending" list for either kind; the browser derives what's still outstanding from accumulated plus the two answered sets.

What

LiveTurnSnapshot::pending_server_requests: Vec<ServerRequest> was a precomputed outstanding set that duplicated a derivation already available on both ends: the outstanding server requests are exactly the rows in accumulated whose latest event is a server_request_received and which are neither in answered_server_requests (the user answered) nor closed by a server_request_resolved later in accumulated (the harness closed it).

  • LiveTurnSnapshot loses pending_server_requests. The client renders answered requests resolved from answered_server_requests and treats the rest as actionable, all from accumulated.
  • The server's pending_attention (the SB5 connect bootstrap) derives the outstanding server requests the same way, via a pending_server_requests(events, resolved) helper that now takes the resolved set, mirroring pending_approvals.
  • The client's renderLiveTurnSnapshot re-asserts the outstanding server requests after the accumulated replay, mirroring the approval path (SR10b): a later error overwrites the thread's activity and clears the active turn, so re-asserting last gives the waiting state the last word. A turn blocked on a server request that then errored still reads as waiting on the user rather than "errored, idle".

answered_server_requests is kept: it is the only record of a user's answer before/without the harness's own resolved event, so dropping it would replay an answered request as actionable and re-answering routes a stale id to the harness (SR6). The larger refactor's SR11 relocates that onto the row; this slice does not.

Test

The SR11b ordering (re-assert after replay, for server requests) is pinned by a new Playwright test that reloads a turn blocked on a server request that then errored, asserting the sidebar still reads activity-waiting with a ! glyph and the request stays answerable. Confirmed load-bearing by moving the re-assert loop before the replay: it fails exactly as the approvals-side test did, passes in order.

Verification

Check Result
cargo fmt --all -- --check clean
cargo clippy --workspace --all-targets -- -D warnings clean
cargo test --workspace all green
tests/e2e/run.sh (full Playwright suite) 37/37

The spec (specs/giskard-specification.md) is updated in the same change: bumped to 1.62 with a Changelog (1.61 → 1.62), drop pending_server_requests entry (SR11a/SR11b), and the LiveTurnSnapshot shape line rewritten to describe the unified derivation for both kinds.

Summary by CodeRabbit

  • New Features

    • Added a scripted “server request then error” scenario for still-open turns, keeping the request actionable after a later non-fatal error.
  • Bug Fixes

    • Improved reconnect/reload handling by deriving “outstanding” server requests from the replay transcript, excluding answered/closed requests and preserving correct re-open ordering.
    • Updated UI “waiting”/“!” status so it reflects the latest replay outcome.
  • Tests

    • Updated unit and end-to-end coverage to validate transcript-derived outstanding rendering and the “then error” scenario.
  • Documentation

    • Updated the protocol specification to v1.62 to remove the separate pending field and document transcript-based derivation.

@coderabbitai

coderabbitai Bot commented Jul 26, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

Changes

The reconnect snapshot no longer carries a separate pending_server_requests field. Server and client logic reconstruct outstanding requests from accumulated lifecycle events, answered IDs, and resolved events. Replay harness and end-to-end tests cover a server request followed by an error.

Server-request replay

Layer / File(s) Summary
Snapshot protocol contract
crates/giskard-proto/src/lib.rs, specs/giskard-specification.md
Removes pending_server_requests from LiveTurnSnapshot and documents deriving outstanding requests from accumulated events and answered or resolved markers.
Server snapshot reconstruction
Cargo.toml, crates/giskard-server/Cargo.toml, crates/giskard-server/live_buffer.rs, crates/giskard-server/tests/server_requests.rs
Derives server-request status from ordered lifecycle events and resolved IDs, with reconnect tests covering unresolved, answered, and reopened requests.
Client replay rendering
crates/giskard-server/static/app.js, crates/giskard-server/tests/ui.rs
Computes outstanding requests from replayed lifecycle events and renders only requests that remain actionable.
Request-then-error replay scenario
crates/giskard-server/src/bin/giskard-server-replay.rs, tests/e2e/tests/helpers.ts, tests/e2e/tests/server-requests.spec.ts
Adds a scripted request-then-error flow and verifies after reload that the turn remains waiting while the request remains answerable.

Estimated code review effort: 4 (Complex) | ~45 minutes

Sequence Diagram(s)

sequenceDiagram
  participant ReplayHarness
  participant LiveBufferStore
  participant LiveTurnSnapshot
  participant Browser
  ReplayHarness->>LiveBufferStore: emit ServerRequestReceived
  ReplayHarness->>LiveBufferStore: emit Error
  LiveBufferStore->>LiveTurnSnapshot: accumulate lifecycle events
  LiveTurnSnapshot->>Browser: replay snapshot
  Browser->>Browser: derive outstandingServerRequests
  Browser->>Browser: show waiting state and Continue action
Loading

Possibly related PRs

Suggested reviewers: claude

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and accurately summarizes the main change: removing pending_server_requests from the reconnect snapshot.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch claude/drop-pending-server-requests

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

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@crates/giskard-server/src/bin/giskard-server-replay.rs`:
- Around line 488-497: Update the AgentEvent::Error send path guarded by
raise_server_request_then_error to capture the broadcast send failure instead of
discarding it. When sending fails, emit a warn! containing thread_id, turn_id,
action, and the send error, while preserving the existing successful send
behavior.

In `@crates/giskard-server/static/app.js`:
- Around line 3189-3198: Update received-event handling to remove the request ID
from closed state before storing the latest payload, so a later
server_request_received reopens it. Apply the corresponding closed-marker reset
in the live-buffer test helper, and update the server-request tests to replay
events sequentially, clear closure state on later receives, and add a received →
resolved → received regression case in
crates/giskard-server/tests/server_requests.rs:645-657; also make the matching
changes in crates/giskard-server/static/app.js:3189-3198 and
crates/giskard-server/src/live_buffer.rs:480-491.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: 383a9dba-4a34-4995-9451-51a4d3a8a3d6

📥 Commits

Reviewing files that changed from the base of the PR and between c84b55d and 6c6c7de.

📒 Files selected for processing (9)
  • crates/giskard-proto/src/lib.rs
  • crates/giskard-server/src/bin/giskard-server-replay.rs
  • crates/giskard-server/src/live_buffer.rs
  • crates/giskard-server/static/app.js
  • crates/giskard-server/tests/server_requests.rs
  • crates/giskard-server/tests/ui.rs
  • specs/giskard-specification.md
  • tests/e2e/tests/helpers.ts
  • tests/e2e/tests/server-requests.spec.ts

Comment thread crates/giskard-server/src/bin/giskard-server-replay.rs
Comment thread crates/giskard-server/static/app.js
@marmeladema
marmeladema force-pushed the claude/drop-pending-server-requests branch from 6c6c7de to 264bb88 Compare July 26, 2026 15:27
@marmeladema

Copy link
Copy Markdown
Owner Author

Addressed the two CodeRabbit findings in 264bb88.

🟠 Finding 2 (reopen after resolution) — fixed

This was a real regression I introduced. The server's pending_server_requests helper re-inserts on ServerRequestReceived, so a harness that resolves a request and then re-raises the same id with a fresher payload reopens it. My new client-side outstandingServerRequests and the test-side outstanding_server_requests helper both added an id to closed on server_request_resolved and never cleared it on a later server_request_received, so the final filter (!closed.has(...)) kept the re-raised request hidden — a reconnected user couldn't answer it.

Fixed on both ends:

  • app.js::outstandingServerRequests now does closed.delete(id) before recording the fresh server_request_received, mirroring the server's requests.insert.
  • live_buffer.rs::outstanding_server_requests (test helper) does the same.

Pinned by a new regression test a_server_request_reopened_after_resolution_is_outstanding_again (received → resolved → received). Confirmed load-bearing by reverting the helper: it fails with [] instead of [id], passes with the fix.

🟠 Finding 1 (log the send failure) — skipped, with reason

The replay binary uses let _ = sender.send(...) uniformly across ~25 scripted-event sends (lines 190–667), including the pre-existing approval-then-error sibling at line 525. This is a deterministic in-process broadcast channel; a send only "fails" when there are no receivers (the turn ended), which is expected during teardown. Logging only the two new error sends would be inconsistent with the file's established convention, and adding warn! logging to all of them is out of scope for this slice. Skipped for consistency.

Verification

Check Result
cargo fmt / cargo clippy -D warnings clean
cargo test --workspace all green (incl. new reopen test)
tests/e2e/run.sh tests/server-requests.spec.ts 6/6

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@crates/giskard-server/static/app.js`:
- Around line 3193-3201: Update the request aggregation logic around the
received/resolved handling to track first-seen IDs independently of the `latest`
map, so reopening a resolved request updates its payload without appending its
ID to `order` again. Preserve the original first-seen ordering and ensure the
final map renders one card using the newest payload. Add a focused regression
test covering received → resolved → received and asserting a single card with
the latest request data.

In `@crates/giskard-server/tests/ui.rs`:
- Around line 290-293: Strengthen the UI regression coverage around
outstandingServerRequests so it executes the browser behavior rather than only
checking JavaScript source text. Verify that replayed answered server requests
render as resolved and non-actionable, while unanswered requests remain
actionable, and retain the source assertion only if still useful.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: cdde4bc8-4608-489b-b095-8c713326ccae

📥 Commits

Reviewing files that changed from the base of the PR and between 6c6c7de and 264bb88.

📒 Files selected for processing (9)
  • crates/giskard-proto/src/lib.rs
  • crates/giskard-server/src/bin/giskard-server-replay.rs
  • crates/giskard-server/src/live_buffer.rs
  • crates/giskard-server/static/app.js
  • crates/giskard-server/tests/server_requests.rs
  • crates/giskard-server/tests/ui.rs
  • specs/giskard-specification.md
  • tests/e2e/tests/helpers.ts
  • tests/e2e/tests/server-requests.spec.ts
🚧 Files skipped from review as they are similar to previous changes (7)
  • tests/e2e/tests/server-requests.spec.ts
  • tests/e2e/tests/helpers.ts
  • crates/giskard-server/src/bin/giskard-server-replay.rs
  • crates/giskard-server/tests/server_requests.rs
  • specs/giskard-specification.md
  • crates/giskard-server/src/live_buffer.rs
  • crates/giskard-proto/src/lib.rs

Comment thread crates/giskard-server/static/app.js Outdated
Comment thread crates/giskard-server/tests/ui.rs
@marmeladema
marmeladema force-pushed the claude/drop-pending-server-requests branch from 264bb88 to 88831ae Compare July 26, 2026 15:50
@marmeladema

Copy link
Copy Markdown
Owner Author

Addressed the two new CodeRabbit findings on the reopen fix in 88831ae.

🟠 Finding 3 (duplicate card on reopen) — fixed

My reopen fix had a follow-on bug CodeRabbit caught: server_request_resolved did latest.delete(id), so a later server_request_received saw !latest.has(id) as true and pushed the id into order again — yielding two cards for one request. Traced: received → resolved → received produced order = [id, id].

Fixed by splitting first-seen tracking from the latest-payload map, as suggested: a seen set records first occurrence (so a re-sent id appends to order once), latest holds the newest payload, and the closed set does the final filtering. The server_request_resolved arm no longer touches seen/order/latest — it just adds to closed, which the final .filter consults. This is simpler and correct: received→resolved leaves the id in order but the closed filter drops it; received→resolved→received clears closed and updates latest, so one card with the newest payload in the original first-seen position.

The existing a_server_request_reopened_after_resolution_is_outstanding_again test already asserts exactly vec![id] (one element), so it pins the no-duplicate invariant on the server side.

🟡 Finding 4 (assert replay behavior, not source text) — already covered, skipped

The ui.rs source-text assertion is a fast smoke that outstandingServerRequests is wired into the replay loop. The behavioral regression CodeRabbit asks for already exists in the Playwright suite:

  • server-requests.spec.ts: "an answered user-input request stays resolved after a browser reload" (asserts answered renders resolved, buttons disabled)
  • server-requests.spec.ts: "an unanswered request survives a reload as actionable" (asserts unanswered stays actionable, buttons enabled)

These execute the real browser behavior, so the invariant is covered. Kept the ui.rs text check as a fast smoke, mirroring the approval side.

Verification

Check Result
cargo fmt / cargo clippy -D warnings clean
cargo test --workspace all green
tests/e2e/run.sh (full Playwright suite) 38/38

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
crates/giskard-server/src/live_buffer.rs (1)

692-693: 📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Use ULID request IDs in the new fixtures.

pending, resolved, and srv are newly introduced identifiers but are not ULIDs. Use fixed valid ULID literals or a fixture generator.

Also applies to: 745-745

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@crates/giskard-server/src/live_buffer.rs` around lines 692 - 693, Update the
new request-ID fixtures near the pending/resolved definitions and the srv
fixture to use fixed valid ULID literals or the existing fixture generator,
replacing the non-ULID values while preserving each fixture’s distinct identity.

Source: Coding guidelines

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@crates/giskard-server/src/live_buffer.rs`:
- Around line 488-492: Update the WireAgentEvent::ServerRequestResolved branch
to mark the request closed without removing its ID from seen or order,
preserving first-seen ordering when it reopens. Extend the relevant live-buffer
test to cover received(A), received(B), resolved(A), received(A) and verify the
order remains [A, B] while closed state toggles.

---

Outside diff comments:
In `@crates/giskard-server/src/live_buffer.rs`:
- Around line 692-693: Update the new request-ID fixtures near the
pending/resolved definitions and the srv fixture to use fixed valid ULID
literals or the existing fixture generator, replacing the non-ULID values while
preserving each fixture’s distinct identity.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: d966de3e-1639-4daf-99e9-3d45da51297b

📥 Commits

Reviewing files that changed from the base of the PR and between 264bb88 and 88831ae.

📒 Files selected for processing (9)
  • crates/giskard-proto/src/lib.rs
  • crates/giskard-server/src/bin/giskard-server-replay.rs
  • crates/giskard-server/src/live_buffer.rs
  • crates/giskard-server/static/app.js
  • crates/giskard-server/tests/server_requests.rs
  • crates/giskard-server/tests/ui.rs
  • specs/giskard-specification.md
  • tests/e2e/tests/helpers.ts
  • tests/e2e/tests/server-requests.spec.ts
🚧 Files skipped from review as they are similar to previous changes (6)
  • tests/e2e/tests/helpers.ts
  • crates/giskard-server/tests/server_requests.rs
  • crates/giskard-server/src/bin/giskard-server-replay.rs
  • crates/giskard-proto/src/lib.rs
  • tests/e2e/tests/server-requests.spec.ts
  • specs/giskard-specification.md

Comment thread crates/giskard-server/src/live_buffer.rs
@marmeladema
marmeladema force-pushed the claude/drop-pending-server-requests branch from 88831ae to 3c3c7c5 Compare July 26, 2026 16:03
@marmeladema

Copy link
Copy Markdown
Owner Author

Addressed the new CodeRabbit finding (#5, live_buffer.rs test helper order) in 3c3c7c5.

🟡 Finding 5 (preserve first-seen order on reopen) — fixed

Valid catch — this was a real client/server-helper divergence. My server-side test helper outstanding_server_requests was still doing seen.remove + order.retain on server_request_resolved, while the client outstandingServerRequests (after the earlier fix) only toggles closed. So the sequence received(A), received(B), resolved(A), received(A) produced [B, A] on the server side but [A, B] on the client — the test helper disagreed with the browser it claims to mirror.

Fixed by making the server helper match the client: seen/order stay intact on resolution, and a closed set drives a final filter. The reopen path (closed.remove on a later server_request_received) preserves first-seen order. Now both derivations are byte-for-byte equivalent in behavior.

Pinned by a new test a_reopened_server_request_keeps_its_original_order (received(A), received(B), resolved(A), received(A) → [A, B]). Confirmed load-bearing by reverting the helper: it fails with [B, A], passes with the fix. The production pending_server_requests helper was already correct (it uses a HashMap and re-inserts on receive), so this was test-helper-only.

Verification

Check Result
cargo fmt / cargo clippy -D warnings clean
cargo test --workspace all green (incl. both reopen tests)
tests/e2e/run.sh tests/server-requests.spec.ts 6/6

`LiveTurnSnapshot::pending_server_requests: Vec<ServerRequest>` was a
precomputed outstanding set that duplicated a derivation already available on
both ends: the outstanding server requests are exactly the rows in
`accumulated` whose latest event is a `server_request_received` and which are
neither in `answered_server_requests` (the user answered) nor closed by a
`server_request_resolved` later in `accumulated` (the harness closed it).

This is the symmetric slice to dropping `pending_approval`: the snapshot now
carries no "pending" list for either kind, and the client derives both from
`accumulated` plus the answered set while replaying.

- `LiveTurnSnapshot` loses `pending_server_requests`. The client renders
  answered requests resolved from `answered_server_requests` and treats the
  rest as actionable, all from `accumulated`.
- The server's `pending_attention` (the SB5 connect bootstrap) derives the
  outstanding server requests the same way, via a `pending_server_requests`
  helper that now takes the resolved set, mirroring `pending_approvals`.
- The client's `renderLiveTurnSnapshot` re-asserts the outstanding server
  requests *after* the `accumulated` replay, mirroring the approval path
  (SR10b): a later `error` overwrites the thread's activity and clears the
  active turn, so re-asserting last gives the waiting state the last word. A
  turn blocked on a server request that then errored still reads as waiting
  on the user rather than "errored, idle".

`answered_server_requests` is kept: it is the only record of a user's answer
before/without the harness's own resolved event, so dropping it would replay an
answered request as actionable and re-answering routes a stale id to the
harness (SR6). The larger refactor's SR11 relocates that onto the row; this
slice does not.

The SR11b ordering (re-assert after replay, for server requests) is pinned by a
new Playwright test that reloads a turn blocked on a server request that then
errored, asserting the sidebar still reads `activity-waiting` with a `!` glyph
and the request stays answerable. Confirmed load-bearing by moving the re-assert
loop before the replay: it fails exactly as described, passes in order.
@marmeladema
marmeladema force-pushed the claude/drop-pending-server-requests branch from 3c3c7c5 to 073ff28 Compare July 26, 2026 17:59

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
crates/giskard-server/static/app.js (2)

3142-3147: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Preserve valid falsy request IDs.

Line 3144 treats request.id === 0 as missing, while outstandingServerRequests explicitly accepts zero-valued IDs. This leaves the request rendered with server_request_id: null.

Suggested fix
-      server_request_id:request && request.id ? String(request.id) : null
+      server_request_id:
+        request && request.id !== undefined && request.id !== null
+          ? String(request.id)
+          : null
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@crates/giskard-server/static/app.js` around lines 3142 - 3147, Update the
server_request_id construction in the outstandingServerRequests loop to preserve
valid falsy IDs such as 0; only use null when request or request.id is actually
absent, while keeping existing string conversion for present IDs.

3142-3147: 📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Regenerate screenshots for this visible replay change.

This changes which server-request cards and waiting states appear during replay. Run tests/e2e/screenshots.sh and commit updated docs/screenshots/ide-desktop.png and docs/screenshots/ide-mobile.png.

As per coding guidelines, frontend changes under crates/giskard-server/static/** that affect visible UI must regenerate the documentation screenshots in the same change.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@crates/giskard-server/static/app.js` around lines 3142 - 3147, Regenerate the
documentation screenshots after the visible replay change in the
outstandingServerRequests replay flow by running tests/e2e/screenshots.sh, then
commit the updated ide-desktop.png and ide-mobile.png artifacts.

Sources: Coding guidelines, Path instructions

♻️ Duplicate comments (1)
crates/giskard-server/static/app.js (1)

3174-3200: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Preserve first-seen order when reopening requests.

pending.delete(id) followed by a later pending.set(id, request) moves a reopened request to the end. For A → B → resolve(A) → receive(A), the result becomes B, A instead of the original A, B, breaking the client/server ordering contract.

Track first-seen IDs separately from the latest payload map, and use a closed set or ordered ID list so resolution hides a request without forgetting its original position.

Suggested fix
   const pending = new Map();
+  const seen = new Set();
+  const order = [];

   for (const ev of (snap.accumulated || [])) {
...
       if (!id) continue;
+      if (!seen.has(id)) {
+        seen.add(id);
+        order.push(id);
+      }
       pending.set(id, request);
...
-  return Array.from(pending.values())
-    .filter(request => request && !answered.has(String(request.id)));
+  return order
+    .map(id => pending.get(id))
+    .filter(request => request && !answered.has(String(request.id)));
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@crates/giskard-server/static/app.js` around lines 3174 - 3200, Update
outstandingServerRequests so resolving a request hides it without discarding its
original order: keep the latest request payload separately from a first-seen
ordered ID collection, and track resolved IDs or active state rather than
deleting the ID’s ordering record. On later server_request_received events,
refresh the payload while preserving the original position, so reopening A after
B still yields A, B.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Outside diff comments:
In `@crates/giskard-server/static/app.js`:
- Around line 3142-3147: Update the server_request_id construction in the
outstandingServerRequests loop to preserve valid falsy IDs such as 0; only use
null when request or request.id is actually absent, while keeping existing
string conversion for present IDs.
- Around line 3142-3147: Regenerate the documentation screenshots after the
visible replay change in the outstandingServerRequests replay flow by running
tests/e2e/screenshots.sh, then commit the updated ide-desktop.png and
ide-mobile.png artifacts.

---

Duplicate comments:
In `@crates/giskard-server/static/app.js`:
- Around line 3174-3200: Update outstandingServerRequests so resolving a request
hides it without discarding its original order: keep the latest request payload
separately from a first-seen ordered ID collection, and track resolved IDs or
active state rather than deleting the ID’s ordering record. On later
server_request_received events, refresh the payload while preserving the
original position, so reopening A after B still yields A, B.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: 7c29dc5b-335b-42a0-bfbd-39b2b8d6f706

📥 Commits

Reviewing files that changed from the base of the PR and between 88831ae and 073ff28.

⛔ Files ignored due to path filters (1)
  • Cargo.lock is excluded by !**/*.lock, !Cargo.lock
📒 Files selected for processing (11)
  • Cargo.toml
  • crates/giskard-proto/src/lib.rs
  • crates/giskard-server/Cargo.toml
  • crates/giskard-server/src/bin/giskard-server-replay.rs
  • crates/giskard-server/src/live_buffer.rs
  • crates/giskard-server/static/app.js
  • crates/giskard-server/tests/server_requests.rs
  • crates/giskard-server/tests/ui.rs
  • specs/giskard-specification.md
  • tests/e2e/tests/helpers.ts
  • tests/e2e/tests/server-requests.spec.ts
🚧 Files skipped from review as they are similar to previous changes (6)
  • crates/giskard-server/tests/ui.rs
  • crates/giskard-server/tests/server_requests.rs
  • crates/giskard-server/src/bin/giskard-server-replay.rs
  • crates/giskard-proto/src/lib.rs
  • specs/giskard-specification.md
  • crates/giskard-server/src/live_buffer.rs

@marmeladema

Copy link
Copy Markdown
Owner Author

Addressed the three new CodeRabbit findings on 073ff28 (the IndexMap refactor).

🟡 #1 (outside diff, app.js:3142-3147) — "Preserve valid falsy request IDs" — skipped, false positive

ServerRequestId is a String newtype (crates/giskard-core/src/ids.rs:33), so on the wire request.id is always a JSON string, never the number 0. A string "0" is truthy in JS, so request.id ? String(request.id) : null cannot drop a valid id. This pattern is already used consistently in outstandingApprovals (line 3168) and outstandingServerRequests. No change needed.

🟡 #2 (outside diff, app.js:3142-3147) — "Regenerate screenshots" — skipped, no visible UI change

The only ordering change in this refactor is the multi-request reopen case (received(A), received(B), resolved(A), received(A)[B, A] instead of [A, B]). The replay harness (giskard-server-replay.rs) scripts a single server request with no ServerRequestResolved and no reopen, so the screenshot scenarios render byte-identically before and after this change. The screenshots don't need regeneration.

🟠 #3 (duplicate, app.js:3174-3200) — "Preserve first-seen order when reopening / breaking the client/server ordering contract" — not a defect, deliberate behavior change

CodeRabbit correctly notices that pending.delete(id) + a later pending.set(id, request) moves a reopened request to the end ([B, A] instead of [A, B]), but mislabels it a contract break. This is the deliberate simplification agreed with the maintainer: collapsing three auxiliary collections (order/seen/closed) into one IndexMap/Map required accepting "reopen moves to the back" rather than "reopen keeps first-seen position."

The client/server ordering contract is changed, not broken — both sides now produce the same [B, A]:

  • Server pending_attention (SB5 bootstrap) uses IndexMap → reopen at end → [B, A]
  • Client outstandingServerRequests uses Map → reopen at end → [B, A]

Pinned by a_reopened_server_request_moves_to_the_end_of_arrival_order (renamed from ..._keeps_its_original_order, expectation flipped [A, B][B, A]), and documented in the spec's SR11a: "a reopen moves to the back, it does not restore its first-seen position."

Verification

Check Result
cargo fmt / cargo clippy -D warnings clean
cargo test --workspace all green
tests/e2e/run.sh tests/server-requests.spec.ts 6/6
CI (rustfmt / clippy / test / playwright / cargo-deny / CodeRabbit) all pass

@marmeladema
marmeladema merged commit 87f68a3 into main Jul 26, 2026
6 checks passed
@marmeladema
marmeladema deleted the claude/drop-pending-server-requests branch July 26, 2026 18:56
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