Skip to content

HL β€” TFW-27 / Phase B: Link Resolution & Dynamic Navigation

Date: 2026-04-08 Author: Coordinator Status: πŸ“ HL_DRAFT Parent HL: HL-TFW-27 RESEARCH: RES-TFW-27 β€” all hypotheses resolved


1. Vision

The TFW documentation site builds successfully but has two systemic problems: (1) ~31 broken internal links where .tfw/ source files reference siblings via relative paths that don't survive the source→output path mapping, and (2) a hardcoded nav: section in mkdocs.yml that needs manual updates every time a knowledge topic, workflow, or task is added.

Phase B makes the docs site self-healing and self-organizing. The link rewriter fixes all broken cross-references automatically at build time. The literate-nav integration generates navigation from the filesystem. After this phase, adding a new task or knowledge file requires zero manual nav/link maintenance β€” mkdocs build --strict passes clean.

Impact: The documentation site becomes a reliable, low-maintenance knowledge surface. Every reference to a TFW entity becomes a clickable link. Navigation reflects the actual project structure without manual curation. --strict mode ensures no silent breakage.

"I added a new knowledge topic file. Rebuilt the docs. The nav updated automatically, all cross-references resolved, and strict mode passed. I didn't touch mkdocs.yml or gen_docs.py."

2. Current State (As-Is)

Aspect Current State
Broken links ~31 internal links broken. .tfw/README.md links to conventions.md (sibling) β†’ resolves as /conventions.md in output, but actual output is /reference/conventions/. README.md links to .tfw/README.md β†’ becomes /concepts/philosophy/
Bare task IDs [TFW-18](../../TFW-18__knowledge_consolidation/HL-TFW-18__knowledge_consolidation.md) in text is plain text, not a link. Only [HL TFW-18](../../TFW-18__knowledge_consolidation/HL__PhaseB__knowledge_quality.md), [TS TFW-18](../../TFW-18__knowledge_consolidation/TS__PhaseB__knowledge_quality.md) resolve (artifact-prefixed)
HTML anchors D1, [TD-72](../../../reference/tech-debt.md), P3 link to section headers (#architecture-decisions) but not to individual rows. No deep linking to specific items
Navigation Hardcoded nav: in mkdocs.yml (43 lines). Adding a knowledge topic requires editing mkdocs.yml. Task nav auto-generated via gen_docs.py section index, but still listed under hardcoded nav: entry
Strict mode mkdocs build --strict fails due to absolute link warnings
Reference resolvers 6 working resolvers (artifact refs, phase refs, HL-dash, TD-N, D-N, backtick paths). Missing: bare task ID, markdown link rewriting

3. Target State (To-Be)

Aspect To-Be
Broken links 0 broken internal links. Markdown link rewriter maps source-relative paths to output-relative paths at build time
Bare task IDs [TFW-18](../../TFW-18__knowledge_consolidation/HL-TFW-18__knowledge_consolidation.md) β†’ clickable link to task HL. Works anywhere in text
HTML anchors id="d1", id="td-72", id="p3", id="f6" on table rows in KNOWLEDGE.md and TECH_DEBT.md. Deep-linkable: /knowledge-index/#d1
Navigation Dynamic via mkdocs-literate-nav. SUMMARY.md auto-generated by gen_docs.py. Static sections (Home, Getting Started, Concepts) defined in code. Dynamic sections (Knowledge, Tasks, Workflows, Templates) from filesystem scan
Strict mode mkdocs build --strict passes clean
Reference resolvers 8 resolvers total (existing 6 + bare task ID + markdown link rewriter)

3.1 Result Visualization

Link rewriter pipeline (build time):

Source file: .tfw/README.md
  Contains: [conventions.md](conventions.md)    ← sibling link
  Mapped to output: concepts/philosophy.md

  Link rewriter:
    1. Resolve relative to SOURCE: .tfw/conventions.md
    2. Look up in Source Manifest: .tfw/conventions.md β†’ reference/conventions.md
    3. Compute relative from OUTPUT: concepts/philosophy.md β†’ ../reference/conventions.md
    4. Rewrite: [conventions.md](../reference/conventions/)

Result: [conventions.md](../reference/conventions/)  βœ… valid

HTML anchors on table rows:

KNOWLEDGE.md source:                    Output:
| [D1](../../../knowledge-index.md#architecture-decisions) | Use .tfw/ as core | ...    β†’    <tr id="d1">| [D1](#d1) | Use .tfw/ as core | ...
| P3 | Traces over code  | ...    β†’    <tr id="p3">| [P3](#p3) | Traces over code  | ...

Deep link: /knowledge-index/#d1  βœ…

Dynamic navigation (literate-nav):

gen_docs.py generates SUMMARY.md:        mkdocs-literate-nav renders:

* [Home](index.md)                       Home
* [Getting Started](getting-started.md)  Getting Started
* Concepts                               Concepts
    * [Philosophy](concepts/philosophy)    β”œ Philosophy
* Knowledge                              Knowledge
    * [Index](knowledge-index.md)          β”œ Index
    * Topics                               β”œ Topics
        * [Convention](knowledge/...)       β”‚ β”œ Convention (auto)
        * [Constraint](knowledge/...)       β”‚ β”œ Constraint (auto)
        * [Philosophy](knowledge/...)       β”‚ β”œ Philosophy (auto)
        * [Process](knowledge/...)          β”‚ β”” Process (auto)
* Reference                              Reference
    * [Conventions](reference/...)         β”œ Conventions
    * [Glossary](reference/...)            β”œ Glossary
    * Workflows                            β”œ Workflows
        * [plan](reference/workflows/...)  β”‚ β”œ plan (auto)
        * [handoff](reference/workflows..) β”‚ β”œ handoff (auto)
        * ...                              β”‚ β”” ...
* Tasks                                   Tasks
    * [Index](tasks/index.md)              β”œ Index (with status)
    * [TFW-1](../../TFW-1__formalize_success_criteria/) ...                            β”œ [TFW-1](../../TFW-1__formalize_success_criteria/) (auto)
    * ...                                  β”” ...

4. Deliverables

4 features in gen_docs.py (~120 LOC) + config changes:

# Feature File LOC TDs Fixed
1 Bare task ID resolver gen_docs.py ~15 β€”
2 Markdown link rewriter gen_docs.py ~50 TD-72, TD-74
3 HTML anchors on table rows gen_docs.py ~30 β€”
4 literate-nav SUMMARY.md generation gen_docs.py ~25 TD-69, TD-70, TD-71
5 Add literate-nav + section-index deps requirements.txt 2 lines β€”
6 Remove hardcoded nav, add literate-nav plugin mkdocs.yml ~10 lines change TD-69

Budget: 0 new files, 3 modifications. ~120 LOC. Limits: max 14 files, max 8 new, max 1200 LOC.

5. Detailed Feature Descriptions

Feature 1: Bare Task ID Resolver

What: Resolve standalone [TFW-18](../../TFW-18__knowledge_consolidation/HL-TFW-18__knowledge_consolidation.md) (without artifact prefix) to the task's HL page.

Current behavior: Only [HL TFW-18](../../TFW-18__knowledge_consolidation/HL__PhaseB__knowledge_quality.md), [TS TFW-18](../../TFW-18__knowledge_consolidation/TS__PhaseB__knowledge_quality.md) resolve. Plain [TFW-18](../../TFW-18__knowledge_consolidation/HL-TFW-18__knowledge_consolidation.md) stays as text.

New behavior: [TFW-18](../../TFW-18__knowledge_consolidation/HL-TFW-18__knowledge_consolidation.md) β†’ [TFW-18](/tasks/TFW-18__knowledge_consolidation/HL-TFW-18__knowledge_consolidation/)

Regex: (?<!\[)(?<!\w)\bTFW-\d+\b(?!\])(?!__) β€” must not match inside existing links, and must not match folder names (TFW-18__...).

Order: Run AFTER artifact resolver (so [HL TFW-18](../../TFW-18__knowledge_consolidation/HL__PhaseB__knowledge_quality.md) gets resolved first, then remaining bare [TFW-18](../../TFW-18__knowledge_consolidation/HL-TFW-18__knowledge_consolidation.md)).

What: Fix [text](relative.md) links that break when source files are mapped to different output paths.

How: 1. Build a path map from STATIC_SOURCES + GLOB_SOURCES: {source_path β†’ output_path} 2. For each page being processed, when encountering a markdown link [text](target.md): a. Resolve target.md relative to the SOURCE file path b. Look up the resolved path in the path map c. If found, compute the relative path from the CURRENT OUTPUT to the TARGET OUTPUT d. Rewrite the link 3. Leave external links (http/https) and anchor-only links (#) unchanged

Critical: Must handle both .md links and directory-URL links. Must not break already-resolved links from reference resolvers.

Feature 3: HTML Anchors on Table Rows

What: Add id="..." attributes to table rows containing entity IDs (D{N}, TD-{N}, P{N}, F{N}, S{N}).

How: Post-process table lines. When a row starts with | [D1](../../../knowledge-index.md#architecture-decisions) or | [TD-72](../../../reference/tech-debt.md), inject an HTML anchor prefix.

Target files: KNOWLEDGE.md β†’ knowledge-index.md, TECH_DEBT.md β†’ reference/tech-debt.md. Applied during copy_with_frontmatter.

Result: /knowledge-index/#d1, /reference/tech-debt/#td-72 become valid deep links.

Feature 4: literate-nav SUMMARY.md

What: Generate a SUMMARY.md file via mkdocs_gen_files.Nav() that mkdocs-literate-nav uses for navigation.

How: 1. Static nav items defined in code (Home, Getting Started, Concepts, Reference) 2. Dynamic sections: scan the output pages already generated by gen_docs.py 3. Write SUMMARY.md via mkdocs_gen_files.open("SUMMARY.md", "w") 4. Remove nav: from mkdocs.yml, add literate-nav plugin

Nav structure: Matches current mkdocs.yml nav: but with dynamic Knowledge topics, Workflows, Templates, and Tasks.

6. Definition of Done (DoD)

  • βœ… 1. mkdocs build --strict passes clean (0 warnings, 0 errors)
  • βœ… 2. Bare [TFW-18](../../TFW-18__knowledge_consolidation/HL-TFW-18__knowledge_consolidation.md) in text β†’ clickable link to task HL
  • βœ… 3. All .tfw/ sibling links resolve correctly (e.g., conventions.md in .tfw/README.md β†’ /reference/conventions/)
  • βœ… 4. README.md links to .tfw/README.md resolve to /concepts/philosophy/
  • βœ… 5. D1, [TD-72](../../../reference/tech-debt.md), P3 deep-linkable via anchors
  • βœ… 6. Navigation auto-generated: adding a file to knowledge/ or .tfw/workflows/ shows up in nav without editing mkdocs.yml
  • βœ… 7. Existing reference resolvers still work (regression test)
  • βœ… 8. Hardcoded nav: removed from mkdocs.yml

7. Definition of Failure (DoF)

  • ❌ 1. Link rewriter breaks existing valid links (regression)
  • ❌ 2. literate-nav produces different nav structure than current hardcoded nav (unexpected section order)
  • ❌ 3. HTML anchors break table rendering in MkDocs
  • ❌ 4. Bare task ID resolver matches inside folder names or existing links (false positives)

8. Risks

Risk Probability Impact Mitigation
Link rewriter doesn't handle all edge cases (anchors, query params, external URLs) Medium Medium Unit tests for each edge case. External URLs explicitly skipped
literate-nav section order differs from current nav Low Low Define static sections first in code, verify against current nav
HTML anchor injection breaks markdown table parsing Medium Medium Test with pymdownx.superfences + tables extension. Fallback: use heading anchors instead of row anchors
Bare task ID regex matches too broadly Low Medium Negative lookaheads for __, [, existing links. Unit test with known false-positive patterns

9. Principles

  1. Resolvers are additive β€” new resolvers must not break existing 6 resolvers
  2. Links are relative β€” rewriter generates relative paths, not absolute, enabling strict mode
  3. Nav follows filesystem β€” no separate nav manifest. Directory structure = navigation structure
  4. Order matters β€” resolvers run in specific order: specific β†’ general (artifact refs β†’ bare task IDs)

10. RESEARCH Case

RESEARCH already completed at master HL level (RES-TFW-27). All hypotheses resolved: - H1: No artifact graph needed (rejected) - H2: literate-nav confirmed - H3: Broken links = two categories (sibling + README→.tfw/) - R3: P{N} auto-resolution rejected (double semantics) - R4: Backtick-only path resolution confirmed

No additional Phase B research needed. Scope is fully specified by RES conclusions.

11. Strategic Session Insights

Human-Only Test: Would this insight be unknown without the user saying it?

No new strategic insights in Phase B β€” this phase is purely technical, fully specified by RES.


HL β€” TFW-27 / Phase B: Link Resolution & Dynamic Navigation | 2026-04-08