Quality pass: green baseline, tests, content validator (+42 content fixes), perf & a11y#3
Merged
Conversation
- SequencePlayer: guard definition in bindStepsToDom; type SVG elements as SVGGraphicsElement so getBBox() resolves. - Introduce shared StoryContent base interface; CategoryStory and SubcategoryStory extend it. CategoryStoryView accepts StoryContent so subcategory stories render without a type mismatch. - StoryDiagramModal: containerEl is now $state and captured locally in the async render closure. - SimulatorView: derive config from protocolId and load via $effect. - ConceptTrigger: fold hover styling into enter/leave; add focus/blur so keyboard users get the concept tooltip. - CategoryIcon: drive hover animation via CSS :hover (+ reduced-motion guard) instead of a JS handler on a roleless span. npm run check: 0 errors, 0 warnings. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Genuine code fixes:
- Remove dead code: FOUNDATION_TEASERS/totalParts/foundationSections and an
unused navigateToBookChapter import (DetailPanel); unused stubChapters helper
(book/chapters); unused imports/vars in SearchBar, OutageView, NodeTooltip,
SequencePlayer, layouts; vestigial gqlRequestLayer params.
- Drop the unused `color` prop from JourneyListView (it colors per-journey) and
the matching caller arg; drop unused `stepIndex` prop from ActorStage + caller.
- Replace empty `interface MeshLink {}` with a type alias.
- Add keys to name-segment {#each} blocks (ProtocolHeader, MobileDetailSheet).
- Type the dev-only window globals (__dev/__tourDriver) in app.d.ts, removing
three `as any` casts; make tourDriver const.
- Remove three unused eslint-disable directives in scripts/.
Rule-level config (with justifications in eslint.config.js):
- Off: svelte/no-navigation-without-resolve (app centralizes ${base} prefixing)
and svelte/prefer-svelte-reactivity (all Map/Set are ephemeral locals).
- Allow _-prefixed unused identifiers.
Per-site disables with justification for intentional patterns:
- svelte/no-dom-manipulating at mermaid innerHTML injection sites.
- svelte/no-at-html-tags for trusted highlighted code + static SVG icons.
npm run check: 0/0. eslint: only no-useless-escape remains (handled next).
Build green.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…en codemod Adds scripts/fix-useless-escapes.ts, which consumes ESLint's JSON report and deletes only the exact backslashes ESLint flagged as unnecessary (right-to-left per line). A naive regex would have been wrong — e.g. in `Berkeley Unix\'s \`.rhosts\``, the necessary `\'` (single-quoted string) must stay while the redundant backtick escapes go; the codemod preserves exactly that distinction. Verification: - ESLint no-useless-escape: 537 → 0; 0 skipped (every position was a backslash). - 34/35 changed files: esbuild-canonicalized output byte-identical → string values provably unchanged. - text-parser.ts: two regex char-class simplifications ([\[ → [ inside classes), proven behaviorally identical across 23 edge-case inputs. - npm run check: 0/0. Build green. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Repo-wide `prettier --write` over source (Svelte/TS/JS/CSS/JSON). Pure formatting — no code changed; check/lint/build all green afterward. Also extends .prettierignore to exclude long-form prose working docs (research/, podcast-blueprints/, *.md notes) where prettier's markdown reformatting is non-idempotent and adds no value; those files are left exactly as authored so the CI `prettier --check` gate stays meaningful for code. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- New ci.yml runs prettier/eslint (npm run lint), svelte-check (npm run check), and a production build on pull requests and non-main pushes. - deploy.yml now runs the same lint + check gates before building, so main cannot deploy a red baseline. (Test + cross-reference-validation gates are added in the test/validator commits that follow.) Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The earlier reactivity-warning fix wrapped simState.load() in an $effect, which
made Svelte flag unsafe state mutation at runtime (config/userValues written
inside an effect). The parent already remounts via {#key protocolId}, so the
load is genuinely once-per-mount; untrack(() => getSimulation(protocolId))
expresses that, silences the state_referenced_locally warning, and removes the
runtime errors. Verified clean in-browser across TCP/TLS simulate + stepping.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Unit (vitest, node env): 39 tests over the pure logic the content layer leans on — sequence-parser (notes/arrows/blocks), text-parser (rich-markup grammar, cross-ref resolution, bold-group recursion, strip), math, and color helpers. Config added to vite.config.ts; scripts test:unit/test:unit:watch added; `npm test` now runs unit then e2e. E2e (playwright, against the production preview — no window.__dev): 15 tests covering graph load + console-clean, /p/tcp detail + overview, Simulate tab, 404, category/subcategory, search, every registry/book/journey page prerender, and a mobile bottom-sheet + no-horizontal-overflow check. Deleted the demo scaffolding routes (src/routes/demo/**) and their placeholder spec. CI (ci.yml) now runs test:unit + e2e. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…refs
Adds scripts/validate-cross-references.ts, wired into `npm run build` and CI.
It loads every registry and resolves all cross-references — structured
(protocol connections, categoryId, subcategory membership, journey steps,
comparison pairs, RFC/outage/pioneer/frontier protocol lists, story parents)
and inline [[id|label]] / {{concept}} markup in prose — failing the build on the
first broken link. Exports categoryStories / subcategoryStories / allPairs so it
can see those registries.
It immediately found 42 real bugs (refs that rendered as dead text / inert
tooltips); all fixed:
- Added 9 missing concepts: egp, congestion-collapse, ossification,
tcp-handshake, http-verbs, comet, phishing, ttls, rsn.
- Corrected misnamed refs: {{as}}→autonomous-system, {{ct}}→certificate-
transparency, [[head-of-line-blocking]]→{{…}} (it's a concept).
- Fixed protocol ids wrapped as concepts: {{ip|quic|websockets|mptcp}} → [[…]].
- [[ipv4]]→[[ip]]; [[ice|stun|turn]]→[[nat-traversal|…]]; [[datagram-transport]]
(a subcategory, not a protocol) → plain text.
- Unwrapped 3 not-yet-added pioneers (Jay Kreps; Stanford-Clark; Nipper) to bold
text rather than fabricate birth-year data — flagged to add later.
- pioneer dan-bernstein categories 'security' (not a category) → 'utilities'.
check/lint/unit/build all green.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
CodeExample imported highlight.js core + 8 grammars statically (~150 KB raw / ~138 KB gz), so every first paint shipped it even when no code example was on screen. Switched to a dynamic import() on mount: code renders as escaped plain text until hljs resolves, then highlights reactively. Measured on the prerendered / (single build, since chunk hashes are non-deterministic): hljs grammar code is no longer in the initial chunk set; initial JS is ~1171 KB gz across 23 chunks. mermaid and driver.js were already dynamic and confirmed absent from initial. The remaining initial weight is hand-authored prose — documented in docs/baselines as the next (riskier) win. Added an e2e guard that http1's code example still highlights after the lazy load. check/lint/16 e2e all green. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- static/screenshot.png (1.9 MB) was not referenced anywhere — removed. - og-image.png was 2678×1612 / 1.9 MB, far larger than a social card needs. Resized to the standard 1200px width (1200×722) → 544 KB, a 71% cut. Format unchanged (crisp text, no JPEG artifacts) and no og:image dimensions are declared, so no meta changes needed. Note: a spec-valid sitemap.xml needs absolute URLs, but no deploy domain (CNAME/og:url) is configured in the repo — deferred until the canonical URL is known rather than guessing the GitHub Pages path. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Added a global reduced-motion reset in layout.css that collapses animations, transitions, and smooth scroll to near-instant when the OS requests it. This covers the detail-panel slide-in plus the modal / tooltip / simulator-arrow entrance animations that previously ignored the preference — in one place rather than per component. The canvas graph already honors the same preference in JS. Added an e2e test asserting reduced-motion users still receive the detail-panel content (animation off must not mean content hidden). check/lint/17 e2e green. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
syncPositions copied simNodes[i] → nodes[i], which only works while d3-force's internal array stays in creation order. A reordered or resized `nodes` array would silently scatter every node to the wrong position. Now keyed by id. Added simulation.test.ts (TDD): a reordered-array case that fails on the old index logic and passes on the id match, plus an orphan-node case. Verified in-browser that the graph still settles (live node positions populated, console clean). 41 unit tests green. Note: the render-loop perf micro-opts from the audit (memoize per-frame node/connection maps, evict settled hover/dim animations, pool gradients, delta-time viewport lerp) are deferred — they touch the canvas hot path with no current user-facing bug, so they want a watched, profiled pass rather than this autonomous run. Tracked in IMPROVEMENT_PLAN Phase 7. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The four diagram components (SequencePlayer, MermaidDiagram, StoryDiagramModal,
StoryDiagram) each repeated the dynamic import('mermaid') + initialize() with
the same base config (theme/security/font), differing only in their sequence/
flowchart tuning. Extracted loadMermaid(overrides) into mermaid-helpers so the
shared base lives in one place; each component passes only its own overrides.
Behavior unchanged — verified TCP sequence diagram + transport category-story
diagrams still render, console clean, build green.
The larger GenericLink (6 inline link components) and ModalShell (3 modals)
dedups from the audit are deferred: they carry visual-regression risk that
wants a watched, screenshot-diffed pass rather than this autonomous run.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
stripRichTextMarkup feeds plain-text surfaces (NodeTooltip body, AccessibleGraph screen-reader text). A label-less ref like [[tcp]] resolved to '' there, silently dropping the word. It now falls back to the display label (protocol abbreviation, concept term, RFC number, …), mirroring how parseRichText renders the same ref — so stripped text matches rendered text. Currently latent (no bare refs sit in oneLiner/description fields today, and the cross-ref validator can't catch it since the ref is valid) — this hardens the path for future content. Added unit tests; 43 unit tests green, validate green. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The cross-ref validator now flags duplicate ids. Protocol/pioneer/RFC/outage/ frontier collisions are fatal (none exist today, so this is a guard). Concepts have 18 pre-existing collisions where two authored definitions share an id and one silently shadows the other in conceptMap — reported as warnings for editorial review rather than auto-deleting authored content or unilaterally picking a "winner" (e.g. sip-invite, ns-record, bgp-update, and the company entries apple/google/microsoft/meta/cloudflare each have two definitions). Surfacing > guessing: the visible definition is valid today, so this is a content-curation task for the owner, now tracked by the build. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
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.
A multi-phase quality pass. Every change is an isolated, verified commit; the working tree builds clean and all gates are green.
Status — all green
npm run lint(was 604 errors) → 0npm run check(was 3 errors / 5 warnings) → 0 / 0npm run validate(new) → passes (18 advisory warnings, see below)Highlights
Content integrity (the big one)
A new build-time cross-reference validator (
scripts/validate-cross-references.ts, gatingbuild+ CI) resolves every structured id reference and every inline[[link]]/{{concept}}in prose. It immediately caught 42 real content bugs — refs rendering as dead text / inert tooltips — all fixed:{{concepts}},[[ipv4]]→[[ip]], STUN/ICE/TURN→nat-traversal, a pioneer's non-existent "security" category, etc.Green baseline + safety net
Perf, correctness, a11y, hygiene
highlight.js(~138 KB gz) now lazy-loads on demand instead of on every first paint.syncPositionsmatches simulation nodes by id, not array index (TDD).prefers-reduced-motionreset across all entrance animations.screenshot.png; shrankog-image.png1.9 MB → 544 KB.loadMermaid()helper; hardenedstripRichTextMarkup.Needs your editorial input (deliberately not auto-changed)
sip-invite,ns-record,bgp-update,apple/google/microsoft/meta/cloudflare). Surfaced as validator warnings rather than me deleting your writing.npm run validatelists them.Deliberately deferred (higher-risk, better watched)
Protocolmetadata/content split for first paint; GenericLink/ModalShell component dedup; canvas render-loop micro-opts; sitemap (needs a canonical deploy URL). All documented inIMPROVEMENT_PLAN.mdanddocs/baselines/bundle-baseline.md.🤖 Generated with Claude Code