CLAUDE.md β Claude Code guide for IT-Journey¶
IT-Journey is a Jekyll site (GitHub Pages, custom domain it-journey.dev): a gamified, open-source platform for learning IT and software development through quests (zer0 β her0). ~394 Markdown files live under pages/ across four populated collections β quests (~233, the center of gravity), quest-reports (~117, machine-authored walkthrough reports from the quest-perfection loop), a slim notes set (~27), and about (~12) β plus 5 loose pages/ files. _config.yml also declares a pages collection, but pages/_pages/ does not exist, so it is currently empty. The remote theme is bamr87/zer0-mistakes, consumed unpinned via remote_theme (there is no theme gem in the Gemfile).
Recent overhaul: the
_posts/_draftsblog,_notebooks,_hobbies,_docs, and_quickstartcollections were removed. General blog content moved to lifehacker.dev (github.com/bamr87/lifehacker.dev); the OverTheWirewargamesdocs (+scripts/docs-aggregator/) were extracted togithub.com/bamr87/wargames.
This repo already has a deep instruction set written for Copilot/Cursor. Read the right file for the task instead of guessing β they are authoritative.
Read-by-task-type (do this before editing)¶
| Task | Read first |
|---|---|
| Anything | AGENTS.md (overview, commands, quest permalink regex, gotchas) |
| Frontmatter / content rules | .github/copilot-instructions.md (constraints table + numbered pitfalls), .github/FRONTMATTER.md |
| A specific collection | .github/instructions/<name>.instructions.md (quest, notes, about β docs and quickstart are RETIRED, kept only as historical reference) |
| Content curation / the daily loop | .claude/skills/cms-curator/SKILL.md + .cms/README.md |
| Running/previewing the site | .claude/skills/run-it-journey/SKILL.md |
A /slash action |
.github/prompts/<name>.prompt.md (15 prompt-agents: write-quest, draft-article, validate-content, publish-prep, retrospective, β¦) |
The AI-augmented CMS (this repo's content layer)¶
The .cms/ directory + scripts/cms/cms.py are the content management system: a full-coverage index of every content file with health scores, issues, and a daily worklist that splits work into mechanical (auto-fixable) and substantive (needs authoring) lanes.
make cms-status # health dashboard by collection
make cms-all # rebuild .cms/index + .cms/reports/<date>.md + .cms/worklists/<date>.md
To run an incremental content-improvement pass, invoke the cms-curator skill β it is the single source of loop behavior, used by both /loop locally and the daily CI workflow (.github/workflows/cms-daily-loop.yml). The future VS Code CMS extension (separate repo) reads .cms/ too; see docs/cms/EXTENSION_DESIGN.md for the contract.
The AI content fleet (Claude-Code-OAuth, on-brand, continuous)¶
The substantive (Lane B) half of the CMS is executed by a fleet of Claude Code agents modeled on lifehacker.dev. All AI goes through one runner β the fleet's claude-run action, consumed by reference (uses: bamr87/bamr87/.github/actions/claude-run@main; nothing vendored here) and configured by this repo's _data/ai.yml β authenticated by a CLAUDE_CODE_OAUTH_TOKEN secret. Roles are in .claude/agents/*.md; the content-curator skill composes the existing cms-curator + brand-voice skills. See scripts/ai/README.md for the full map and setup.
content-factory.yml(daily) βcontent-curatorimproves one page per collection from the.cmsworklist β oneauto:contentPR each.content-review.yml(content PR) βcontent-reviewereditorial pass.content-quality.ymlβ deterministicscripts/ci/brand_lint.pygate; spelling drift fails the check (blocks auto-merge).content-auto-merge.ymlβ smuggle-guard (classify_changes.py, content-only) + checks-green β squash-merge. Routes on live API labels, never the event payload snapshot.pr-freshness.yml(push tomain; after a merge; 6-hourly) β the reconciler that keeps the loops' open PRs mergeable: behind β merge the base in; a conflicted report PR βscripts/quest/refresh_report_branch.shrebuilds its generated ledger/dashboard/collection from their inputs; otherwise re-drive the merge lane.PR_FRESHNESS_ENABLED.quest-forge.yml(issue labeledepic-quest//forge-questcomment) β thequest-forgeagent collects an epic-quest proposal issue deterministically (scripts/quest/forge_issue.py) and authors a fullepic_questhub +bonus_questchapters inpages/_quests/codex/β oneauto:questPR. Closes the loop with lifehacker.dev (its quest-forge hook files the proposal; this consumes it).quest-idea-intake.yml(issue labeledquest-idea//refinecomment) β the Quest Idea Forge lane. The portal page (pages/quest-ideas.md, permalink/quests/ideas/) shapes a visitor's quest idea client-side (registry-driven autocomplete, duplicate radar, readiness meter that gates the submit button) and files it through thequest-idea.ymlissue form; theidea-refineragent then reviews it β deterministic floor first (scripts/quest/idea_intake.py: rubric score, spam flags, duplicate radar; the model can only lower its verdict) β one review comment + oneidea:ready/idea:needs-detail/idea:declinedlabel. Never closes, never escalates; a human promotes a ready idea into quest-forge by addingepic-quest.quest-walkthrough.yml(dispatch-only; the daily sweep is quest-perfection) β walks one linked (character, level) quest slice end-to-end in the runner sandbox as a learner: a deterministic workflow step runs thetest/quest-validator/agentic_validate.pyexecute engine and seals the evidence (the engine can't run inside an agent β Claude Code scrubs auth env vars from Bash-tool subprocesses, so its childclaudeprocesses would auth-abort); thequest-walkeragent then writes the session report, and a report PR opens undertest/quest-validator/walkthroughs/. Also uploads session screenshots (rendered quest pages + a terminal render of the recorded transcript, viascripts/quest/walkthrough_screenshots.mjs) as run artifacts. Read-only over content; never merges.quest-video.yml(dispatch-only) β the quest walkthrough VIDEO lane: records a side-by-side video of a slice's main-quest run (rendered quest page β animated terminal replay of the sealed execute-engine transcript; fresh engine pass orsource_run_idreuse of a perfection artifact), and β withpublish: true+ theYOUTUBE_*secrets β uploads it unlisted to YouTube and opens onequest-video-labeled PR writing thewalkthrough_video:frontmatter block + the.quests/videos.ymlcatalog (no auto-merge policy matches that label β a human reviews every published reference; quest pages embed via_includes/quest/quest-video.html). Deterministic after the engine β no agent step.QUEST_VIDEO_ENABLED; design:docs/quests/VIDEO_FRAMEWORK.md.- Quest environment matrix β quests declare an
environment:block (OS / shell / editor / package manager + variables likeproject_dir), derived across the corpus byscripts/quest/env_migrate.pyfrom the platform sections they already document. It drives a reader-configurable control on the quest page (_includes/quest/quest-env.html+assets/js/quest-env.js: detects the OS, hides the paths that are not theirs, rewrites snippets to their folder) AND the testing framework (quest_steps.py --env os=windows,project_dir=my-labwalks the quest as that machine). Axes live once inquest_registry.py;make quest-datapublishes them to_data/quests/environments.yml. Design:docs/quests/ENVIRONMENT_MATRIX.md. - Whole-stack quest capture β
quest_steps.py(quest β executable step plan) +stack_capture.mjs(sandbox walk + per-step browser screenshots at the viewports each step is about + box-model probe) produce evidence the video recorder renders as a three-pane whole-stack video (docs β built UI β terminal). Model-free;make quest-steps QUEST=β¦ && make quest-stack-capture. quest-perfection.yml(daily) β the autonomous quest-perfection loop. For every character path it walks the highest-priority not-yet-perfect (character, level) slice (same sealed, workflow-minted evidence pattern as above), opens ONE consolidated walkthroughs+ledger report PR per run, thenquest-fix.ymlopens a separate content-only fix PR per granted slice that repairs only that walkthrough's verified issues (kept solely on a deterministic signal β tier-1 score + brand lint + sandbox commands β never the model's own grade) β auto-merges when green β repeats "until perfect". Each slice walks a rotating window ofcaps.max_quests_per_slicequests (.quests/budget.yml, default 5) viawalkthrough_plan.py --windowβ a level holds 20β30 quests and walking them all in one run exhausts the OAuth token's rate limit; the ledger accumulates per-quest coverage across runs and only certifiesperfectonce the whole level is swept + passing. A committed ledger + generated dashboard in.quests/are the source of truth; staged kill switchesQUEST_PERFECTION_ENABLED(orchestrator) andQUEST_FIX_ENABLED(write lane). Both lanes role-play the slice's class via the per-character sheets (.claude/skills/quest-character-<key>/β persona, voice, per-level exercises; roadmap blocks generated frompaths.yml+ the registry bymake quest-skills, drift-checked by the audit's advisoryskillslayer; design:docs/quests/CHARACTER_SKILLS.md).agent-audit.yml(weekly) βagent-auditorkeeps the fleet accurate/least-privilege.
OFF by default. Each workflow gates on a *_ENABLED repo variable and the auth secret, so nothing runs until you add CLAUDE_CODE_OAUTH_TOKEN and flip the variables. The old heuristic ai-content-review.yml (per-collection advisory issue spam) was replaced by this fleet + the deterministic brand gate.
Essential commands¶
make serve # local dev server (Docker via run-it-journey, port 4002)
make build-ci # CI-parity Jekyll build β run before any PR
make content-audit # frontmatter + quest + network validation
make content-normalize-apply # deterministic frontmatter fixes (mechanical lane)
make prose-oneline-apply # unwrap soft-wrapped prose β one paragraph per line
make quest-audit # quest content + dependency network validation
make quest-data # regenerate _data/quests/* after quest frontmatter edits
Host Ruby cannot build this site (the jekyll-theme-zer0 gem β₯1.21 needs Ruby β₯3.2); use the Docker path documented in the run-it-journey skill.
Non-negotiable conventions¶
- Frontmatter is CI-enforced. Required:
title, description, date, author, categories, tags.titleβ€ 60 chars;description120β160; dates ISO-8601 with ms (YYYY-MM-DDTHH:MM:SS.000Z);tags/categoriesare YAML lists. The PR will failfrontmatter-validationotherwise. - One paragraph per line is CI-enforced. Markdown body prose stays unwrapped β
one paragraph per line, never soft-wrapped at ~80 cols. The
markdown-onelinecheck (tools/unwrap-prose.py) fails the PR otherwise. LLMs soft-wrap by habit, so runmake prose-oneline-applyafter authoring (it joins only prose; code, tables, Liquid, and front matter are untouched). The AI content workflows apply this deterministically before opening a PR. - Never commit to
main. Branch with the repo's prefixes (feature/ fix/ docs/ chore/ content/) orautomated/cms-daily-<date>for the loop. Conventional Commits. - Quests are registry-governed.
_data/quests/*.ymlis generated fromscripts/quest/quest_registry.py; never hand-edit it. After changing quest frontmatter runmake quest-dataorquest-validationCI fails on stale data. - Vendored content is read-only. Any upstream content carrying
source_repo/source_urlfrontmatter is synced, never rewritten. - Validate before you push.
make build-ci+make content-audit(+make quest-auditif quests changed) must pass. - UI/UX changes ship with before/after screenshots. Any change that alters
rendered layout, styling, or interaction (CSS/SCSS, templates/includes, nav, JS that affects the DOM) must include before and after screenshots in the PR β at minimum a mobile viewport (β390px), plus desktop when the change affects it. Capture them by driving the running site (see the
run-it-journeyskill); a labeled side-by-side comparison is preferred. Commit the images underTODO/screenshots/(build-excluded) and embed them in the PR body. State what to look for, and call out that no other viewport regressed.
Repo map (quick)¶
pages/_<collection>/β all site content (_quests,_quest-reports,_notes,_about; loosepages/files). The brand/voice system governsquests(_data/brand/sections/holdsquest.mdplus a now-orphaneddocs.md, left over from the removed_docscollection)._data/β site data (quests/, navigation/, statistics). Much is generated.scripts/β Python/Ruby/Bash tooling (cms, quest, validation, generation, β¦)..cms/β CMS index, schema, reports, worklists (Jekyll-ignored)..github/β workflows, instructions, prompts (see each directory), copilot config.TODO/β worklist hub (SEO, links, reports). Excluded from the build.frontmatter.json+.frontmatter/β the Front Matter CMS VS Code config (content types, taxonomy, templates) the new CMS extends.
Fleet context¶
This repo is one of ~40 managed by the bamr87/bamr87 dash (registry: _data/projects.yml; tiered baseline: docs/STANDARDS.md). It is vendored there as a git submodule: commit and push changes here first β the hub only bumps its pointer afterwards. Shared CI, release, schema, and agent kits are seeded from the hub's templates/; prefer adopting those over hand-rolling equivalents. The AI runner (ai-runner kit) is the exception to seeding: it is consumed by reference from the hub (bamr87/bamr87/.github/actions/claude-run@main), never copied in β only its companions (_data/ai.yml, scripts/ai/usage.rb + usage_report.rb + api_call.rb, .prose-excludes) live here.
Standard deviations¶
UPS-REPO-02β tests live undertest/(grandfathered).markdown-oneline.ymlcarriesbranches: [main]on itspull_requesttrigger on top of the hub prose kit 0.3.0 shape: the recurring "sync gh-pages with main" deploy PRs target the builtgh-pagesbranch, GitHub cannot resolve a merge ref for them, and the kit's unfiltered trigger failed every one at startup with a red X unrelated to prose. The two extra--excludepatterns (machine-authored quest reports + walkthroughs) are the same list.prose-excludeshands the fleet'sclaude-runrunner.