Skip to content

Add video links for Stygian recommendations, build goals for simmed characters, alter Stygian algorithm - #46

Merged
woopxwoop merged 15 commits into
mainfrom
develop
Aug 13, 2026
Merged

Add video links for Stygian recommendations, build goals for simmed characters, alter Stygian algorithm#46
woopxwoop merged 15 commits into
mainfrom
develop

Conversation

@woopxwoop

@woopxwoop woopxwoop commented Aug 13, 2026

Copy link
Copy Markdown
Owner

Summary by CodeRabbit

  • New Features

    • Added a Stygian solution board with solver modes, difficulty settings, cost limits, team assignments, clear times, usage rates, fallback suggestions, and pagination.
    • Added clear-video links with thumbnails for supported team and enemy combinations.
    • Added diversified character build examples, alternate-team selection, and relevant stat displays.
    • Added improved stat-goal layouts and new search/play icons.
  • Improvements

    • Added caching for faster Stygian recommendations and video loading.
    • Five-star weapons can now display as names or R1 depending on context.
    • Build statistics now prevent invalid substat roll values.
    • Improved popover, select, and investment card layouts.

@coderabbitai

coderabbitai Bot commented Aug 13, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Warning

Review limit reached

@woopxwoop, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 90 minutes

You've used all free OSS reviews for now. Wait for the free limit to reset to keep reviewing this public repository.

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: Repository UI (base), Organization UI (inherited)

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 4326dd98-f5d9-4c9c-9236-d135467c9f09

📥 Commits

Reviewing files that changed from the base of the PR and between 1e1299c and 437c4c5.

📒 Files selected for processing (15)
  • src/lib/app/race-abort.ts
  • src/lib/app/stygian-cheap-clears.ts
  • src/lib/app/stygian-clear-videos.ts
  • src/lib/character-builds.test.ts
  • src/lib/character-builds.ts
  • src/lib/definitions.ts
  • src/lib/e2e/fixtures.ts
  • src/lib/solver.test.ts
  • src/lib/solver.ts
  • src/lib/ui/components/StygianSolutionBoard.svelte
  • src/routes/api/stygian-clear-videos/+server.ts
  • src/routes/characters/[slug]/+page.svelte
  • src/routes/dev/ui/+page.svelte
  • src/routes/teams/[slug]/+page.svelte
  • tests/helpers.ts
📝 Walkthrough

Walkthrough

This change adds Stygian clear-solving modes, cost and video APIs, client caching, and a reusable solution board. It also adds character build-example contracts, stat-roll clamping, build conversion helpers, character-page presentation, investment-label options, and a development stat-goal gallery.

Changes

Stygian solving and clear data

Layer / File(s) Summary
Stygian contracts and request validation
src/lib/definitions.ts, src/lib/types/database.types.ts, src/lib/server/request-validation.ts, src/lib/stores.ts
Adds Stygian payload types, database contracts, solver preferences, persistence, defaults, and request validators.
Cost rules and Stygian solvers
src/lib/team-cost.ts, src/lib/solver.ts, src/lib/team-cost.test.ts, src/lib/solver.test.ts
Adds cost-frontier helpers, hybrid solving, cheap-clear solving, fallback accumulation, and coverage for cost and ranking behavior.
Clear APIs and client request orchestration
src/routes/api/stygian-cheap-clears/+server.ts, src/routes/api/stygian-clear-videos/+server.ts, src/lib/app/stygian-cheap-clears.ts, src/lib/app/stygian-clear-videos.ts
Adds validated server endpoints and client caches with request deduplication, timeouts, grouped results, thumbnail helpers, and caller-side cancellation.
Reusable Stygian solution board
src/lib/ui/components/StygianSolutionBoard.svelte, src/routes/tools/stygian/+page.svelte, src/routes/dev/features/+page.svelte, src/lib/ui/components/InfoPopover.svelte, src/lib/ui/components/Select.svelte, src/lib/ui/icons/*
Moves solution rendering into a reusable board with solver controls, clear videos, loading states, retries, pagination, fallback messaging, and responsive styling.

Character build examples

Layer / File(s) Summary
Build-example contracts and stat limits
src/lib/types/investment.ts, src/lib/build-stats.ts
Adds build-example and recommendation models. Artifact substat rolls are sanitized and capped by eligible pieces and main stats.
Build-example conversion helpers
src/lib/character-builds.ts, src/lib/character-builds.test.ts
Adds example filtering, stat-row generation, Favonius handling, team extraction, set normalization, tier conversion, and tests for roll and set behavior.
Character build presentation
src/routes/characters/[slug]/+page.svelte, src/lib/ui/components/InvestmentBuildCard.svelte
Adds selectable example teams, alternate-team navigation, relevant-stat filtering, normalized set badges, and responsive Builds-tab layouts.

Presentation refinements and development studies

Layer / File(s) Summary
Investment label display rules
src/lib/equipment-data.ts, src/routes/teams/[slug]/+page.svelte, src/routes/teams/configs/[slug]/+page.svelte
Adds configurable five-star weapon rendering and uses names for owned variants or R1 for other variants.
Stat-goal layout study
src/routes/dev/ui/+page.svelte
Adds four interactive stat-goal presentation patterns with archetype selection, alternate teams, and responsive styling.

Estimated code review effort: 5 (Critical) | ~120 minutes

Mergeability Score: 🟡 Moderate · up to 1e129

This PR adds Stygian recommendations and simulated-character build goals, but the current head can show incorrect build-stat tiers, omit usable partial recommendations, and display misleading fallback guidance. These concrete user-facing correctness issues should be fixed or explicitly accepted before merging.

Possibly related PRs

Sequence Diagram(s)

sequenceDiagram
  participant Player
  participant StygianSolutionBoard
  participant Solver
  participant ClearCache
  participant ClearAPI
  Player->>StygianSolutionBoard: select solver mode and difficulty
  StygianSolutionBoard->>ClearCache: request clear rows or videos
  ClearCache->>ClearAPI: fetch missing normalized data
  ClearAPI-->>ClearCache: return validated clear data
  StygianSolutionBoard->>Solver: compute ranked solutions
  Solver-->>StygianSolutionBoard: return complete solution boards
  StygianSolutionBoard-->>Player: render teams, times, videos, and controls
Loading

Poem

A rabbit reviews each clear in the night,
While cached teams hop into sight.
Rolls find their caps, builds bloom bright,
Three fields align in solver light.
“Good hops!” says the rabbit—“the board is right!”

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 64.86% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly summarizes the three main changes: Stygian video links, character build goals, and algorithm updates.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches 💡 1
📝 Generate docstrings 💡
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch develop

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.

@coderabbitai coderabbitai Bot left a comment

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.

Actionable comments posted: 12

Caution

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

⚠️ Outside diff range comments (1)
src/lib/solver.ts (1)

1032-1062: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Preserve partial Stygian fallback boards.

When no complete board exists in allTeams, buildMinMissingStygianSolutions discards every partial result. solveStygianWithFallback then returns [], so the UI cannot show neededCharacters. Restore the previous all-team partial fallback or add an explicit replacement behavior and regression test.

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

In `@src/lib/solver.ts` around lines 1032 - 1062, Update
buildMinMissingStygianSolutions and its solveStygianWithFallback flow so partial
Stygian results are retained when no complete board exists, rather than
discarding every solution with nonempty unfilled. Preserve complete-result
prioritization, restore the prior all-team partial fallback behavior (including
neededCharacters), and add a regression test covering the no-complete-board
case.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/lib/app/stygian-cheap-clears.ts`:
- Around line 31-40: Use the exported StygianClearDifficulty union for the
difficulty parameter in cacheKey and the difficulty option type around the opts
handling at the referenced section, including the
STYGIAN_CHEAP_CLEARS_DIFFICULTY default. Preserve the existing validation flow
while ensuring unsupported difficulty values are rejected at compile time.
- Around line 43-66: Extract the duplicated raceAbort helper into one shared
module, preserving its signal.reason fallback and abort-listener cleanup
behavior. In src/lib/app/stygian-cheap-clears.ts lines 43-66 and
src/lib/app/stygian-clear-videos.ts lines 76-99, remove the local
implementations and import the shared helper instead.

In `@src/lib/app/stygian-clear-videos.ts`:
- Around line 127-165: Update the missing-pair fetch flow to split missing into
chunks of MAX_TEAM_ENEMY_PAIRS before calling the API, so each request stays
within the server limit. In the fetch/cache logic around fetchPromise, process
each chunk independently and register inflight entries and cleanup against that
chunk; preserve per-pair caching and await all chunk promises.

In `@src/lib/character-builds.ts`:
- Around line 581-591: Use CharacterBuildExample.invest as the investment-tier
source of truth in exampleHasHighConfig: require example.invest to equal "high"
and retain the existing validation that high_substat_rolls is a non-empty
object. Ensure mid-investment examples remain on the mid sheet even when
high_substat_rolls is present.
- Around line 640-650: Update exampleRelevantGoodKeys and its callers so tier is
propagated consistently: pass tier from exampleRelevantSheetRows and the page
caller, select baseline keys for "mid", and select high-tier keys only for
"high" when high-roll data exists. Adjust high-tier tests to explicitly pass
"high".

In `@src/lib/definitions.ts`:
- Around line 180-184: Update isStygianClearDifficulty to derive its result from
STYGIAN_CLEAR_DIFFICULTY_OPTIONS instead of repeating the "Fearless" and "Dire"
literals, following the options-driven pattern used by isStygianSolverMode.
Preserve the existing type-guard behavior.

In `@src/lib/ui/components/StygianSolutionBoard.svelte`:
- Around line 667-672: Update the fallback note condition and message in
StygianSolutionBoard so it distinguishes video solver mode from yshelper and
hybrid modes. Keep the existing difficulty-based text only for video mode, and
show non-video fallback text explaining that the roster lacks required
characters; do not reference clearDifficulty for yshelper or hybrid.

In `@src/routes/api/stygian-clear-videos/`+server.ts:
- Around line 25-34: Align the isPlaywrightE2e() short-circuit ordering in the
POST handlers for src/routes/api/stygian-clear-videos/+server.ts lines 25-34 and
src/routes/api/stygian-cheap-clears/+server.ts lines 41-48: place the E2E
empty-payload return consistently before enforceApiRateLimit and
requireJsonObject in both endpoints, so E2E requests bypass validation and rate
limiting uniformly.

In `@src/routes/characters/`[slug]/+page.svelte:
- Around line 1589-1597: Add an explicit accessible name to the alternate-team
button in the example picker using the corresponding example metadata, such as
its team name or label. Update the button containing CharacterIcon so screen
readers can identify which alternate team it selects while preserving the
existing selection behavior.

In `@src/routes/dev/ui/`+page.svelte:
- Around line 1201-1206: Update the two gear indicator spans in the sg-d-gear
block to expose their weapon and artifact-set values through semantic roles and
accessible names, using statGoalArchetype.weapon and statGoalArchetype.set
respectively. Preserve the existing visual classes and title attributes while
ensuring the values are available without pointer hover.
- Around line 1136-1149: Update the STAT_GOAL_ARCHETYPES control group to use a
normal button-group pattern: remove the tablist/tab semantics and replace
aria-selected with aria-pressed on each button while preserving the existing
active-state styling and onclick behavior.

In `@src/routes/teams/`[slug]/+page.svelte:
- Line 310: Update the explanatory text near the weapon alternative rows to
define the +1 tag as one extra swap cost and explicitly state that it is not a
pull recommendation, while preserving the existing baseline and DPS context.

---

Outside diff comments:
In `@src/lib/solver.ts`:
- Around line 1032-1062: Update buildMinMissingStygianSolutions and its
solveStygianWithFallback flow so partial Stygian results are retained when no
complete board exists, rather than discarding every solution with nonempty
unfilled. Preserve complete-result prioritization, restore the prior all-team
partial fallback behavior (including neededCharacters), and add a regression
test covering the no-complete-board case.
🪄 Autofix

Fix all unresolved CodeRabbit comments on this PR:

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

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI (base), Organization UI (inherited)

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: ddb2f936-e4f0-49df-9cd5-332137e092ce

📥 Commits

Reviewing files that changed from the base of the PR and between 89d82d1 and 1e1299c.

⛔ Files ignored due to path filters (1)
  • patch-notes/2026-08-13-video-clears-and-build-goals.md is excluded by none and included by none
📒 Files selected for processing (31)
  • src/lib/app/stygian-cheap-clears.ts
  • src/lib/app/stygian-clear-videos.ts
  • src/lib/build-stats.ts
  • src/lib/character-builds.test.ts
  • src/lib/character-builds.ts
  • src/lib/definitions.ts
  • src/lib/equipment-data.ts
  • src/lib/server/request-validation.test.ts
  • src/lib/server/request-validation.ts
  • src/lib/solver.test.ts
  • src/lib/solver.ts
  • src/lib/stores.ts
  • src/lib/team-cost.test.ts
  • src/lib/team-cost.ts
  • src/lib/types/database.types.ts
  • src/lib/types/investment.ts
  • src/lib/ui/components/InfoPopover.svelte
  • src/lib/ui/components/InvestmentBuildCard.svelte
  • src/lib/ui/components/Select.svelte
  • src/lib/ui/components/StygianSolutionBoard.svelte
  • src/lib/ui/icons/IconFileSearch.svelte
  • src/lib/ui/icons/IconPlay.svelte
  • src/routes/api/stygian-cheap-clears/+server.ts
  • src/routes/api/stygian-clear-videos/+server.ts
  • src/routes/characters/[slug]/+page.svelte
  • src/routes/dev/features/+page.svelte
  • src/routes/dev/features/+page.ts
  • src/routes/dev/ui/+page.svelte
  • src/routes/teams/[slug]/+page.svelte
  • src/routes/teams/configs/[slug]/+page.svelte
  • src/routes/tools/stygian/+page.svelte

Comment on lines +31 to +40
function cacheKey(
characters: string[],
stygianVersion: number,
enemyIds: number[],
difficulty: string,
maxCost: number,
): string {
const enemies = [...enemyIds].sort((a, b) => a - b).join(",");
return `${stygianVersion}:${difficulty}:c${maxCost}:${enemies}:${rosterKey(characters)}`;
}

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.

🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win

Type the difficulty option as StygianClearDifficulty.

opts.difficulty and the cacheKey parameter use string. The default value comes from STYGIAN_CHEAP_CLEARS_DIFFICULTY, and the server rejects any other value with a 400 through requireStygianClearDifficulty. Use the exported union so an invalid difficulty fails at compile time instead of at request time.

♻️ Proposed refactor
 import type {
   CharacterOwned,
   StygianCheapClearRow,
   StygianCheapClearsPayload,
+  StygianClearDifficulty,
 } from "$lib/definitions";
 function cacheKey(
   characters: string[],
   stygianVersion: number,
   enemyIds: number[],
-  difficulty: string,
+  difficulty: StygianClearDifficulty,
   maxCost: number,
 ): string {
 export async function ensureCheapClears(opts: {
   owned: CharacterOwned[];
   stygianVersion: number;
   enemyIds: number[];
-  difficulty?: string;
+  difficulty?: StygianClearDifficulty;
   maxCost?: number;
   signal?: AbortSignal;
 }): Promise<StygianCheapClearRow[]> {

Also applies to: 72-82

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

In `@src/lib/app/stygian-cheap-clears.ts` around lines 31 - 40, Use the exported
StygianClearDifficulty union for the difficulty parameter in cacheKey and the
difficulty option type around the opts handling at the referenced section,
including the STYGIAN_CHEAP_CLEARS_DIFFICULTY default. Preserve the existing
validation flow while ensuring unsupported difficulty values are rejected at
compile time.

Comment thread src/lib/app/stygian-cheap-clears.ts Outdated
Comment thread src/lib/app/stygian-clear-videos.ts Outdated
Comment thread src/lib/character-builds.ts
Comment thread src/lib/character-builds.ts
Comment on lines +25 to +34
await enforceApiRateLimit({ request, getClientAddress });
const body = await requireJsonObject(request);
const pairs = requireTeamEnemyPairs(body.pairs);

if (isPlaywrightE2e()) {
const payload: StygianClearVideosPayload = { clears: [] };
return json(payload, {
headers: { "Cache-Control": "no-store", "X-Playwright-E2E": "1" },
});
}

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.

📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Align the Playwright E2E short-circuit position across the two new endpoints. The two sibling endpoints place the isPlaywrightE2e() branch at different points. One returns the empty payload before rate limiting and body validation. The other rate-limits and validates first. E2E runs then exercise different validation paths per endpoint, and the rate-limit budget is consumed inconsistently.

  • src/routes/api/stygian-clear-videos/+server.ts#L25-L34: move the isPlaywrightE2e() branch above enforceApiRateLimit and requireJsonObject, or keep it here and document why validation must run.
  • src/routes/api/stygian-cheap-clears/+server.ts#L41-L48: keep one ordering and apply the same ordering in both endpoints.
📍 Affects 2 files
  • src/routes/api/stygian-clear-videos/+server.ts#L25-L34 (this comment)
  • src/routes/api/stygian-cheap-clears/+server.ts#L41-L48
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/routes/api/stygian-clear-videos/`+server.ts around lines 25 - 34, Align
the isPlaywrightE2e() short-circuit ordering in the POST handlers for
src/routes/api/stygian-clear-videos/+server.ts lines 25-34 and
src/routes/api/stygian-cheap-clears/+server.ts lines 41-48: place the E2E
empty-payload return consistently before enforceApiRateLimit and
requireJsonObject in both endpoints, so E2E requests bypass validation and rate
limiting uniformly.

Comment thread src/routes/characters/[slug]/+page.svelte
Comment thread src/routes/dev/ui/+page.svelte Outdated
Comment thread src/routes/dev/ui/+page.svelte
ownedVariantsList,
"Owned weapon options",
"Already-owned 5★ weapon alternatives compared to this team baseline. The +1 tag is the extra cost for that swap — not a pull recommendation. Highlighted rows are within 2.5% DPS of the best option in this list.",
"Already-owned 5★ weapon alternatives compared to this team baseline. Highlighted rows are within 2.5% DPS of the best option in this list.",

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.

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Restore the meaning of the +1 row tag.

The +1 tag is still rendered for owned weapon alternatives, but this text no longer defines it. Users can interpret it as a refinement or pull recommendation. State that +1 means one extra swap cost and is not a pull recommendation.

Proposed wording
-        "Already-owned 5★ weapon alternatives compared to this team baseline. Highlighted rows are within 2.5% DPS of the best option in this list.",
+        "Already-owned 5★ weapon alternatives compared to this team baseline. The +1 tag means one extra swap cost, not a pull recommendation. Highlighted rows are within 2.5% DPS of the best option in this list.",
📝 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
"Already-owned 5★ weapon alternatives compared to this team baseline. Highlighted rows are within 2.5% DPS of the best option in this list.",
"Already-owned 5★ weapon alternatives compared to this team baseline. The +1 tag means one extra swap cost, not a pull recommendation. Highlighted rows are within 2.5% DPS of the best option in this list.",
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/routes/teams/`[slug]/+page.svelte at line 310, Update the explanatory
text near the weapon alternative rows to define the +1 tag as one extra swap
cost and explicitly state that it is not a pull recommendation, while preserving
the existing baseline and DPS context.

@woopxwoop
woopxwoop merged commit f99c273 into main Aug 13, 2026
3 checks passed
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