diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml
index 24268c68..d3c39142 100644
--- a/.pre-commit-config.yaml
+++ b/.pre-commit-config.yaml
@@ -293,9 +293,7 @@ repos:
args:
- --skip=*.json,*.lock,*-lock.yaml,*.min.js,*.min.css,.git,__pycache__,.venv,*.txt
- --ignore-words-list=als,ans,ect,nd,som,sur,te,nam,numer,lew,sie,wil,postion,clen,ther,folow,derrive,ony,tje,noe,theses,crate,doubleclick,wile,tabel,pary,blok,bloc,proces,serwer,parametr,adres,hart,dout,metod,tekst,synonim,grup,mosty,lokal,skalar,milion,nowe,tre,hel,alph,iif
- # guide-ladder.html inlines upstream build-your-own-x tutorial titles
- # verbatim (third-party data, incl. real source typos) — not authored text.
- exclude: ^(Bash/ffmpeg-build/|LaTeX/|.*\.geojson$|python_pkg/byox_ladder/guide-ladder\.html$)
+ exclude: ^(Bash/ffmpeg-build/|LaTeX/|.*\.geojson$)
# ===========================================================================
# DOCFORMATTER - Format docstrings (disabled - causes recursion errors)
diff --git a/CLAUDE.md b/CLAUDE.md
index 4e2b4bd5..0522919d 100644
--- a/CLAUDE.md
+++ b/CLAUDE.md
@@ -18,6 +18,10 @@ Extracted to their own repos:
`linux_configuration` dufs scripts + the standalone `dufs_client` repo. The
live `media-cloud-sync` systemd units now run the scripts from
`~/dufs-cloud/scripts/`.
+- [`build-your-x`](https://github.com/kuhyx/build-your-x) — build-your-own-x
+ difficulty ladder + `byox` progress tracker (crdt_sync-backed) and the builds
+ themselves (`builds/`). Extracted from `python_pkg/byox_ladder/` + the
+ standalone `~/build_your_x` builds. Lives at `~/build_your_x`.
Archived / unmaintained projects live in the sibling repository
[`testsAndMisc-archive`](https://github.com/kuhyx/testsAndMisc-archive).
diff --git a/docs/superpowers/evidence/remove-build-your-x-2026-07-17.json b/docs/superpowers/evidence/remove-build-your-x-2026-07-17.json
new file mode 100644
index 00000000..4ccd60fc
--- /dev/null
+++ b/docs/superpowers/evidence/remove-build-your-x-2026-07-17.json
@@ -0,0 +1,34 @@
+{
+ "intent": "Remove byox_ladder from the monorepo after extracting it to its own GitHub repo (kuhyx/build-your-x), where it gained a byox progress tracker + crdt_sync sync alongside the builds.",
+ "scope": [
+ "Delete python_pkg/byox_ladder/ (moved to https://github.com/kuhyx/build-your-x with full git history)",
+ "Update CLAUDE.md 'Extracted to their own repos' list",
+ "Drop the now-stale byox_ladder codespell exclude in .pre-commit-config.yaml",
+ "Non-goal: no behaviour change to any remaining monorepo package"
+ ],
+ "changes": [
+ "git rm -r python_pkg/byox_ladder (generator + tests + built pages)",
+ "CLAUDE.md: add build-your-x to the extracted-repos list",
+ ".pre-commit-config.yaml: remove the guide-ladder.html codespell exclude + its stale comment"
+ ],
+ "verification": [
+ {
+ "command": "grep -rn byox_ladder --include=*.py --include=*.toml --include=*.yaml . (excluding the package)",
+ "result": "pass",
+ "evidence": "No remaining references to byox_ladder anywhere in the monorepo after removal."
+ },
+ {
+ "command": "new repo: python -m pytest byox_ladder/tests/ --cov=byox_ladder --cov-branch --cov-fail-under=100",
+ "result": "pass",
+ "evidence": "71 passed, 100.00% branch coverage in kuhyx/build-your-x (pre-push gate)."
+ }
+ ],
+ "risks": [
+ "byox_ladder had no cross-package imports, so no remaining package depends on it.",
+ "Derived, gitignored artifacts (guides.json, byox-readme.md) were regenerable and copied into the new repo's checkout."
+ ],
+ "rollback": [
+ "Clone https://github.com/kuhyx/build-your-x and restore byox_ladder/ under python_pkg/ with imports rewritten back to python_pkg.byox_ladder.*",
+ "Validate: pytest python_pkg/byox_ladder/tests/ passes at 100%."
+ ]
+}
diff --git a/python_pkg/byox_ladder/.gitignore b/python_pkg/byox_ladder/.gitignore
deleted file mode 100644
index cdad6bdd..00000000
--- a/python_pkg/byox_ladder/.gitignore
+++ /dev/null
@@ -1,4 +0,0 @@
-# Fetched upstream README (third-party content — not vendored) and the derived
-# dataset. The built *-ladder.html pages ARE committed as the runnable output.
-byox-readme.md
-guides.json
diff --git a/python_pkg/byox_ladder/Makefile b/python_pkg/byox_ladder/Makefile
deleted file mode 100644
index bee3e018..00000000
--- a/python_pkg/byox_ladder/Makefile
+++ /dev/null
@@ -1,20 +0,0 @@
-# Regenerate the Build-Your-Own-X difficulty ladders.
-#
-# make build # fetch README -> parse -> build both standalone pages
-# make clean # remove the fetched README copy and derived data
-
-README_URL = https://raw.githubusercontent.com/codecrafters-io/build-your-own-x/master/README.md
-
-.PHONY: build clean
-
-build: guides.json
- python3 build_pages.py
-
-guides.json: byox-readme.md parse_guides.py
- python3 parse_guides.py
-
-byox-readme.md:
- curl -fsSL "$(README_URL)" -o byox-readme.md
-
-clean:
- rm -f byox-readme.md guides.json
diff --git a/python_pkg/byox_ladder/README.md b/python_pkg/byox_ladder/README.md
deleted file mode 100644
index 07bb7168..00000000
--- a/python_pkg/byox_ladder/README.md
+++ /dev/null
@@ -1,48 +0,0 @@
-# Build-Your-Own-X Difficulty Ladder
-
-Two self-contained, offline HTML pages that rank the projects from
-[codecrafters-io/build-your-own-x](https://github.com/codecrafters-io/build-your-own-x)
-from easiest to hardest:
-
-- **`guide-ladder.html`** — every individual tutorial (~360 guides), grouped
- into difficulty tiers and filterable by tier, language, and category.
-- **`category-ladder.html`** — the 30 top-level categories, grouped into
- Beginner / Intermediate / Advanced / Expert tiers.
-
-Both are single files with all CSS, JS, and data inlined — no server, fonts, or
-network needed. Open either directly in a browser (`file://`) and it works
-offline, in your OS light/dark theme.
-
-## Difficulty model
-
-Difficulty is a judgment call, scored on three axes: prerequisite domain
-knowledge, minimal-version scope, and how much step-by-step guidance exists.
-
-- A guide **inherits its category's tier** — the dominant signal. A Regex-Engine
- guide is easier than an Operating-System guide whatever the language.
-- **Language only nudges the order _within_ a tier**: lower-level languages
- (C, Rust, Assembly) sort a little harder than Python or JavaScript. It never
- moves a guide across a tier boundary.
-- The **Unsorted** group is the repo's own "Uncategorized" bucket. Its guides
- span every level, so they are deliberately left un-ranked rather than forced
- into a tier.
-
-The exact head-to-head order inside a tier is therefore approximate by design;
-the tier boundaries are the trustworthy part.
-
-## Regenerating
-
-```sh
-make build # curl the README -> parse_guides.py -> build_pages.py
-make clean # remove the fetched README copy and guides.json
-```
-
-The pipeline:
-
-1. `parse_guides.py` — parses a local copy of the upstream README
- (`byox-readme.md`) into `guides.json`, applying the difficulty model above.
-2. `build_pages.py` — injects `guides.json` into `templates/guide.template.html`
- and wraps `templates/category.html`, writing the two standalone pages.
-
-`byox-readme.md` and `guides.json` are derived artifacts and are gitignored; the
-built `*-ladder.html` pages are committed as the runnable output.
diff --git a/python_pkg/byox_ladder/__init__.py b/python_pkg/byox_ladder/__init__.py
deleted file mode 100644
index 76eb5fcc..00000000
--- a/python_pkg/byox_ladder/__init__.py
+++ /dev/null
@@ -1,4 +0,0 @@
-"""Generate the Build-Your-Own-X difficulty ladder pages.
-
-See ``README.md`` for the difficulty model and regeneration steps.
-"""
diff --git a/python_pkg/byox_ladder/build_pages.py b/python_pkg/byox_ladder/build_pages.py
deleted file mode 100755
index 05a49f93..00000000
--- a/python_pkg/byox_ladder/build_pages.py
+++ /dev/null
@@ -1,80 +0,0 @@
-#!/usr/bin/env python3
-"""Build the standalone HTML ladder pages from templates and parsed guide data.
-
-Produces two self-contained, dependency-free pages next to this script:
-
-* ``guide-ladder.html`` — every individual guide, built by injecting
- ``guides.json`` (written by ``parse_guides.py``) into the guide template.
-* ``category-ladder.html`` — the 30-category ladder, a static template.
-
-Both are wrapped in a minimal HTML document skeleton so they open directly via
-``file://`` with no server, fonts, or network access. Run ``make build`` to
-regenerate the whole pipeline.
-"""
-
-from __future__ import annotations
-
-import json
-import logging
-from pathlib import Path
-
-logger = logging.getLogger(__name__)
-
-HERE = Path(__file__).resolve().parent
-TEMPLATES = HERE / "templates"
-GUIDES_JSON = HERE / "guides.json"
-
-# Placeholder in the guide template that the inlined dataset replaces.
-MARKER = "/*__GUIDES__*/"
-
-# Minimal, valid document skeleton added around the published page content
-# (claude.ai injects an equivalent wrapper; local files need their own).
-SKELETON_HEAD = (
- "\n"
- '\n'
- "
\n"
- '\n'
- '\n'
-)
-SKELETON_TAIL = "\n\n"
-
-
-def standalone(content: str) -> str:
- """Wrap page content in a minimal HTML document skeleton."""
- return SKELETON_HEAD + content + SKELETON_TAIL
-
-
-def build_guide_page() -> str:
- """Inject ``guides.json`` into the guide template; return standalone HTML."""
- guides = json.loads(GUIDES_JSON.read_text(encoding="utf-8"))
- # Compact payload; guard any "" so inline data can't close the script tag.
- payload = json.dumps(guides, separators=(",", ":")).replace("", "<\\/")
- template = (TEMPLATES / "guide.template.html").read_text(encoding="utf-8")
- if MARKER not in template:
- message = f"marker {MARKER!r} not found in guide template"
- raise ValueError(message)
- return standalone(template.replace(MARKER, f"const GUIDES = {payload};"))
-
-
-def build_category_page() -> str:
- """Return the standalone category ladder (static content, no data)."""
- content = (TEMPLATES / "category.html").read_text(encoding="utf-8")
- return standalone(content)
-
-
-def main() -> int:
- """Write both standalone ladder pages next to this script; return an exit code."""
- if not GUIDES_JSON.exists():
- logger.error(
- "missing %s — run parse_guides.py first (or make build)", GUIDES_JSON
- )
- return 1
- (HERE / "guide-ladder.html").write_text(build_guide_page(), encoding="utf-8")
- (HERE / "category-ladder.html").write_text(build_category_page(), encoding="utf-8")
- logger.info("wrote guide-ladder.html and category-ladder.html")
- return 0
-
-
-if __name__ == "__main__":
- logging.basicConfig(level=logging.INFO, format="%(message)s")
- raise SystemExit(main())
diff --git a/python_pkg/byox_ladder/category-ladder.html b/python_pkg/byox_ladder/category-ladder.html
deleted file mode 100644
index 72135767..00000000
--- a/python_pkg/byox_ladder/category-ladder.html
+++ /dev/null
@@ -1,363 +0,0 @@
-
-
-
-
-
-Build Your Own X — Difficulty Ladder
-
-
-
-
-
codecrafters-io / build-your-own-x
-
The Build-Your-Own-X Difficulty Ladder
-
All 30 project categories from the
- build-your-own-x
- collection, ranked easiest → hardest and grouped into four tiers.
- Sort or filter, then follow a card to its tutorials.
-
-
-
PrerequisitesDomain knowledge a beginner must bring — crypto, linear algebra, OS internals.
-
ScopeHow much code a working toy version actually needs.
-
GuidanceWhether a canonical step-by-step tutorial exists to lean on.
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/python_pkg/byox_ladder/guide-ladder.html b/python_pkg/byox_ladder/guide-ladder.html
deleted file mode 100644
index c445e4e5..00000000
--- a/python_pkg/byox_ladder/guide-ladder.html
+++ /dev/null
@@ -1,320 +0,0 @@
-
-
-
-
-
-Build Your Own X — Guide Ladder
-
-
-
-
-
codecrafters-io / build-your-own-x
-
The Build-Your-Own-X Guide Ladder
-
Every individual tutorial from the
- build-your-own-x
- collection — 0 guides across 30 categories — placed on a difficulty
- ladder. Search, filter by tier / language / category, and click any guide to open it.
-
How difficulty is assigned: each guide inherits its category's tier
- (a Regex-Engine guide is easier than an OS guide, whatever the language). Language only nudges
- the order within a tier — lower-level languages (C, Rust, Asm) sit a little higher than
- Python or JS. The Unsorted group is the repo's own uncategorized bucket; its guides span
- all levels, so they aren't ranked.
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/python_pkg/byox_ladder/parse_guides.py b/python_pkg/byox_ladder/parse_guides.py
deleted file mode 100755
index a5d75c7b..00000000
--- a/python_pkg/byox_ladder/parse_guides.py
+++ /dev/null
@@ -1,197 +0,0 @@
-#!/usr/bin/env python3
-"""Parse the build-your-own-x README into a ranked, tiered list of guides.
-
-Reads a local copy of the upstream README (``byox-readme.md`` — fetch it with the
-command documented in this folder's ``README.md``, or run ``make build``) and
-writes ``guides.json``.
-
-Difficulty model: every guide inherits its category's tier — the dominant
-signal — while its language only nudges the ordering *within* that tier
-(lower-level languages such as C or Rust sort a little harder than Python or
-JavaScript). See ``README.md`` for the full rationale.
-"""
-
-from __future__ import annotations
-
-import json
-import logging
-from pathlib import Path
-import re
-
-logger = logging.getLogger(__name__)
-
-HERE = Path(__file__).resolve().parent
-README = HERE / "byox-readme.md"
-OUTPUT = HERE / "guides.json"
-
-# Category display name -> (global difficulty rank 1..30, tier). The rank order
-# mirrors the approved per-category ladder; keys match the exact backtick names
-# used in the README headers.
-CATEGORIES: dict[str, tuple[int, str]] = {
- "Command-Line Tool": (1, "beginner"),
- "Bot": (2, "beginner"),
- "Template Engine": (3, "beginner"),
- "Text Editor": (4, "beginner"),
- "Git": (5, "beginner"),
- "Shell": (6, "intermediate"),
- "Web Server": (7, "intermediate"),
- "Search Engine": (8, "intermediate"),
- "BitTorrent Client": (9, "intermediate"),
- "Front-end Framework / Library": (10, "intermediate"),
- "Regex Engine": (11, "intermediate"),
- "Game": (12, "intermediate"),
- "Programming Language": (13, "advanced"),
- "Emulator / Virtual Machine": (14, "advanced"),
- "Neural Network": (15, "advanced"),
- "Memory Allocator": (16, "advanced"),
- "Blockchain / Cryptocurrency": (17, "advanced"),
- "Database": (18, "advanced"),
- "3D Renderer": (19, "advanced"),
- "Physics Engine": (20, "advanced"),
- "Docker": (21, "advanced"),
- "Voxel Engine": (22, "advanced"),
- "Network Stack": (23, "expert"),
- "AI Model": (24, "expert"),
- "Visual Recognition System": (25, "expert"),
- "Augmented Reality": (26, "expert"),
- "Web Browser": (27, "expert"),
- "Processor": (28, "expert"),
- "Operating System": (29, "expert"),
- "Distributed Systems": (30, "expert"),
-}
-
-# Rank used for the repo's "Uncategorized" bucket, sorted after every real tier.
-UNSORTED_RANK = 99
-
-# Per-language ordering nudge (0.0 = high-level, higher = lower-level / more
-# effort). Only orders guides *within* a category; it never crosses a tier.
-LANG_EFFORT: dict[str, float] = {
- "assembly": 0.9,
- "asm": 0.9,
- "c": 0.8,
- "c++": 0.8,
- "cpp": 0.8,
- "rust": 0.75,
- "zig": 0.75,
- "d": 0.7,
- "nim": 0.6,
- "haskell": 0.6,
- "ocaml": 0.6,
- "go": 0.55,
- "scala": 0.55,
- "c#": 0.5,
- "java": 0.5,
- "kotlin": 0.5,
- "swift": 0.5,
- "f#": 0.5,
- "clojure": 0.45,
- "elixir": 0.4,
- "shell": 0.4,
- "bash": 0.4,
- "r": 0.4,
- "typescript": 0.35,
- "php": 0.35,
- "perl": 0.35,
- "dart": 0.35,
- "javascript": 0.3,
- "node.js": 0.3,
- "node": 0.3,
- "ruby": 0.3,
- "python": 0.3,
- "lua": 0.3,
- "any": 0.25,
- "pseudocode": 0.2,
-}
-
-# Default nudge for a language not present in ``LANG_EFFORT``.
-DEFAULT_EFFORT = 0.5
-
-HEADER_RE = re.compile(r"^####\s+Build your own\s+`([^`]+)`")
-UNCAT_RE = re.compile(r"^####\s+Uncategorized")
-GUIDE_RE = re.compile(r"^\*\s+\[(.+)\]\((\S+?)\)(.*)$")
-LABEL_RE = re.compile(r"^\*\*(.+?)\*\*:\s*(.*)$")
-
-
-def split_langs(raw: str) -> list[str]:
- """Split a ``C# / TypeScript`` language blob into a clean list of names."""
- parts = re.split(r"[/,&]| or ", raw)
- names = [part.strip().strip("*").strip() for part in parts]
- cleaned = [name for name in names if name]
- return cleaned or ["Various"]
-
-
-def lang_effort(langs: list[str]) -> float:
- """Return the easiest listed language's nudge (you pick your best one)."""
- values = [LANG_EFFORT.get(lang.lower(), DEFAULT_EFFORT) for lang in langs]
- return min(values) if values else DEFAULT_EFFORT
-
-
-def clean_title(rest: str) -> str:
- """Strip surrounding markdown italics/emphasis from a guide title."""
- title = rest.strip()
- title = re.sub(r"^_+", "", title)
- title = re.sub(r"_+$", "", title)
- return title.strip().strip("*").strip()
-
-
-def parse_guide_line(line: str, category: str) -> dict[str, object] | None:
- """Parse one ``* [**Lang**: _Title_](url)`` line into a guide record."""
- match = GUIDE_RE.match(line.strip())
- if match is None:
- return None
- label, url, trailer = match.group(1), match.group(2), match.group(3)
- label_match = LABEL_RE.match(label)
- if label_match is not None:
- langs = split_langs(label_match.group(1))
- title = clean_title(label_match.group(2))
- else:
- langs = ["Various"]
- title = clean_title(label)
- rank, tier = CATEGORIES.get(category, (UNSORTED_RANK, "unsorted"))
- return {
- "title": title,
- "url": url,
- "langs": langs,
- "category": category,
- "cat_rank": rank,
- "tier": tier,
- "video": "[video]" in trailer.lower(),
- "score": round(rank * 10 + lang_effort(langs), 2),
- }
-
-
-def parse_readme(text: str) -> list[dict[str, object]]:
- """Parse the whole README text into a score-sorted list of guides."""
- guides: list[dict[str, object]] = []
- current: str | None = None
- for line in text.splitlines():
- header = HEADER_RE.match(line)
- if header is not None:
- current = header.group(1).strip()
- continue
- if UNCAT_RE.match(line):
- current = "Uncategorized"
- continue
- if current is None:
- continue
- guide = parse_guide_line(line, current)
- if guide is not None:
- guides.append(guide)
- guides.sort(key=lambda item: (item["score"], str(item["title"]).lower()))
- return guides
-
-
-def main() -> int:
- """Parse ``byox-readme.md`` and write ``guides.json``; return an exit code."""
- if not README.exists():
- logger.error("missing %s — fetch it first (see README.md / make build)", README)
- return 1
- guides = parse_readme(README.read_text(encoding="utf-8"))
- OUTPUT.write_text(json.dumps(guides, indent=1), encoding="utf-8")
- logger.info("wrote %s (%d guides)", OUTPUT.name, len(guides))
- return 0
-
-
-if __name__ == "__main__":
- logging.basicConfig(level=logging.INFO, format="%(message)s")
- raise SystemExit(main())
diff --git a/python_pkg/byox_ladder/templates/category.html b/python_pkg/byox_ladder/templates/category.html
deleted file mode 100644
index cfbfbdc1..00000000
--- a/python_pkg/byox_ladder/templates/category.html
+++ /dev/null
@@ -1,356 +0,0 @@
-Build Your Own X — Difficulty Ladder
-
-
-
-
-
codecrafters-io / build-your-own-x
-
The Build-Your-Own-X Difficulty Ladder
-
All 30 project categories from the
- build-your-own-x
- collection, ranked easiest → hardest and grouped into four tiers.
- Sort or filter, then follow a card to its tutorials.
-
-
-
PrerequisitesDomain knowledge a beginner must bring — crypto, linear algebra, OS internals.
-
ScopeHow much code a working toy version actually needs.
-
GuidanceWhether a canonical step-by-step tutorial exists to lean on.
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/python_pkg/byox_ladder/templates/guide.template.html b/python_pkg/byox_ladder/templates/guide.template.html
deleted file mode 100644
index 5f26214e..00000000
--- a/python_pkg/byox_ladder/templates/guide.template.html
+++ /dev/null
@@ -1,313 +0,0 @@
-Build Your Own X — Guide Ladder
-
-
-
-
-
codecrafters-io / build-your-own-x
-
The Build-Your-Own-X Guide Ladder
-
Every individual tutorial from the
- build-your-own-x
- collection — 0 guides across 30 categories — placed on a difficulty
- ladder. Search, filter by tier / language / category, and click any guide to open it.
-
How difficulty is assigned: each guide inherits its category's tier
- (a Regex-Engine guide is easier than an OS guide, whatever the language). Language only nudges
- the order within a tier — lower-level languages (C, Rust, Asm) sit a little higher than
- Python or JS. The Unsorted group is the repo's own uncategorized bucket; its guides span
- all levels, so they aren't ranked.