Skip to content

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/_drafts blog, _notebooks, _hobbies, _docs, and _quickstart collections were removed. General blog content moved to lifehacker.dev (github.com/bamr87/lifehacker.dev); the OverTheWire wargames docs (+ scripts/docs-aggregator/) were extracted to github.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-curator improves one page per collection from the .cms worklist β†’ one auto:content PR each.
  • content-review.yml (content PR) β€” content-reviewer editorial pass.
  • content-quality.yml β€” deterministic scripts/ci/brand_lint.py gate; 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 to main; 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.sh rebuilds its generated ledger/dashboard/collection from their inputs; otherwise re-drive the merge lane. PR_FRESHNESS_ENABLED.
  • quest-forge.yml (issue labeled epic-quest / /forge-quest comment) β€” the quest-forge agent collects an epic-quest proposal issue deterministically (scripts/quest/forge_issue.py) and authors a full epic_quest hub + bonus_quest chapters in pages/_quests/codex/ β†’ one auto:quest PR. Closes the loop with lifehacker.dev (its quest-forge hook files the proposal; this consumes it).
  • quest-idea-intake.yml (issue labeled quest-idea / /refine comment) β€” 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 the quest-idea.yml issue form; the idea-refiner agent 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 + one idea:ready/idea:needs-detail/idea:declined label. Never closes, never escalates; a human promotes a ready idea into quest-forge by adding epic-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 the test/quest-validator/agentic_validate.py execute 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 child claude processes would auth-abort); the quest-walker agent then writes the session report, and a report PR opens under test/quest-validator/walkthroughs/. Also uploads session screenshots (rendered quest pages + a terminal render of the recorded transcript, via scripts/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 or source_run_id reuse of a perfection artifact), and β€” with publish: true + the YOUTUBE_* secrets β€” uploads it unlisted to YouTube and opens one quest-video-labeled PR writing the walkthrough_video: frontmatter block + the .quests/videos.yml catalog (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 like project_dir), derived across the corpus by scripts/quest/env_migrate.py from 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-lab walks the quest as that machine). Axes live once in quest_registry.py; make quest-data publishes 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, then quest-fix.yml opens 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 of caps.max_quests_per_slice quests (.quests/budget.yml, default 5) via walkthrough_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 certifies perfect once the whole level is swept + passing. A committed ledger + generated dashboard in .quests/ are the source of truth; staged kill switches QUEST_PERFECTION_ENABLED (orchestrator) and QUEST_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 from paths.yml + the registry by make quest-skills, drift-checked by the audit's advisory skills layer; design: docs/quests/CHARACTER_SKILLS.md).
  • agent-audit.yml (weekly) β€” agent-auditor keeps 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; description 120–160; dates ISO-8601 with ms (YYYY-MM-DDTHH:MM:SS.000Z); tags/categories are YAML lists. The PR will fail frontmatter-validation otherwise.
  • One paragraph per line is CI-enforced. Markdown body prose stays unwrapped β€” one paragraph per line, never soft-wrapped at ~80 cols. The markdown-oneline check (tools/unwrap-prose.py) fails the PR otherwise. LLMs soft-wrap by habit, so run make prose-oneline-apply after 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/) or automated/cms-daily-<date> for the loop. Conventional Commits.
  • Quests are registry-governed. _data/quests/*.yml is generated from scripts/quest/quest_registry.py; never hand-edit it. After changing quest frontmatter run make quest-data or quest-validation CI fails on stale data.
  • Vendored content is read-only. Any upstream content carrying source_repo/source_url frontmatter is synced, never rewritten.
  • Validate before you push. make build-ci + make content-audit (+ make quest-audit if 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-journey skill); a labeled side-by-side comparison is preferred. Commit the images under TODO/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; loose pages/ files). The brand/voice system governs quests (_data/brand/sections/ holds quest.md plus a now-orphaned docs.md, left over from the removed _docs collection).
  • _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 under test/ (grandfathered).
  • markdown-oneline.yml carries branches: [main] on its pull_request trigger on top of the hub prose kit 0.3.0 shape: the recurring "sync gh-pages with main" deploy PRs target the built gh-pages branch, 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 --exclude patterns (machine-authored quest reports + walkthroughs) are the same list .prose-excludes hands the fleet's claude-run runner.