Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,9 @@ jobs:
cache: npm
- run: npm ci
- run: npm run content:validate
- run: npm run typecheck
# typecheck must run AFTER a build once: Next.js generates the route
# types (.next/types) that typed App Router files reference. Running it
# first made CI fail on every clean clone. See docs/V1-UPGRADE-AUDIT.md §3.1.
- run: npm run lint
- run: npm test
- run: npm run build
Expand All @@ -25,3 +27,4 @@ jobs:
BETTER_AUTH_URL: http://localhost:3000
BETTER_AUTH_SECRET: ci-secret-ci-secret-ci-secret-ci
TURSO_DATABASE_URL: file:./data/learnpath.db
- run: npm run typecheck
4 changes: 4 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -48,3 +48,7 @@ yarn-error.log*
# typescript
*.tsbuildinfo
next-env.d.ts

# python caches (brand asset render scripts)
__pycache__/
*.pyc
71 changes: 62 additions & 9 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,13 +1,66 @@
# Changelog

## 0.1.0 — 2026-09-12
## 1.0.0 — 2026-09-13

V1 product upgrade: audited, repaired, integrated, redesigned.

### Repaired (verified defects)

- **CI order:** `typecheck` ran before the build that generates Next's route types, failing every
clean clone. Workflow now typechecks after build; `layout.tsx` no longer depends on generated
globals either.
- **Progress sync:** re-marking an existing entity uploaded the wrong record (array tail instead of
the changed record). `mark()` now sends exactly the record that changed.
- **YouTube fallback:** DOM `onerror` on a cross-origin iframe never fires; the documented
"unavailable" state was unreachable. Now detected via the official IFrame Player API `onError`,
with alternatives + report so the path continues.
- **Notes corruption:** server-side `replace(/<script/gi, …)` rewrote learners' text for zero
security benefit. Removed; sanitization returns only if Markdown rendering lands.
- **Editor honesty:** `@monaco-editor/react` was installed but unused behind a textarea named
`MonacoField`. Monaco now ships as a lazy client chunk with a functional textarea fallback.
- **Placeholder CTAs:** GitHub/Discord links defaulted to `github.com` / `discord.com` home pages.
Defaults now point at the real repository; the Discord CTA renders only when an invite exists.
- **Raw ids in UI:** skill prerequisites rendered as slugs; they are now linked skill titles.
- **Error page** no longer leaks `error.message` to visitors.

### Content

- Integrated the embed-verified research dataset: **+167 resources** (218 total), 0 schema errors,
every YouTube item `embed_status: verified`; 5 exact-duplicate videos skipped with reasons.
- Added 10 skills the dataset proves out (programming fundamentals, DSA, Python web APIs,
containers & orchestration, CI/CD & cloud, system design, Next.js, MCP, agent frameworks,
security fundamentals) and wired 36 existing skills with alternative/quick/project/docs slots.
- Roadmaps are now **staged journeys** (4–5 stages each) with real branch edges; schema gained
`stages[]`, resources gained `publishedAt`, `warnings[]`, `learningOutcomes[]`,
`playlistVideoCount`.
- Practice top-ups: JavaScript, TypeScript, HTML and SQL exercises plus four new challenges, so the
Full Stack path has a real proof-of-work loop.
- Import pipeline: `scripts/import-resources.mjs` + `tests/import.test.ts` gate (verified embeds,
no rejected videos, one `best-overall` per skill).

Initial V1:
### Design & UI

- Design system v2: token contract, three-step elevation, restrained glass, motion scale with a
reduced-motion contract, 2px hover lift, cursor spotlight, focus and touch-target standards.
- New brand: vector mark (path + destination), generated favicon/apple-touch/OG rasters (~150KB
total, was 4.5MB of duplicates), boilerplate starter assets deleted.
- Homepage rebuilt to the product rhythm: hero + live loop constellation, continue-learning strip,
roadmap cards with real progress, how-it-works, editor's picks with real thumbnails, field
categories with real counts, practice preview, projects, open source, community.
- Roadmap graph: stage bands, curved dependency edges, done/active/locked/open states with icons,
keyboard-usable nodes, legend; detail pages group skills by stage.
- Resource cards: thumbnail, provider, duration, level, score, badge, CTA, hover.
- Resource pages: why-recommended, outcomes, warnings, facts, alternatives, JSON-LD.
- Learning workspace: ARIA tabs, mobile path rail, sticky roadmap list, coherent three-pane desktop.
- Library: filterable by topic/type/level with live counts; search is instant and grouped.

### Platform

- SEO: canonicals, JSON-LD, stable sitemap dates, `/search` noindex.
- Docs truth pass: content location, editor, lazy loading, postMessage reality, community framing.

## 0.1.0 — 2026-09-12

- Six independently authored roadmaps
- Skill pages, YouTube facade embeds, notes, progress
- Practice: Python, JavaScript, TypeScript, HTML/CSS, SQL
- Challenges and projects
- Better Auth (email + OAuth when configured)
- Admin report queue
- Search, SEO, dark/light theme
Initial V1: six independently authored roadmaps; skill pages, YouTube facade embeds, notes,
progress; practice (Python, JavaScript, TypeScript, HTML/CSS, SQL); challenges and projects;
Better Auth (email + OAuth when configured); admin report queue; search, SEO, dark/light theme.
3 changes: 2 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ This is not YouTube, Coursera, Udemy, a certificate mill, or an AI course genera
- Better Auth (Google, GitHub, Discord, email)
- Turso / libSQL + Drizzle ORM
- Git-canonical curriculum (Zod-validated)
- Monaco + Pyodide + sql.js + sandboxed JS/HTML (browser only)
- Monaco (lazy client chunk) + Pyodide + sql.js + sandboxed JS/HTML (browser only)

## Local setup

Expand All @@ -39,6 +39,7 @@ OAuth buttons appear only when the corresponding client IDs are set. Email/passw
| `npm run build` | Production build |
| `npm run test` | Unit tests |
| `npm run content:validate` | Curriculum integrity |
| `node scripts/import-resources.mjs` | Regenerate catalog from research dataset |
| `npm run db:push` | Push Drizzle schema |
| `npm run lint` | ESLint |
| `npm run typecheck` | `tsc --noEmit` |
Expand Down
Binary file removed branding/logo-dark.png
Binary file not shown.
Binary file added branding/mark-light-512.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
8 changes: 8 additions & 0 deletions branding/mark-light.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
10 changes: 10 additions & 0 deletions branding/mark.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file removed branding/og-image.png
Binary file not shown.
Binary file added branding/social-preview.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
26 changes: 26 additions & 0 deletions content/research/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
# Research inputs (build-time only)

These two files are the output of LearnPath's resource-research pass (2026-09-12):

- `learnpath-youtube-resources.json` — the full research dataset (schema v5): 19 categories,
172 shipped resources, 8 rejected (embed disabled by owner), verification evidence per item.
- `learnpath-resources.import.json` — the same 172 records pre-shaped to LearnPath's
`resourceSchema` field names.

They are **inputs**, not runtime content. Nothing in `src/` imports them. The canonical,
validated catalog is generated from them by:

```bash
node scripts/import-resources.mjs # rewrites resources.imported.ts + skill-wiring.ts + skills.imported.ts
npm run content:validate # must pass
npm test # tests/import.test.ts gates embed_status + schema
```

Rules enforced at import time:

1. Every record must satisfy `src/lib/content/schema.ts`.
2. Every record must be `embed_status: "verified"` in the research file, or it is dropped.
3. Dataset `skill_id`s are mapped onto repo skill ids by an explicit table in the script;
unmapped topics stay as `topics` for search/filtering and never become dangling refs.
4. One `best-overall` label per repo skill (highest `editorScore` wins); other holders are
demoted to `alternative`, per the dataset's own `curation.role_rule`.
Loading
Loading