Skip to content

Commit a7f0125

Browse files
committed
Close remaining audit follow-ups
1 parent a854447 commit a7f0125

56 files changed

Lines changed: 3354 additions & 9506 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.gitattributes

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,11 @@
1-
# `src/asset_manifest.py` is generated by `scripts/fingerprint_assets.py`
2-
# and `src/example_sources_data.py` by `scripts/embed_example_sources.py`.
1+
# `src/asset_manifest.py` is generated by `scripts/fingerprint_assets.py`,
2+
# `src/example_sources_data.py` by `scripts/embed_example_sources.py`, and
3+
# `src/editorial_registry_data.py` by `scripts/embed_editorial_registry.py`.
34
# On merge/rebase, keep our side of the conflict — the post-merge and
4-
# post-rewrite hooks regenerate both files deterministically afterwards.
5+
# post-rewrite hooks regenerate these files deterministically afterwards.
56
# This works once `scripts/install-git-hooks.sh` has been run locally,
67
# which registers `merge.ours.driver = true` and points `core.hooksPath`
78
# at `.githooks/`.
89
src/asset_manifest.py merge=ours
910
src/example_sources_data.py merge=ours
11+
src/editorial_registry_data.py merge=ours

.githooks/post-merge

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,10 @@
11
#!/usr/bin/env bash
2-
# Regenerate generated files after a merge or pull so embedded example
3-
# data and the asset manifest reflect the merged tree, not whichever
4-
# parent won the conflict.
2+
# Regenerate generated files after a merge or pull so embedded data,
3+
# asset manifests, fingerprints, and prototype pages reflect the merged
4+
# tree, not whichever parent won the conflict.
55
set -e
66
cd "$(git rev-parse --show-toplevel)"
7-
uv run --python 3.13 python scripts/embed_example_sources.py >/dev/null
8-
uv run --python 3.13 python scripts/fingerprint_assets.py >/dev/null
9-
if ! git diff --quiet src/example_sources_data.py src/asset_manifest.py public/_headers; then
7+
make build >/dev/null
8+
if ! git diff --quiet src/example_sources_data.py src/editorial_registry_data.py src/asset_manifest.py public/_headers public/prototyping; then
109
echo "post-merge: generated files regenerated; stage and amend if needed"
1110
fi

.githooks/post-rewrite

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,10 @@
11
#!/usr/bin/env bash
2-
# Regenerate generated files after rebase/amend so embedded example data
3-
# and the asset manifest match the rewritten history, not whichever
4-
# commit happened to win each step.
2+
# Regenerate generated files after rebase/amend so embedded data,
3+
# asset manifests, fingerprints, and prototype pages match the rewritten
4+
# history, not whichever commit happened to win each step.
55
set -e
66
cd "$(git rev-parse --show-toplevel)"
7-
uv run --python 3.13 python scripts/embed_example_sources.py >/dev/null
8-
uv run --python 3.13 python scripts/fingerprint_assets.py >/dev/null
9-
if ! git diff --quiet src/example_sources_data.py src/asset_manifest.py public/_headers; then
7+
make build >/dev/null
8+
if ! git diff --quiet src/example_sources_data.py src/editorial_registry_data.py src/asset_manifest.py public/_headers public/prototyping; then
109
echo "post-rewrite: generated files regenerated; stage and amend if needed"
1110
fi

.github/pull_request_template.md

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@
55
## Verification
66

77
- [ ] `make verify`
8-
- [ ] `scripts/check_example_migration_parity.py`
98
- [ ] `scripts/format_examples.py --check`
109
- [ ] `make verify-python-version VERSION=3.13`
1110
- [ ] `git diff --check`

.github/workflows/regenerate-generated-files.yml

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
11
name: Regenerate generated files
22

33
# Merges made through the GitHub UI bypass the local git hooks that keep
4-
# src/asset_manifest.py and src/example_sources_data.py in sync, so a merge to
5-
# main can land with stale generated files (and a red Verify run). This
4+
# src/asset_manifest.py, src/example_sources_data.py, and
5+
# src/editorial_registry_data.py in sync, so a merge to main can land with
6+
# stale generated files (and a red Verify run). This
67
# workflow regenerates them and pushes a fix-up commit. Pushes made with
78
# GITHUB_TOKEN do not trigger other workflows, so the fix-up commit is not
89
# re-verified by CI; it only ever contains deterministic `make build` output.
@@ -43,7 +44,7 @@ jobs:
4344
for attempt in 1 2 3; do
4445
git pull --rebase origin main
4546
make build
46-
git add -A src/example_sources_data.py src/asset_manifest.py public
47+
git add -A src/example_sources_data.py src/editorial_registry_data.py src/asset_manifest.py public
4748
if git diff --cached --quiet; then
4849
echo "Generated files are current."
4950
exit 0

.github/workflows/verify.yml

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,6 @@ jobs:
5151
CHROME_PATH: /usr/bin/google-chrome
5252
run: |
5353
make verify
54-
scripts/check_example_migration_parity.py
5554
scripts/format_examples.py --check
5655
make verify-python-version VERSION=3.13
5756
git diff --check

CHANGELOG.md

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -14,20 +14,26 @@ The format is inspired by [Keep a Changelog](https://keepachangelog.com/en/1.1.0
1414
- Figure contracts: XML well-formedness and text-escaping (Contract 11), mechanical caption rules (Contract 5c), reverse section-figure containment, and an end-to-end check that attached figures appear in rendered pages.
1515
- The marginalia gestalt shows one card per attachment with the production figcaption, so caption/figure disagreements are visible during review; prototype pages regenerate in `make build` and are covered by `make check-generated`.
1616
- Behavioral test suite for Turnstile clearance signing, challenge modes, cookie attributes, and HTML cache keys.
17-
- `scripts/refresh_golden_fixture.py`: regenerates the golden fixture as a structural snapshot and prints a reviewable summary of teaching-structure changes.
18-
- Security headers on Worker HTML responses and static assets (nosniff, referrer-policy, frame protections); POST body size cap with a friendly 413 page.
17+
- Security headers on Worker HTML responses and static assets (nosniff, referrer-policy, frame protections, and a full CSP); POST body size cap with a friendly 413 page.
1918
- An end-to-end "adding a new example" workflow and a complete quality-check table in `CONTRIBUTING.md`.
2019

2120
### Changed
2221

2322
- The Makefile pins every Python invocation to 3.13 via `uv run`, so `make verify` matches CI regardless of the system interpreter; `make dev`/`deploy` use the workers dependency group.
2423
- CI workflows: deduplicated PR runs with explicit permissions and concurrency; preview workflow passes inputs through env vars (no shell interpolation next to the Cloudflare token), pins wrangler, and passes the smoke-bypass secret; the regenerate workflow pulls before building and retries with a rebuild on push races.
25-
- Quality gates got teeth: curated scores are bounded against the criterion heuristic (`--max-delta`), waivers must carry future ISO expiry dates and are flagged when stale, the journey-section average floor is enforced, confusable-pair tokens cannot be satisfied inside a longer sibling token, every `:::note` block is checked, `paired_pages` requires `see_also` discoverability, and the rubric-audit snapshot computes its findings from live registries instead of hardcoding PASS verdicts.
26-
- The golden parity gate compares the full parsed cell structure (including cell kinds) against the snapshot through the loader, with no dependence on the legacy walkthrough grouping.
24+
- Quality gates got teeth: curated scores are bounded against the criterion heuristic (`--max-delta`), waivers must carry future ISO expiry dates and are flagged when stale, the journey-section average floor is enforced, confusable-pair tokens must appear inside teaching cells and cannot be satisfied inside a longer sibling token, every `:::note` block is checked, `paired_pages` requires both `see_also` discoverability and registry-named cell evidence, scope-first-pass pages must link registry-named focused neighbors, and the rubric-audit snapshot computes its findings from live registries instead of hardcoding PASS verdicts.
25+
- Editorial metadata moved from Python literals into `docs/quality-registries.toml`: journeys, see-also edge labels, figure attachments, captions, and curated scores now load through `src/editorial_registry.py`.
26+
- Removed the migration-era golden fixture and parity/refresh scripts after the Markdown-backed release window; ordinary content edits now rely on verifiers, content gates, generated diffs, and browser/runtime checks rather than snapshot refreshes.
2727
- ASGI bridge: WebSocket close events reach the close handler (`onclose` was assigned to `onopen`), headers use latin-1 per the ASGI spec, and application lifespan runs once per Worker instead of per request.
2828
- Unknown `TURNSTILE_CHALLENGE_MODE` values fail closed to requiring a challenge; the smoke-bypass header compares in constant time.
2929
- Template substitution is single-pass, so submitted code containing `__TOKEN__` text can no longer corrupt the rendered page; embedded JSON escapes `<` so `</script>` in example code cannot break out of the inline script.
30-
- Small accent-colored text uses `--accent-text` (#C83800) for WCAG AA contrast; the editor textarea has an accessible label.
30+
- Worker HTML cache keys ignore ordinary query strings (routes ignore them too), so arbitrary `?utm=` or cache-busting params cannot fragment the rendered-page cache.
31+
- FastAPI GET handlers now call renderers directly instead of delegating to the legacy pure-Python `route()` helper and reparsing URLs.
32+
- Oversized request bodies are capped in the ASGI bridge before the FastAPI app reads them, so the Worker no longer buffers unbounded POST bodies before rejecting them.
33+
- Deployment smoke refuses to send the Turnstile smoke-bypass secret to non-HTTPS origins.
34+
- Worker and static responses now include HSTS alongside CSP, frame, referrer, and nosniff headers.
35+
- Check-script boilerplate is consolidated through `scripts/_common.py`; quality gates share the canonical loader, registry reader, and frontmatter parser.
36+
- Small accent-colored text and the primary Run button use WCAG-AA contrast colors; the editor textarea has an accessible label.
3137
- Content corrections across 29 example pages: dict-iteration RuntimeError scoped to key insertion/removal, PEP 695 `type` statement teaching, NotImplemented-returning operator methods, visible positional-only/keyword-only TypeError demos, divergent broad-except demonstration, honest sandbox-boundary framing for subprocesses/threads/networking, cell-specific prose replacing copied intros, and four broken inline links now rendering as real anchors.
3238

3339
## 2026-05-16

CONTRIBUTING.md

Lines changed: 8 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,6 @@ Then run:
4949

5050
```bash
5151
make verify
52-
scripts/check_example_migration_parity.py
5352
scripts/format_examples.py --check
5453
make verify-python-version VERSION=3.13
5554
git diff --check
@@ -68,9 +67,9 @@ These scripts enforce the catalog-level rules from `docs/example-quality-rubric.
6867

6968
| Script | What it gates |
7069
| --- | --- |
71-
| `scripts/check_registry_integrity.py` | Every owner slug in `docs/quality-registries.toml` exists in `manifest.toml`; tokens are present; each `paired_pages` pair is discoverable through `see_also`. |
72-
| `scripts/check_confusable_pairs.py` | Each confusable pair's owning page contains every token that signals the contrast; a token shadowed inside a longer sibling token (plain `def` inside `async def`) does not count. |
73-
| `scripts/check_broad_surface_tours.py` | Each broad-title page either covers every required form or sets `scope_first_pass = true` with `see_also` links to focused neighbors. |
70+
| `scripts/check_registry_integrity.py` | Every owner slug in `docs/quality-registries.toml` exists in `manifest.toml`; tokens are present; each `paired_pages` pair is discoverable through `see_also` and has registry-named cell tokens demonstrated inside a teaching cell. |
71+
| `scripts/check_confusable_pairs.py` | Each confusable pair's owning page contains every token inside teaching cells; a token shadowed inside a longer sibling token (plain `def` inside `async def`) does not count. |
72+
| `scripts/check_broad_surface_tours.py` | Each broad-title page either covers every required form or sets `scope_first_pass = true` with registry-named `focused_neighbors` linked through `see_also`. |
7473
| `scripts/check_footgun_coverage.py` | Each canonical Python footgun has a page that contains both broken-form and fixed-form tokens. |
7574
| `scripts/check_notes_supported.py` | Every `:::note` bullet (across all note blocks) shares at least one keyword with the page body outside the notes, so notes cannot assert behavior the page never demonstrates. |
7675
| `scripts/check_program_covers_cells.py` | Every executable cell shares substantive code with the `:::program` block, so the editor reproduces what the cells teach; `standalone_cells = true` opts out visibly. |
@@ -82,16 +81,16 @@ These scripts enforce the catalog-level rules from `docs/example-quality-rubric.
8281
| `scripts/check_journey_outcomes.py` | Every journey section declares learner outcomes. |
8382
| `scripts/audit_example_graph.py --check` | The `see_also` graph has no broken targets, self-links, over-linked pages, or orphaned examples. |
8483

85-
The single source of truth for the registries is `docs/quality-registries.toml`. Add a new pair, broad tour, or footgun there, then update the owning page so the tokens appear in cells or prose.
84+
The single source of truth for the registries is `docs/quality-registries.toml`. Add confusable pairs, broad tours, footguns, journey metadata, figure attachments, captions, and curated scores there, then update the owning page so the tokens appear in cells or prose.
8685

8786
## Adding a new example end to end
8887

8988
1. Write `src/example_sources/<slug>.md` (frontmatter, intro prose, one `:::program`, cells, notes) and add the slug to `manifest.toml`'s `order`.
9089
2. Add `see_also` links in both directions — `scripts/audit_example_graph.py --check` fails orphaned pages.
91-
3. Score the page against `docs/example-quality-rubric.md` and add the entry to `EXAMPLE_QUALITY_SCORES` in `src/marginalia.py`.
92-
4. Attach a figure in `src/marginalia.py` (or add a `no_figure_rationales` entry with a review date) and score it; review it on `public/prototyping/marginalia-gestalt.html`, which shows the production caption under every figure.
93-
5. Run `make build`, `make verify-examples`, `make quality-checks`, then `uv run python scripts/refresh_golden_fixture.py` and review the structural summary it prints.
94-
6. Run the full `make verify` with a local Worker running, then commit — including the regenerated `src/example_sources_data.py` and fixture.
90+
3. Score the page against `docs/example-quality-rubric.md` and add the `[[example_quality_scores]]` entry to `docs/quality-registries.toml`.
91+
4. Attach a figure by adding `[[figure_attachments]]` and `[[example_figure_scores]]` entries to `docs/quality-registries.toml` (or add a `no_figure_rationales` entry with a review date); keep only new paint functions in `src/marginalia.py`. Review the result on `public/prototyping/marginalia-gestalt.html`, which shows the production caption under every figure.
92+
5. Run `make build`, `make verify-examples`, `make quality-checks`, and `scripts/format_examples.py --check`.
93+
6. Run the full `make verify` with a local Worker running, then commit — including the regenerated `src/example_sources_data.py`, `src/editorial_registry_data.py`, `src/asset_manifest.py`, and fingerprinted assets.
9594

9695
## Secrets and deploy configuration
9796

Makefile

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,21 +2,24 @@
22
# machine regardless of the system python3 (examples use 3.12+ `type` syntax).
33
PY := uv run --python 3.13
44

5-
.PHONY: test embed-examples build check-generated fingerprint prototypes browser-layout-test seo-cache-lint verify-examples check-registry-integrity check-confusable-pairs check-broad-surface-tours check-footgun-coverage check-notes-supported check-program-covers-cells check-prose-duplication check-inline-links score-example-criteria check-quality-scores check-no-figure-rationales check-journey-outcomes audit-example-graph quality-checks rubric-audit format-examples verify-python-version verify smoke-deployment dev deploy lint
5+
.PHONY: test embed-examples embed-editorial-registry build check-generated fingerprint prototypes browser-layout-test seo-cache-lint verify-examples check-registry-integrity check-confusable-pairs check-broad-surface-tours check-footgun-coverage check-notes-supported check-program-covers-cells check-prose-duplication check-inline-links score-example-criteria check-quality-scores check-no-figure-rationales check-journey-outcomes audit-example-graph quality-checks rubric-audit format-examples verify-python-version verify smoke-deployment dev deploy lint
66

77
test:
88
$(PY) python -m unittest discover -s tests -v
99

1010
embed-examples:
1111
$(PY) scripts/embed_example_sources.py
1212

13-
build: embed-examples fingerprint prototypes
13+
embed-editorial-registry:
14+
$(PY) scripts/embed_editorial_registry.py
15+
16+
build: embed-examples embed-editorial-registry fingerprint prototypes
1417

1518
check-generated: build
16-
git diff --exit-code src/example_sources_data.py src/asset_manifest.py public/_headers public/prototyping
19+
git diff --exit-code src/example_sources_data.py src/editorial_registry_data.py src/asset_manifest.py public/_headers public/prototyping
1720
test -z "$$(git status --porcelain public/prototyping public/*.css public/*.js)"
1821

19-
fingerprint: embed-examples
22+
fingerprint: embed-examples embed-editorial-registry
2023
$(PY) scripts/fingerprint_assets.py
2124

2225
prototypes: embed-examples

README.md

Lines changed: 7 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -47,8 +47,8 @@ Python documentation links point to the official [Python documentation](https://
4747
- `src/example_sources/*.md` contains the human-editable examples.
4848
- `src/example_loader.py` parses Markdown examples into the runtime catalog.
4949
- `src/examples.py` is a compatibility shim for the loaded catalog.
50-
- `src/example_sources_data.py` is generated embedded source data for Cloudflare Workers.
51-
- `src/marginalia.py` curates the figure registry (paint functions, attachments, scores); `src/marginalia_grammar.py` is the locked SVG drawing grammar.
50+
- `src/example_sources_data.py` and `src/editorial_registry_data.py` are generated embedded data for Cloudflare Workers.
51+
- `src/marginalia.py` keeps executable figure paint functions; attachments, captions, scores, journeys, and edge labels live in `docs/quality-registries.toml` and load through `src/editorial_registry.py`.
5252
- `src/textfmt.py` renders inline prose (code spans and internal links) for pages and figure captions.
5353
- `public/` contains static assets served by Workers Assets.
5454
- `python_modules/` is generated by the Workers tooling and intentionally ignored by Git.
@@ -101,7 +101,6 @@ Then run the main checks before deploying or pushing:
101101

102102
```bash
103103
make verify
104-
scripts/check_example_migration_parity.py
105104
scripts/format_examples.py --check
106105
make verify-python-version VERSION=3.13
107106
git diff --check
@@ -142,6 +141,7 @@ public/site.<hash>.css
142141
public/syntax-highlight.<hash>.js
143142
public/editor.<hash>.js
144143
src/asset_manifest.py
144+
src/editorial_registry_data.py
145145
```
146146

147147
Rendered HTML uses only fingerprinted asset URLs. `public/_headers` gives these immutable cache headers:
@@ -160,7 +160,6 @@ Run checks, then deploy:
160160

161161
```bash
162162
make verify
163-
scripts/check_example_migration_parity.py
164163
scripts/format_examples.py --check
165164
make deploy
166165
```
@@ -190,21 +189,20 @@ make build
190189
make verify-examples
191190
make quality-checks
192191
scripts/format_examples.py --check
193-
scripts/check_example_migration_parity.py
194192
make check-generated
195193
```
196194

197-
If the parity script reports intentional content differences, regenerate the
198-
structural snapshot with `uv run python scripts/refresh_golden_fixture.py`,
199-
review the summary it prints, and commit the fixture with your edit.
195+
The migration-era golden fixture has been removed; intentional content edits are
196+
reviewed through the Markdown diff, generated-source diff, verifier output, and
197+
quality gates rather than a refreshed snapshot.
200198

201199
`make quality-checks` runs the content gates: registry integrity, confusable
202200
pairs, broad-surface tours, footguns, notes grounding, program-covers-cells,
203201
prose duplication, inline links, criterion scoring with a curated-score delta
204202
bound, quality scores (waiver expiry, journey average), no-figure rationales,
205203
journey outcomes, and the see_also graph audit.
206204

207-
`src/example_sources_data.py` is generated and committed so Cloudflare Workers can load examples in production. Do not edit it by hand.
205+
`src/example_sources_data.py` and `src/editorial_registry_data.py` are generated and committed so Cloudflare Workers can load examples and editorial registries in production. Do not edit them by hand.
208206

209207
For a Python version migration, update `python_version` and `docs_base_url` in `src/example_sources/manifest.toml`, then run:
210208

0 commit comments

Comments
 (0)