Interactive lessons surface: generator, lessons, glossary (draft)#79
Draft
nonreagent wants to merge 19 commits into
Draft
Interactive lessons surface: generator, lessons, glossary (draft)#79nonreagent wants to merge 19 commits into
nonreagent wants to merge 19 commits into
Conversation
Generated per-lesson HTML under ent/lessons/ with clickable glossary terms and one-level prerequisite chains, built by a dependency-free Node generator from docs/lessons/*.md as the single source of truth. Engine/theme seam keeps the eventual reusable skill a lift, not a rewrite. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Ej8GZ5gZEaB2nsXG7aMxqn
Nine TDD tasks building ent/lessons/build.mjs: inline + block markdown renderers, lesson parser, glossary validation/index/annotation, page and index templates, the browser drawer, orchestration with a --check gate, and the full glossary authoring pass. renderInline verified against the Task 1 suite before landing. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Ej8GZ5gZEaB2nsXG7aMxqn
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Ej8GZ5gZEaB2nsXG7aMxqn
Build the placeholder sentinel with String.fromCharCode(0) so the source carries no raw NUL byte (git was treating build.mjs as binary). Share one slots array and loop the restore so a link label containing inline code resolves instead of rendering "undefined". Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Ej8GZ5gZEaB2nsXG7aMxqn
Implements renderMarkdown function that converts lesson markdown (headings, paragraphs, fenced code, and unordered/ordered lists with one nesting level) to HTML. Delegates inline rendering to renderInline and escapes code blocks literally without inline interpretation. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Ej8GZ5gZEaB2nsXG7aMxqn
List items now accumulate lazy-continuation lines the way paragraphs do, so a bullet or numbered item spanning several source lines stays one <li> instead of truncating and leaking the remainder into a stray <p>. Multiple nested siblings now share one nested list, and a marker-type change splits into separate lists. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Ej8GZ5gZEaB2nsXG7aMxqn
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Ej8GZ5gZEaB2nsXG7aMxqn
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Ej8GZ5gZEaB2nsXG7aMxqn
Appends THEME, renderTagChips, renderLessonPage, renderStylesheet, and renderGlossaryScript to build.mjs. All exports consume existing functions and pass unit tests. CSS stylesheet generated from theme palette; HTML page template includes provenance header and asset links (lessons.css, glossary.js, drawer.js). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Ej8GZ5gZEaB2nsXG7aMxqn
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Ej8GZ5gZEaB2nsXG7aMxqn
esc() fills double-quoted HTML attributes (data-term, href) but only escaped &<>. Add " escaping so a glossary key or link URL containing a quote can't break out of the attribute, matching the server-side escapeAttr convention. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Ej8GZ5gZEaB2nsXG7aMxqn
Implements parseReadmeIndex() to extract lesson rows and reading paths from the README markdown table, and renderIndexPage() to render a full HTML index page with styled table and theme navigation. Includes CSS rules for index table layout with sparse-row markers. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Ej8GZ5gZEaB2nsXG7aMxqn
A GFM table cell escapes a literal pipe as backslash-pipe; parseReadmeIndex now restores it, so PR #48's takeaway renders .unwrap_or_else(|err| ...) instead of leaking the backslashes into the page. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Ej8GZ5gZEaB2nsXG7aMxqn
…erated site buildSite composes the prior renderers into a pure filename->content map; main() does the surrounding fs read/validate/write and exits nonzero on a dangling glossary prereq/alias or cross-lesson link. Runs the generator to produce the real site (31 lesson pages + index.html + lessons.css + drawer.js + glossary.js) and wires `just build-lessons` / `just serve`. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Ej8GZ5gZEaB2nsXG7aMxqn
Expand docs/lessons/glossary.json from 5 seed terms to 45 foundational entries, each with a plain-language definition, a why-it-matters line grounded in the project, a prerequisite ladder, and a doc link. Regenerate the site: 36 distinct terms now annotate across the lessons, and prereq chains descend to their roots. Also fix the drawer scrim swallowing every click: the closed scrim sat over the page with opacity 0, which does not stop pointer events. It now has pointer-events: none until open. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Ej8GZ5gZEaB2nsXG7aMxqn
The teaching series behind the generated ent/lessons/ site: 31 per-PR lessons plus _TEMPLATE.md and the README index. Committing the source so the site is regenerable from a fresh clone via `node ent/lessons/build.mjs`. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Ej8GZ5gZEaB2nsXG7aMxqn
Authoring-first v1: a reusable skill to draft per-PR teaching lessons from real diffs in any repo, packaging the honesty rule, one-concept, real-snippets, and fixed-template process. Tracks a vocabulary index as the seed for a later glossary; the interactive site and full glossary are deferred. Lives in the public ~/.claude, so the portability scrub is an explicit acceptance gate. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Ej8GZ5gZEaB2nsXG7aMxqn
Durable notes on the non-obvious traps from building ent/lessons/build.mjs: the literal-escape-becomes-NUL-byte trap, testing against real repo content vs synthetic fixtures, the browser-verification gap that let the scrim pointer-events bug through, text-vs-attribute escaping, and the portability scrub gate for the extracted skill. Each cost a real debugging loop. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Ej8GZ5gZEaB2nsXG7aMxqn
The prior commit wrote three literal backslash-u-0000 escapes that the edit tool turned into real NUL bytes, so git saw the file as binary. Restored the intended text so the doc is readable, exactly the trap it documents. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Ej8GZ5gZEaB2nsXG7aMxqn
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Draft PR to track the interactive lessons work. Not ready to merge as-is — opened for visibility and to keep the branch from getting lost.
Builds a "learn by example, one PR at a time" surface for this repo:
ent/lessons/build.mjs, 39 tests) that turnsdocs/lessons/*.mdinto an interactive site: click a backticked term and a side panel opens with a plain-language definition, a why-it-matters line, and clickable prerequisite chips you can descend into._TEMPLATE.mdand the README index.docs/lessons/glossary.json) with prerequisite ladders.just build-lessons/just servewiring; generated site committed underent/lessons/.Design docs (on this branch)
docs/superpowers/specs/2026-07-11-interactive-lessons-design.mddocs/superpowers/plans/2026-07-11-interactive-lessons.mddocs/superpowers/specs/2026-07-12-lessons-from-prs-skill-design.mddocs/superpowers/lessons-generator-landmines.mdWhy draft / status
resolve/fileURLToPathimports inbuild.mjs, plus a few style nits).ent/lessons/*.html+ assets are committed (a deliberate spec decision), so regeneration diffs will surface in future changes — worth deciding whether to keep committing them or gitignore + build in CI.pointer-eventsbug was caught and fixed that way.Follow-ups tracked elsewhere
lessons-from-prsauthoring skill: Build the lessons-from-prs authoring skill dotfiles#13.Open questions before this could merge
mainor a long-lived docs branch.