From c0e902e5cc85ce306c7ca62989c707d91630f305 Mon Sep 17 00:00:00 2001 From: Krzysztof kuhy Rudnicki Date: Fri, 23 Oct 2026 15:43:00 +0200 Subject: [PATCH] Toolbox: no alias may be a cluster problem's title; min's identity is +Infinity "merge intervals" is #56's own title, so the leak bounce would have bounced the coach's restatement of it; "dictionary" was a bare common word. A test now pins that no entry name or alias equals any clusters.json title. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01PjvF5DZG9JduSBYKxqyysi --- .../leetcode_coach/tests/test_toolbox.py | 15 ++++++++++++++- leetcode_coach/leetcode_coach/toolbox.json | 6 +++--- 2 files changed, 17 insertions(+), 4 deletions(-) diff --git a/leetcode_coach/leetcode_coach/tests/test_toolbox.py b/leetcode_coach/leetcode_coach/tests/test_toolbox.py index 399f7ad..db9a43c 100644 --- a/leetcode_coach/leetcode_coach/tests/test_toolbox.py +++ b/leetcode_coach/leetcode_coach/tests/test_toolbox.py @@ -13,7 +13,7 @@ from typing import TYPE_CHECKING import pytest -from leetcode_coach import card_rules, toolbox, toolbox_cards +from leetcode_coach import card_rules, progress, toolbox, toolbox_cards if TYPE_CHECKING: from pathlib import Path @@ -47,6 +47,19 @@ def test_names_and_aliases_are_unique() -> None: assert len(names) == len(set(names)) +def test_no_name_is_a_cluster_problem_title() -> None: + # The leak bounce fires on any entry name or alias before the first code + # event, so one that is a problem's own title bounces its restatement. + titles = { + p["slug"].replace("-", " ") + for problems in progress.load_clusters().values() + for p in problems + } + for entry in toolbox.load()["entries"]: + for name in [entry["name"], *entry["aliases"]]: + assert name.casefold() not in titles, name + + def test_no_entry_property_set_nests_inside_another() -> None: entries = toolbox.load()["entries"] assert len(entries) >= 20 diff --git a/leetcode_coach/leetcode_coach/toolbox.json b/leetcode_coach/leetcode_coach/toolbox.json index 7d7c6f9..2f0dd6d 100644 --- a/leetcode_coach/leetcode_coach/toolbox.json +++ b/leetcode_coach/leetcode_coach/toolbox.json @@ -4,7 +4,7 @@ "invertible": "every step has a known undo: a + x - x = a", "commutative": "operand order does not matter: a + b = b + a", "associative": "grouping does not matter, so a running fold works: (a + b) + c = a + (b + c)", - "identity element": "a neutral value e with a op e = a: 0 for + ^ gcd, -Infinity for max", + "identity element": "a neutral value e with a op e = a: 0 for + ^ gcd, -Infinity for max, +Infinity for min", "idempotent": "combining a value with itself changes nothing: max(a, a) = a", "selective": "the result is always one of the operands: max(a, b) is a or b", "divides its inputs": "the result divides every operand, so folding more values can only shrink it", @@ -74,7 +74,7 @@ }, { "name": "hash map", - "aliases": ["hashmap", "hash table", "new Map", "dictionary"], + "aliases": ["hashmap", "hash table", "new Map"], "properties": ["O(1) lookup", "stores a value per key", "arbitrary keys"] }, { @@ -129,7 +129,7 @@ }, { "name": "sort by start + merge", - "aliases": ["merge intervals", "sort by start"], + "aliases": ["sort by start", "sort then combine"], "properties": ["needs sorted input", "overlaps become adjacent", "single pass"] }, {