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 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PjvF5DZG9JduSBYKxqyysi
This commit is contained in:
Krzysztof Rudnicki 2026-10-23 15:43:00 +02:00
parent 28c243d066
commit c0e902e5cc
2 changed files with 17 additions and 4 deletions

View File

@ -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

View File

@ -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"]
},
{