Skip to content

T-PP4I-books-consolidation-p1-content

Status: open/ready · Impact: high · Complexity: large

Book content lives inside one app (learn_web/data/) while the desktop app that distributes books has none — an untracked symlink is currently load-bearing — and every consumer hardcodes its content path. Create the solutions/augmented_learning/ solution’s content/ tier, move both corpora there, and introduce one path resolver so consumers stop hardcoding. This is Phase 1 of the books consolidation; T-SRJQ (apps) and T-V57A (packages) build on it, and its resolver’s walk-up design is what makes those later moves zero-code-edit.

LocationRole today
solutions/augmented/apps/learn_web/data/books/Model A corpus (+book.md hierarchy format; 131 tracked files, incl. flat remnants like books/test.json — move as-is, cleanup is backlog)
solutions/augmented/apps/learn_web/data/books_v2/Model B corpus (flat ref-based; 133 tracked files; the actively SERVED one; 5 editor-scratch books ride along — backlog)
solutions/augmented/apps/learn_web/layers/books/server/utils/books.tsHardcodes data/books_v2 joined to process.cwd(); feeds createBookServiceFSv1; 30+ Nitro routes ride the singleton
solutions/augmented/apps/learn_web/scripts/migrate-v1-to-v2.tsOne-shot A→B converter; hardcodes cwd-relative data/books
solutions/augmented/apps/learn_web/scripts/migrate-to-hybrid-ids.tsSame hardcode pattern
solutions/augmented/packages/ts/hierarchy/tests/v2/integration/learn_web_books.test.tsdescribe.skip with a STALE “data was not vendored” comment; its relative path currently resolves — repoint AND attempt un-skip
solutions/augmented/packages/ts/hierarchy/tests/v2/integration/sqlite_example.test.tsSame skip + relative reach
solutions/augmented/apps/augmented-books/src-tauri/src/api.rs#resolve_content_books_pathMulti-candidate search for content/books (manifest-parent / cwd / cwd-parent); finds nothing without the symlink
solutions/augmented/apps/augmented-books/src-tauri/src/bin/prepare_books.rsDefault input content/books, output assets/catalog
solutions/augmented/apps/augmented-books/scripts/zip-books.shHardcodes $PROJECT_ROOT/content/books
solutions/augmented/apps/augmented-books/.gitignoreDoes NOT ignore assets/catalog/ (currently untracked-not-ignored)
lefthook.ymlmarkdown-fmt pre-commit runs rumdl on staged *.md with exclude solutions/augmented/apps/learn_web/** — the moved corpus at its NEW path would be reflowed unless excluded FIRST
packages/ts/vault-path/src/index.tsThe resolution-order pattern to mirror (override > env > walk) — do NOT depend on it; wrong anchor markers for books
docs/planning/decisions/D-0001-project-structure.mdSolutions tier definition; needs the in-place content/ tier amendment
solutions/augmented_learning/
content/
books/ # Model A corpus (git mv, as-is)
books_v2/ # Model B corpus (git mv, as-is) — canonical
packages/ts/
books-content/ # NEW @augmented/books-content resolver package

Resolver semantics (both languages): explicit override → env BOOKS_CONTENT_DIR → walk-up from start dirs to the nearest ancestor containing solutions/augmented_learning/content → error naming the env var. TS start dirs: opts.from ?? process.cwd(), then the module’s own dir (so node .output/server/index.mjs from any cwd still lands in-repo). Rust start dirs: CARGO_MANIFEST_DIR, then current_dir().

@augmented/books-content ships source like vault-path (exports./src/index.ts, no build, no deps). API: resolveBooksContentDir(opts?) and booksCorpusDir("books" | "books_v2", opts?). Rust side is a new content_path module inside the desktop crate (no shared crate until a second Rust consumer appears — D-0001 promotion rule).

  1. FIRST edit lefthook.yml: add solutions/augmented_learning/content/** to the markdown-fmt exclude list (comment: vendored corpus, not the dev rumdl regime). This must be in the working tree before any commit that stages the moved .md files, or the pre-commit hook reflows the corpus.
  2. git mv solutions/augmented/apps/learn_web/data/books solutions/augmented_learning/content/books and …/data/books_v2 → solutions/augmented_learning/content/books_v2. Move as-is (remnants/scratch books are backlog). After committing, assert git diff origin/main --stat -- 'solutions/augmented_learning/content' shows pure renames (100% similarity).
  3. Create solutions/augmented_learning/packages/ts/books-content/: package.json (name @augmented/books-content, source-shipped), src/index.ts (API above), src/index.test.ts (override wins; env wins over walk; walk from a nested dir finds the corpus; total miss throws naming BOOKS_CONTENT_DIR), thin moon.yml (mirror solutions/augmented/packages/ts/books/moon.yml, minus build). Root workspaces/moon globs (solutions/*/packages/ts/*, solutions/*/packages/*/*) already cover it — no root edits.
  4. Repoint learn_web: in layers/books/server/utils/books.ts delete the books_DATA_PATH constant + getDataPath(), pass booksCorpusDir("books_v2") as dataPath; add "@augmented/books-content": "workspace:*" to learn_web deps. Repoint the two migrate scripts via booksCorpusDir.
  5. Repoint hierarchy tests to booksCorpusDir("books") (devDep @augmented/books-content); replace the stale skip comment; attempt describe.skipdescribe. If the suites fail on corpus drift, keep the skip with the repointed path and an accurate comment — phase stays green either way; record the outcome in the PR.
  6. Desktop app: new src-tauri/src/content_path.rs (BOOKS_CONTENT_ENV const + resolve_books_content_dir()), register pub mod content_path; in lib.rs (the bin reaches it as augmented_books_lib::content_path). In api.rs#resolve_content_books_path, try the resolver first (.join("books")), keep the legacy candidate list as fallback, fold all attempted paths into the error. In prepare_books.rs, default the input dir to the resolver (explicit argv still wins). In zip-books.sh, derive from git rev-parse --show-toplevel with a BOOKS_CONTENT_DIR override.
  7. Delete the untracked symlink solutions/augmented/apps/augmented-books/content/books (local-only; note in the PR body so other checkouts do the same). Add assets/catalog/ to the app .gitignore.
  8. Docs: rewrite the desktop README’s “content/books is absent” section (canonical path, resolver order, env var) and its docs/architecture/project-structure.md mention. Amend D-0001 in place: solutions gain an optional content/ tier — extend the Summary bullet and Decision sentence ({apps/, packages/{ts,rust}, content/}), add a content/ line to the end-state tree, one placement sentence (“corpus data consumed by more than one app of a solution lives at solutions/<s>/content/, reached via a resolver, never by app-relative hardcode”), and name solutions/augmented_learning as the second occupant. Then sdlc docs generate and commit any drift.
  9. Regenerate root bun.lock (bun install); verify with bun install --frozen-lockfile.
  10. Verify (all from repo root unless noted): books-content bun test
    • typecheck; bun run test in books and hierarchy packages; learn_web bun run build, then boot node solutions/augmented/apps/learn_web/.output/server/index.mjs from the REPO ROOT and curl localhost:3000/api/books/v2 (proves walk-up via module dir); cargo check -p augmented_books; cargo run -p augmented_books --bin prepare-books (resolves the new path, writes assets/catalog); zip-books.sh; moon query projects | grep books-content; bun run apps/sdlc/cli/sdlc.ts docs generate --check.
LocationKindChange
lefthook.ymlmodifymarkdown-fmt exclude += solutions/augmented_learning/content/** (FIRST, before the move commit)
solutions/augmented/apps/learn_web/data/books/deletegit mvsolutions/augmented_learning/content/books/
solutions/augmented/apps/learn_web/data/books_v2/deletegit mvsolutions/augmented_learning/content/books_v2/
solutions/augmented_learning/content/newDestination of both corpora (pure rename)
solutions/augmented_learning/packages/ts/books-content/new@augmented/books-content resolver package + tests + moon.yml
solutions/augmented/apps/learn_web/layers/books/server/utils/books.tsmodifyDrop hardcode; dataPath: booksCorpusDir("books_v2")
solutions/augmented/apps/learn_web/package.jsonmodifydep @augmented/books-content: workspace:*
solutions/augmented/apps/learn_web/scripts/migrate-v1-to-v2.tsmodifyRepoint via resolver
solutions/augmented/apps/learn_web/scripts/migrate-to-hybrid-ids.tsmodifyRepoint via resolver
solutions/augmented/packages/ts/hierarchy/tests/v2/integration/learn_web_books.test.tsmodifyResolver path; un-skip attempt; fix stale comment
solutions/augmented/packages/ts/hierarchy/tests/v2/integration/sqlite_example.test.tsmodifySame
solutions/augmented/packages/ts/hierarchy/package.jsonmodifydevDep @augmented/books-content
solutions/augmented/apps/augmented-books/src-tauri/src/content_path.rsnewEnv + ancestor-walk resolver
solutions/augmented/apps/augmented-books/src-tauri/src/lib.rsmodifypub mod content_path;
solutions/augmented/apps/augmented-books/src-tauri/src/api.rs#resolve_content_books_pathmodifyResolver first, legacy fallback, richer error
solutions/augmented/apps/augmented-books/src-tauri/src/bin/prepare_books.rsmodifyDefault input via resolver
solutions/augmented/apps/augmented-books/scripts/zip-books.shmodifyrepo-root + BOOKS_CONTENT_DIR override
solutions/augmented/apps/augmented-books/.gitignoremodify+= assets/catalog/
solutions/augmented/apps/augmented-books/README.mdmodifyContent section rewrite
solutions/augmented/apps/augmented-books/docs/architecture/project-structure.mdmodifyPath update
docs/planning/decisions/D-0001-project-structure.mdmodifyIn-place content/ tier amendment
docs/index.mdmodifyRegenerated (docs generate)
bun.lockmodifyNew workspace member + dep edits
  • AC-1: Both corpora live under solutions/augmented_learning/content/ and git diff origin/main --stat -- 'solutions/augmented_learning/content' shows pure renames (no content-modified .md — the lefthook exclude held).
  • AC-2: bun test passes in solutions/augmented_learning/packages/ts/books-content/ covering override / env / walk-up / error cases.
  • AC-3: learn_web bun run build succeeds and the prod server booted from the REPO ROOT answers /api/books/v2.
  • AC-4: cargo run -p augmented_books --bin prepare-books resolves the corpus at the new path with no symlink present and writes assets/catalog.
  • AC-5: bun run test passes in the hierarchy package; the integration suites are un-skipped, or the PR records why the skip stays.
  • AC-6: No hardcoded data/books, data/books_v2, or app-relative content/books string remains in learn_web, hierarchy tests, or the desktop crate outside content_path.rs’s legacy fallback (grep-proof in PR).
  • AC-7: D-0001 names the content/ tier and solutions/augmented_learning; docs generate --check passes.
  • AC-8: bun install --frozen-lockfile passes at root.
  • Moving the apps (T-SRJQ) or packages (T-V57A).
  • Corpus cleanup (scratch books, flat remnants) and format convergence — captured as backlog.
  • Bundling content into the desktop app’s build (tauri.conf.json ships no resources today; BOOKS_CONTENT_DIR is the bundled escape hatch).
  • none — first phase; PR #1122 (the conflicting rename) was closed as superseded 2026-08-01.
  • Owner decision 2026-08-01: consolidate the book ecosystem under solutions/augmented_learning/; reading-log notes in the dormant sksizer/augmented repo are explicitly excluded (different kind of thing). Plan file: ~/.claude/plans/twinkly-meandering-rabbit.md.

← Back to Tasks