Skip to content

Interactive lessons surface: generator, lessons, glossary (draft)#79

Draft
nonreagent wants to merge 19 commits into
mainfrom
interactive-lessons
Draft

Interactive lessons surface: generator, lessons, glossary (draft)#79
nonreagent wants to merge 19 commits into
mainfrom
interactive-lessons

Conversation

@nonreagent

@nonreagent nonreagent commented Jul 14, 2026

Copy link
Copy Markdown
Collaborator

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:

  • A dependency-free Node generator (ent/lessons/build.mjs, 39 tests) that turns docs/lessons/*.md into 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.
  • The 31-lesson source series (one per merged PR) plus _TEMPLATE.md and the README index.
  • A 45-entry glossary (docs/lessons/glossary.json) with prerequisite ladders.
  • just build-lessons / just serve wiring; generated site committed under ent/lessons/.

Design docs (on this branch)

  • Spec: docs/superpowers/specs/2026-07-11-interactive-lessons-design.md
  • Plan: docs/superpowers/plans/2026-07-11-interactive-lessons.md
  • Reusable-skill extraction spec: docs/superpowers/specs/2026-07-12-lessons-from-prs-skill-design.md
  • Landmines (traps hit while building): docs/superpowers/lessons-generator-landmines.md

Why draft / status

  • Built via subagent-driven-development; all 9 tasks were task-reviewed, each fix loop is in the commit history.
  • The final whole-branch review has not run yet. Known minor findings are pending (e.g. unused resolve / fileURLToPath imports in build.mjs, plus a few style nits).
  • Generated 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.
  • The interactive drawer's live behavior was verified by hand (the Chrome extension was offline for automation); a scrim pointer-events bug was caught and fixed that way.

Follow-ups tracked elsewhere

Open questions before this could merge

  • Commit generated HTML vs gitignore + regenerate in CI.
  • Run the final whole-branch review and clear the minor findings.
  • Whether the lessons + site belong on main or a long-lived docs branch.

nonrational and others added 19 commits July 11, 2026 14:44
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
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants