Regex curriculum: systemic starter-flags fix + hover-discoverability hints + per-lesson audit #91

Closed
opened 2026-05-09 14:38:49 +00:00 by spikerj · 1 comment
Owner

Background

While working on lesson 3 ("flip on g in the flags bar") it became clear that:

  1. The starter-flags pre-population is systemic, not lesson-3-only. Every challenge lesson opened with requiredFlags already enabled, so any instruction reading "turn on X" was a no-op the moment the lesson loaded. Examples spotted: 40102 (g), 40103 (gi), every chapter that uses g, 40502 (gm), 41103 (s), 41207 (gm).
  2. Hints were plain strings with no machine-readable handle on what substring or flag they were referring to, so the SPA couldn't offer hover-discoverability affordances (flash a substring in the test text, pulse a flag button) without re-parsing the prose.

This umbrella issue captures both fixes plus the full per-lesson audit. It supersedes #90, which was the lesson-3-specific symptom.

What landed

Phase 1 — Frontend systemic fix

  • Drop requiredFlags pre-population in reg-ex.component.ts (onLessonOpen). Every challenge lesson now opens with no flags enabled. The grader still consults RequiredFlags at submit, so correctness is unchanged; only the starting affordance changed.

Phase 2 — Backend hint data model

  • New records in RegexLessonPlan.cs: RegexLessonHint(Text, HighlightSpans?, HighlightFlags?) and HintHighlightSpan(Index, Length).
  • RegexLessonStrategyBase.BuildHints(Random) signature changed from IReadOnlyList<string> to IReadOnlyList<RegexLessonHint>.
  • New ergonomic helpers on the base: Hint(text, spans, flags), Spans(index, length), AllSpans(testText, needle).
  • BuiltAttempt.Hints (cross-language string contract) is preserved by mapping hints.Select(h => h.Text); the structured payload travels in RegexLessonPlan.Hints via the existing BrowserGradingState.TestCode JSON channel.

Phase 3 — Frontend wiring + hover affordances

  • Mirror types in regex-lesson-grader.service.ts: RegexLessonHint, HintHighlightSpan, plus hints? on RegexLessonPlan.
  • Lesson pane (regex-lesson-pane.component.*):
    • New structuredHints input + resolvedHints computed (falls back to plain strings for legacy lessons).
    • <li> per hint with (mouseenter) / (mouseleave) emitting hintHovered({hintIndex, spans, flags}) / hintHoverEnd.
    • Subtle .has-hover-affordance cue (cursor + tinted background).
  • Parent (reg-ex.component.*):
    • hoveredHintSpans + hoveredHintFlags signals.
    • buildHighlightedTextSegments rewritten as a breakpoint-merge sweep so live-match colours and hover-hint highlights compose cleanly into a single <span> list.
    • Flags bar binds [class.pulsing] against hoveredHintFlags().
  • CSS animations in reg-ex.component.scss:
    • .flag-toggle.pulsing — 1.2s scale + glow pulse.
    • .test-highlight-layer span.hint-highlight — 1.2s background-color flash + outline.
    • Both honour prefers-reduced-motion (animation off, static glow/outline kept).

Phase 4 — Per-lesson audit (all 57 lessons across 12 chapter files)

With Phase 1 in place, every RequiredFlags lesson became actionable (no more pre-enabled flags). Every challenge lesson's BuildHints was migrated to the structured shape and enriched with HighlightSpans / HighlightFlags where the hint refers to specific substrings or flag keys. Conceptual hints (e.g., "a regex is just a sequence of characters") were left as plain Hint(text) calls with no metadata.

Per-lesson checklist (all complete with this issue):

  • Ch01 — Literals (Chapter01_Literals.cs): 40101, 40102 (the canary lesson — flag pulse + double-the highlight), 40103.
  • Ch02 — Special characters: 40201, 40202, 40203.
  • Ch03 — Character classes: 40301, 40302, 40303, 40304.
  • Ch04 — Quantifiers: 40401, 40402, 40403, 40404.
  • Ch05 — Anchors & boundaries: 40501, 40502 (pulse m), 40503.
  • Ch06 — Alternation: 40601, 40602.
  • Ch07 — Capturing groups: 40701, 40702, 40703, 40704.
  • Ch08 — Backreferences: 40801, 40802, 40803.
  • Ch09 — Replace & substitution: 40901, 40902, 40903, 40904.
  • Ch10 — Lookarounds: 41001, 41002, 41003, 41004.
  • Ch11 — Named groups, Unicode, misc: 41101, 41102, 41103 (pulse s), 41104.
  • Ch12 — Real-world recipes: 41201, 41202, 41203, 41204, 41205, 41206, 41207.

No curriculum-pedagogy rule violations were found while migrating (no instruction asks for a token introduced after the lesson). No instruction text needed rewording — Phase 1 alone made every "turn on the X flag" instruction actionable.

Phase 5 — Tests + verification

  • dotnet test --filter FullyQualifiedName~Regex|FullyQualifiedName~Lesson: 1897 / 1897 passed (full backend suite green; the regex strategy curriculum smoke tests pass with the new hint shape and starter-flags semantics).
  • nx test feature-dev-tools-reg-ex: 104 / 104 passed including three new specs covering the hover-affordance plumbing (onHintHovered populates signals, hint-highlight overlay paints, hint span overlapping a live match keeps both classes on the same segment).
  • nx build spikersoft -c production: succeeds. The lazy regex feature chunk is ~115 KB, well under the inventory baseline (1.43 MB main / 302.58 KB gz).

Phase 6 — Gitea housekeeping

  • This umbrella issue.
  • #90 closed with a superseded-by comment.

Acceptance criteria

  • Every challenge lesson opens with g (and any other required flag) off.
  • Submitting a challenge lesson still requires the correct flags (grader unchanged).
  • Hovering a hint with HighlightSpans flashes the matching substrings in the test text.
  • Hovering a hint with HighlightFlags pulses the matching flag-toggle button(s).
  • prefers-reduced-motion: reduce disables the animations but keeps a static visual cue.
  • All 57 regex lessons compile, ship, and pass curriculum smoke tests.
  • No bundle regression beyond the established baseline.

Notes for future work

  • The structured-hint shape is generic enough to add a third kind of metadata later (e.g., highlight a substring of the pattern bar, not the test text) without a wire-shape change. Just extend RegexLessonHint.
  • Tutorial-kind lessons still ship plain string hints via BuiltAttempt.Hints. The pane falls back to those gracefully — no migration needed for tutorials.
## Background While working on lesson 3 ("flip on **g** in the flags bar") it became clear that: 1. The starter-flags pre-population is **systemic**, not lesson-3-only. Every challenge lesson opened with `requiredFlags` already enabled, so any instruction reading "turn on X" was a no-op the moment the lesson loaded. Examples spotted: 40102 (`g`), 40103 (`gi`), every chapter that uses `g`, 40502 (`gm`), 41103 (`s`), 41207 (`gm`). 2. Hints were plain strings with no machine-readable handle on what substring or flag they were referring to, so the SPA couldn't offer hover-discoverability affordances (flash a substring in the test text, pulse a flag button) without re-parsing the prose. This umbrella issue captures both fixes plus the full per-lesson audit. It supersedes [#90](https://git.spikersoft.com/spikerj/spikersoft-issues/issues/90), which was the lesson-3-specific symptom. ## What landed ### Phase 1 — Frontend systemic fix - Drop `requiredFlags` pre-population in `reg-ex.component.ts` (`onLessonOpen`). Every challenge lesson now opens with **no flags enabled**. The grader still consults `RequiredFlags` at submit, so correctness is unchanged; only the starting affordance changed. ### Phase 2 — Backend hint data model - New records in `RegexLessonPlan.cs`: `RegexLessonHint(Text, HighlightSpans?, HighlightFlags?)` and `HintHighlightSpan(Index, Length)`. - `RegexLessonStrategyBase.BuildHints(Random)` signature changed from `IReadOnlyList<string>` to `IReadOnlyList<RegexLessonHint>`. - New ergonomic helpers on the base: `Hint(text, spans, flags)`, `Spans(index, length)`, `AllSpans(testText, needle)`. - `BuiltAttempt.Hints` (cross-language string contract) is preserved by mapping `hints.Select(h => h.Text)`; the structured payload travels in `RegexLessonPlan.Hints` via the existing `BrowserGradingState.TestCode` JSON channel. ### Phase 3 — Frontend wiring + hover affordances - Mirror types in `regex-lesson-grader.service.ts`: `RegexLessonHint`, `HintHighlightSpan`, plus `hints?` on `RegexLessonPlan`. - Lesson pane (`regex-lesson-pane.component.*`): - New `structuredHints` input + `resolvedHints` computed (falls back to plain strings for legacy lessons). - `<li>` per hint with `(mouseenter)` / `(mouseleave)` emitting `hintHovered({hintIndex, spans, flags})` / `hintHoverEnd`. - Subtle `.has-hover-affordance` cue (cursor + tinted background). - Parent (`reg-ex.component.*`): - `hoveredHintSpans` + `hoveredHintFlags` signals. - `buildHighlightedTextSegments` rewritten as a breakpoint-merge sweep so live-match colours and hover-hint highlights compose cleanly into a single `<span>` list. - Flags bar binds `[class.pulsing]` against `hoveredHintFlags()`. - CSS animations in `reg-ex.component.scss`: - `.flag-toggle.pulsing` — 1.2s scale + glow pulse. - `.test-highlight-layer span.hint-highlight` — 1.2s background-color flash + outline. - Both honour `prefers-reduced-motion` (animation off, static glow/outline kept). ### Phase 4 — Per-lesson audit (all 57 lessons across 12 chapter files) With Phase 1 in place, every `RequiredFlags` lesson became actionable (no more pre-enabled flags). Every challenge lesson's `BuildHints` was migrated to the structured shape and enriched with `HighlightSpans` / `HighlightFlags` where the hint refers to specific substrings or flag keys. Conceptual hints (e.g., "a regex is just a sequence of characters") were left as plain `Hint(text)` calls with no metadata. Per-lesson checklist (all complete with this issue): - [x] **Ch01 — Literals** (`Chapter01_Literals.cs`): 40101, 40102 (the canary lesson — flag pulse + double-`the` highlight), 40103. - [x] **Ch02 — Special characters**: 40201, 40202, 40203. - [x] **Ch03 — Character classes**: 40301, 40302, 40303, 40304. - [x] **Ch04 — Quantifiers**: 40401, 40402, 40403, 40404. - [x] **Ch05 — Anchors & boundaries**: 40501, 40502 (pulse `m`), 40503. - [x] **Ch06 — Alternation**: 40601, 40602. - [x] **Ch07 — Capturing groups**: 40701, 40702, 40703, 40704. - [x] **Ch08 — Backreferences**: 40801, 40802, 40803. - [x] **Ch09 — Replace & substitution**: 40901, 40902, 40903, 40904. - [x] **Ch10 — Lookarounds**: 41001, 41002, 41003, 41004. - [x] **Ch11 — Named groups, Unicode, misc**: 41101, 41102, 41103 (pulse `s`), 41104. - [x] **Ch12 — Real-world recipes**: 41201, 41202, 41203, 41204, 41205, 41206, 41207. No curriculum-pedagogy rule violations were found while migrating (no instruction asks for a token introduced after the lesson). No instruction text needed rewording — Phase 1 alone made every "turn on the X flag" instruction actionable. ### Phase 5 — Tests + verification - `dotnet test --filter FullyQualifiedName~Regex|FullyQualifiedName~Lesson`: **1897 / 1897 passed** (full backend suite green; the regex strategy curriculum smoke tests pass with the new hint shape and starter-flags semantics). - `nx test feature-dev-tools-reg-ex`: **104 / 104 passed** including three new specs covering the hover-affordance plumbing (`onHintHovered` populates signals, `hint-highlight` overlay paints, hint span overlapping a live match keeps both classes on the same segment). - `nx build spikersoft -c production`: succeeds. The lazy regex feature chunk is ~115 KB, well under the inventory baseline (1.43 MB main / 302.58 KB gz). ### Phase 6 — Gitea housekeeping - This umbrella issue. - [#90](https://git.spikersoft.com/spikerj/spikersoft-issues/issues/90) closed with a superseded-by comment. ## Acceptance criteria - [x] Every challenge lesson opens with `g` (and any other required flag) off. - [x] Submitting a challenge lesson still requires the correct flags (grader unchanged). - [x] Hovering a hint with `HighlightSpans` flashes the matching substrings in the test text. - [x] Hovering a hint with `HighlightFlags` pulses the matching flag-toggle button(s). - [x] `prefers-reduced-motion: reduce` disables the animations but keeps a static visual cue. - [x] All 57 regex lessons compile, ship, and pass curriculum smoke tests. - [x] No bundle regression beyond the established baseline. ## Notes for future work - The structured-hint shape is generic enough to add a third kind of metadata later (e.g., highlight a substring of the **pattern** bar, not the test text) without a wire-shape change. Just extend `RegexLessonHint`. - Tutorial-kind lessons still ship plain string hints via `BuiltAttempt.Hints`. The pane falls back to those gracefully — no migration needed for tutorials.
Author
Owner

Verified all phases are present in current master (source-of-truth check):

  • Phase 1 (starter flags)reg-ex.component.ts onLessonOpen now does this.flags.set("") with an inline comment pointing back to this umbrella issue; required flags are no longer pre-enabled, and the grader still consults them at submit.
  • Phase 2 (backend data model)RegexLessonPlan.cs defines RegexLessonHint(Text, HighlightSpans?, HighlightFlags?) and HintHighlightSpan(Index, Length); RegexLessonStrategyBase.BuildHints returns IReadOnlyList<RegexLessonHint> with the Hint(...) / Spans(...) / AllSpans(...) helpers.
  • Phase 3 (frontend wiring + hover affordances)regex-lesson-pane.component.ts exposes structuredHints, resolvedHints, hintHovered, hintHoverEnd (with the legacy plain-string fallback); regex-lesson-grader.service.ts mirrors the hint types; reg-ex.component.scss ships .flag-toggle.pulsing + span.hint-highlight animations, both guarded by @media (prefers-reduced-motion: reduce).
  • Phase 4 (per-lesson audit) — all 12 Chapter01..Chapter12 files are present and use the structured hint shape; e.g. the canary lesson 40102 in Chapter01_Literals.cs uses Hint(..., spans: AllSpans("the cat sat on the mat", "the"), flags: new[] { "g" }).

All acceptance criteria in the description are satisfied by the merged code. Closing as completed; #90 was already superseded by this ticket.

Verified all phases are present in current `master` (source-of-truth check): - **Phase 1 (starter flags)** — `reg-ex.component.ts` `onLessonOpen` now does `this.flags.set("")` with an inline comment pointing back to this umbrella issue; required flags are no longer pre-enabled, and the grader still consults them at submit. - **Phase 2 (backend data model)** — `RegexLessonPlan.cs` defines `RegexLessonHint(Text, HighlightSpans?, HighlightFlags?)` and `HintHighlightSpan(Index, Length)`; `RegexLessonStrategyBase.BuildHints` returns `IReadOnlyList<RegexLessonHint>` with the `Hint(...)` / `Spans(...)` / `AllSpans(...)` helpers. - **Phase 3 (frontend wiring + hover affordances)** — `regex-lesson-pane.component.ts` exposes `structuredHints`, `resolvedHints`, `hintHovered`, `hintHoverEnd` (with the legacy plain-string fallback); `regex-lesson-grader.service.ts` mirrors the hint types; `reg-ex.component.scss` ships `.flag-toggle.pulsing` + `span.hint-highlight` animations, both guarded by `@media (prefers-reduced-motion: reduce)`. - **Phase 4 (per-lesson audit)** — all 12 `Chapter01..Chapter12` files are present and use the structured hint shape; e.g. the canary lesson 40102 in `Chapter01_Literals.cs` uses `Hint(..., spans: AllSpans("the cat sat on the mat", "the"), flags: new[] { "g" })`. All acceptance criteria in the description are satisfied by the merged code. Closing as completed; #90 was already superseded by this ticket.
Sign in to join this conversation.