Skip to content

Content spec

This page summarizes SPEC.md - the contract between content and app. Content is plain markdown/YAML with zero references to the runtime, so everything ports unchanged to any future implementation.

tracks/<track-id>/
├── track.yaml # title, description, optional provenance, ordered lesson list
├── <lesson-id>/
│ ├── lesson.md # frontmatter (title) + GFM body
│ └── sandbox/ # optional - Dockerfile presence enables the terminal
│ ├── Dockerfile
│ └── … # seed data, scripts
├── interview-prep.md # recommended
└── resources.md # recommended

Inside lesson.md bodies:

  • > **Tip:** … blockquotes render as highlighted callouts.
  • Conceptual sections end with “Explain it” verbal-articulation prompts.
  • Tool mentions: when a lesson uses a tool it doesn’t teach, it gives a one-line survival hint at first mention (e.g. nano - Ctrl+O saves, Ctrl+X exits”) rather than assuming familiarity. External links are reserved for concept-sized topics, point only at official references (man pages, the tool’s own docs - never blogs or tutorial sites), appear at first mention only, and are calibrated to the track’s level. A lesson needing more than two or three such links is assuming too much for its level - the fix is teaching the tool or changing the level, not more links.

A fenced code block with language quiz containing valid JSON:

```quiz
{ "questions": [
{ "q": "What does WHERE filter?",
"options": ["Rows before grouping", "Groups after aggregation"],
"answer": 0,
"explain": "WHERE filters rows; HAVING filters groups." }
] }
```

answer is the zero-based index of the correct option.

The runner builds the lesson’s sandbox/ directory, runs it with 512 MB / 1 CPU limits, and attaches the terminal via bash -l. Images must include bash, create /work, and provide an executable check on PATH.

The terminal opens in /work by default. When the exercise’s files live elsewhere, the image declares it so the learner lands where the work is:

LABEL supercharger.workdir="/var/www/site"

Keep the /work default only when starting away from the files is the point (e.g. filesystem navigation is the skill) - and then the lesson must say where the files are. Every command shown in a lesson body must work verbatim from the directory the terminal opens in, and lessons must warn where a correct command legitimately prints nothing (grep with no match), so silence isn’t mistaken for a broken environment.

check prints JSON to stdout:

{ "checkpoints": [
{ "name": "Table orders_clean exists", "pass": true },
{ "name": "Query saved to /work/answer.sql", "pass": false,
"hint": "Save your final query in /work/answer.sql." }
] }

All validation logic runs inside the container; the lesson auto-completes when every checkpoint passes.

preps/<prep-id>/
├── job-posting.md # written by the app
├── resume.md # optional, written by the app
├── analysis.md # requirements → skills; inferred items flagged
├── plan.md # ordered study plan linking tracks/lessons
├── interview-prep.md # role-specific questions and talking points
├── curriculum.json # optional ordered curriculum for the prep UI
├── onboarding-requests.json # optional product-doc onboarding requests
├── onboarding/<id>/source.md # optional pasted docs/excerpts
└── track-requests.json # optional requested tracks for agent handoff

Any .md files in a prep render as tabs in the app. curriculum.json renders as the Curriculum tab when present.

curriculum.json defines the best study order across existing tracks and requested gap tracks:

[
{ "id": "sql-fundamentals", "kind": "existing", "level": "beginner", "order": 1,
"modificationHints": "Posting emphasizes log investigation - consider a log-table query scenario." },
{ "id": "readme-product-onboarding", "kind": "docs-onboarding", "level": "beginner", "order": 2 },
{ "id": "api-debugging-ai-support", "kind": "requested", "level": "intermediate", "order": 3 }
]

modificationHints is an optional per-item string written during prep generation: a job-posting-specific suggestion for how an existing track could be tuned for this role. The app surfaces it as a one-tap suggestion when the learner opens the Modify form. Agents should only write it where the posting genuinely suggests a tuning - never generic advice.

The app treats tracks/<id>/track.yaml as the source of truth for whether a track is ready. If the folder exists, the Curriculum tab shows Start and lesson progress. If it does not, the item remains a request.

The app can reorder curriculum.json when the learner moves tracks in the Curriculum tab. Agents should preserve intentional ordering unless asked to optimize the sequence.

onboarding-requests.json stores approved docs sources for product-specific practice:

[
{
"id": "readme-product-onboarding",
"title": "ReadMe Product Onboarding",
"kind": "docs-onboarding",
"goal": "support-product",
"level": "beginner",
"docsUrl": "https://docs.example.com",
"sourcePath": "onboarding/readme-product-onboarding/source.md",
"notes": "Focus on API references, auth setup, and common support issues.",
"status": "suggested",
"createdBy": "User"
}
]

Docs onboarding should create operational fluency, not a summary. Expected outputs include product map, glossary, relevant workflows, common failure modes, mock support tickets, customer reply practice, escalation-writing practice, final readiness assessment, and suggested tracks only for remaining gaps.

Agents must use only approved source docs and flag assumptions when they infer behavior not directly supported by those sources.

track-requests.json gives agents and the GUI a machine-readable creation queue:

[
{
"id": "api-debugging-ai-support",
"title": "API Debugging for AI Product Support",
"level": "intermediate",
"depth": "standard",
"priority": "high",
"reason": "The role emphasizes customer API debugging and command-line reproduction.",
"status": "suggested",
"createdBy": "Codex"
}
]

Requests live in two places with the same entry format: per prep at preps/<prep-id>/track-requests.json, and - for standalone tracks not tied to any prep - a track-requests.json at the repo root, written by the home screen’s Add track button. Standalone tracks omit sourcePrep from their track.yaml.

The app may update status to creating or modify-requested. An external agent reads that state, creates or revises the track, sets status back to created when done, and records provenance in the track’s track.yaml, for example:

title: API Debugging for AI Product Support
description: Reproduce customer API issues with curl, request IDs, auth headers, and escalation notes.
level: intermediate
depth: standard
icon: world
createdBy: Codex
sourcePrep: product-support-specialist
modifiedFor: [acme-tse] # preps this track was later tuned for
modifiedBy: Claude Code # tool that applied the latest modification
lessons:
- 01-reading-api-docs
- 02-auth-header-mistakes

A modify-requested entry carries modificationNotes (what to change), an optional target level, and a mode:

  • mode: "in-place" - revise the track where it is, and add the prep’s id to modifiedFor (plus modifiedBy) in its track.yaml. The app renders modifiedFor as a tuned for tag - green in the prep it was tuned for, a warning in any other prep using the same track.
  • mode: "fork" - leave the original untouched. The request includes a forkTo id (<track>-<prep>); the agent copies the track there, applies the changes to the copy with parentTrack, sourcePrep, and modifiedFor set, and repoints the prep’s curriculum.json at the fork.

Agents should keep lesson directory ids stable during in-place modifications when the learner has progress on them, because progress.json keys by <track>/<lesson>.

icon is optional and picks the duotone icon shown on the track card (e.g. disk for SQL, world for APIs, bug for debugging, chip for CLI tools). The repo’s SPEC.md lists all allowed names; without it the app guesses from keywords in the title and description.

Tracks and requests should set level:

LevelUse when
beginnerThe learner has job context but does not yet know the tool, acronyms, or workflow. Define jargon before using it.
intermediateThe learner knows the basics and needs realistic support-ticket practice.
advancedThe learner is ready for edge cases, tradeoffs, and harder closed-book screens.

Use depth to set the bar:

DepthTypical shape
primer4-6 lessons for a compact foundation.
standard6-8 lessons for most job-ready tracks.
deep-dive8-10 lessons for advanced or broad topics.

Six lessons is a reference pattern, not a rule. Do not pad a narrow topic, and do not compress a broad beginner topic so much that it starts using unexplained jargon.

progress.json at the repo root holds lesson completion and quiz state. It is personal data: content generation and app updates must never modify it.