Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
42acd73
feat(study): contracts for v1 study system (parallel-team scaffold)
Bissbert May 5, 2026
dde6385
feat(study): foundation — LocalStudyStore, migrations, hook wiring (T1)
Bissbert May 5, 2026
a0d650f
feat(study): T4 schema & tooling — question bank scaffolding and CI gate
Bissbert May 5, 2026
f5fa7be
feat(study): implement SM-2 scheduler, due-aware selector, near-miss …
Bissbert May 5, 2026
f6d0d34
test(study): scheduler/selector/interleaver test suites, 71 tests ≥90…
Bissbert May 5, 2026
17870d4
feat(study): T3 — 7 study UI components with tests and dev harness
Bissbert May 5, 2026
98abcf5
docs(study): add study-system overview, authoring guide, SM-2 ADR
Bissbert May 5, 2026
b22df29
merge(study): integrate T1 foundation — LocalStudyStore + migrations …
Bissbert May 5, 2026
5ad0c53
merge(study): integrate T2 algorithms — SM-2 + selector + interleaver
Bissbert May 5, 2026
f650b5d
merge(study): integrate T3 UI — 7 study components + dev harness
Bissbert May 5, 2026
b1f5f55
merge(study): integrate T4 schema — questionsCollection + validator +…
Bissbert May 5, 2026
bfc91dc
merge(study): integrate T6 docs — study-system + authoring guide + ADR
Bissbert May 5, 2026
87d689a
chore(study): integration fixes — unify vitest env to jsdom, refresh …
Bissbert May 5, 2026
132943f
chore(forge): add CODEOWNERS, study-track PR template, and v1 release…
Bissbert May 5, 2026
acf4ee7
feat(study-content): add 10 equipment questions on Sturman refractome…
Bissbert May 5, 2026
d544f88
feat(study-content): add 10 fundamentals questions on crystal systems…
Bissbert May 5, 2026
323a271
feat(study-content): add 10 fundamentals questions on RI/SG/hardness …
Bissbert May 5, 2026
2bbdfaf
feat(study-content): add 10 species questions on five-mineral discrim…
Bissbert May 5, 2026
a8bdf85
feat(study-content): add 10 identification questions on inclusion rec…
Bissbert May 5, 2026
fffab51
merge(study): integrate T5a editorial — 50 curated questions across 4…
Bissbert May 5, 2026
6bf54bb
merge(study): sync feat/study-content with integration tip for T5b
Bissbert May 5, 2026
ddf8226
fix(study): address T7 audit P1/High findings
Bissbert May 5, 2026
0e92b87
feat(study-content): annotate rationales with reference markers and c…
Bissbert May 5, 2026
b884e59
merge(study): integrate T5b citations — references blocks across all …
Bissbert May 5, 2026
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
37 changes: 37 additions & 0 deletions .github/CODEOWNERS
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
# CODEOWNERS — study v1 file ownership lattice
#
# Patterns follow GitHub CODEOWNERS syntax: last matching rule wins.
# All paths are relative to the repository root.
# @Bissbert is the sole maintainer; every path below auto-requests a review
# from them on any PR that touches the listed files or directories.

# Study v1 — core type contracts
/src/lib/quiz/study-types.ts @Bissbert

# Study v1 — scheduling algorithm
/src/lib/quiz/scheduler.ts @Bissbert

# Study v1 — question selection
/src/lib/quiz/selector.ts @Bissbert

# Study v1 — near-miss interleaving
/src/lib/quiz/interleaver.ts @Bissbert

# Study v1 — store implementations and migrations
/src/lib/quiz/store/ @Bissbert

# Quiz UI components (all tracks write here; single reviewer)
/src/components/quiz/ @Bissbert

# Curated question bank (T5a/T5b write here)
/src/content/questions/ @Bissbert

# Astro content collection schema (T4 sole owner)
/src/content/config.ts @Bissbert

# Build and authoring scripts
/scripts/ @Bissbert

# Documentation and ADRs
/docs/ @Bissbert
/docs/adr/ @Bissbert
24 changes: 24 additions & 0 deletions .github/PULL_REQUEST_TEMPLATE/study-track.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
## Track
- [ ] T1 Foundation
- [ ] T2 Algorithms
- [ ] T3 UI
- [ ] T4 Schema
- [ ] T5a Editorial
- [ ] T5b Citations
- [ ] T6 Docs
- [ ] T7 Audit (read-only)
- [ ] T8 Forge

## Summary
<one paragraph>

## Acceptance checklist (V1-PLAN §13.8)
- [ ] Type-check clean (`npx tsc --noEmit` — pre-existing errors elsewhere ignored)
- [ ] Tests added & passing (`npm test`)
- [ ] Coverage threshold met for changed files
- [ ] No modifications outside this track's owned paths (see CODEOWNERS)
- [ ] Conventional commit message
- [ ] Branch targets `feature/study-v1`, not `main`

## Evidence
<links / screenshots / test output>
4 changes: 4 additions & 0 deletions .github/workflows/deploy.yml
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,10 @@ jobs:
- name: Copy database files
run: npm run copy-db

- name: Validate question bank
if: ${{ hashFiles('src/content/questions/**/*.yaml') != '' }}
run: npm run validate:questions

- name: Build
run: npm run build

Expand Down
241 changes: 241 additions & 0 deletions audits/T7a-conversion.md

Large diffs are not rendered by default.

249 changes: 249 additions & 0 deletions audits/T7b-security.md

Large diffs are not rendered by default.

91 changes: 91 additions & 0 deletions audits/T7c-seo-orchestrator-stopgap.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
# T7c — SEO/Crawlability Audit (study-system v1)

Read-only static audit of the study-system v1 changes on `feature/study-v1`.
Scope: route additions, sitemap inclusion/exclusion, robots, meta tags, JS-rendered indexability. No live-site fetching.

---

## F-01 — Dev harness route uses inline Astro layout, not BaseLayout

- **Severity**: P2
- **Evidence**: `src/pages/_dev/study-components.astro:8-17` declares its own `<!doctype html>` document and does not use `BaseLayout.astro`. It does correctly gate behind `import.meta.env.DEV` and returns a 404 in production.
- **SEO impact**: Because the page returns 404 in `astro build`, no static HTML is emitted — Astro's SSG routing skips routes that explicitly return 404. So crawlers cannot discover or index it. Risk is low. The bypass is the gate working as intended; missing layout means there's no `noindex` robots tag, but there's also no document.
- **Suggested fix**: Add a defensive `<meta name="robots" content="noindex, nofollow">` inside the inline `<head>` so that even if the gate ever regresses (e.g. someone removes the `import.meta.env.DEV` check), the page still de-indexes. One-line change.

---

## F-02 — Sitemap filter does not exclude `/_dev/*`

- **Severity**: P3 (defence-in-depth)
- **Evidence**: `astro.config.mjs:23` — `sitemap({ filter: (page) => !page.includes('/admin'), ... })`. Only `/admin` is excluded.
- **SEO impact**: With the current 404 gate the dev page is never built and so it never enters the sitemap. But if the `import.meta.env.DEV` check is removed accidentally, the route would auto-emit and the sitemap would include it. Cheap to harden.
- **Suggested fix**: Update the filter to exclude both: `filter: (page) => !page.includes('/admin') && !page.includes('/_dev/')`.

---

## F-03 — `/quiz` is auth-gated but not noindex

- **Severity**: P2
- **Evidence**: `src/pages/quiz/index.astro` wraps content in `<ProtectedQuiz>` (auth gate). `BaseLayout` defaults `noindex={false}` and the page does not pass `noindex`. The gate is rendered client-side; crawler sees a near-empty CTA shell.
- **SEO impact**: Google may index a thin gate page with little useful content, which dilutes site-wide quality signals and creates an unhelpful SERP entry ("Sign in to take quiz"). A signed-out crawler experience is also flagged by Google's "Indexing of paywall content" guidance — the public content (gate copy) doesn't reflect the value of the page.
- **Suggested fix**: Either (a) pass `noindex` from `quiz/index.astro` so the gate is excluded, or (b) render a static, content-rich SEO description of the quiz system above the gate so what's indexed is a real page, not the lock screen. Option (b) is preferable for visibility.

---

## F-04 — `/study/review` and `/study/settings` routes referenced in CLAUDE.md but not implemented

- **Severity**: P1 (broken-link risk)
- **Evidence**: `CLAUDE.md` lines describing v1 routes mention `/study/review` and `/study/settings`; `src/pages/study/` does not exist on this branch. Documentation was added by T6 but the dedicated routes were not built.
- **SEO impact**: Internal docs that reference non-existent routes create 404s if linked from the marketing site or learn pages, and confuse users hitting the URLs from release notes. `<StudySettingsPanel>` and `<ExportImportPanel>` components exist but are only consumed by the dev harness; production users have no path to them.
- **Suggested fix**: Add minimal `src/pages/study/settings.astro` and `src/pages/study/review.astro` pages that wrap the existing components in `BaseLayout`. Both should pass `noindex={false}` (or true if gated). If shipping without them, remove the documentation references in `docs/study-system.md` and `CLAUDE.md`.

---

## F-05 — Question YAML content is build-time data, correctly not crawlable

- **Severity**: Info
- **Evidence**: `src/content/config.ts:84` defines `questionsCollection` with `type: 'data'`. Astro `data` collections are JSON-loaded at build time and are NOT emitted as routes. `src/content/questions/**/*.yaml` (50 files post-T5a) generates zero crawlable URLs.
- **SEO impact**: Correct. Question content stays inside the React app, not search-indexable. This is the intended behaviour for a study/quiz product (we don't want crawlers leaking answers).
- **Suggested fix**: None. Document this decision in `docs/study-system.md` as a positive design choice.

---

## F-06 — Quiz/study UI is a React island, no pre-rendered content for crawlers

- **Severity**: P2
- **Evidence**: `src/pages/quiz/index.astro` renders `<ProtectedQuiz client:load entries={entries}/>` — an island. The static HTML emitted by Astro is a small skeleton; the question list and category navigation are client-rendered.
- **SEO impact**: Even if the gate were removed, a crawler without JS execution sees no quiz content. Modern Googlebot does render JS but pages relying on it for primary content rank lower than pages with server-rendered HTML.
- **Suggested fix**: Pre-render category overview, count of available questions, and "What you'll learn" copy into the Astro shell before mounting the React island. This gives crawlers something to index without exposing answer content.

---

## F-07 — `BaseLayout` `meta description` defaults to a generic site-wide string for all study pages

- **Severity**: P2
- **Evidence**: `src/layouts/BaseLayout.astro:18` — `description = 'Crystal structure visualization and gemmological reference for FGA students and professionals.'`. Several v1 routes (`quiz/index.astro`) do not override this.
- **SEO impact**: Identical meta descriptions across multiple URLs cause Google to flag duplicate snippets and re-write its own. Loss of CTR control on SERP.
- **Suggested fix**: Pass a route-specific `description` from each page that uses `BaseLayout`. For `/quiz`: "Practice gemmology with adaptive spaced-repetition quizzes — 50+ FGA-aligned questions with instant rationale and SM-2 scheduling."

---

## F-08 — Sitemap `lastmod` uses `new Date()` evaluated at build time

- **Severity**: P3
- **Evidence**: `astro.config.mjs:30` — `lastmod: new Date()`.
- **SEO impact**: Every sitemap entry gets the build timestamp regardless of whether the underlying page changed. Google deprioritises sitemaps where every URL claims to update simultaneously, treating the signal as noise.
- **Suggested fix**: Use per-page git commit timestamps via the sitemap integration's `serialize` hook, or use the content collection `pubDate` if present. As a minimum, scope `lastmod` only to the URLs whose content actually changed in this release.

---

## F-09 — `robots.txt` lacks Bing/IndexNow signals

- **Severity**: P3
- **Evidence**: `public/robots.txt` declares the sitemap and an `LLM-Content` line but no `IndexNow` ping endpoint.
- **SEO impact**: Out of scope of v1 study system but the release checklist references IndexNow for `/study/*` routes.
- **Suggested fix**: The IndexNow workflow exists in CI per `docs/release-checklist-study-v1.md`; this is an Info note. No change needed here, but ensure new study URLs are listed in the IndexNow batch when they exist (see F-04).

---

## Summary

5 of the 9 findings are P2 or P1. The two with the largest user-facing impact are **F-04** (missing `/study/*` routes that docs already describe) and **F-03** (the `/quiz` gate page is indexable and would create a thin-content SERP entry). Everything in the new study-system code emits correctly to the static build, the dev harness is properly gated, and question YAML stays build-time-only — so v1 itself does not regress SEO. The recommendations are: (a) ship minimal `/study/settings` and `/study/review` pages this release or strip the docs references, (b) noindex the `/quiz` gate or enrich it with public copy, (c) harden the sitemap filter to exclude `/_dev/*` even if the env-gate ever fails.
Loading
Loading