Skip to content

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:

  1. ~/.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.
  2. ~/.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).
  3. 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 hub
  • https://plainai.aguidetocloud.com/learn/families/ — Families audience landing
  • https://plainai.aguidetocloud.com/learn/families/family-foundations/ — Family Foundations course landing
  • https://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 topicToLessons inverted index. Lesson anchor_topics and related_topics drive the topic-page back-link rail. Auto-populates the moment a lesson with status review or published ships.
  • Lines 240–272: builds _lessonEntries and _courseEntries for the ⌘K search modal.
  • Lines 776–791: extends sitemap.xml with all /learn/* URLs.
  • Lines 845–859: await import('./build-learn.mjs') if _audiences.json exists.

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 \n is 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 body should 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:

  1. ~/.copilot/plain-ai-voice-guardrail.md (the four tests + forbidden words)
  2. ~/.copilot/plain-ai-curriculum-philosophy.md (Permanent NO list + content rules)
  3. cosmos/plain-ai/curriculum-build.md (this doc)
  4. 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:

  1. Mum test — could a non-tech parent read this at the dinner table and understand the gist on first read?
  2. Dinner-table test — would I say this sentence at a dinner table without sounding like a robot?
  3. Kid test — could a bright 12-year-old understand this?
  4. 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.

  • 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:

"lesson_slugs": ["what-ai-actually-is", "spotting-ai-mistakes"]

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

cd C:\ssClawy\plainai
node build.mjs
npm run qa

npm run qa will:

  • Auto-spin a local http.server on 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):

npm run qa:live

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 — reviewpublished

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):

  1. Read the lesson at the live URL end-to-end.
  2. Edit the JSON directly for any voice fixes (re-run npm run qa after).
  3. Change "status": "review""status": "published".
  4. Bump last_updated to today's date.
  5. npm run qa (local) → git commitgit pushnpm 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 review and published — 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.json if 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.mjs
  • templates/styles.css · templates/_changes.css · templates/_learn.css (if present)
  • templates/theme.js · templates/search.js
  • content/learn/_audiences.json · any _course.json · any lesson JSON
  • public/_headers (CSP changes)
  • package.json build 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:

Content-Security-Policy: ... script-src 'self' https://cosmos.aguidetocloud.com; ...

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' to script-src in _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:live after 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:

  1. Update cosmos-atlas/src/data/atlas.json (find the curriculum body inside Plain AI's moons array).
  2. Run cd C:\ssClawy\cosmos-atlas && npm run cosmos-bar to regenerate public/atlas-bar.json and public/cosmos-bar.js.
  3. Run node scripts/qa-audit.mjs http://127.0.0.1:4287/ (after starting astro dev). This is a mandatory pre-push gate for any cosmos-atlas atlas.json change.
  4. Commit + push cosmos-atlas.
  5. Plain AI's pageHead activeCosmos value still needs to match the new slug — update build-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.


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.