[P1] isIncompleteReply() relies on 7 hardcoded magic strings #40

Open
opened 2026-05-23 21:52:25 +00:00 by thabeta · 5 comments
Owner

Problem
The UI decides whether a turn hit the step limit / is incomplete by regex-matching ~7 fixed phrases ("step limit", "iteration budget", …). If the model's wording drifts, a step-limited turn is shown as complete and the operator thinks the work finished.

Evidence

  • crates/hero_shrimp_web/ui/src/store.ts isIncompleteReply().

Proposed fix
Have the server stamp an explicit incomplete/stop_reason field on the turn/job result and key the UI off that, not prose.


Filed from a comparative audit of Hero Shrimp vs Qwen-Code / kimi-cli / picoclaw (2026-05-23). Severity in title: P0=correctness/trust, P1=reliability/UX, P2=cleanup.

**Problem** The UI decides whether a turn hit the step limit / is incomplete by regex-matching ~7 fixed phrases ("step limit", "iteration budget", …). If the model's wording drifts, a step-limited turn is shown as complete and the operator thinks the work finished. **Evidence** - `crates/hero_shrimp_web/ui/src/store.ts` `isIncompleteReply()`. **Proposed fix** Have the server stamp an explicit `incomplete`/`stop_reason` field on the turn/job result and key the UI off that, not prose. --- _Filed from a comparative audit of Hero Shrimp vs Qwen-Code / kimi-cli / picoclaw (2026-05-23). Severity in title: P0=correctness/trust, P1=reliability/UX, P2=cleanup._
Member

Implementation Spec for Issue #40

Objective

Replace the UI's brittle prose-matching (isIncompleteReply() regex on ~8 hardcoded phrases) with an explicit, structured incomplete (boolean) and stop_reason (string) field stamped by the server on every turn/job reply. The UI keys its Continue/Retry affordance off that field, falling back to the existing prose heuristic only when the field is absent (backward compatibility with older server responses).

Requirements

  • Server stamps incomplete: bool and stop_reason: string on the structured results the UI consumes: the synchronous message.send chat response, the chat.complete SSE event, the turn:end SSE event, and the autonomy/proof job result (details + job.detail).
  • Detection REUSES existing server-side signals — no new prose matcher. Concretely: the agent_step_limit_reached event already recorded into execution_control (surfaced by step_limit_event_from_execution_control), the existing reply_indicates_incomplete_run() matcher, and the proof.step_limit.reached flag. reply_indicates_incomplete_run() becomes the single, server-owned text fallback (one place, not eight in the UI).
  • UI: add incomplete? / stopReason? to the response TS shapes; at all 3 call sites prefer the server field; keep isIncompleteReply() only as a fallback when the server field is undefined.
  • Behavior unchanged when the new field is absent (old daemon vs new UI, and new daemon vs old UI).

Files to Modify/Create

All modifications (no new files):

  • crates/hero_shrimp_server/src/rpc/methods/session.rs — stamp incomplete/stop_reason on the run_simple_chat_turn response + chat.complete event.
  • crates/hero_shrimp_engine/src/agent_core/agent/core.rs — stamp incomplete/stop_reason on the turn:end SSE event.
  • crates/hero_shrimp_server/src/rpc/methods/job/reconcile.rs (and/or job/contract.rs) — stamp incomplete/stop_reason into the persisted job details so job.detail carries it.
  • crates/hero_shrimp_server/src/rpc/methods/job/proof_run.rsreply_indicates_incomplete_run() is the reused single text fallback; expose a small helper returning the stop_reason string.
  • crates/hero_shrimp_web/ui/src/store.ts — add TS fields; consume server field at the 3 call sites; demote isIncompleteReply() to fallback.

Implementation Plan

Step 1: Add a server-side helper that derives (incomplete, stop_reason) from existing signals

Files: crates/hero_shrimp_server/src/rpc/methods/job/proof_run.rs (near reply_indicates_incomplete_run, ~line 93).

  • Add pub fn turn_incomplete_signal(reply: &str) -> Option<&'static str> returning Some(stop_reason) when incomplete, else None. Reuses the existing reply_indicates_incomplete_run(reply) matcher (no new phrases): Some("step_limit") on match, None otherwise.
  • This is the single text-based fallback for paths that only have the reply string. Where a structured step_limit_event / proof.step_limit.reached is available, that takes precedence.
    Dependencies: none.

Step 2: Stamp incomplete/stop_reason on the synchronous chat response and chat.complete event

Files: crates/hero_shrimp_server/src/rpc/methods/session.rs (in run_simple_chat_turn, Ok(raw_reply) arm, ~lines 967-997).

  • After let reply = strip_fake_tool_call_envelopes(&raw_reply); compute let stop_reason = crate::rpc::methods::job::proof_run::turn_incomplete_signal(&reply);
  • Add to BOTH the complete_payload (chat.complete, ~line 969) and the response JSON (~line 984): "incomplete": stop_reason.is_some() and "stop_reason": stop_reason (serializes to null when None).
    Dependencies: Step 1.

Step 3: Stamp incomplete/stop_reason on the turn:end SSE event

Files: crates/hero_shrimp_engine/src/agent_core/agent/core.rs (~lines 84-93, the match &result that emits turn:end).

  • In the Ok(reply) arm, derive incompleteness from the engine's own fixed step-limit fallback sentence (engine-owned, not drift-prone). Add "incomplete": <bool> and "stop_reason": "step_limit" | null. In the Err arm: "incomplete": true, "stop_reason": "error" (already sets ok: false).
  • Prefer a shared constant for the engine's step-limit fallback sentence (literals in loop_finalize.rs:52, iteration_shell.rs:121, chat_turn.rs, autonomy_dispatch.rs) so the emitter compares against the same constant the finalizer writes. If a constant is out of scope, match the stable substring "agent step limit" only (one place, engine-owned).
    Dependencies: none (can share the token string with Step 1).

Step 4: Stamp incomplete/stop_reason into persisted job details for autonomy/proof jobs

Files: crates/hero_shrimp_server/src/rpc/methods/job/reconcile.rs (terminal-details reconciliation, ~lines 339-400, 478) and/or crates/hero_shrimp_server/src/rpc/methods/job/contract.rs (cleanup_terminal_job_details, ~line 612).

  • When finalizing a terminal job's details, set details["incomplete"] = true / details["stop_reason"] = "step_limit" when proof.step_limit.reached == true (stamped at proof.rs:480-488) OR details["failure_kind"] == "agent_step_limit" (set at proof_run.rs:759) OR reply_indicates_incomplete_run(details["reply"]). Otherwise incomplete = false / omit stop_reason.
  • failure_kind is cleared during reconciliation (contract.rs:538); stamp BEFORE that removal or derive from proof.step_limit (which persists). This path has the richest structured signal and should NOT rely on prose.
    Dependencies: Step 1.

Step 5: Add TS fields and consume the server field at the 3 store.ts call sites

Files: crates/hero_shrimp_web/ui/src/store.ts.

  • Keep Msg.incomplete?: boolean (~line 43).
  • Keep isIncompleteReply() (lines 57-69) but re-document as the LEGACY FALLBACK used only when the server omits the field.
  • Call site ~1105 (trackJobUntilDone terminal): const serverIncomplete = detailJob?.details?.incomplete; then incomplete: ok && (serverIncomplete ?? isIncompleteReply(kept)).
  • Call site ~1263 (sync message.send return): response now carries incomplete; set incomplete: result?.incomplete ?? isIncompleteReply(finalText) (verify the RPC-result variable name near line 1204).
  • Call site ~1682 (turn:end SSE): replace isIncompleteReply(strippedReply || reply) with (p.incomplete ?? isIncompleteReply(strippedReply || reply)), still gated by p.ok !== false.
    Dependencies: Steps 2, 3, 4.

Step 6: Tests

Files: crates/hero_shrimp_server/src/rpc/jsonrpc/tests/chunk_3.rs (near execution_control_surfaces_step_limit_event, line 597) and chunk_2.rs (near step_limit_reply_is_not_successful_completion, line 372).

  • Assert finalized job details contains incomplete == true / stop_reason == "step_limit" when proof.step_limit.reached is set, and a clean reply yields incomplete == false / no stop_reason. Add a unit test for turn_incomplete_signal().
    Dependencies: Steps 1, 4.

Acceptance Criteria

  • A step-limited turn produces incomplete: true + stop_reason: "step_limit" on the message.send sync response, the chat.complete event, the turn:end event, and the job details (via job.detail) — without relying on UI prose matching.
  • The UI shows the Continue/Retry affordance for that turn because it read the server field, not the phrase.
  • A normal completed turn yields incomplete: false (or absent stop_reason) and renders as a complete answer.
  • With the field absent (simulating an old daemon), the UI behaves exactly as today via the isIncompleteReply() fallback.
  • No new prose phrases introduced; the only remaining text heuristic is the single server-side reply_indicates_incomplete_run().
  • Existing tests pass; new Step 6 tests pass.

Notes

  • Engine return-type constraint: EngineClient::send returns Result<String> with two impls (in_process.rs:161, rpc_client.rs:199). Widening it to a struct is a large blast-radius change and is intentionally avoided — the structured signal is carried on the JSON envelopes the UI already consumes.
  • Source-of-truth drift: the engine's fixed fallback sentences live in four spots; prefer matching the structured agent_step_limit_reached event wherever available; only the synchronous chat turn needs the text fallback, now in one function.
  • Wire compatibility: serialize stop_reason as Option<String>null when not incomplete; UI treats null/undefined/absent uniformly via ?? fallback.
## Implementation Spec for Issue #40 ### Objective Replace the UI's brittle prose-matching (`isIncompleteReply()` regex on ~8 hardcoded phrases) with an explicit, structured `incomplete` (boolean) and `stop_reason` (string) field stamped by the server on every turn/job reply. The UI keys its Continue/Retry affordance off that field, falling back to the existing prose heuristic only when the field is absent (backward compatibility with older server responses). ### Requirements - Server stamps `incomplete: bool` and `stop_reason: string` on the structured results the UI consumes: the synchronous `message.send` chat response, the `chat.complete` SSE event, the `turn:end` SSE event, and the autonomy/proof job result (`details` + `job.detail`). - Detection REUSES existing server-side signals — no new prose matcher. Concretely: the `agent_step_limit_reached` event already recorded into `execution_control` (surfaced by `step_limit_event_from_execution_control`), the existing `reply_indicates_incomplete_run()` matcher, and the `proof.step_limit.reached` flag. `reply_indicates_incomplete_run()` becomes the single, server-owned text fallback (one place, not eight in the UI). - UI: add `incomplete?` / `stopReason?` to the response TS shapes; at all 3 call sites prefer the server field; keep `isIncompleteReply()` only as a fallback when the server field is `undefined`. - Behavior unchanged when the new field is absent (old daemon vs new UI, and new daemon vs old UI). ### Files to Modify/Create All modifications (no new files): - `crates/hero_shrimp_server/src/rpc/methods/session.rs` — stamp `incomplete`/`stop_reason` on the `run_simple_chat_turn` response + `chat.complete` event. - `crates/hero_shrimp_engine/src/agent_core/agent/core.rs` — stamp `incomplete`/`stop_reason` on the `turn:end` SSE event. - `crates/hero_shrimp_server/src/rpc/methods/job/reconcile.rs` (and/or `job/contract.rs`) — stamp `incomplete`/`stop_reason` into the persisted job `details` so `job.detail` carries it. - `crates/hero_shrimp_server/src/rpc/methods/job/proof_run.rs` — `reply_indicates_incomplete_run()` is the reused single text fallback; expose a small helper returning the stop_reason string. - `crates/hero_shrimp_web/ui/src/store.ts` — add TS fields; consume server field at the 3 call sites; demote `isIncompleteReply()` to fallback. ### Implementation Plan #### Step 1: Add a server-side helper that derives `(incomplete, stop_reason)` from existing signals Files: `crates/hero_shrimp_server/src/rpc/methods/job/proof_run.rs` (near `reply_indicates_incomplete_run`, ~line 93). - Add `pub fn turn_incomplete_signal(reply: &str) -> Option<&'static str>` returning `Some(stop_reason)` when incomplete, else `None`. Reuses the existing `reply_indicates_incomplete_run(reply)` matcher (no new phrases): `Some("step_limit")` on match, `None` otherwise. - This is the single text-based fallback for paths that only have the reply string. Where a structured `step_limit_event` / `proof.step_limit.reached` is available, that takes precedence. Dependencies: none. #### Step 2: Stamp `incomplete`/`stop_reason` on the synchronous chat response and `chat.complete` event Files: `crates/hero_shrimp_server/src/rpc/methods/session.rs` (in `run_simple_chat_turn`, `Ok(raw_reply)` arm, ~lines 967-997). - After `let reply = strip_fake_tool_call_envelopes(&raw_reply);` compute `let stop_reason = crate::rpc::methods::job::proof_run::turn_incomplete_signal(&reply);` - Add to BOTH the `complete_payload` (`chat.complete`, ~line 969) and the `response` JSON (~line 984): `"incomplete": stop_reason.is_some()` and `"stop_reason": stop_reason` (serializes to `null` when `None`). Dependencies: Step 1. #### Step 3: Stamp `incomplete`/`stop_reason` on the `turn:end` SSE event Files: `crates/hero_shrimp_engine/src/agent_core/agent/core.rs` (~lines 84-93, the `match &result` that emits `turn:end`). - In the `Ok(reply)` arm, derive incompleteness from the engine's own fixed step-limit fallback sentence (engine-owned, not drift-prone). Add `"incomplete": <bool>` and `"stop_reason": "step_limit" | null`. In the `Err` arm: `"incomplete": true, "stop_reason": "error"` (already sets `ok: false`). - Prefer a shared constant for the engine's step-limit fallback sentence (literals in `loop_finalize.rs:52`, `iteration_shell.rs:121`, `chat_turn.rs`, `autonomy_dispatch.rs`) so the emitter compares against the same constant the finalizer writes. If a constant is out of scope, match the stable substring `"agent step limit"` only (one place, engine-owned). Dependencies: none (can share the token string with Step 1). #### Step 4: Stamp `incomplete`/`stop_reason` into persisted job `details` for autonomy/proof jobs Files: `crates/hero_shrimp_server/src/rpc/methods/job/reconcile.rs` (terminal-details reconciliation, ~lines 339-400, 478) and/or `crates/hero_shrimp_server/src/rpc/methods/job/contract.rs` (`cleanup_terminal_job_details`, ~line 612). - When finalizing a terminal job's `details`, set `details["incomplete"] = true` / `details["stop_reason"] = "step_limit"` when `proof.step_limit.reached == true` (stamped at `proof.rs:480-488`) OR `details["failure_kind"] == "agent_step_limit"` (set at `proof_run.rs:759`) OR `reply_indicates_incomplete_run(details["reply"])`. Otherwise `incomplete = false` / omit `stop_reason`. - `failure_kind` is cleared during reconciliation (`contract.rs:538`); stamp BEFORE that removal or derive from `proof.step_limit` (which persists). This path has the richest structured signal and should NOT rely on prose. Dependencies: Step 1. #### Step 5: Add TS fields and consume the server field at the 3 store.ts call sites Files: `crates/hero_shrimp_web/ui/src/store.ts`. - Keep `Msg.incomplete?: boolean` (~line 43). - Keep `isIncompleteReply()` (lines 57-69) but re-document as the LEGACY FALLBACK used only when the server omits the field. - Call site ~1105 (`trackJobUntilDone` terminal): `const serverIncomplete = detailJob?.details?.incomplete;` then `incomplete: ok && (serverIncomplete ?? isIncompleteReply(kept))`. - Call site ~1263 (sync `message.send` return): response now carries `incomplete`; set `incomplete: result?.incomplete ?? isIncompleteReply(finalText)` (verify the RPC-result variable name near line 1204). - Call site ~1682 (`turn:end` SSE): replace `isIncompleteReply(strippedReply || reply)` with `(p.incomplete ?? isIncompleteReply(strippedReply || reply))`, still gated by `p.ok !== false`. Dependencies: Steps 2, 3, 4. #### Step 6: Tests Files: `crates/hero_shrimp_server/src/rpc/jsonrpc/tests/chunk_3.rs` (near `execution_control_surfaces_step_limit_event`, line 597) and `chunk_2.rs` (near `step_limit_reply_is_not_successful_completion`, line 372). - Assert finalized job `details` contains `incomplete == true` / `stop_reason == "step_limit"` when `proof.step_limit.reached` is set, and a clean reply yields `incomplete == false` / no `stop_reason`. Add a unit test for `turn_incomplete_signal()`. Dependencies: Steps 1, 4. ### Acceptance Criteria - [ ] A step-limited turn produces `incomplete: true` + `stop_reason: "step_limit"` on the `message.send` sync response, the `chat.complete` event, the `turn:end` event, and the job `details` (via `job.detail`) — without relying on UI prose matching. - [ ] The UI shows the Continue/Retry affordance for that turn because it read the server field, not the phrase. - [ ] A normal completed turn yields `incomplete: false` (or absent `stop_reason`) and renders as a complete answer. - [ ] With the field absent (simulating an old daemon), the UI behaves exactly as today via the `isIncompleteReply()` fallback. - [ ] No new prose phrases introduced; the only remaining text heuristic is the single server-side `reply_indicates_incomplete_run()`. - [ ] Existing tests pass; new Step 6 tests pass. ### Notes - Engine return-type constraint: `EngineClient::send` returns `Result<String>` with two impls (`in_process.rs:161`, `rpc_client.rs:199`). Widening it to a struct is a large blast-radius change and is intentionally avoided — the structured signal is carried on the JSON envelopes the UI already consumes. - Source-of-truth drift: the engine's fixed fallback sentences live in four spots; prefer matching the structured `agent_step_limit_reached` event wherever available; only the synchronous chat turn needs the text fallback, now in one function. - Wire compatibility: serialize `stop_reason` as `Option<String>` → `null` when not incomplete; UI treats `null`/`undefined`/absent uniformly via `?? fallback`.
Member

Test Results

Rust — cargo test -p hero_shrimp_server

  • Total: 298
  • Passed: 298
  • Failed: 0

Relevant cases:

  • step_limit_reply_is_not_successful_completion — passed
  • turn_incomplete_signal_maps_step_limit_to_stop_reason (new) — passed
  • execution_control_surfaces_step_limit_event — passed

cargo check -p hero_shrimp_engine -p hero_shrimp_server — clean.

Web UI — crates/hero_shrimp_web

  • vite build — succeeded (99 modules transformed); built bundle regenerated and committed.
  • tsc --noEmit reports only pre-existing type errors in unrelated files (ChatActivity, CostBadge, HeroWelcome, and store.ts lines 1375/1385 reasoningText); none are introduced by this change. The production build path is esbuild via vite, which bundles cleanly.

Status: PASS.

## Test Results ### Rust — `cargo test -p hero_shrimp_server` - Total: 298 - Passed: 298 - Failed: 0 Relevant cases: - `step_limit_reply_is_not_successful_completion` — passed - `turn_incomplete_signal_maps_step_limit_to_stop_reason` (new) — passed - `execution_control_surfaces_step_limit_event` — passed `cargo check -p hero_shrimp_engine -p hero_shrimp_server` — clean. ### Web UI — `crates/hero_shrimp_web` - `vite build` — succeeded (99 modules transformed); built bundle regenerated and committed. - `tsc --noEmit` reports only pre-existing type errors in unrelated files (ChatActivity, CostBadge, HeroWelcome, and store.ts lines 1375/1385 `reasoningText`); none are introduced by this change. The production build path is esbuild via vite, which bundles cleanly. Status: PASS.
Member

Test Results

Rust — cargo test -p hero_shrimp_server

  • Total: 301
  • Passed: 301
  • Failed: 0

New cases (all passed):

  • turn_incomplete_signal_maps_step_limit_to_stop_reason
  • stamp_incomplete_signal_from_structured_step_limit
  • stamp_incomplete_signal_from_failure_kind
  • stamp_incomplete_signal_clean_run_has_no_stop_reason

Existing regression cases still pass:

  • step_limit_reply_is_not_successful_completion
  • execution_control_surfaces_step_limit_event

cargo check -p hero_shrimp_engine -p hero_shrimp_server — clean.

Web UI — crates/hero_shrimp_web

  • vite build — succeeded (99 modules transformed); built bundle regenerated and committed.
  • tsc --noEmit reports only pre-existing type errors in unrelated code (the reasoningText accesses in store.ts and a few other components); none are introduced by this change.

Status: PASS.

## Test Results ### Rust — `cargo test -p hero_shrimp_server` - Total: 301 - Passed: 301 - Failed: 0 New cases (all passed): - `turn_incomplete_signal_maps_step_limit_to_stop_reason` - `stamp_incomplete_signal_from_structured_step_limit` - `stamp_incomplete_signal_from_failure_kind` - `stamp_incomplete_signal_clean_run_has_no_stop_reason` Existing regression cases still pass: - `step_limit_reply_is_not_successful_completion` - `execution_control_surfaces_step_limit_event` `cargo check -p hero_shrimp_engine -p hero_shrimp_server` — clean. ### Web UI — `crates/hero_shrimp_web` - `vite build` — succeeded (99 modules transformed); built bundle regenerated and committed. - `tsc --noEmit` reports only pre-existing type errors in unrelated code (the `reasoningText` accesses in store.ts and a few other components); none are introduced by this change. Status: PASS.
Member

Implementation Summary

The UI no longer decides incompleteness by regex-matching model prose. The server now stamps an explicit incomplete / stop_reason on every turn/job reply, and the UI reads that field (prose heuristic kept only as a fallback for older daemons).

Files changed

  • crates/hero_shrimp_server/src/rpc/methods/job/proof_run.rs — added turn_incomplete_signal() (single text fallback, reuses reply_indicates_incomplete_run); stamp signal at job-details persistence.
  • crates/hero_shrimp_server/src/rpc/methods/session.rs — stamp incomplete/stop_reason on the sync message.send response and chat.complete event.
  • crates/hero_shrimp_engine/src/agent_core/agent/core.rs — stamp incomplete/stop_reason on the turn:end SSE event (engine-owned marker; error on the failure arm).
  • crates/hero_shrimp_server/src/rpc/methods/job/contract.rsstamp_incomplete_signal_into_details() (structured step-limit → failure_kind → reply fallback); also stamped during subagent reconciliation before failure_kind is cleared.
  • crates/hero_shrimp_web/ui/src/store.ts — prefer the server field at all 3 call sites; isIncompleteReply() demoted to a legacy fallback. Bundle regenerated.
  • crates/hero_shrimp_server/src/rpc/jsonrpc/tests/chunk_2.rs, chunk_3.rs — 4 new tests.

Acceptance criteria

  • Step-limited turns surface incomplete: true / stop_reason: "step_limit" on the sync response, chat.complete, turn:end, and persisted job details — without UI prose matching.
  • Normal turns yield incomplete: false and no stop_reason.
  • With the field absent, the UI behaves exactly as before via the isIncompleteReply() fallback.
  • No new prose phrases; the only text heuristic is the single server-side reply_indicates_incomplete_run().

Tests

cargo test -p hero_shrimp_server — 301 passed, 0 failed. vite build succeeded.

## Implementation Summary The UI no longer decides incompleteness by regex-matching model prose. The server now stamps an explicit `incomplete` / `stop_reason` on every turn/job reply, and the UI reads that field (prose heuristic kept only as a fallback for older daemons). ### Files changed - `crates/hero_shrimp_server/src/rpc/methods/job/proof_run.rs` — added `turn_incomplete_signal()` (single text fallback, reuses `reply_indicates_incomplete_run`); stamp signal at job-details persistence. - `crates/hero_shrimp_server/src/rpc/methods/session.rs` — stamp `incomplete`/`stop_reason` on the sync `message.send` response and `chat.complete` event. - `crates/hero_shrimp_engine/src/agent_core/agent/core.rs` — stamp `incomplete`/`stop_reason` on the `turn:end` SSE event (engine-owned marker; `error` on the failure arm). - `crates/hero_shrimp_server/src/rpc/methods/job/contract.rs` — `stamp_incomplete_signal_into_details()` (structured step-limit → failure_kind → reply fallback); also stamped during subagent reconciliation before `failure_kind` is cleared. - `crates/hero_shrimp_web/ui/src/store.ts` — prefer the server field at all 3 call sites; `isIncompleteReply()` demoted to a legacy fallback. Bundle regenerated. - `crates/hero_shrimp_server/src/rpc/jsonrpc/tests/chunk_2.rs`, `chunk_3.rs` — 4 new tests. ### Acceptance criteria - Step-limited turns surface `incomplete: true` / `stop_reason: "step_limit"` on the sync response, `chat.complete`, `turn:end`, and persisted job `details` — without UI prose matching. - Normal turns yield `incomplete: false` and no `stop_reason`. - With the field absent, the UI behaves exactly as before via the `isIncompleteReply()` fallback. - No new prose phrases; the only text heuristic is the single server-side `reply_indicates_incomplete_run()`. ### Tests `cargo test -p hero_shrimp_server` — 301 passed, 0 failed. `vite build` succeeded.
Member

Pull request opened: #96

This PR implements the changes discussed in this issue, targeting the integration branch.

Pull request opened: https://forge.ourworld.tf/lhumina_code/hero_shrimp/pulls/96 This PR implements the changes discussed in this issue, targeting the integration branch.
Sign in to join this conversation.
No milestone
No project
No assignees
2 participants
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
lhumina_code/hero_shrimp#40
No description provided.