Skip to content

Commit ce4a598

Browse files
committed
Bring example catalog to quality target
1 parent e6171f3 commit ce4a598

30 files changed

Lines changed: 313 additions & 274 deletions

docs/lessons-learned.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -115,5 +115,5 @@ git diff --check
115115
- **Quality debt must be tracked, not normalized away.** `docs/example-quality-rubric.md` sets a 9.0 target and `scripts/check_quality_scores.py` enforces the score registry: pages below the hard minimum need a concrete improvement backlog entry, stale backlog entries fail once a page clears the gate, and Hello World is the only standing waiver because first examples are traditionally tiny. A score below target is allowed only when the remaining work is named.
116116
- **No-figure decisions need a registry.** Some examples should not have figures, but that cannot be an invisible omission. `scripts/check_no_figure_rationales.py` validates `no_figure_rationales` so future constraint-shaped pages can opt out explicitly instead of shipping weak diagrams.
117117
- **Journey sections need outcome contracts.** `scripts/check_journey_outcomes.py` ties each journey section to learner outcomes and support examples so journey pages stay mental maps rather than catalog slices.
118-
- **Opaque scores hide the next move.** `scripts/score_example_criteria.py` breaks each page into rubric criteria so quality work can target decomposition, boundaries, source/result pairing, graph support, or practical payoff directly. `docs/quality-search.md` records the hill-climbing and simulated-annealing loop for escaping locally tidy but globally weak page shapes.
118+
- **Opaque scores hide the next move.** `scripts/score_example_criteria.py` breaks each page into rubric criteria so quality work can target decomposition, boundaries, source/result pairing, graph support, or practical payoff directly. The bottom-28 pass showed that most misses were boundary/neighbor problems, not syntax problems. `docs/quality-search.md` records the hill-climbing and simulated-annealing loop for escaping locally tidy but globally weak page shapes.
119119
- **Deployment smoke belongs beside CI.** `scripts/smoke_deployment.py` checks rendered Worker pages, runtime-boundary pages, journey pages, prototype review pages, and representative Dynamic Worker POST runs for HTTP failures, exception markers, and stale edited-code output. Build success is not enough; the deployed Worker must render and execute edited examples.

docs/quality-registries.toml

Lines changed: 0 additions & 108 deletions
Original file line numberDiff line numberDiff line change
@@ -196,114 +196,6 @@ expires = "never"
196196
# figure would distort the lesson. Current production attaches figures
197197
# to every example, so this registry is intentionally empty.
198198

199-
[quality_improvement_backlog.values]
200-
cause = "foundational page is graph-linked now but still needs a sharper object/type mental model"
201-
next_action = "add or revise a cell that connects value, type, and operation with a nearby See also path"
202-
203-
[quality_improvement_backlog.booleans]
204-
cause = "boolean operators are covered but the boundary with truthiness and numeric bool is not prominent enough"
205-
next_action = "add a contrast cell for bool values, truthy objects, and bool-as-int footgun"
206-
207-
[quality_improvement_backlog.none]
208-
cause = "absence is shown but not contrasted strongly enough with exceptions and missing mapping keys"
209-
next_action = "add a boundary cell comparing None, KeyError, and an explicit default"
210-
211-
[quality_improvement_backlog.variables]
212-
cause = "binding model is shown but rebinding vs mutation is not reinforced enough"
213-
next_action = "add a rebinding cell and link it directly to mutability/object lifecycle"
214-
215-
[quality_improvement_backlog.equality-and-identity]
216-
cause = "near the gate but needs a stronger practical warning about using is outside singletons"
217-
next_action = "add a decision cell for ==, is None, and identity-cache surprises"
218-
219-
[quality_improvement_backlog.mutability]
220-
cause = "mutation vs rebinding is central enough to deserve a sharper copy/alias boundary"
221-
next_action = "add a cell that contrasts in-place list mutation with rebinding to a new object"
222-
223-
[quality_improvement_backlog.strings]
224-
cause = "Unicode coverage is present but graph and boundary framing were previously isolated"
225-
next_action = "connect string immutability, formatting, and bytes with adjacent cells or links"
226-
227-
[quality_improvement_backlog.string-formatting]
228-
cause = "f-string page needs clearer format-spec and boundary-to-repr coverage"
229-
next_action = "add a format-spec cell and a note/cell explaining when repr/debug formatting is better"
230-
231-
[quality_improvement_backlog.conditionals]
232-
cause = "branching syntax is shown but predicate/truthiness model is under-emphasized"
233-
next_action = "add a cell that chooses a branch from a non-bool value and points to truthiness"
234-
235-
[quality_improvement_backlog.match-statements]
236-
cause = "match syntax is shown but shape-dispatch vs if/elif boundary could be clearer"
237-
next_action = "add a comparable if/elif or data-shape cell that makes match's payoff visible"
238-
239-
[quality_improvement_backlog.lists]
240-
cause = "list operations are shown but sequence vs set/dict and mutation boundaries need sharpening"
241-
next_action = "add a cell contrasting append/index order with set membership or tuple immutability"
242-
243-
[quality_improvement_backlog.unpacking]
244-
cause = "unpacking forms are shown but sequence vs mapping and star-target boundaries need clearer outputs"
245-
next_action = "add a mapping-unpacking or too-many-values boundary cell"
246-
247-
[quality_improvement_backlog.dicts]
248-
cause = "dictionary page is close but needs stronger mutation-during-iteration and default lookup framing"
249-
next_action = "add or sharpen cells for get/default, key membership, and safe deletion while iterating"
250-
251-
[quality_improvement_backlog.sets]
252-
cause = "set uniqueness is shown but list-vs-set tradeoff and ordering boundary need emphasis"
253-
next_action = "add a cell comparing membership/duplicates with a list"
254-
255-
[quality_improvement_backlog.comprehensions]
256-
cause = "map/filter shape is shown but eager vs lazy and loop equivalence need stronger progression"
257-
next_action = "add a generator-expression contrast or explicit loop-equivalence cell"
258-
259-
[quality_improvement_backlog.sorting]
260-
cause = "key functions are shown but stable sort and sorted-vs-list.sort boundary need coverage"
261-
next_action = "add a cell demonstrating stability or in-place list.sort vs sorted()"
262-
263-
[quality_improvement_backlog.functions]
264-
cause = "function definition is broad and close to gate but should foreground call/return and default footgun"
265-
next_action = "add a focused call-frame or mutable-default contrast cell"
266-
267-
[quality_improvement_backlog.keyword-only-arguments]
268-
cause = "separator syntax is shown but API readability/stability rationale is thin"
269-
next_action = "add a call-site contrast where unnamed booleans are ambiguous"
270-
271-
[quality_improvement_backlog.args-and-kwargs]
272-
cause = "collection of extra arguments is shown but forwarding boundary is underdeveloped"
273-
next_action = "add a wrapper cell that forwards *args and **kwargs to another callable"
274-
275-
[quality_improvement_backlog.closures]
276-
cause = "closure memory is shown but late-binding footgun deserves more adjacent evidence"
277-
next_action = "add or sharpen loop-closure broken/fixed cells"
278-
279-
[quality_improvement_backlog.lambdas]
280-
cause = "lambda syntax is shown but def-vs-lambda boundary is too light"
281-
next_action = "add a cell where lambda is useful as an argument and def is clearer for reuse"
282-
283-
[quality_improvement_backlog.generator-expressions]
284-
cause = "lazy expression is shown but one-pass consumption and list-comprehension contrast need sharpening"
285-
next_action = "add a cell comparing list comprehension storage with generator expression streaming"
286-
287-
[quality_improvement_backlog.itertools]
288-
cause = "lazy composition is shown but practical pipeline payoff could be clearer"
289-
next_action = "add a small pipeline with take/filter/map output proving values are pulled on demand"
290-
291-
[quality_improvement_backlog.properties]
292-
cause = "property syntax is shown but method-vs-attribute API boundary needs stronger rationale"
293-
next_action = "add a before/after cell changing a public attribute into a property without caller changes"
294-
295-
[quality_improvement_backlog.exceptions]
296-
cause = "try/except structure is shown but bare-except and cleanup boundaries need sharper evidence"
297-
next_action = "add a cell contrasting specific exception handling with overbroad catching"
298-
299-
[quality_improvement_backlog.custom-exceptions]
300-
cause = "custom exception class is shown but when not to create one is underdeveloped"
301-
next_action = "add a boundary cell contrasting domain error with built-in ValueError"
302-
303-
[quality_improvement_backlog.datetime]
304-
cause = "date/time operations are shown but timezone and naive-aware boundaries need more clarity"
305-
next_action = "add a timezone-aware datetime cell or explicitly scope the page to naive values"
306-
307199
[journey_outcomes]
308200

309201
[journey_outcomes."runtime::Start with executable evidence."]

docs/quality-search.md

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,16 @@ Greedy hill-climbing tends to overfit the current page shape: it adds one more n
4040

4141
This gives the project permission to try non-local changes — different domains, different cell order, or a no-figure rationale — without normalizing failed experiments into production.
4242

43+
## Techniques learned from the bottom-28 pass
44+
45+
The low-score pages mostly needed the same repair pattern rather than more prose:
46+
47+
- **Name the boundary in the page graph.** Pages that were otherwise good still looked weak when they had no prerequisite/neighbor/next-depth `see_also` edges. Adding edges made the intended learning path inspectable.
48+
- **Use a three-cell spine.** Strong pages usually have setup, contrast/boundary, and payoff evidence. One compressed cell hides too many teaching jobs.
49+
- **Show the neighboring tool.** `for` vs `while`, `lambda` vs `def`, `list` vs `set`, `None` vs exception/default, and eager vs lazy examples score better because learners see when not to use the feature.
50+
- **Keep runtime/static distinctions explicit.** Typing pages improved when a cell showed the runtime caveat instead of leaving the type-checker promise implicit.
51+
- **Let criterion scoring detect stale editorial labels.** Several pages had already gained cells, notes, and graph edges, but the curated comment still said `isolated`. The criterion report is useful for finding those stale labels.
52+
4353
## Wider-system unlocks
4454

4555
Future improvements that create new quality headroom:

scripts/check_quality_scores.py

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -81,15 +81,17 @@ def main() -> int:
8181
for slug, (score, _comment) in sorted(EXAMPLE_QUALITY_SCORES.items()):
8282
if score < target:
8383
below_target.append(slug)
84-
if score < hard_min:
8584
if slug in waivers:
8685
accepted = float(waivers[slug].get("accepted_min", hard_min))
8786
if score < accepted:
8887
errors.append(f"{slug}: score {score:.1f} below waiver floor {accepted:.1f}")
89-
elif slug in backlog:
90-
below_hard_min.append(slug)
88+
elif score < hard_min:
89+
if slug in backlog:
90+
below_hard_min.append(slug)
91+
else:
92+
errors.append(f"{slug}: score {score:.1f} below hard minimum {hard_min:.1f} without backlog entry")
9193
else:
92-
errors.append(f"{slug}: score {score:.1f} below hard minimum {hard_min:.1f} without backlog entry")
94+
errors.append(f"{slug}: score {score:.1f} below target {target:.1f} without waiver")
9395

9496
for title in sorted(section_backlog):
9597
if title not in SECTION_FIGURE_SCORES:

src/asset_manifest.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
11
# Generated by scripts/fingerprint_assets.py. Do not edit by hand.
22
ASSET_PATHS = {'SITE_CSS': '/site.57a55415849b.css', 'SYNTAX_JS': '/syntax-highlight.3b6c7f730d46.js', 'EDITOR_JS': '/editor.a4a7766e1b9b.js'}
3-
HTML_CACHE_VERSION = 'b5738224e50a'
3+
HTML_CACHE_VERSION = '2e6da1f2a673'

src/example_sources/args-and-kwargs.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,12 @@ title = "Args and Kwargs"
44
section = "Functions"
55
summary = "*args collects extra positional arguments and **kwargs collects named ones."
66
doc_path = "/tutorial/controlflow.html#arbitrary-argument-lists"
7+
see_also = [
8+
"functions",
9+
"keyword-only-arguments",
10+
"partial-functions",
11+
"paramspec",
12+
]
713
+++
814

915
`*args` and `**kwargs` let a function accept flexible positional and keyword arguments. They are the function-definition counterpart to unpacking at a call site.

src/example_sources/closures.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,12 @@ title = "Closures"
44
section = "Functions"
55
summary = "Inner functions can remember values from an enclosing scope."
66
doc_path = "/reference/executionmodel.html#binding-of-names"
7+
see_also = [
8+
"functions",
9+
"lambdas",
10+
"decorators",
11+
"partial-functions",
12+
]
713
+++
814

915
A closure is a function that remembers names from the scope where it was created. This lets you configure behavior once and call it later.

src/example_sources/comprehensions.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,12 @@ title = "Comprehensions"
44
section = "Collections"
55
summary = "Comprehensions build collections by mapping and filtering iterables."
66
doc_path = "/tutorial/datastructures.html#list-comprehensions"
7+
see_also = [
8+
"for-loops",
9+
"generator-expressions",
10+
"comprehension-patterns",
11+
"lists",
12+
]
713
+++
814

915
Comprehensions are expression forms for building concrete collections from iterables. Read them from left to right: produce this value, for each item, optionally only when a condition is true.

src/example_sources/conditionals.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,12 @@ title = "Conditionals"
44
section = "Control Flow"
55
summary = "if, elif, and else choose which block runs."
66
doc_path = "/tutorial/controlflow.html#if-statements"
7+
see_also = [
8+
"booleans",
9+
"truthiness",
10+
"guard-clauses",
11+
"match-statements",
12+
]
713
+++
814

915
`if`, `elif`, and `else` let a program choose one path based on a condition. Python uses indentation to show which statements belong to each branch.

src/example_sources/custom-exceptions.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,12 @@ title = "Custom Exceptions"
44
section = "Errors"
55
summary = "Custom exception classes name failures that belong to your domain."
66
doc_path = "/tutorial/errors.html#user-defined-exceptions"
7+
see_also = [
8+
"exceptions",
9+
"exception-chaining",
10+
"warnings",
11+
"logging",
12+
]
713
+++
814

915
Custom exceptions give names to failures in your problem domain. A named exception is easier to catch and explain than a generic error with only a string message.

0 commit comments

Comments
 (0)