Plain AI Curriculum — Build & Lesson Authoring¶
The everything-you-need page for working on the Plain AI Curriculum (
plainai.aguidetocloud.com/learn/). Sister of the Plain AI Playbook — that one covers the reference/topic site; this one covers the structured-curriculum moon.Written 10 May 2026 — after the curriculum infrastructure landed and Lesson 1 of Family Foundations shipped at status
review.
When this applies
Any code change in C:\ssClawy\plainai\ that touches build-learn.mjs, templates/_learn.css, templates/styles.css (lesson sections), content/learn/_audiences.json, any _course.json, any lesson JSON, or scripts/qa.mjs. Also any UI work on /learn/* pages or anything that affects mobile collapse / cosmos-bar active state on Plain AI.
Read these first (mandatory pre-reads)
Before authoring any lesson content or curriculum copy:
~/.copilot/plain-ai-voice-guardrail.md— the four voice tests (mum / dinner-table / 12-year-old / honesty) + the forbidden-words list. Voice is the soul of Plain AI.~/.copilot/plain-ai-curriculum-philosophy.md— the constitution. The Permanent NO list (no streaks · no badges · no email capture · no celebration animations · no certificates · no sponsorships · no affiliate · no comments · no login · no A/B testing · no progress trackers · no recommended-next-based-on-history · no time-limited content · no stock illustrations).- The voice-and-tone reference at voice-and-tone.md — Sush's broader voice fingerprints across the cosmos.
A session that ships content without running the voice tests has shipped a brand violation, not a feature.
TL;DR — what shipped on 10 May 2026¶
| # | Commit | What |
|---|---|---|
| 1 | 62bec2b |
UX refresh — hero recompose, beside-hero cards, mobile-minimal trim, light/dark sweep, voice gates |
| 2 | 1366cf7 |
Mobile-collapse + growing-QA suite (scripts/qa.mjs) |
| 3 | 59d8529 |
Mobile-collapse fix — refactored to CSS-only after CSP blocked the inline script in production |
| 4 | 7b1fd74 |
Lesson 1 of Family Foundations shipped at status review (Sush owns final voice pass) |
Live URLs (as of 10 May 2026):
https://plainai.aguidetocloud.com/— homepage with hero + 2 beside-cards (path + course)https://plainai.aguidetocloud.com/learn/— curriculum hubhttps://plainai.aguidetocloud.com/learn/families/— Families audience landinghttps://plainai.aguidetocloud.com/learn/families/family-foundations/— Family Foundations course landinghttps://plainai.aguidetocloud.com/learn/families/family-foundations/what-ai-actually-is/— Lesson 1
Pre-push gate (mandatory): npm run qa (local) before every push that touches build/template/content; npm run qa:live after deploy.
File map (curriculum-specific)¶
| Path | What it is |
|---|---|
build-learn.mjs |
Curriculum-specific generator. Imported at the end of build.mjs if content/learn/_audiences.json exists. Renders /learn/, /learn/<aud>/, /learn/<aud>/<course>/, and /learn/<aud>/<course>/<lesson>/ (when status is review/published). |
content/learn/_audiences.json |
Array of audiences. v1 has only families. New audiences only when their first course is written. |
content/learn/<audience>/<course>/_course.json |
Course manifest — title, objective, status, outline (all planned lessons with locked slugs), lesson_slugs (the subset actually rendered). |
content/learn/<audience>/<course>/<slug>.json |
One lesson. Block-typed body (intro / key / follow / example / try-this / qa / wrap). |
templates/styles.css |
Lesson layout CSS lives here (block-typed sections, anchor chips, lesson-nav, credit). Same file as topics — search for === Lesson page === comment block. |
scripts/qa.mjs |
The growing-QA suite. Pre-push gate. Includes lesson-page assertions. |
package.json scripts |
qa (local) / qa:live (production). |
_headers (in public/) |
The CSP. script-src 'self' only — no 'unsafe-inline'. Inline <script> tags are blocked in production. |
Already wired in build.mjs:
- Lines 99–129: builds
topicToLessonsinverted index. Lessonanchor_topicsandrelated_topicsdrive the topic-page back-link rail. Auto-populates the moment a lesson with statusrevieworpublishedships. - Lines 240–272: builds
_lessonEntriesand_courseEntriesfor the⌘Ksearch modal. - Lines 776–791: extends
sitemap.xmlwith all/learn/*URLs. - Lines 845–859:
await import('./build-learn.mjs')if_audiences.jsonexists.
Architecture — three-layer cosmos identity¶
The reader-facing identity has three layers that need to stay in sync:
| Layer | What | Source of truth |
|---|---|---|
| 1. Plain AI (the planet) | Reference site — topics, paths, /changes/. kind: planet in cosmos atlas. Cosmos-bar shows it as a big bubble. |
content/topics/*.json, content/paths.json, content/changes/*.json |
| 2. Curriculum (the moon) | Structured walk — lessons in a particular order, for a particular reader. kind: moon in cosmos atlas. Cosmos-bar shows it as a small moon icon next to Plain AI. |
content/learn/_audiences.json, course + lesson JSONs |
| 3. Cosmos-bar identity | Top sticky bar served from cosmos.aguidetocloud.com/cosmos-bar.js. Reads the active="…" attribute on <cosmos-bar> to dim the right body. |
build-shared.mjs pageHead({ activeCosmos: '…' }) |
The contract every page must honour:
| Page | activeCosmos value |
|---|---|
/, /<topic>, /path-<slug>, /changes/, /changes/<slug>/, /about |
plainai |
/learn/, /learn/<aud>/, /learn/<aud>/<course>/, /learn/<aud>/<course>/<slug>/ |
curriculum |
build-learn.mjs sets activeCosmos: 'curriculum' for the entire /learn/ tree. If you ever add a new generator that emits /learn/* pages, replicate this. The npm run qa suite asserts the active state per page kind.
Lesson JSON schema (canonical)¶
{
"_comment": "Lesson N of <course>. Drafted by AI assistant in voice-gate mode (mum/dinner/12yo/honesty + curriculum philosophy NO list). Status starts at 'review' — Sush owns the final voice pass before flipping to 'published'.",
"slug": "what-ai-actually-is",
"lesson_number": 1,
"title": "What AI actually is, in one paragraph",
"subtitle": "The plainest possible answer. No jargon, no metaphors that mislead.",
"objective": "By the end you'll have a one-paragraph mental model of AI that holds up — and three honest things that follow from it.",
"status": "review",
"duration_min": 10,
"last_updated": "2026-05-10",
"anchor_topics": ["large-language-model", "training-data", "hallucination"],
"related_topics": ["genai-vs-ai", "prompt"],
"blocks": [
{ "type": "intro", "body": "..." },
{ "type": "key", "title": "...", "body": "..." },
{ "type": "follow", "title": "...", "items": [
{ "lead": "...", "body": "..." }
] },
{ "type": "example", "title": "...", "body": "..." },
{ "type": "try-this", "title": "...", "estimate": "1 minute", "body": "..." },
{ "type": "qa", "title": "...", "items": [
{ "q": "...", "a": "..." }
] },
{ "type": "wrap", "body": "..." }
],
"credit": "Drafted by an AI assistant in voice-gate mode; final voice pass owned by Susanth Sutheesh."
}
Field reference¶
| Field | Required | Notes |
|---|---|---|
slug |
yes | Matches the JSON filename. Also matches the outline[].slug in _course.json and the entry in lesson_slugs. URL becomes /learn/<aud>/<course>/<slug>/. |
lesson_number |
yes | 1-indexed within course. Used for "Lesson 1" in breadcrumb + hero kicker. |
title |
yes | Hero h1. Plain English. Shorter is better. |
subtitle |
optional | Shown under hero h1. Sets expectations. |
objective |
yes | One sentence — what reader walks away with. Rendered in indigo-bordered callout under hero. Voice-gate it. |
status |
yes | draft (not rendered), review (rendered with amber "In review" badge — pre-Sush voice pass), published (green "Ready"). |
duration_min |
optional | Reading time. Shown in hero meta. Honest estimate based on word count + try-this. |
last_updated |
yes | ISO YYYY-MM-DD. Drives JSON-LD dateModified and the hero meta. |
anchor_topics |
optional, array of topic slugs | Drives the topic-page back-link rail. The lesson appears under "Lessons that teach this" on each anchor topic page. Also rendered as solid-border chips in the lesson footer. Validate at build time — every slug must point to an existing content/topics/<slug>.json. |
related_topics |
optional, array of topic slugs | Same back-link mechanic, dashed-border chip in the lesson footer. Use for "if you want to wander" links. |
blocks |
yes | Array of typed block objects. Order matters — that's the reading flow. See "Block types" below. |
credit |
optional | Disclosed in the lesson footer. Per philosophy: when AI is the primary maker of a lesson, disclosure is required. |
Block types¶
| Type | Visual register | When to use |
|---|---|---|
intro |
Plain prose, first paragraph slightly emphasized. | Cold-open. Meet the reader where they are. Acknowledge the noise around AI before answering. |
key |
Indigo-bordered tinted block — "this is the takeaway". title becomes a kicker. |
The one-paragraph mental model. Use sparingly — there should be ONE key block per lesson. |
follow |
Numbered list with big monospace numerals. Each item has lead (bold) + body (muted). |
"X things that follow from the key." 2–4 items. Lead is one short sentence; body is one explanatory sentence. |
example |
Warm cyan/pink gradient tint — "what this means at home". title becomes a pink kicker. |
Concrete real-world anchor a family reader can picture. Rotate examples — don't reuse the same metaphor across lessons (philosophy rule). |
try-this |
Dashed indigo callout with ▸ icon. estimate ("1 minute") shown as a badge in the header. |
Hands-on activity. Must be checkable. Use a different domain from the example block (rotation rule applies within a lesson too). |
qa |
Compact paragraph cards (Q + A). items is an array of {q, a}. |
Self-checks (not gamification). 2–3 pairs. Each Q anticipates a real reader doubt; each A answers honestly without lecturing. |
wrap |
Dashed top divider, "WRAP" kicker, quiet finish. | Quiet pointer to the next lesson. No celebration animation, no progress %, no "you did it!" — philosophy rule. Last paragraph can be lightly emphasized. |
Block body conventions¶
- Paragraphs separated by
\n\n(double newline in the JSON string). Single\nis treated as a soft break. - Italics with single asterisks:
*"like this"*becomes<em>"like this"</em>. Use sparingly — for quoted speech and emphasized phrases. - HTML tags are NOT allowed in body strings (escaped at render time). If you need a link, restructure the prose around it for the next iteration of the schema.
- Try-this
bodyshould end with the reader having a checkable answer. Avoid open-ended "reflect on this" — give them something to verify.
How to author a new lesson (step by step)¶
Step 0 — open the four pre-reads¶
Before you write a single sentence, open these in tabs and skim the section headings:
~/.copilot/plain-ai-voice-guardrail.md(the four tests + forbidden words)~/.copilot/plain-ai-curriculum-philosophy.md(Permanent NO list + content rules)cosmos/plain-ai/curriculum-build.md(this doc)- The previously shipped lesson's JSON (for tone calibration)
Step 1 — pick the lesson slug + outline placement¶
The course's _course.json has the outline — all 5 planned lessons with locked slugs. Pick the next one (e.g. Lesson 2 = spotting-ai-mistakes for Family Foundations). Don't invent a new slug — reuse the locked one so URLs stay stable and back-links keep pointing at the right place.
If a slug must change, update it in _course.json AND the lesson JSON filename AND _audiences.json (if referenced) AND any references in roadmap docs. Treat as a small breaking change.
Step 2 — draft the body in the block schema¶
Open the lesson JSON file. Fill in each block from top to bottom. Don't optimize for length — optimize for "would my mum understand this on first read?" The shortest version that passes all four tests is the right version.
Apply the philosophy's content rules:
- Real-world examples, always — every concept gets anchored to a moment the reader can picture.
- Examples everyone can relate to — no SaaS metaphors, no enterprise scenarios. Use family / home / phone / kid / neighbour examples for the Families audience. (Other audiences will have other anchors.)
- Rotate examples — don't reuse the same handful within a course. Lesson 1 used "school-closure" + "town-population". Lesson 2 should use different domains (homework / restaurant / news / weather / etc.). Across courses, vary by country/culture too.
Step 3 — voice-gate every paragraph¶
For each prose paragraph, run:
- Mum test — could a non-tech parent read this at the dinner table and understand the gist on first read?
- Dinner-table test — would I say this sentence at a dinner table without sounding like a robot?
- Kid test — could a bright 12-year-old understand this?
- Honesty test — have I said anything I'd be embarrassed to defend in 5 years? Have I said anything to please a vendor or hype the technology?
Fail any → rewrite until it passes. Document any borderline judgement calls in the session journal entry.
Forbidden-words sweep — search the lesson JSON for these and replace if found:
frontier · ecosystem · multimodal · agentic · capability shift · mass-market · vendor · robust · scalable · mission-critical · game changer · holistic · synergies · AI-powered · in layman's terms · inference · benchmark · evaluation · long-context · token (when used as jargon) · hallucination · chain-of-thought · "It's worth noting that…" · "At the end of the day…" · "Going forward…" · "Best in class" · "World-class" · "State of the art" · "Disrupting" · "Unlock value" · "Drive outcomes" · "Empower users"
Exception: you may use forbidden words in commentary ("most explanations use words like 'neural network' — we're not going to do that"), but not as explanations themselves.
Step 4 — set status, anchor topics, related topics¶
- Status: start with
"review". Sush owns the final voice pass — you do not promote to"published". - Anchor topics: 2–4 existing topic slugs from
content/topics/that the lesson is built on. These drive solid-border chips in the footer + the topic-page back-link rail. - Related topics: 1–3 existing topic slugs that are adjacent but not the foundation. These render as dashed chips ("if you want to wander").
Verify each slug exists. Run Get-ChildItem C:\ssClawy\plainai\content\topics\ and check before saving.
Step 5 — register in _course.json¶
Add the lesson's slug to lesson_slugs:
Make sure the outline row for this lesson already has the matching slug field (lock-in was done 10 May 2026). If not, add it now.
Step 6 — local build + QA¶
npm run qa will:
- Auto-spin a local
http.serveron port 8772 - Visit the new lesson URL
/learn/<aud>/<course>/<slug>/ - Currently asserts on the existing Lesson 1 page — add new assertions for the new lesson (growing-guardrail rule)
- Walk desktop + iPhone viewports
Add lesson-specific assertions in scripts/qa.mjs mirroring the lesson-1 block. The pattern: status badge text, breadcrumb depth, follow item count, Q&A pair count, anchor chip count, JSON-LD schema correctness.
Step 7 — commit + push¶
git status --short
git add content/learn/families/family-foundations/<slug>.json
git add content/learn/families/family-foundations/_course.json
git add scripts/qa.mjs # if you grew the suite
# explicit paths only — never git add . or git add -A
git commit -m "Plain AI Curriculum — Lesson N <title> shipped (review)"
git pull --rebase
git push
Step 8 — live verify¶
After Cloudflare Pages deploys (~60–90s):
This runs the same suite against https://plainai.aguidetocloud.com. All checks must pass. If a check fails on live but passed locally, suspect a CSP or _headers issue (see CSP section below).
Status workflow — review → published¶
This is the only part of the lesson lifecycle that's reserved for Sush. The philosophy says: "Sush owns final voice on every word, image, animation. AI drafts; Sush edits. Nothing ships without a human pass through the voice gate."
Procedure (Sush only):
- Read the lesson at the live URL end-to-end.
- Edit the JSON directly for any voice fixes (re-run
npm run qaafter). - Change
"status": "review"→"status": "published". - Bump
last_updatedto today's date. npm run qa(local) →git commit→git push→npm run qa:live(verify).
What changes when status flips:
- Lesson hero badge: amber "In review" → green "Ready".
- Course landing outline-row tag: amber "In review" → green "Ready".
- The URL itself doesn't change. Topic-page back-link rail already renders for both
reviewandpublished— no change there.
What does NOT happen automatically:
- Newsletter announcement (there is no newsletter — philosophy NO list).
- Email to subscribers (no email capture — philosophy NO list).
- Social post (manual, optional, Sush's call).
- Featured slot on homepage (manual update to
_course.jsonif needed).
The pre-push gate — npm run qa¶
The growing-QA suite at scripts/qa.mjs is mandatory before every push that touches:
build.mjs·build-shared.mjs·build-changes.mjs·build-learn.mjstemplates/styles.css·templates/_changes.css·templates/_learn.css(if present)templates/theme.js·templates/search.jscontent/learn/_audiences.json· any_course.json· any lesson JSONpublic/_headers(CSP changes)package.jsonbuild scripts
14 checks across 7 page types × 2 viewports as of 10 May 2026:
| Check class | What it validates |
|---|---|
| HTTP 200 | Every page returns 200, not a CF Pages fallback |
<cosmos-bar active="…"> |
Matches expected per page kind (plainai vs curriculum) |
| Curriculum tab in masthead | Visible site-wide |
| Required selectors per page | .hero-grid, .hero-side-card, .cat-collapse-mobile-content, .learn-hero, .audience-card, .course-card, .outline-row, .lesson-status--inprogress, .course-status, .entry-hero, .lesson-hero, .lesson-block--*, .lesson-topics, .lesson-credit, .cosmos-footer |
| Mobile (<720px) | .count + .cat-blurb + .feed-card .topic-tags all display:none; only 1 .cat-section visible (rest collapsed); first cat shows 2 cards (preview-2); collapse toggle :checked is false by default |
| Desktop (≥720px) | 7 cat-sections visible; .cat-collapse-mobile-content is display: contents; collapse summary hidden |
| Theme toggle | Switching light↔dark produces zero pageerror events |
| Lesson 1 | Status badge "In review", breadcrumb 5-deep, 3 follow items, 3 Q&A pairs, 3 anchor topic chips, "1 minute" try-estimate, JSON-LD @type "Article" with correct headline |
The growing-guardrail rule: every new bug found in production MUST be added as an automated check here BEFORE the fix is deployed. The suite only grows — never shrinks. (Same rule as cosmos-atlas/scripts/qa-audit.mjs and guided/test-guided-qa.cjs.)
Local: npm run qa (auto-spins http.server on :8772).
Live: npm run qa:live (against https://plainai.aguidetocloud.com).
The CSP rule (this matters — it cost us a deploy on 10 May)¶
Plain AI's CSP forbids inline scripts. Always.
public/_headers sets:
There is NO 'unsafe-inline'. Any <script>...</script> block emitted by build.mjs, build-changes.mjs, or build-learn.mjs will be blocked by the browser in production, silently. The page will render but the script never runs.
Locally, python -m http.server doesn't send CSP headers — so inline scripts run fine and you'll see false greens. npm run qa:live is the only place the CSP is exercised.
What happened on 10 May 2026¶
The first pass at the homepage mobile-collapse used <details open> plus an inline <script> that ran during HTML parsing to remove the open attribute on mobile (document.currentScript.parentElement.removeAttribute('open')). Clean approach. Local npm run qa was 14/14 green. Pushed (commit 1366cf7). Ran npm run qa:live after CF Pages deploy: 2 failures — 7 cat-sections visible on iPhone, collapse open by default. The CSP had blocked the inline script silently in production.
What to do instead¶
| If you need… | Use this pattern |
|---|---|
| A toggle / collapse / disclosure | CSS-only checkbox + label trick. Hidden <input type=checkbox> + <label for> as the visible toggle + sibling <div> content. Mobile: display:none by default, :checked ~ content { display: block }. Desktop: display: none on toggle UI, display: contents on content wrapper. Used in production for .cat-collapse-mobile-content — read it as the canonical example. |
| Theme switching, search modal, anything that needs JS | Put it in templates/theme.js or templates/search.js — they're served from 'self' (same origin) and CSP allows them. Hash-busted via ?v=<sha8> on every change. |
| A new dynamic feature on a single page | Create a new templates/<feature>.js file, hash-bust in pageHead, and reference it via <script src="/<feature>.js?v=<hash>">. Same-origin = CSP-allowed. |
| Something cosmos-bar-ish (cross-planet runtime) | The CSP allows https://cosmos.aguidetocloud.com for scripts, connects, and images. If your widget belongs on multiple planets, ship it from cosmos-atlas like the cosmos bar. |
What NOT to do¶
- ❌ Add
'unsafe-inline'toscript-srcin_headers. (Weakens security; we have plenty of room without it.) - ❌ Use a CSP nonce. (Static-site build doesn't have a per-request nonce; nonces require server-side rendering.)
- ❌ Use
data:URIs for scripts. (Banned by default; would also need CSP relaxation.) - ❌ Skip
npm run qa:liveafter deploy. (The only place the CSP is exercised against your build output.)
Mobile-collapse pattern — CSS-only checkbox + label¶
For any "show more / show less" toggle on Plain AI, copy this pattern. Layout-transparent on desktop, native CSS toggle on mobile, no JS, CSP-safe.
HTML¶
<input type="checkbox"
id="my-collapse-toggle"
class="my-collapse-toggle"
aria-hidden="true"
tabindex="-1">
<label for="my-collapse-toggle"
class="my-collapse__summary"
role="button"
tabindex="0"
aria-controls="my-collapse-content">
<span class="my-collapse__label">Show 64 more topics across 6 categories</span>
<span class="my-collapse__icon" aria-hidden="true">↓</span>
</label>
<div id="my-collapse-content" class="my-collapse-content">
<!-- the children that toggle -->
</div>
CSS¶
/* Hidden checkbox — visually-hidden, keyboard-focusable as a fallback. */
.my-collapse-toggle {
position: absolute;
width: 1px; height: 1px; padding: 0; margin: -1px;
overflow: hidden; clip: rect(0,0,0,0);
white-space: nowrap; border: 0;
}
.my-collapse__summary { /* visible toggle styles */ }
.my-collapse__icon { transition: transform 160ms ease-out; }
.my-collapse-toggle:checked ~ .my-collapse__summary .my-collapse__icon {
transform: rotate(180deg);
}
/* MOBILE — content hidden by default; revealed on :checked. */
@media (max-width: 719px) {
.my-collapse-content { display: none; }
.my-collapse-toggle:checked ~ .my-collapse-content { display: block; }
}
/* DESKTOP — hide toggle UI; content renders flat. */
@media (min-width: 720px) {
.my-collapse-toggle,
.my-collapse__summary { display: none; }
.my-collapse-content { display: contents; }
}
Why display: contents matters on desktop: the wrapper element is layout-transparent — its children render as if it didn't exist. So sibling sections stay direct children of <main> and any pre-existing CSS that selects them keeps working. No layout shift between mobile and desktop.
Accessibility: keyboard-focusable via the <label role=button tabindex=0>. Screen readers announce checkbox state. aria-controls points to the toggled content. Without JS, aria-expanded cannot dynamically reflect state — that's a future refinement (would need a small templates/<feature>.js).
Curriculum philosophy — the Permanent NO list (summary)¶
Full text: ~/.copilot/plain-ai-curriculum-philosophy.md. Summary so future sessions don't have to re-read for casual reference:
| Forbidden | Why |
|---|---|
| Streaks · badges · levels · scores · achievement systems | Engagement design, not learning design. |
| Email capture · "subscribe for new lessons" · newsletter signup on lesson pages | Privacy-first. Plain AI doesn't have your email. |
| Paid tier · premium · "pro" lessons · paywall on translations | Paid content lives on Earth — never here. |
| Paid certificates / "completion certificates for $X" | Free knowledge already does the job. |
| Sponsorships · "Brought to you by [vendor]" · vendor logos on lessons | Distorts editorial honesty. |
| Affiliate links on lesson content | Even a small commission is an angle. |
| Donation tip jars / Ko-fi on lesson pages | Even a soft ask is a money angle. |
| A/B testing of lesson copy | We ship our best honest answer; iteration comes from voice review. |
| Individual user tracking (cookies, fingerprinting, analytics events) | Privacy-first. CF Web Analytics aggregate URL hits only. |
| Push notifications · email reminders · "you haven't visited in 3 days" | Engagement design. Never ours. |
| Locked content behind a login | Free-forever means free-without-account. |
| Stock illustrations | Wrong signal — says "we have an art budget" instead of "made by a person, for people". |
| Course-end celebration animations / dopamine bursts / "🎉 you did it!" cards | Engagement design even if subtle. The deck just ends. |
| Comments / forums / "ask the author" widgets on lessons | Moderation cost + engagement angle. |
| "Recommended next lessons based on your history" | Nudges retention. We let readers choose. |
| Time-limited content ("free this week!") | Manipulation. All content stays free, always. |
A session that ships a feature that violates this list has shipped a brand violation, not a feature. Surface the conflict and ask for explicit confirmation before ever shipping anything from this list — even if a casual ask seems to imply it.
What we DO add (the value test in action)¶
When in doubt, ask: does this help the reader leave understanding more, with less anxiety, faster? If yes, ship it. Examples that pass:
- Lessons that teach one thing well
- Try-this activities (value via doing, not just reading)
- Self-check Q&A inline (confirms understanding without testing register)
- CC BY-SA 4.0 licence
- Topic back-links on existing pages (value via discovery)
- Privacy-first analytics
- Translations under CC BY-SA 4.0
- Sources cited on every claim
- Plain English voice
Cosmos atlas — curriculum is a moon¶
The cosmos atlas (C:\ssClawy\cosmos-atlas\src\data\atlas.json) defines curriculum as a moon of Plain AI with kind: "moon". The cosmos-bar runtime feed at https://cosmos.aguidetocloud.com/atlas-bar.json reflects this.
When you navigate to any /learn/* page, the cosmos bar dims the moon icon (because <cosmos-bar active="curriculum"> is set in the page head). When you click the moon icon from any planet's cosmos bar, you land on /learn/. Two layers of identity, legible everywhere.
If you ever change curriculum's name, slug, or URL:
- Update
cosmos-atlas/src/data/atlas.json(find the curriculum body inside Plain AI'smoonsarray). - Run
cd C:\ssClawy\cosmos-atlas && npm run cosmos-barto regeneratepublic/atlas-bar.jsonandpublic/cosmos-bar.js. - Run
node scripts/qa-audit.mjs http://127.0.0.1:4287/(after startingastro dev). This is a mandatory pre-push gate for any cosmos-atlas atlas.json change. - Commit + push cosmos-atlas.
- Plain AI's
pageHeadactiveCosmos value still needs to match the new slug — updatebuild-learn.mjs.
The freshness lastShippedAt field in atlas.json drives a brief pulse on the cosmos canvas + bar for ~14 days post-ship. Bump it whenever something visible ships (a new lesson, a redesign). As of 10 May 2026 the bump is deferred — the cosmos-atlas qa-audit has 6 pre-existing failures (V5 drift toggle off + ambient player + 4 known iPhone overlap waivers) that block the gate. Future cosmos polish session needs to fix those before the date bump can land.
Topic-page back-link rail (auto-wired)¶
When a lesson's anchor_topics or related_topics includes a topic slug, the topic page renders a "Lessons that teach this" rail near the bottom. This is built by build.mjs lines 99–129 — the topicToLessons inverted index reads every lesson JSON in lesson_slugs and groups by topic.
The rail only renders for lesson status review or published — drafts don't drive back-links.
You don't need to do anything to wire this. Just set anchor_topics correctly in the lesson JSON. The next build will populate the rail on every relevant topic page.
As of 10 May 2026: Lesson 1's anchors large-language-model, training-data, hallucination show the rail with a link to Lesson 1. Verified live. Future Lesson 2's anchors will join — the rail aggregates multiple lessons per topic.
What's NOT shipped — the deferred list¶
Future sessions can pick these up.
| Item | Status | Notes |
|---|---|---|
| Lessons 2–5 of Family Foundations | Not started. Slugs locked in _course.json outline. |
Pick the next one (spotting-ai-mistakes). Apply the same schema + voice gates + content rules. |
| Translations (Te Reo Māori bridge) | Per philosophy persona-first sequencing — only after v2 (Mid-career non-tech) ships. | CC BY-SA 4.0 licence on translations; vetted by Sush's colleagues. |
| v2 audience: At work (Mid-career non-tech) | Not started. | Add second entry to _audiences.json, scaffold <aud>/<course>/_course.json. |
| Cosmos atlas freshness bump for plainai + curriculum | Deferred. | Blocked by pre-existing cosmos-atlas qa-audit failures (V5 drift toggle off + ambient player + 4 iPhone waivers). Needs a cosmos polish session first. |
Mobile-collapse aria-expanded |
Deferred. | Would need JS (in templates/<feature>.js so it's CSP-safe); current pattern is keyboard-accessible via label-for-checkbox but doesn't dynamically reflect state in screen readers. |
Search modal ⌘K deep test |
Deferred. | Add to scripts/qa.mjs: open the modal, type a query, assert results count. |
| WCAG AA contrast per page | Deferred. | Hook axe-core into scripts/qa.mjs. |
| Lesson page nav with prev/next | ✅ Already shipped (10 May 2026). | Renders placeholders for not-yet-shipped lessons. |
| Topic-page back-link rail | ✅ Already shipped (auto-populates from lesson JSONs). | |
| Course in-progress honest count | ✅ Already shipped. | Reads actual lessonsByPath count, not a fake schedule. |
Cross-references¶
- Plain AI Playbook — the reference/topic site (sister doc; covers everything except curriculum)
- Plain AI Roadmap — chronological shipping record + future plans
- Curriculum Kickoff — original kickoff prompt (6 May 2026)
- Cosmos Philosophy — the rules of the universe
- Voice & Tone — Sush's broader voice fingerprints
- Production Incident Log — every guardrail's origin story (includes the 10 May CSP incident)
- Deployment Playbook — the 20-step pre-push checklist (universal)
- Parallel Git Rules — explicit-paths-only, never
git add . ~/.copilot/plain-ai-voice-guardrail.md— the four voice tests + forbidden words (mandatory pre-read)~/.copilot/plain-ai-curriculum-philosophy.md— the constitution (mandatory pre-read)
Last updated: 10 May 2026 · After commits 62bec2b → 1366cf7 → 59d8529 → 7b1fd74. All 14 live QA checks green. Lesson 1 of Family Foundations live at status review, awaiting Sush's final voice pass before promotion to published.