Quest Framework & Tooling¶
Everything about the quest taxonomy and schema is defined exactly once, in quest_registry.py. Every data file, validator, template, and generator derives from it, so the framework cannot drift.
Single source of truth¶
quest_registry.py owns:
- Taxonomy โ 4 tiers (Apprentice ๐ฑ / Adventurer โ๏ธ / Warrior ๐ฅ / Master โก;
there is no "Legend" tier โ level 1111 is Master), the 16 binary levels (
0000โ1111), their themes, XP ranges, and Bootstrap icons. - Controlled vocabularies โ
QUEST_TYPES(main_quest,side_quest,epic_quest,bonus_quest),FM_CONTENT_TYPES(quest,documentation,template,codex),DIFFICULTIES(๐ข Easy / ๐ก Medium / ๐ด Hard / โ๏ธ Epic),SKILL_FOCUS,LEARNING_STYLE. - Frontmatter schema โ
REQUIRED_FIELDS,OPTIONAL_FIELDS, and the nested contracts forprerequisites,rewards,quest_dependencies. - Collection rules โ
SKIP_SUBDIRS,SKIP_STEMS,is_quest(),is_draft(). - Routing โ canonical permalink helpers + slug rules + regexes.
๐ is the epic_quest icon, never a tier emoji.
Quest type vs content type¶
quest_type(a playable quest's kind) is one ofQUEST_TYPES.fmContentTypedecides whether a page is a quest. OnlyfmContentType: questenters quest collections, scoring, and the dependency graph. Support pages usedocumentation/template/codexand are excluded.
Canonical permalinks¶
| kind | pattern |
|---|---|
| main / side / epic quest | /quests/{level}/{slug}/ |
| bonus quest / codex | /quests/codex/{slug}/ |
| documentation | /quests/docs/{slug}/ |
| template | /quests/templates/{slug}/ |
The filename slug must equal the permalink slug. Side quests are flattened (no /side-quests/ segment).
Quest lifecycle¶
placeholder โ draft โ published
A quest containing placeholder scaffolding ([technology] tokens or the "๐ฎ Placeholder" footer) fails validation unless it declares draft: true. Drafts are allowed through CI; remove draft only when the quest is real and passes the validator at โฅ 70%.
Generated data (never hand-edit)¶
All four are regenerated from the registry + quest files by make quest-data:
| file | accessor | generator |
|---|---|---|
_data/quests/levels.yml |
site.data.quests.levels[code] |
generate-quest-levels-data.py |
_data/quests/tiers.yml |
site.data.quests.tiers[name] |
generate-quest-levels-data.py |
_data/quests/order.yml |
site.data.quests.order |
generate-quest-levels-data.py |
_data/navigation/quests.yml |
sidebar nav | generate-quest-navigation.py |
_data/quests/network.yml + assets/data/quest-network.json |
dependency graph | build-quest-network.py |
Jekyll keys data files by path, so the three files above split what used to be one nested file โ that's why the accessors are shallow (
site.data.quests.tiers, notsite.data.quests.levels.tiers). The network output is deterministic so CI can detect stale data.
The toolset¶
| script | purpose |
|---|---|
quest_registry.py |
the single source of truth (imported by everything) |
quest_lib.py |
the ONE frontmatter parser + quest-file iterator + QuestDoc + code-snippet extractor (extract_code_blocks); every validator imports it so parsing/discovery can't drift |
quest_audit.py |
the unified validation system โ runs content + network + data-freshness (+ optional Claude tier-2) in one report and exit code |
generate-quest-levels-data.py |
emit levels.yml / tiers.yml / order.yml |
generate-quest-navigation.py |
emit _data/navigation/quests.yml |
build-quest-network.py |
emit the dependency graph (JSON + YAML) |
validate-quest-network.py |
graph integrity: required-cycle, broken-dep, duplicate-permalink, dangling-edge, orphan, retired-field checks (registry-driven) |
normalize-quest-frontmatter.py |
the ONE idempotent frontmatter normalizer |
generate-placeholder-quest.sh |
scaffold a new placeholder quest |
docker-entrypoint.sh |
runs quest_audit.py inside the quest-audit Docker service |
walkthrough_plan.py |
deterministic walkthrough planner: one (character, level) slice โ dependency-ordered quest list (walk-plan.json) |
character_skills.py |
generator + drift check for the per-character skills (.claude/skills/quest-character-*/SKILL.md): regenerates each sheet's roadmap block from paths.yml + the registry, verifies pathโskill coverage and per-level exercise headings (see docs/quests/CHARACTER_SKILLS.md) |
walkthrough_screenshots.mjs |
session screenshots: rendered quest pages (mobile + desktop) + a terminal render of the recorded transcript |
walkthrough_video.mjs |
side-by-side walkthrough VIDEO: rendered quest page โ animated terminal replay of the sealed evidence transcript, recorded with Playwright (the quest-video lane's renderer) |
video_manifest.py |
deterministic quest-video arm: build composes the YouTube upload plan (title/description/chapters/tags); apply writes walkthrough_video: frontmatter + the .quests/videos.yml catalog (tests: test_video_manifest.py) |
youtube_upload.py |
stdlib-only YouTube Data API v3 resumable uploader (catalog-idempotent; --verify-only checks credentials) |
ledger.py |
the quest-perfection loop's deterministic ledger (.quests/ledger.json + dashboard) |
Quest content quality is validated by test/quest-validator/quest_validator.py, which imports its schema from the registry. An optional Claude Code tier-2 (test/quest-validator/agentic_validate.py) reads/plays quests for a deeper quality verdict; it's advisory and opt-in (see that directory's README).
Runbook¶
make quest-data # regenerate all derived data from the registry + files
make quest-normalize # idempotently normalize quest frontmatter
make quest-validate # content-quality validation (quest_validator.py)
make quest-network # dependency-graph validation
make quest-audit # UNIFIED audit: content + network + data-freshness (one report)
make quest-skills # regenerate the character-skill roadmap blocks (.claude/skills/quest-character-*)
make quest-skills-check # verify the character skills match paths.yml + the registry
make docker-validate # the same unified audit, in Docker (CI-parity, no host Python)
make docker-audit-tier2 MODE=review # + Claude tier-2 in a container (needs token)
make quest-execute QUEST=pages/_quests/0001/terminal-mastery.md # Claude RUNS the quest's snippets, isolated
Running a quest's code snippets (execute mode)¶
make quest-execute has a Claude Code agent walk a quest and actually run its runnable code snippets (bash/python/node/โฆ) in a disposable Docker container, then report which worked. The container is the isolation boundary โ the agent's commands never touch the host. quest_lib.extract_code_blocks() deterministically inventories the runnable snippets, and the report shows ran N/M coverage with per-snippet passed/failed/skipped/reasoned status. It is opt-in and advisory (it costs money and needs CLAUDE_CODE_OAUTH_TOKEN); without a token it falls back to a no-cost mock. Pass SAMPLE=N instead of QUEST= to run a spread across levels, or make quest-execute-host to run on the host sandbox (no container โ riskier).
make quest-audit only validates (it never writes files); if its freshness layer reports stale data, run make quest-data to regenerate, then commit.
Scaffold a new quest, then fill and validate it:
./scripts/quest/generate-placeholder-quest.sh 0110 sql-sorcery "SQL Sorcery"
# write real content, then:
python3 test/quest-validator/quest_validator.py pages/_quests/0110/sql-sorcery.md --fail-threshold 70
CI gate¶
.github/workflows/quest-validation.yml enforces, on every quest PR:
- content quality โ
quest_validator.pyon changed files (--fail-threshold 70); placeholders fail unlessdraft: true. - graph integrity โ
validate-quest-network.py(errors fail the job). - non-stale data โ regenerates all derived data and fails if the committed copies differ (so a registry change must ship with regenerated data).
These should be marked required in branch protection (see .github/workflows/README.md).