feat: show refractor progress in post-game summary embed (#147) #160

Merged
cal merged 1 commits from issue/147-feat-show-refractor-progress-in-post-game-summary into main 2026-04-08 23:21:59 +00:00
Collaborator

Closes #147

Summary

Adds a Refractor Progress field to the post-game summary embed so players can see card progression without hunting through /refractor status.

  • Tier-ups (⬆ Name → Tier Name): pulled directly from the evaluate-game response already captured during post-game processing
  • Cards close to next tier (◈ Name (pct%)): fetched via refractor/cards?progress=close for both teams; capped at 5 entries; failures are non-fatal
  • Field is omitted entirely when there is nothing to show (no tier-ups, no near-threshold cards)

Files Changed

  • command_logic/logic_gameplay.py
    • _run_post_game_refractor_hook — now returns evo_result | None (was None)
    • _build_refractor_progress_text — new async helper that formats the field value
    • complete_game — captures hook return value and conditionally adds the field
    • Import: TIER_NAMES from helpers.refractor_constants
  • tests/test_refractor_progress_embed.py — 11 new tests covering tier-up formatting, near-threshold display, API failure resilience, cap logic, and hook return value

Test Results

104 refractor-related tests pass (13 pre-existing + 11 new + existing suite).

Other Observations

  • The progress=close API filter is server-side (≥80%) — no client-side threshold math needed
  • Near-threshold section makes 2 extra API calls per game completion; both are fire-and-forget within the already-async embed build path
Closes #147 ## Summary Adds a **Refractor Progress** field to the post-game summary embed so players can see card progression without hunting through `/refractor status`. - **Tier-ups** (⬆ **Name** → Tier Name): pulled directly from the `evaluate-game` response already captured during post-game processing - **Cards close to next tier** (◈ Name (pct%)): fetched via `refractor/cards?progress=close` for both teams; capped at 5 entries; failures are non-fatal - Field is **omitted entirely** when there is nothing to show (no tier-ups, no near-threshold cards) ## Files Changed - `command_logic/logic_gameplay.py` - `_run_post_game_refractor_hook` — now returns `evo_result | None` (was `None`) - `_build_refractor_progress_text` — new async helper that formats the field value - `complete_game` — captures hook return value and conditionally adds the field - Import: `TIER_NAMES` from `helpers.refractor_constants` - `tests/test_refractor_progress_embed.py` — 11 new tests covering tier-up formatting, near-threshold display, API failure resilience, cap logic, and hook return value ## Test Results 104 refractor-related tests pass (13 pre-existing + 11 new + existing suite). ## Other Observations - The `progress=close` API filter is server-side (≥80%) — no client-side threshold math needed - Near-threshold section makes 2 extra API calls per game completion; both are fire-and-forget within the already-async embed build path
Claude added 1 commit 2026-04-08 18:36:52 +00:00
feat: show refractor progress in post-game summary embed (#147)
All checks were successful
Ruff Lint / lint (pull_request) Successful in 24s
39424f7157
Closes #147

Adds a "Refractor Progress" field to the game summary embed showing:
- Cards that tiered up during this game (⬆ Name → Tier Name)
- Cards currently ≥80% toward their next tier on either team (◈ Name (pct%))

The field is omitted entirely when there is nothing to show.

Implementation:
- _run_post_game_refractor_hook() now returns evo_result (or None on failure)
- New _build_refractor_progress_text() fetches close-to-tier cards from both
  teams via refractor/cards?progress=close and formats the combined output
- complete_game() adds the field between Rewards and Highlights sections

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Claude added the
ai-reviewing
label 2026-04-08 18:45:50 +00:00
Claude reviewed 2026-04-08 18:48:27 +00:00
Claude left a comment
Author
Collaborator

AI Code Review

Files Reviewed

  • command_logic/logic_gameplay.py (modified)
  • tests/test_refractor_progress_embed.py (added)

Findings

Correctness

No issues found.

  • _run_post_game_refractor_hook return value change is clean: return evo_result on the success path, return None in the except block. All existing callers that ignored the return value are unaffected.
  • _build_refractor_progress_text: tier-up formatting is correct — TIER_NAMES.get(new_tier, f"T{new_tier}") provides a safe fallback for unknown tiers.
  • int(card.get("next_threshold") or 0) handles None, "", and 0 cleanly; the if next_threshold: guard prevents division-by-zero.
  • int(card.get("current_value", 0)) uses default 0 only when the key is absent; a present-but-None value would raise inside the try/except, gracefully dropping the close-cards section — consistent with the non-fatal design.
  • evo_result is captured before session.delete(this_play) and used later for read-only API calls; no session-state issues.
  • Embed field insertion between Rewards and Highlights is appropriate.

Security

No issues found. All data originates from internal API responses; no user input reaches the formatting or API calls.

Style & Conventions

  • Follows the project's non-fatal try/except pattern (silent except Exception: pass for best-effort enrichment is appropriate here, consistent with the badge-lookup precedent).
  • Type annotations (dict | None, str | None) match the actual return paths.
  • Docstrings on both new functions are informative and match the pattern used in _trigger_variant_renders.

Suggestions

  • Cap fairness: close_lines[:5] slices after accumulating cards from both teams. Since the server-side limit=5 applies per-team call, the winning team can claim all 5 slots if it has enough near-threshold cards, leaving the losing team unrepresented. The current behavior is consistent with the PR description ("capped at 5 entries") so this is not a correctness issue — just worth noting if the product intent is balanced representation.
  • Silent exception scope in close-cards loop: a single malformed card value (e.g. a present-but-None current_value) raises and drops the entire close-cards section for both teams. Per-card try/except would be more resilient, but the current all-or-nothing approach matches the non-fatal philosophy used elsewhere in the file.
  • test_close_cards_capped_at_five validates the cap with 10 cards per team (20 total → sliced to 5). The <= 5 assertion is sufficient for the stated goal.

Verdict: APPROVED

Clean implementation. Logic is sound, error handling is non-fatal throughout, TIER_NAMES is correctly imported from the shared constants module (PR #155), and the 11 new tests cover all key paths including API failure resilience and the hook return-value contract. No correctness or security issues.


Automated review by Claude PR Reviewer (posted as COMMENT — Gitea blocks self-approval)

## AI Code Review ### Files Reviewed - `command_logic/logic_gameplay.py` (modified) - `tests/test_refractor_progress_embed.py` (added) ### Findings #### Correctness No issues found. - `_run_post_game_refractor_hook` return value change is clean: `return evo_result` on the success path, `return None` in the `except` block. All existing callers that ignored the return value are unaffected. - `_build_refractor_progress_text`: tier-up formatting is correct — `TIER_NAMES.get(new_tier, f"T{new_tier}")` provides a safe fallback for unknown tiers. - `int(card.get("next_threshold") or 0)` handles `None`, `""`, and `0` cleanly; the `if next_threshold:` guard prevents division-by-zero. - `int(card.get("current_value", 0))` uses default `0` only when the key is absent; a present-but-`None` value would raise inside the try/except, gracefully dropping the close-cards section — consistent with the non-fatal design. - `evo_result` is captured before `session.delete(this_play)` and used later for read-only API calls; no session-state issues. - Embed field insertion between Rewards and Highlights is appropriate. #### Security No issues found. All data originates from internal API responses; no user input reaches the formatting or API calls. #### Style & Conventions - Follows the project's non-fatal try/except pattern (silent `except Exception: pass` for best-effort enrichment is appropriate here, consistent with the badge-lookup precedent). - Type annotations (`dict | None`, `str | None`) match the actual return paths. - Docstrings on both new functions are informative and match the pattern used in `_trigger_variant_renders`. #### Suggestions - **Cap fairness**: `close_lines[:5]` slices after accumulating cards from both teams. Since the server-side `limit=5` applies per-team call, the winning team can claim all 5 slots if it has enough near-threshold cards, leaving the losing team unrepresented. The current behavior is consistent with the PR description ("capped at 5 entries") so this is not a correctness issue — just worth noting if the product intent is balanced representation. - **Silent exception scope in close-cards loop**: a single malformed card value (e.g. a present-but-`None` `current_value`) raises and drops the entire close-cards section for both teams. Per-card try/except would be more resilient, but the current all-or-nothing approach matches the non-fatal philosophy used elsewhere in the file. - `test_close_cards_capped_at_five` validates the cap with 10 cards per team (20 total → sliced to 5). The `<= 5` assertion is sufficient for the stated goal. ### Verdict: APPROVED Clean implementation. Logic is sound, error handling is non-fatal throughout, `TIER_NAMES` is correctly imported from the shared constants module (PR #155), and the 11 new tests cover all key paths including API failure resilience and the hook return-value contract. No correctness or security issues. --- *Automated review by Claude PR Reviewer (posted as COMMENT — Gitea blocks self-approval)*
Claude added
ai-reviewed
and removed
ai-reviewing
labels 2026-04-08 18:48:47 +00:00
cal merged commit 5d86641fda into main 2026-04-08 23:21:59 +00:00
cal deleted branch issue/147-feat-show-refractor-progress-in-post-game-summary 2026-04-08 23:21:59 +00:00
Sign in to join this conversation.
No reviewers
No Milestone
No project
No Assignees
1 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: cal/paper-dynasty-discord#160
No description provided.