feat: add sticky error-aware key mastery

Persist ranked-only mastery and use it consistently across progression, milestones, counts, and localized UI. Preserve mastery during history rebuilds, replay errors accurately, and generate replay-derived test profiles. Document the follow-up plan to remove the drill history cap.
This commit is contained in:
2026-08-12 01:09:52 -04:00
parent 0f8493eb02
commit 5daa644d3b
47 changed files with 2683 additions and 722 deletions
+9 -8
View File
@@ -89,7 +89,7 @@ impl Widget for BranchProgressList<'_> {
let unlocked = self.skill_tree.branch_unlocked_count(branch_id);
let mastered = self
.skill_tree
.branch_confident_keys(branch_id, self.key_stats);
.branch_mastered_keys(branch_id, self.key_stats);
let (m_bar, u_bar, e_bar) = compact_dual_bar_parts(mastered, unlocked, total, 12);
lines.push(Line::from(vec![
Span::styled(
@@ -113,7 +113,7 @@ impl Widget for BranchProgressList<'_> {
}
let total = self.skill_tree.total_unique_keys;
let unlocked = self.skill_tree.total_unlocked_count();
let mastered = self.skill_tree.total_confident_keys(self.key_stats);
let mastered = self.skill_tree.total_mastered_keys(self.key_stats);
let left_pad = if area.width >= 90 {
3
} else if area.width >= 70 {
@@ -126,12 +126,13 @@ impl Widget for BranchProgressList<'_> {
let right_pad = if area.width >= 75 { 2 } else { 0 };
let overall_label = t!("progress.overall_key_progress");
let label = format!("{}{} ", " ".repeat(left_pad), overall_label);
let unlocked_mastered = t!("progress.unlocked_mastered", unlocked = unlocked, total = total, mastered = mastered);
let suffix = format!(
" {}{}",
unlocked_mastered,
" ".repeat(right_pad)
let unlocked_mastered = t!(
"progress.unlocked_mastered",
unlocked = unlocked,
total = total,
mastered = mastered
);
let suffix = format!(" {}{}", unlocked_mastered, " ".repeat(right_pad));
let reserved = label.len() + suffix.len();
let bar_width = (area.width as usize).saturating_sub(reserved).max(6);
let (m_bar, u_bar, e_bar) =
@@ -170,7 +171,7 @@ fn render_branch_cell<'a>(
let def = get_branch_definition(branch_id);
let total = SkillTree::branch_total_keys(branch_id);
let unlocked = skill_tree.branch_unlocked_count(branch_id);
let mastered = skill_tree.branch_confident_keys(branch_id, key_stats);
let mastered = skill_tree.branch_mastered_keys(branch_id, key_stats);
let prefix = if is_active { "\u{25b6} " } else { "\u{00b7} " };
let label_color = if is_active {