Skip to content

feat(term-challenge): add ratatui CLI, agent storage module, and documentation#4

Open
echobt wants to merge 3 commits intomainfrom
feat/cli-tui-agent-storage-wasm
Open

feat(term-challenge): add ratatui CLI, agent storage module, and documentation#4
echobt wants to merge 3 commits intomainfrom
feat/cli-tui-agent-storage-wasm

Conversation

@echobt
Copy link

@echobt echobt commented Feb 18, 2026

Summary

Adds a native ratatui-based TUI binary (term-cli) for real-time chain monitoring, introduces on-chain agent code/log storage in the WASM module with size-enforced limits, and updates project documentation with Mermaid architecture diagrams.

Changes

CLI (cli/)

  • New term-cli binary crate with ratatui TUI featuring four tabs: Leaderboard, Live Evaluation, Agent Submission, and Network Status
  • JSON-RPC 2.0 client (rpc.rs) for connecting to platform-v2 RPC endpoints with support for challenge_call, epoch_current, system_health, validator_list, and challenge_list methods
  • Keyboard-driven navigation (Tab to switch views, arrow keys to scroll, q to quit)
  • Auto-refresh polling with configurable RPC endpoint, hotkey filtering, and challenge ID selection via CLI args
  • App state management (app.rs) with async data fetching and tab-based view rendering (ui.rs)

WASM Agent Storage (wasm/src/agent_storage.rs)

  • store_agent_code() — stores compressed agent zip packages (≤1MB enforced) keyed by agent_code:<hotkey>:<epoch>
  • store_agent_logs() — stores evaluation logs (≤256KB enforced) keyed by agent_logs:<hotkey>:<epoch>, with per-task output truncated to 4KB
  • get_agent_code() / get_agent_logs() — retrieval functions using host_storage_get
  • All functions are no_std compatible using alloc types

WASM Module Changes (wasm/src/)

  • Added AgentLogEntry and AgentLogs types to types.rs for structured log storage
  • Added /agent/:hotkey/code and /agent/:hotkey/logs route definitions in routes.rs
  • Enhanced evaluate() in lib.rs to store agent code and truncated logs after evaluation
  • Enhanced validate() to reject submissions with package_zip exceeding 1MB
  • Cleaned up unused code in dataset.rs and tasks.rs, added doc comments throughout
  • Added calculate_aggregate and to_weight doc comments in scoring.rs

Documentation

  • README.md: Added system architecture, evaluation flow, CLI data flow, log consensus, and agent storage Mermaid diagrams; expanded project overview and workspace structure docs
  • AGENTS.md: Added CLI development guidelines, agent storage conventions, and updated project rules for the new crates
  • Updated root Cargo.toml to include cli in workspace members with default-members set to wasm only (prevents CLI from being built under wasm32 target)

Summary by CodeRabbit

Release Notes

  • New Features

    • Added a native CLI application with a tabbed terminal dashboard for monitoring agent performance, leaderboard rankings, evaluation progress, submissions, and network status.
    • Implemented on-chain storage for agent code and execution logs with per-task tracking and audit trails.
    • Added agent log consensus mechanism for verifying agent code and execution data integrity.
  • Documentation

    • Updated architecture documentation with system diagrams, data flows, and storage semantics.
    • Added CLI usage guide with example commands.

- Create agent_storage module with store/retrieve functions for agent
  code, hashes, and evaluation logs using host storage
- Add AgentLogEntry and AgentLogs types to types.rs
- Add 1MB package_zip size validation in validate()
- Store agent code, hash, and logs during evaluate()
- Add GET /agent/:hotkey/code and /agent/:hotkey/logs routes
Add a new native binary crate 'cli/' with a ratatui-based terminal UI
for monitoring the term-challenge. Features include:

- Four tabs: Leaderboard, Evaluation, Submission, Network
- JSON-RPC 2.0 client for fetching data from chain endpoint
- Auto-refresh every 10 seconds with manual refresh via 'r' key
- Tab/Shift+Tab navigation, Up/Down scrolling, 'q' to quit
- Status bar showing epoch, phase, block height, validators
- Graceful error handling with TUI error display
- CLI args via clap: --rpc-url, --hotkey, --challenge-id, --tab

The workspace uses default-members to prevent the CLI from being
built when targeting wasm32-unknown-unknown.
Remove unused dataset consensus functions from dataset.rs that were not yet
integrated with the P2P consensus system. The active dataset is currently
managed via ChallengeParams passed to evaluate(), making these functions
dead code. The module now contains documentation notes for future
reimplementation.

Remove unused get_dataset_history() from tasks.rs which also contained
an .unwrap_or_default() pattern on deserialization.

Add doc comments and #[allow(dead_code)] annotations to agent_storage
retrieval functions (get_agent_code, get_agent_logs), route definitions
(get_route_definitions), and scoring utilities (to_weight, apply_decay)
that are defined for future platform-v2 integration but not yet called
internally. Add module-level documentation to routes.rs explaining the
challenge_call RPC integration path.
@coderabbitai
Copy link

coderabbitai bot commented Feb 18, 2026

📝 Walkthrough

Walkthrough

This pull request extends the term-challenge workspace by adding a new native CLI application with terminal UI and introducing on-chain storage for agent code, hashes, and execution logs. The WASM evaluation flow integrates with the new agent storage module, while dataset consensus functionality is removed. Documentation is significantly expanded to describe the architecture and data flows.

Changes

Cohort / File(s) Summary
CLI Application
cli/Cargo.toml, cli/src/main.rs, cli/src/app.rs, cli/src/rpc.rs, cli/src/ui.rs
Introduces new native CLI crate with ratatui-based TUI. Includes tabbed interface (Leaderboard, Evaluation, Submission, Network), JSON-RPC client for blockchain data fetching, UI rendering with tables and status panels, and async refresh logic with periodic updates and error handling.
Agent Storage & Type Definitions
wasm/src/agent_storage.rs, wasm/src/types.rs
Adds new agent storage module with APIs for storing/retrieving agent code, hashes, and execution logs with size validation. Introduces AgentLogs and AgentLogEntry public types to represent on-chain audit trail of agent execution with task details, scores, and previews.
WASM Evaluation Integration
wasm/src/lib.rs
Modifies evaluation flow to extract agent metadata, build execution logs with per-task previews, enforce package size limits (1 MiB), and persist agent code, hash, and logs to storage. Exports new types and integrates agent_storage module.
WASM Cleanup & Refactoring
wasm/src/dataset.rs, wasm/src/tasks.rs
Removes dataset consensus functionality (select_random_task_indices, store_my_selection, build_consensus_dataset, create_dataset_selection) and get_dataset_history function. Reduces dataset module to placeholder for future P2P consensus work.
WASM Utilities & Routes
wasm/src/scoring.rs, wasm/src/routes.rs
Adds to_weight and apply_decay utility functions for score normalization and decay calculation. Introduces two new HTTP routes: GET /agent/:hotkey/code and GET /agent/:hotkey/logs for agent code and log retrieval.
Workspace Configuration & Documentation
Cargo.toml, README.md, AGENTS.md
Adds cli crate to workspace members with wasm as default. Extensively expands README with system architecture, evaluation flow, CLI data flow, agent log consensus, and agent code storage sections with Mermaid diagrams. Updates AGENTS.md to document new storage-related architecture components.

Sequence Diagrams

sequenceDiagram
    participant Agent as Agent Submission
    participant Eval as WASM Evaluate
    participant Storage as Agent Storage
    participant OnChain as On-Chain Storage
    
    Agent->>Eval: submit(code_zip, params)
    Eval->>Eval: extract metadata (hotkey, epoch, hash)
    Eval->>Eval: execute tasks & collect results
    Eval->>Eval: build AgentLogs with previews
    Eval->>Storage: store_agent_code(hotkey, epoch, zip)
    Storage->>OnChain: persist code
    Eval->>Storage: store_agent_hash(hotkey, epoch, hash)
    Storage->>OnChain: persist hash
    Eval->>Storage: store_agent_logs(hotkey, epoch, logs)
    Storage->>OnChain: persist execution log
    Eval->>Agent: return result
Loading
sequenceDiagram
    participant User as User (CLI)
    participant CLI as CLI App
    participant RPC as RPC Client
    participant Blockchain as Blockchain/RPC
    
    User->>CLI: refresh (manual or periodic)
    CLI->>RPC: fetch_system_health()
    RPC->>Blockchain: rpc_call(system_health)
    Blockchain-->>RPC: health data
    RPC-->>CLI: NetworkStatus updated
    CLI->>RPC: fetch_leaderboard(challenge_id)
    RPC->>Blockchain: rpc_call(leaderboard)
    Blockchain-->>RPC: leaderboard rows
    RPC-->>CLI: LeaderboardRow vec
    CLI->>RPC: fetch_evaluation_progress(hotkey)
    RPC->>Blockchain: rpc_call(eval_progress)
    Blockchain-->>RPC: eval tasks
    RPC-->>CLI: EvalTaskRow vec
    CLI->>CLI: render UI with tabs
    CLI-->>User: display tabbed interface
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Poem

🐰 hops excitedly

New CLI bounds with tabs so bright,
Agent logs stored, on-chain insight,
Storage flows from code to hash,
Evaluation's audit trail won't clash,
Dashboard monitors the network's flight! 🌙

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 41.30% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The PR title accurately summarizes the three main changes: adding a ratatui CLI, introducing an agent storage module, and updating documentation.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch feat/cli-tui-agent-storage-wasm

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

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

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 8

Caution

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

⚠️ Outside diff range comments (2)
wasm/src/scoring.rs (1)

82-102: ⚠️ Potential issue | 🟠 Major

Remove apply_decay function — it is unused dead code.

The #[allow(dead_code)] annotation on apply_decay violates the coding guideline: "Do NOT add #[allow(dead_code)] broadly. Fix unused code or remove it." Verification confirms apply_decay is not called anywhere in the codebase. Since it is documented as "reserved for future use," it should be removed now and reintroduced when decay mechanics are actually integrated.

Proposed fix
-/// Apply decay to weight based on hours since top score.
-///
-/// Note: This function is reserved for future use when decay mechanics are
-/// integrated with the scoring system via challenge configuration.
-#[allow(dead_code)]
-pub fn apply_decay(weight: f64, hours_since_top: f64, params: &DecayParams) -> f64 {
-    let grace = params.grace_period_hours as f64;
-    if hours_since_top <= grace {
-        return weight;
-    }
-
-    let elapsed = hours_since_top - grace;
-    let half_life = params.half_life_hours as f64;
-    if half_life <= 0.0 {
-        return params.min_multiplier;
-    }
-
-    let multiplier = 0.5f64.powf(elapsed / half_life);
-    let clamped = multiplier.max(params.min_multiplier);
-    weight * clamped
-}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@wasm/src/scoring.rs` around lines 82 - 102, Remove the unused apply_decay
function and its accompanying doc comment and #[allow(dead_code)] attribute from
the file (the function signature pub fn apply_decay(weight: f64,
hours_since_top: f64, params: &DecayParams) -> f64 and its body), and also
remove any related unused imports or references to DecayParams in this module if
they become unused after deletion; ensure no other code references apply_decay
before committing.
AGENTS.md (1)

9-34: ⚠️ Potential issue | 🟡 Minor

Add a language specifier to the fenced code block to fix the MD040 linting warning.

🛠️ Proposed fix
-```
+```text
 term-challenge/
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@AGENTS.md` around lines 9 - 34, The fenced code block that shows the project
tree starting with "term-challenge/" in AGENTS.md lacks a language specifier;
add a language tag (e.g., ```text) to the opening fence for that code block so
the MD040 lint warning is resolved and keep the closing ``` intact.
🧹 Nitpick comments (10)
wasm/src/dataset.rs (1)

1-10: Consider removing this placeholder file.

This file contains only comments with no functional code. Keeping empty placeholder modules adds noise. The intent can be documented in a tracking issue or AGENTS.md instead, and the module can be reintroduced when P2P consensus is actually implemented.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@wasm/src/dataset.rs` around lines 1 - 10, This module (wasm/src/dataset.rs)
contains only explanatory comments and should be removed to reduce noise; either
delete the file from the repo or replace it with a single-line module comment
that references a tracking issue or AGENTS.md for future P2P dataset consensus
work (mentioning the intended features: host_random_seed selection,
host_storage_set validator storage, >50% consensus, and dataset
serialization/hash verification) so the intent is preserved without an empty
placeholder.
cli/src/rpc.rs (1)

92-99: No HTTP timeout configured — CLI may hang on unresponsive RPC.

reqwest::Client::new() uses no timeout by default. If the RPC endpoint is down or slow, every call will block the refresh loop indefinitely. Configure a connect + request timeout.

Proposed fix
 pub fn new(url: &str) -> Self {
     Self {
         url: url.to_string(),
-        client: reqwest::Client::new(),
+        client: reqwest::Client::builder()
+            .connect_timeout(std::time::Duration::from_secs(5))
+            .timeout(std::time::Duration::from_secs(15))
+            .build()
+            .unwrap_or_default(),
         request_id: AtomicU64::new(1),
     }
 }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@cli/src/rpc.rs` around lines 92 - 99, RpcClient::new currently constructs a
reqwest client with Client::new() which has no timeouts; update RpcClient::new
to build a reqwest::Client with sensible connect and overall request timeouts
(e.g. via
reqwest::Client::builder().connect_timeout(Duration::from_secs(...)).timeout(Duration::from_secs(...)).build()),
ensuring you import std::time::Duration; keep the AtomicU64 initialization and
other fields unchanged so RpcClient::new still returns Self with url, client,
and request_id.
cli/src/ui.rs (2)

247-309: draw_network duplicates the Paragraph + Block setup across both branches.

The only difference between the Some and None branches is the optional "Challenge" line. Consider building lines first, then conditionally appending, and rendering once.

Proposed fix
 fn draw_network(frame: &mut Frame, area: Rect, app: &App) {
     let ns = &app.network_status;
     let connected_style = if ns.connected {
         Style::default().fg(Color::Green)
     } else {
         Style::default().fg(Color::Red)
     };
     let connected_text = if ns.connected {
         "Connected"
     } else {
         "Disconnected"
     };

-    let lines = vec![
+    let mut lines = vec![
         Line::from(vec![
             Span::styled("Status:      ", Style::default().fg(Color::Yellow).bold()),
             Span::styled(connected_text, connected_style),
         ]),
         // ... remaining lines ...
     ];

     if let Some(cid) = &app.challenge_id {
-        let mut all_lines = lines;
-        all_lines.push(Line::from(vec![
+        lines.push(Line::from(vec![
             Span::styled("Challenge:   ", Style::default().fg(Color::Yellow).bold()),
             Span::raw(cid.clone()),
         ]));
-        let paragraph = Paragraph::new(all_lines).block(
-            Block::default()
-                .borders(Borders::ALL)
-                .title("Network Status"),
-        );
-        frame.render_widget(paragraph, area);
-    } else {
-        let paragraph = Paragraph::new(lines).block(
-            Block::default()
-                .borders(Borders::ALL)
-                .title("Network Status"),
-        );
-        frame.render_widget(paragraph, area);
     }
+
+    let paragraph = Paragraph::new(lines).block(
+        Block::default()
+            .borders(Borders::ALL)
+            .title("Network Status"),
+    );
+    frame.render_widget(paragraph, area);
 }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@cli/src/ui.rs` around lines 247 - 309, In draw_network, avoid duplicating
Paragraph/Block rendering by building the base `lines` vector then conditionally
appending the challenge line when `app.challenge_id` is Some (use the existing
`cid`), and finally create a single `Paragraph::new(...)` with the
`Block::default().borders(Borders::ALL).title("Network Status")` and call
`frame.render_widget` once; reference symbols: function draw_network, variable
lines, optional app.challenge_id (cid), Paragraph, Block, frame.render_widget.

348-354: truncate_hotkey uses byte slicing which panics on multi-byte UTF-8 boundaries.

&hotkey[..max_len] slices by byte index. If hotkey ever contains non-ASCII characters, this will panic. While SS58 hotkeys are ASCII in practice, this is fragile. Use chars() for safe truncation.

Proposed fix
 fn truncate_hotkey(hotkey: &str, max_len: usize) -> String {
-    if hotkey.len() > max_len {
-        format!("{}...", &hotkey[..max_len])
+    if hotkey.chars().count() > max_len {
+        let truncated: String = hotkey.chars().take(max_len).collect();
+        format!("{truncated}...")
     } else {
         hotkey.to_string()
     }
 }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@cli/src/ui.rs` around lines 348 - 354, The function truncate_hotkey uses byte
slicing (&hotkey[..max_len]) which will panic on UTF-8 multi-byte boundaries;
update truncate_hotkey to operate on Unicode scalar values instead: count
characters with hotkey.chars(), if the character count is greater than max_len
build a truncated string with hotkey.chars().take(max_len).collect::<String>()
and append "..." otherwise return hotkey.to_string(); ensure you use the
function name truncate_hotkey and the hotkey/max_len parameters so the change is
localized and preserves existing behavior.
cli/src/app.rs (3)

176-184: Evaluation progress errors are silently swallowed at debug level.

Leaderboard fetch failures are displayed to the user via error_message, but evaluation progress failures are only logged with tracing::debug!. For a monitoring TUI, this inconsistency may confuse users who see stale or empty evaluation data without explanation. Consider setting error_message here too, or at least using tracing::warn!.

Proposed fix
             Err(e) => {
-                tracing::debug!("Evaluation progress: {e}");
+                tracing::warn!("Evaluation progress: {e}");
             }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@cli/src/app.rs` around lines 176 - 184, The error branch in the evaluation
progress fetch silently logs at debug level; update the Err(e) handling inside
the match on rpc.fetch_evaluation_progress(&hotkey) so it both surfaces the
error to the user (e.g., set self.error_message = Some(format!(...)) or call the
existing error display helper) and logs it at warn/error level (use
tracing::warn! or tracing::error! instead of tracing::debug!); modify the block
that currently references self.evaluation_progress and tracing::debug! so
failures update user-facing state and produce a higher-severity log.

144-187: Error messages can silently overwrite each other during refresh.

If the challenge list fetch (Line 161) sets error_message and the leaderboard fetch (Line 171) also fails, the leaderboard error overwrites the challenge error. Consider accumulating errors or showing the most recent one with a count indicator.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@cli/src/app.rs` around lines 144 - 187, The refresh method currently assigns
to self.error_message multiple times so later failures overwrite earlier ones;
update refresh (and the struct) to accumulate errors instead of replacing them:
add a persistent container (e.g., Vec<String> errors or an error_count +
last_error string) on the same struct that holds error_message, then change each
failure branch that sets self.error_message (the refresh_network call,
rpc.fetch_challenge_list, rpc.fetch_leaderboard, rpc.fetch_evaluation_progress)
to push/append the new error text (or increment count and update last_error)
rather than assign, and update any UI/consumers to display the accumulated
summary or "N errors: <last error>" accordingly. Ensure refresh_network and the
RPC failure match arms use the new accumulation API so no error can silently
overwrite a previous one.

140-142: scroll_down has no upper-bound check — user can scroll past all data.

saturating_add(1) increases the offset without limit. When the offset exceeds the number of items, the visible table will be empty. Consider clamping against the active tab's item count.

Proposed fix
 pub fn scroll_down(&mut self) {
-    self.scroll_offset = self.scroll_offset.saturating_add(1);
+    let max = match self.tab {
+        Tab::Leaderboard => self.leaderboard.len(),
+        Tab::Evaluation => self.evaluation_progress.len(),
+        _ => 0,
+    };
+    if self.scroll_offset < max.saturating_sub(1) {
+        self.scroll_offset = self.scroll_offset.saturating_add(1);
+    }
 }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@cli/src/app.rs` around lines 140 - 142, scroll_down currently increments
self.scroll_offset with saturating_add(1) and can move past available items;
change it to clamp against the active tab's item count by computing the current
item_count (e.g., via self.tabs[self.active_tab].items.len() or a helper like
active_tab_item_count()), handle the empty-case by setting scroll_offset to 0
when item_count == 0, otherwise compute max_offset =
item_count.saturating_sub(1) and set self.scroll_offset =
(self.scroll_offset.saturating_add(1)).min(max_offset) so the offset never
exceeds the last valid index.
wasm/src/lib.rs (1)

256-258: Replace magic number with agent_storage::MAX_AGENT_PACKAGE_SIZE to keep the limit in sync.

The literal 1_048_576 is the same value as pub const MAX_AGENT_PACKAGE_SIZE defined in agent_storage.rs. If the constant is ever updated, this check in validate() won't follow.

🛠️ Proposed fix
-        if submission.package_zip.len() > 1_048_576 {
+        if submission.package_zip.len() > agent_storage::MAX_AGENT_PACKAGE_SIZE {
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@wasm/src/lib.rs` around lines 256 - 258, In validate() the check using the
magic number 1_048_576 should use the shared constant to stay in sync; replace
the literal in the condition that reads submission.package_zip.len() > 1_048_576
with agent_storage::MAX_AGENT_PACKAGE_SIZE (or compare against that constant) so
the package size limit uses agent_storage::MAX_AGENT_PACKAGE_SIZE instead of a
hard-coded value.
wasm/src/types.rs (1)

108-115: total_size_bytes semantics are misleading — consider renaming or clarifying.

In lib.rs this field is populated by summing only output_preview.len() values (not task_id, scores, error, miner_hotkey, etc.), so it does not represent the actual serialised size of AgentLogs. Rename to something like total_preview_bytes or add a doc comment clarifying what it counts, to avoid confusing consumers (e.g., the CLI).

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@wasm/src/types.rs` around lines 108 - 115, The AgentLogs struct's field
total_size_bytes is misleading because lib.rs populates it by summing only each
entry's output_preview.len() rather than the serialized size of AgentLogs;
update this by either renaming the field to total_preview_bytes (and update all
references to AgentLogs::total_size_bytes in lib.rs and other modules to the new
name) or add a doc comment on AgentLogs::total_size_bytes clarifying it strictly
represents the sum of AgentLogEntry.output_preview lengths (not full serialized
size), and ensure the population site in lib.rs that sums output_preview.len()
is left consistent with the chosen name/comment.
wasm/src/agent_storage.rs (1)

59-65: get_agent_logs deserializes with unbounded default options — consider adding a size limit for consistency.

The rest of the codebase (lib.rs) uses bincode_options_*() builders with explicit .with_limit(...) for all deserialization. bincode::deserialize(&data) here uses the default unbounded decoder. Because the data was already bounded by MAX_LOG_SIZE at storage time the practical risk is low, but the inconsistency could catch a future reader off-guard.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@wasm/src/agent_storage.rs` around lines 59 - 65, The get_agent_logs function
currently calls bincode::deserialize(&data) with unbounded defaults; replace
that call with the project's bincode options builder used elsewhere (e.g., the
bincode_options_*() helper) and call .with_limit(...) using the same
MAX_LOG_SIZE used when storing logs so deserialization is bounded; update the
final expression to use something like
bincode_options_*().with_limit(MAX_LOG_SIZE).deserialize(&data).ok() to
deserialize AgentLogs consistently (function: get_agent_logs, types: AgentLogs,
helper: make_key).
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@AGENTS.md`:
- Around line 92-95: The keyboard docs list Esc and j/k but the input handling
in cli/src/main.rs only matches KeyCode::Char('q'), KeyCode::Up and
KeyCode::Down; update the key handler in main.rs (the match/handler that
currently checks KeyCode::Char('q'), KeyCode::Up, KeyCode::Down) to also handle
KeyCode::Esc as quit (same action as 'q') and KeyCode::Char('j') /
KeyCode::Char('k') mapping to the same actions as KeyCode::Down and KeyCode::Up
respectively so the AGENTS.md shortcuts are actually implemented.

In `@cli/src/main.rs`:
- Around line 47-52: The current flow calls run(&mut terminal, cli).await
producing result and then calls ratatui::try_restore()? which may propagate and
hide the original run() error; change this so that the restore result is ignored
or handled non-fatally (e.g., assign to let _ = ratatui::try_restore(); or match
and log the restore error) and then return the original result variable so any
Err from run() is always surfaced; update the code around the run/try_restore
calls (references: run, ratatui::try_restore, result, terminal, cli) to ensure
try_restore failure does not overwrite result.
- Around line 76-95: The match on key.code is missing handlers for KeyCode::Esc
and the vim-style keys KeyCode::Char('j') and KeyCode::Char('k') referenced in
AGENTS.md; update the match in the block handling input (the match key.code) to
add KeyCode::Esc => { app.should_quit = true; }, KeyCode::Char('j') =>
app.scroll_down(), and KeyCode::Char('k') => app.scroll_up() so they map to the
existing app.should_quit, app.scroll_down, and app.scroll_up behaviors.

In `@README.md`:
- Around line 143-144: The README entry for dataset.rs is inaccurate because
dataset.rs currently only contains placeholder comments; update the README.md
description for "dataset.rs" to indicate its placeholder status (e.g., change
"Dataset selection consensus" to "Dataset selection consensus (placeholder)") or
remove the dataset.rs line from the tree; locate the README.md listing that
references dataset.rs and edit the descriptive text accordingly so it matches
the current state of the dataset.rs file.
- Line 109: The README still advertises "P2P Dataset Consensus" even though the
related functions were removed from wasm/src/dataset.rs
(select_random_task_indices, store_my_selection, build_consensus_dataset,
create_dataset_selection); update the README to either remove this bullet or
mark it as planned/placeholder (e.g., "P2P Dataset Consensus (planned)"), and
ensure the copy matches current code state so readers aren't misled about
existing functionality.

In `@wasm/src/agent_storage.rs`:
- Around line 44-66: Remove the unnecessary #[allow(dead_code)] attributes from
the retrieval functions get_agent_code and get_agent_logs; these functions are
public and documented so the dead-code lint is not applicable, so simply delete
the #[allow(dead_code)] lines above each function (keeping the doc comments and
function bodies unchanged).
- Around line 68-73: The truncate_output function currently slices the &str with
output[..max_len] which can panic if max_len falls mid-UTF-8 character; update
truncate_output to compute a safe byte index <= max_len that is on a character
boundary (use str::is_char_boundary or iterate char_indices to find the last
valid boundary <= max_len) and then slice using that safe index (e.g.,
&output[..safe_len]) before converting to String; keep the original fast-path
when output.len() <= max_len and ensure the function returns a valid UTF-8
String without panicking.

In `@wasm/src/routes.rs`:
- Around line 13-18: Remove the unnecessary #[allow(dead_code)] attribute on the
get_route_definitions function: delete that attribute from the function
definition (leave the pub fn get_route_definitions(...) and its doc comment
intact) so the crate follows the rule against broad #[allow(dead_code)] usage;
if the function really is unused and you intend to keep it, consider marking it
pub(crate) or wiring up its registration, but do not add #[allow(dead_code)].

---

Outside diff comments:
In `@AGENTS.md`:
- Around line 9-34: The fenced code block that shows the project tree starting
with "term-challenge/" in AGENTS.md lacks a language specifier; add a language
tag (e.g., ```text) to the opening fence for that code block so the MD040 lint
warning is resolved and keep the closing ``` intact.

In `@wasm/src/scoring.rs`:
- Around line 82-102: Remove the unused apply_decay function and its
accompanying doc comment and #[allow(dead_code)] attribute from the file (the
function signature pub fn apply_decay(weight: f64, hours_since_top: f64, params:
&DecayParams) -> f64 and its body), and also remove any related unused imports
or references to DecayParams in this module if they become unused after
deletion; ensure no other code references apply_decay before committing.

---

Nitpick comments:
In `@cli/src/app.rs`:
- Around line 176-184: The error branch in the evaluation progress fetch
silently logs at debug level; update the Err(e) handling inside the match on
rpc.fetch_evaluation_progress(&hotkey) so it both surfaces the error to the user
(e.g., set self.error_message = Some(format!(...)) or call the existing error
display helper) and logs it at warn/error level (use tracing::warn! or
tracing::error! instead of tracing::debug!); modify the block that currently
references self.evaluation_progress and tracing::debug! so failures update
user-facing state and produce a higher-severity log.
- Around line 144-187: The refresh method currently assigns to
self.error_message multiple times so later failures overwrite earlier ones;
update refresh (and the struct) to accumulate errors instead of replacing them:
add a persistent container (e.g., Vec<String> errors or an error_count +
last_error string) on the same struct that holds error_message, then change each
failure branch that sets self.error_message (the refresh_network call,
rpc.fetch_challenge_list, rpc.fetch_leaderboard, rpc.fetch_evaluation_progress)
to push/append the new error text (or increment count and update last_error)
rather than assign, and update any UI/consumers to display the accumulated
summary or "N errors: <last error>" accordingly. Ensure refresh_network and the
RPC failure match arms use the new accumulation API so no error can silently
overwrite a previous one.
- Around line 140-142: scroll_down currently increments self.scroll_offset with
saturating_add(1) and can move past available items; change it to clamp against
the active tab's item count by computing the current item_count (e.g., via
self.tabs[self.active_tab].items.len() or a helper like
active_tab_item_count()), handle the empty-case by setting scroll_offset to 0
when item_count == 0, otherwise compute max_offset =
item_count.saturating_sub(1) and set self.scroll_offset =
(self.scroll_offset.saturating_add(1)).min(max_offset) so the offset never
exceeds the last valid index.

In `@cli/src/rpc.rs`:
- Around line 92-99: RpcClient::new currently constructs a reqwest client with
Client::new() which has no timeouts; update RpcClient::new to build a
reqwest::Client with sensible connect and overall request timeouts (e.g. via
reqwest::Client::builder().connect_timeout(Duration::from_secs(...)).timeout(Duration::from_secs(...)).build()),
ensuring you import std::time::Duration; keep the AtomicU64 initialization and
other fields unchanged so RpcClient::new still returns Self with url, client,
and request_id.

In `@cli/src/ui.rs`:
- Around line 247-309: In draw_network, avoid duplicating Paragraph/Block
rendering by building the base `lines` vector then conditionally appending the
challenge line when `app.challenge_id` is Some (use the existing `cid`), and
finally create a single `Paragraph::new(...)` with the
`Block::default().borders(Borders::ALL).title("Network Status")` and call
`frame.render_widget` once; reference symbols: function draw_network, variable
lines, optional app.challenge_id (cid), Paragraph, Block, frame.render_widget.
- Around line 348-354: The function truncate_hotkey uses byte slicing
(&hotkey[..max_len]) which will panic on UTF-8 multi-byte boundaries; update
truncate_hotkey to operate on Unicode scalar values instead: count characters
with hotkey.chars(), if the character count is greater than max_len build a
truncated string with hotkey.chars().take(max_len).collect::<String>() and
append "..." otherwise return hotkey.to_string(); ensure you use the function
name truncate_hotkey and the hotkey/max_len parameters so the change is
localized and preserves existing behavior.

In `@wasm/src/agent_storage.rs`:
- Around line 59-65: The get_agent_logs function currently calls
bincode::deserialize(&data) with unbounded defaults; replace that call with the
project's bincode options builder used elsewhere (e.g., the bincode_options_*()
helper) and call .with_limit(...) using the same MAX_LOG_SIZE used when storing
logs so deserialization is bounded; update the final expression to use something
like bincode_options_*().with_limit(MAX_LOG_SIZE).deserialize(&data).ok() to
deserialize AgentLogs consistently (function: get_agent_logs, types: AgentLogs,
helper: make_key).

In `@wasm/src/dataset.rs`:
- Around line 1-10: This module (wasm/src/dataset.rs) contains only explanatory
comments and should be removed to reduce noise; either delete the file from the
repo or replace it with a single-line module comment that references a tracking
issue or AGENTS.md for future P2P dataset consensus work (mentioning the
intended features: host_random_seed selection, host_storage_set validator
storage, >50% consensus, and dataset serialization/hash verification) so the
intent is preserved without an empty placeholder.

In `@wasm/src/lib.rs`:
- Around line 256-258: In validate() the check using the magic number 1_048_576
should use the shared constant to stay in sync; replace the literal in the
condition that reads submission.package_zip.len() > 1_048_576 with
agent_storage::MAX_AGENT_PACKAGE_SIZE (or compare against that constant) so the
package size limit uses agent_storage::MAX_AGENT_PACKAGE_SIZE instead of a
hard-coded value.

In `@wasm/src/types.rs`:
- Around line 108-115: The AgentLogs struct's field total_size_bytes is
misleading because lib.rs populates it by summing only each entry's
output_preview.len() rather than the serialized size of AgentLogs; update this
by either renaming the field to total_preview_bytes (and update all references
to AgentLogs::total_size_bytes in lib.rs and other modules to the new name) or
add a doc comment on AgentLogs::total_size_bytes clarifying it strictly
represents the sum of AgentLogEntry.output_preview lengths (not full serialized
size), and ensure the population site in lib.rs that sums output_preview.len()
is left consistent with the chosen name/comment.

Comment on lines +92 to +95
| `Tab` / `Shift+Tab` | Switch between tabs |
| `↑` / `↓` / `j` / `k` | Navigate rows |
| `r` | Refresh data |
| `q` / `Esc` | Quit |
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Keyboard shortcut table documents Esc and j/k that are not implemented.

The table lists q / Esc for quit and / / j / k for navigation, but cli/src/main.rs only handles KeyCode::Char('q'), KeyCode::Up, and KeyCode::Down. Either add the handlers in main.rs (suggested fix in the main.rs review comment) or remove the undocumented shortcuts here.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@AGENTS.md` around lines 92 - 95, The keyboard docs list Esc and j/k but the
input handling in cli/src/main.rs only matches KeyCode::Char('q'), KeyCode::Up
and KeyCode::Down; update the key handler in main.rs (the match/handler that
currently checks KeyCode::Char('q'), KeyCode::Up, KeyCode::Down) to also handle
KeyCode::Esc as quit (same action as 'q') and KeyCode::Char('j') /
KeyCode::Char('k') mapping to the same actions as KeyCode::Down and KeyCode::Up
respectively so the AGENTS.md shortcuts are actually implemented.

Comment on lines +47 to +52

let result = run(&mut terminal, cli).await;

ratatui::try_restore()?;

result
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Original run() error is silently dropped if try_restore() also fails.

If run() returns an Err and try_restore() subsequently also errors, the ? on try_restore() propagates the restore error while discarding the root cause from run(). Consider ignoring the restore result so the run() error is always surfaced:

🛠️ Proposed fix
 let result = run(&mut terminal, cli).await;
-ratatui::try_restore()?;
+let _ = ratatui::try_restore();
 result
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
let result = run(&mut terminal, cli).await;
ratatui::try_restore()?;
result
let result = run(&mut terminal, cli).await;
let _ = ratatui::try_restore();
result
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@cli/src/main.rs` around lines 47 - 52, The current flow calls run(&mut
terminal, cli).await producing result and then calls ratatui::try_restore()?
which may propagate and hide the original run() error; change this so that the
restore result is ignored or handled non-fatally (e.g., assign to let _ =
ratatui::try_restore(); or match and log the restore error) and then return the
original result variable so any Err from run() is always surfaced; update the
code around the run/try_restore calls (references: run, ratatui::try_restore,
result, terminal, cli) to ensure try_restore failure does not overwrite result.

Comment on lines +76 to +95
match key.code {
KeyCode::Char('q') => {
app.should_quit = true;
}
KeyCode::Tab => app.next_tab(),
KeyCode::BackTab => app.prev_tab(),
KeyCode::Up => app.scroll_up(),
KeyCode::Down => app.scroll_down(),
KeyCode::Char('r') => {
app.refresh(&rpc).await;
last_tick = Instant::now();
}
_ => {}
}
}
}
}

if app.should_quit {
break;
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Esc and j/k shortcuts are documented in AGENTS.md but not handled here.

AGENTS.md (lines 93–95) lists Esc as a quit key and j/k as scroll keys, but neither KeyCode::Esc, KeyCode::Char('j'), nor KeyCode::Char('k') appear in the match arm. Either add the missing handlers or update the documentation.

🛠️ Proposed fix (add missing handlers)
 KeyCode::Down => app.scroll_down(),
+KeyCode::Char('j') => app.scroll_down(),
+KeyCode::Char('k') => app.scroll_up(),
 KeyCode::Char('r') => {
     app.refresh(&rpc).await;
     last_tick = Instant::now();
 }
+KeyCode::Esc => {
+    app.should_quit = true;
+}
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
match key.code {
KeyCode::Char('q') => {
app.should_quit = true;
}
KeyCode::Tab => app.next_tab(),
KeyCode::BackTab => app.prev_tab(),
KeyCode::Up => app.scroll_up(),
KeyCode::Down => app.scroll_down(),
KeyCode::Char('r') => {
app.refresh(&rpc).await;
last_tick = Instant::now();
}
_ => {}
}
}
}
}
if app.should_quit {
break;
match key.code {
KeyCode::Char('q') => {
app.should_quit = true;
}
KeyCode::Tab => app.next_tab(),
KeyCode::BackTab => app.prev_tab(),
KeyCode::Up => app.scroll_up(),
KeyCode::Down => app.scroll_down(),
KeyCode::Char('j') => app.scroll_down(),
KeyCode::Char('k') => app.scroll_up(),
KeyCode::Char('r') => {
app.refresh(&rpc).await;
last_tick = Instant::now();
}
KeyCode::Esc => {
app.should_quit = true;
}
_ => {}
}
}
}
}
if app.should_quit {
break;
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@cli/src/main.rs` around lines 76 - 95, The match on key.code is missing
handlers for KeyCode::Esc and the vim-style keys KeyCode::Char('j') and
KeyCode::Char('k') referenced in AGENTS.md; update the match in the block
handling input (the match key.code) to add KeyCode::Esc => { app.should_quit =
true; }, KeyCode::Char('j') => app.scroll_down(), and KeyCode::Char('k') =>
app.scroll_up() so they map to the existing app.should_quit, app.scroll_down,
and app.scroll_up behaviors.

@@ -20,6 +108,11 @@ Term Challenge is a WASM evaluation module for AI agents on the Bittensor networ
- **Top Agent Decay**: 72h grace period, 50% daily decay to 0 weight
- **P2P Dataset Consensus**: Validators collectively select 50 evaluation tasks
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Stale feature claim: "P2P Dataset Consensus" is listed but the functionality was removed.

wasm/src/dataset.rs now only contains placeholder comments — the consensus functions (select_random_task_indices, store_my_selection, build_consensus_dataset, create_dataset_selection) were all removed. This line should either be removed or reworded (e.g., "P2P Dataset Consensus (planned)") to avoid misleading readers.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@README.md` at line 109, The README still advertises "P2P Dataset Consensus"
even though the related functions were removed from wasm/src/dataset.rs
(select_random_task_indices, store_my_selection, build_consensus_dataset,
create_dataset_selection); update the README to either remove this bullet or
mark it as planned/placeholder (e.g., "P2P Dataset Consensus (planned)"), and
ensure the copy matches current code state so readers aren't misled about
existing functionality.

Comment on lines +143 to +144
│ ├── tasks.rs # Active dataset management
│ ├── dataset.rs # Dataset selection consensus
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

dataset.rs description is misleading.

Line 144 describes dataset.rs as "Dataset selection consensus," but the file now only contains placeholder comments and no functional code. Consider updating the description to reflect its current state (e.g., "Dataset selection consensus (placeholder)") or removing it from the tree.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@README.md` around lines 143 - 144, The README entry for dataset.rs is
inaccurate because dataset.rs currently only contains placeholder comments;
update the README.md description for "dataset.rs" to indicate its placeholder
status (e.g., change "Dataset selection consensus" to "Dataset selection
consensus (placeholder)") or remove the dataset.rs line from the tree; locate
the README.md listing that references dataset.rs and edit the descriptive text
accordingly so it matches the current state of the dataset.rs file.

Comment on lines +44 to +66
/// Retrieve stored agent code for a miner/epoch.
/// Called by platform-v2's challenge route handler for `/agent/:hotkey/code` requests.
#[allow(dead_code)]
pub fn get_agent_code(miner_hotkey: &str, epoch: u64) -> Option<Vec<u8>> {
let key = make_key(b"agent_code:", miner_hotkey, epoch);
let data = host_storage_get(&key).ok()?;
if data.is_empty() {
return None;
}
Some(data)
}

/// Retrieve stored agent logs for a miner/epoch.
/// Called by platform-v2's challenge route handler for `/agent/:hotkey/logs` requests.
#[allow(dead_code)]
pub fn get_agent_logs(miner_hotkey: &str, epoch: u64) -> Option<AgentLogs> {
let key = make_key(b"agent_logs:", miner_hotkey, epoch);
let data = host_storage_get(&key).ok()?;
if data.is_empty() {
return None;
}
bincode::deserialize(&data).ok()
}
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

#[allow(dead_code)] on retrieval functions violates the coding guidelines.

Both get_agent_code and get_agent_logs are annotated with #[allow(dead_code)] pending route-handler integration. The project rule is explicit: "Do NOT add #[allow(dead_code)]. Fix unused code or remove it." Since these functions are pub, the Rust dead-code lint won't fire for them in a library crate anyway, making the attribute doubly unnecessary.

Remove the attributes; the doc comments already document the intended future use.

🛠️ Proposed fix
 /// Retrieve stored agent code for a miner/epoch.
 /// Called by platform-v2's challenge route handler for `/agent/:hotkey/code` requests.
-#[allow(dead_code)]
 pub fn get_agent_code(miner_hotkey: &str, epoch: u64) -> Option<Vec<u8>> {
 /// Retrieve stored agent logs for a miner/epoch.
 /// Called by platform-v2's challenge route handler for `/agent/:hotkey/logs` requests.
-#[allow(dead_code)]
 pub fn get_agent_logs(miner_hotkey: &str, epoch: u64) -> Option<AgentLogs> {

As per coding guidelines: "Do NOT add #[allow(dead_code)] broadly. Fix unused code or remove it."

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
/// Retrieve stored agent code for a miner/epoch.
/// Called by platform-v2's challenge route handler for `/agent/:hotkey/code` requests.
#[allow(dead_code)]
pub fn get_agent_code(miner_hotkey: &str, epoch: u64) -> Option<Vec<u8>> {
let key = make_key(b"agent_code:", miner_hotkey, epoch);
let data = host_storage_get(&key).ok()?;
if data.is_empty() {
return None;
}
Some(data)
}
/// Retrieve stored agent logs for a miner/epoch.
/// Called by platform-v2's challenge route handler for `/agent/:hotkey/logs` requests.
#[allow(dead_code)]
pub fn get_agent_logs(miner_hotkey: &str, epoch: u64) -> Option<AgentLogs> {
let key = make_key(b"agent_logs:", miner_hotkey, epoch);
let data = host_storage_get(&key).ok()?;
if data.is_empty() {
return None;
}
bincode::deserialize(&data).ok()
}
/// Retrieve stored agent code for a miner/epoch.
/// Called by platform-v2's challenge route handler for `/agent/:hotkey/code` requests.
pub fn get_agent_code(miner_hotkey: &str, epoch: u64) -> Option<Vec<u8>> {
let key = make_key(b"agent_code:", miner_hotkey, epoch);
let data = host_storage_get(&key).ok()?;
if data.is_empty() {
return None;
}
Some(data)
}
/// Retrieve stored agent logs for a miner/epoch.
/// Called by platform-v2's challenge route handler for `/agent/:hotkey/logs` requests.
pub fn get_agent_logs(miner_hotkey: &str, epoch: u64) -> Option<AgentLogs> {
let key = make_key(b"agent_logs:", miner_hotkey, epoch);
let data = host_storage_get(&key).ok()?;
if data.is_empty() {
return None;
}
bincode::deserialize(&data).ok()
}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@wasm/src/agent_storage.rs` around lines 44 - 66, Remove the unnecessary
#[allow(dead_code)] attributes from the retrieval functions get_agent_code and
get_agent_logs; these functions are public and documented so the dead-code lint
is not applicable, so simply delete the #[allow(dead_code)] lines above each
function (keeping the doc comments and function bodies unchanged).

Comment on lines +68 to +73
pub fn truncate_output(output: &str, max_len: usize) -> String {
if output.len() <= max_len {
return String::from(output);
}
let truncated = &output[..max_len];
String::from(truncated)
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

Critical: output[..max_len] will panic if max_len falls mid-UTF-8 character.

Direct byte-indexing into a &str panics when the index is not on a character boundary. Agent outputs may contain non-ASCII text (error messages, stack traces, unicode identifiers), and with MAX_TASK_OUTPUT_PREVIEW = 4_096 bytes any multi-byte character straddling byte 4 096 will cause a WASM trap during evaluate(), losing the miner's score entirely.

str::is_char_boundary is available in no_std (it is a core function) and fixes this cleanly:

🐛 Proposed fix
 pub fn truncate_output(output: &str, max_len: usize) -> String {
     if output.len() <= max_len {
         return String::from(output);
     }
-    let truncated = &output[..max_len];
-    String::from(truncated)
+    // Walk back to the nearest valid UTF-8 char boundary
+    let mut end = max_len;
+    while end > 0 && !output.is_char_boundary(end) {
+        end -= 1;
+    }
+    String::from(&output[..end])
 }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
pub fn truncate_output(output: &str, max_len: usize) -> String {
if output.len() <= max_len {
return String::from(output);
}
let truncated = &output[..max_len];
String::from(truncated)
pub fn truncate_output(output: &str, max_len: usize) -> String {
if output.len() <= max_len {
return String::from(output);
}
// Walk back to the nearest valid UTF-8 char boundary
let mut end = max_len;
while end > 0 && !output.is_char_boundary(end) {
end -= 1;
}
String::from(&output[..end])
}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@wasm/src/agent_storage.rs` around lines 68 - 73, The truncate_output function
currently slices the &str with output[..max_len] which can panic if max_len
falls mid-UTF-8 character; update truncate_output to compute a safe byte index
<= max_len that is on a character boundary (use str::is_char_boundary or iterate
char_indices to find the last valid boundary <= max_len) and then slice using
that safe index (e.g., &output[..safe_len]) before converting to String; keep
the original fast-path when output.len() <= max_len and ensure the function
returns a valid UTF-8 String without panicking.

Comment on lines +13 to +18
/// Returns route definitions for the term-challenge module.
///
/// Note: This function is currently unused pending integration with platform-v2's
/// challenge route registration system. The routes are defined here for documentation
/// and future automatic registration.
#[allow(dead_code)]
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

#[allow(dead_code)] violates the coding guidelines — remove it.

The doc comment already explains why the function is currently unused. Since get_route_definitions is already pub, the dead_code lint won't fire for it in a normal library crate. Adding #[allow(dead_code)] is unnecessary here and contradicts the project rule "Do NOT add #[allow(dead_code)]. Fix unused code or remove it."

🛠️ Proposed fix
-/// Note: This function is currently unused pending integration with platform-v2's
-/// challenge route registration system. The routes are defined here for documentation
-/// and future automatic registration.
-#[allow(dead_code)]
 pub fn get_route_definitions() -> Vec<RouteDefinition> {

As per coding guidelines: "Do NOT add #[allow(dead_code)] broadly. Fix unused code or remove it."

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
/// Returns route definitions for the term-challenge module.
///
/// Note: This function is currently unused pending integration with platform-v2's
/// challenge route registration system. The routes are defined here for documentation
/// and future automatic registration.
#[allow(dead_code)]
/// Returns route definitions for the term-challenge module.
///
/// Note: This function is currently unused pending integration with platform-v2's
/// challenge route registration system. The routes are defined here for documentation
/// and future automatic registration.
pub fn get_route_definitions() -> Vec<RouteDefinition> {
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@wasm/src/routes.rs` around lines 13 - 18, Remove the unnecessary
#[allow(dead_code)] attribute on the get_route_definitions function: delete that
attribute from the function definition (leave the pub fn
get_route_definitions(...) and its doc comment intact) so the crate follows the
rule against broad #[allow(dead_code)] usage; if the function really is unused
and you intend to keep it, consider marking it pub(crate) or wiring up its
registration, but do not add #[allow(dead_code)].

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