Skip to content

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 for prerequisites, 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 of QUEST_TYPES.
  • fmContentType decides whether a page is a quest. Only fmContentType: quest enters quest collections, scoring, and the dependency graph. Support pages use documentation / template / codex and are excluded.
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, not site.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.py on changed files (--fail-threshold 70); placeholders fail unless draft: 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).