ONB — TFW-27 / Phase B: Link Resolution & Dynamic Navigation¶
Date: 2026-04-08 Author: Executor Status: 🟠 ONB — Awaiting answers Parent HL: HL__PhaseB__link_resolution.md TS: TS__PhaseB__link_resolution.md
1. Understanding¶
Phase B adds 4 features to gen_docs.py (~120 LOC): (1) bare task ID resolver, (2) markdown link rewriter for broken cross-references, (3) HTML anchors on entity table rows, (4) literate-nav SUMMARY.md generation. Then config changes: remove hardcoded nav: from mkdocs.yml, add literate-nav + section-index plugins to requirements.txt. End state: mkdocs build --strict passes clean, all 31 broken internal links are auto-fixed, navigation is auto-generated from filesystem.
2. Entry Points¶
| File | Purpose | Lines of Interest |
|---|---|---|
docs/scripts/gen_docs.py |
Main build script (445 LOC). All 4 features go here | resolve_references() L243-387, copy_with_frontmatter() L88-98, main() L392-444, STATIC_SOURCES L18-28, GLOB_SOURCES L31-36 |
docs/mkdocs.yml |
Site config. Hardcoded nav: block L43-63 to remove, plugins: L37-41 to extend |
L37-63 |
docs/requirements.txt |
Python deps. Add 2 new packages | L1-5 |
docs/scripts/test_gen_docs.py |
Unit tests (241 lines, 19 tests). Add tests for 3 new features | Entire file |
docs/scripts/test_integration.py |
Integration tests (187 lines). Add build verification | Entire file |
3. Questions (blocking — cannot proceed without answers)¶
| # | Question | Answer |
|---|---|---|
| 1 | TS Step 4 (literate-nav SUMMARY.md) uses mkdocs_gen_files.Nav() which requires the mkdocs-gen-files API. Current gen_docs.py does not import Nav from mkdocs_gen_files. The mkdocs_gen_files.Nav() API was added in mkdocs-gen-files>=0.5 — should I verify the minimum version in requirements.txt is sufficient, or pin to a higher minimum? |
Current requirements.txt already has mkdocs-gen-files>=0.5. This is sufficient — Nav() is available since 0.5. No version bump needed. |
4. Recommendations (suggestions, not blocking)¶
-
Resolver ordering clarity. The TS specifies link rewriter runs BEFORE
resolve_references()(Step 2), but bare task ID runs INSIDEresolve_references()as the last sub-resolver (Step 1). This is correct and avoids conflicts — link rewriter handles pre-existing markdown links, then reference resolvers handle text patterns. I propose adding a comment block documenting the full pipeline order for maintainability.✅ Accepted. Add a pipeline order comment block.
-
os.path.relpathon Windows. TS §6 calls out the risk ofos.path.relpathplatform differences. I propose usingPurePosixPathfor all URL-relative computations instead, keepingos.pathout of URL logic entirely. This is consistent with the existing pattern in gen_docs.py which uses.replace("\\", "/")everywhere.✅ Accepted. Use
PurePosixPath. Noimport os. -
Test coverage. TS does not specify test count, but the existing test suite has 19 unit tests + 14 integration tests. I plan to add ~8-10 unit tests (covering bare task ID, link rewriter, table anchors) and ~3-4 integration tests (strict mode, deep links, auto-nav). This keeps the test file manageable.
✅ Accepted. Good coverage plan.
-
import osscope. TS Step 2 notesimport osis needed. I recommend usingfrom pathlib import PurePosixPathinstead to avoid introducingosmodule dependency and keep path handling consistent with the existing codebase.✅ Accepted. Addressed by Rec 2.
5. Risks Found (edge cases, potential issues not in TS)¶
-
Existing
D{N}resolver produces absolute links. — Safe, rewriter skips/-prefixed. -
<span id="...">inside table cells. — Acceptable risk.tablesextension present. Proceed. -
literate-nav Tasks section. — Correct: flat Tasks entry is sufficient for Phase B. Per-task sidebar is Phase C if needed.
-
RELEASE.md optional. — Good catch. Conditionally add nav item:
if (root / "RELEASE.md").exists():. Apply same logic to other optional sources (KNOWLEDGE.md, TECH_DEBT.md).✅ Must fix in implementation.
-
Bare task ID regex and section headers. — Acceptable trade-off. Links in headings are valid.
6. Inconsistencies with Code (spec vs reality)¶
-
TS Step 2 signature uses
source_path: strinrewrite_markdown_links(), but the functioncopy_with_frontmatter()at L88 receivessource_path: stras the first argument. The TS correctly shows it being called asrewrite_markdown_links(result, source_path, path_map)— this is consistent with current code. -
TS Step 5 says "Remove the entire
nav:block (lines 43-63)" — actual mkdocs.ymlnav:is at lines 43-63 (22 lines). This is accurate. -
TS Step 4 implies
_generate_nav()is called inmain()but doesn't specify exact placement. It should run AFTER all pages are generated (after both static and glob processing), since it needs to reference pages that have been written. I'll add it at the end ofmain().
ONB — TFW-27 / Phase B: Link Resolution & Dynamic Navigation | 2026-04-08