Skip to content

feat(tui): expand restore snapshot listing#2513

Closed
cyq1017 wants to merge 2 commits into
Hmbown:mainfrom
cyq1017:codex/2494-restore-list
Closed

feat(tui): expand restore snapshot listing#2513
cyq1017 wants to merge 2 commits into
Hmbown:mainfrom
cyq1017:codex/2494-restore-list

Conversation

@cyq1017
Copy link
Copy Markdown
Contributor

@cyq1017 cyq1017 commented Jun 1, 2026

Refs #2494

Problem

The /restore list can be too shallow when a user has many recent checkpoints, and the entries do not show when a checkpoint was created.

Change

  • make /restore show the 20 most recent snapshots by default
  • add /restore list [N], capped at 100, to inspect more rollback points
  • include each snapshot's UTC timestamp in the listing
  • allow /restore <N> to target entries beyond the default list size

Verification

  • cargo test -p codewhale-tui restore_ --all-features --locked -- --nocapture
  • cargo fmt --all -- --check
  • cargo check -p codewhale-tui --all-features --locked
  • cargo clippy -p codewhale-tui --all-features --locked -- -D warnings
  • git diff --check origin/main..HEAD

Greptile Summary

This PR expands the /restore command by bumping the default listing from 10 to 20 snapshots, adding a /restore list [N] subcommand (capped at 100), and including UTC timestamps in each listing row.

  • Expanded listing & new subcommand: DEFAULT_LIST_LIMIT raised to 20; parse_list_arg routes list [N] through a separate fetch path capped at MAX_LIST_LIMIT = 100, while plain numeric args are guarded by a new MAX_RESTORE_INDEX = 1000 ceiling.
  • Timestamp display: format_snapshot_time converts the stored Unix timestamp via chrono::Utc and formats it as YYYY-MM-DD HH:MM UTC, with \"unknown time\" as a safe fallback for invalid values.
  • Tests: Six new integration tests cover the expanded default count, timestamp rendering, explicit-limit filtering, invalid-limit rejection, out-of-default-range restoration, and the new upper-bound guard.

Confidence Score: 5/5

Safe to merge — all changed paths are additive and well-tested; the only rough edge is a silent truncation of oversized list requests.

The restore command logic is straightforward and all new branches are exercised by the six added tests. Snapshot fetching, timestamp formatting, and the trust-mode gate are unchanged. The silent capping of /restore list N when N > 100 is a UX nit rather than a data-correctness problem.

crates/tui/src/commands/restore.rs — specifically the silent cap in parse_list_arg

Important Files Changed

Filename Overview
crates/tui/src/commands/restore.rs Core logic for the expanded restore listing: adds DEFAULT_LIST_LIMIT/MAX_LIST_LIMIT/MAX_RESTORE_INDEX constants, parse_list_arg() for the new list subcommand, UTC timestamp formatting, and comprehensive tests. One minor issue: limits exceeding MAX_LIST_LIMIT are silently capped without user feedback.
crates/tui/src/commands/mod.rs Usage string updated from /restore [N] to `/restore [N
docs/MODES.md Documentation updated to mention /restore list [N] alongside the existing restore description. Accurate and concise.

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart TD
    A["/restore [arg]"] --> B{arg present?}
    B -- No --> C["repo.list(DEFAULT_LIST_LIMIT=20)"]
    C --> D{empty?}
    D -- Yes --> E["'No snapshots yet'"]
    D -- No --> F["format_listing()"]

    B -- Yes --> G["parse_list_arg(arg)"]
    G -- "Err" --> H["CommandResult::error"]
    G -- "Ok(Some(limit))" --> I["repo.list(limit ≤ 100)"]
    I --> D2{empty?}
    D2 -- Yes --> E
    D2 -- No --> F

    G -- "Ok(None)" --> J["parse arg as usize N"]
    J -- "0 or parse fail" --> K["Usage error"]
    J -- "N > MAX_RESTORE_INDEX(1000)" --> L["'index must be ≤ 1000' error"]
    J -- "1 ≤ N ≤ 1000" --> M["repo.list(max(N, 20))"]
    M --> N{empty?}
    N -- Yes --> E
    N -- No --> O{N > snapshots.len?}
    O -- Yes --> P["'Only X available' error"]
    O -- No --> Q{yolo or trust_mode?}
    Q -- No --> R["'Refusing to restore' message"]
    Q -- Yes --> S["repo.restore(target.id)"]
    S -- Err --> T["'Restore failed' error"]
    S -- Ok --> U["'Restored snapshot #N' message"]
Loading

Fix All in Codex Fix All in Claude Code Fix All in Cursor

Reviews (2): Last reviewed commit: "fix(tui): bound restore snapshot lookup" | Re-trigger Greptile

Copy link
Copy Markdown
Contributor

@gemini-code-assist gemini-code-assist Bot left a comment

Choose a reason for hiding this comment

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

Code Review

This pull request enhances the /restore command by increasing the default listing limit from 10 to 20 snapshots, introducing a /restore list [N] subcommand to list up to 100 snapshots, and displaying UTC timestamps for each snapshot. It also updates the documentation and adds corresponding unit tests. The reviewer suggested capping the query limit to 1000 when restoring by a numeric index to prevent potential failures with extremely large inputs.

));
}
};
let snapshots = match repo.list(n.max(DEFAULT_LIST_LIMIT)) {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

medium

If n is extremely large (e.g., close to usize::MAX), passing it directly to repo.list can cause git log to fail or overflow its internal parser for --max-count. Since the snapshot repository is capped and typically has far fewer than 1000 entries, we can safely cap the query limit at 1000 using n.min(1000). If n is larger than 1000, the check n > snapshots.len() will still correctly fail and report the actual number of available snapshots.

Suggested change
let snapshots = match repo.list(n.max(DEFAULT_LIST_LIMIT)) {
let snapshots = match repo.list(n.min(1000).max(DEFAULT_LIST_LIMIT)) {

Comment on lines +139 to +141
let mut out = String::from(
"Recent snapshots (newest first; pass /restore <N> to revert; /restore list 50 shows more):\n",
);
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

P2 The listing header is hardcoded to suggest /restore list 50 shows more regardless of how the listing was invoked. When a user runs /restore list 100 (the maximum), the hint implies more entries exist beyond the 100 they already fetched, which is misleading. Consider omitting the hint — or making it conditional on whether the returned count equals the requested limit.

Suggested change
let mut out = String::from(
"Recent snapshots (newest first; pass /restore <N> to revert; /restore list 50 shows more):\n",
);
let mut out = String::from(
"Recent snapshots (newest first; pass /restore <N> to revert; /restore list [N] shows more, up to 100):\n",
);

Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!

Fix in Codex Fix in Claude Code Fix in Cursor

Comment on lines +113 to +115
let Some(value) = parts.next() else {
return Ok(Some(DEFAULT_LIST_LIMIT));
};
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

P2 /restore list with no N falls through to Ok(Some(DEFAULT_LIST_LIMIT)), so it returns the same 20 entries as a plain /restore. A user who types /restore list expecting to see more snapshots will see identical output and no indication of why. Defaulting to a higher count (e.g. MAX_LIST_LIMIT) when list is given without a number would match the subcommand's stated purpose of revealing more rollback points.

Suggested change
let Some(value) = parts.next() else {
return Ok(Some(DEFAULT_LIST_LIMIT));
};
let Some(value) = parts.next() else {
return Ok(Some(MAX_LIST_LIMIT));
};

Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!

Fix in Codex Fix in Claude Code Fix in Cursor

@Hmbown
Copy link
Copy Markdown
Owner

Hmbown commented Jun 5, 2026

Thanks @cyq1017 — the restore snapshot listing slice was harvested into the public v0.9.0 integration branch (codex/v0.9.0-stewardship) as 311eb4002.

The landed version adds /restore list [N], keeps plain /restore at the 20 most recent snapshots, and adds explicit rejection for list requests above the bounded visible cap rather than silently truncating.

Verification recorded for the harvest: cargo test -p codewhale-tui --locked restore_, cargo fmt --all -- --check, and cargo clippy -p codewhale-tui --locked -- -D warnings. The commit includes Harvested from PR #2513 by @cyq1017 and your GitHub-mappable co-author trailer.

Closing this PR as harvested into the integration branch. Issue #2494 remains open because this landed only the restore-listing slice, not the full rollback UX report.

@Hmbown Hmbown closed this Jun 5, 2026
Hmbown added a commit that referenced this pull request Jun 5, 2026
Update the execution map after closing harvested or superseded PRs #2476, #2498, #2502, #2513, #2530, #2576, #2581, #2636, #2639, #2640, #2708, and #2730, and refresh the live PR count.
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.

2 participants