Phase 4 of #393. Make the structure hold without anyone policing it.
What's wrong
Two failure modes have already happened once and nothing prevents a repeat:
- The junk drawer re-forms.
audit_navigation.py checks integrity and shape, but nothing checks that a page belongs in the tab it sits under. Documentation grew to 61 pages across 8 groups with no rule to stop it.
- Redirect discipline slips silently.
/getting_started/service-accounts has a redirect and is still linked as a broken internal link from tutorials/working_with_controls.mdx, because mint broken-links does not follow redirects. Nothing in CI catches the inverse either: a moved page with no redirect at all.
Why it matters
Phases 0 to 3 are one-off cleanups. Without this phase they decay, and the next contributor faces the same "where does this go?" question the parent issue exists to answer.
Tasks
URL impact
Out of scope
- Do not add checks that fail the build on advisory shape findings. Shape has always been advisory and there are legitimate exceptions, notably the generated
Reference > CLI Reference subtree.
Verification
Phase 4 of #393. Make the structure hold without anyone policing it.
What's wrong
Two failure modes have already happened once and nothing prevents a repeat:
audit_navigation.pychecks integrity and shape, but nothing checks that a page belongs in the tab it sits under.Documentationgrew to 61 pages across 8 groups with no rule to stop it./getting_started/service-accountshas a redirect and is still linked as a broken internal link fromtutorials/working_with_controls.mdx, becausemint broken-linksdoes not follow redirects. Nothing in CI catches the inverse either: a moved page with no redirect at all.Why it matters
Phases 0 to 3 are one-off cleanups. Without this phase they decay, and the next contributor faces the same "where does this go?" question the parent issue exists to answer.
Tasks
scripts/audit_navigation.pywith a per-tab membership check. Each tab declares its one-sentence test and the page-path prefixes that satisfy it; a page under a tab it does not match is a finding. Advisory at first, promoted to--checkonce the noise is understood.tests/: for every path inconfig/redirects.json, the destination resolves to a real page; and no page file has been moved in git history without a corresponding redirect.tab == "Reference"anditem == "CLI Reference"(update-cli-nav.py:154-158).kosli-dev/cliandkosli-dev/serverresolves. It needs a checked-in list rather than a live grep of sibling repos..claude/skills/doc-write/SKILL.mdandCLAUDE.mdwith the new tab set and membership tests, and delete the warning telling authors to ignore theTutorialslabel once that group no longer exists.URL impact
Out of scope
Reference > CLI Referencesubtree.Verification
python3 scripts/audit_navigation.py— the new membership check reports against the current treepython3 scripts/audit_navigation.py --check— still exit 0 on a clean treepython3 -m pytest tests/— new redirect and label tests pass, and fail when deliberately brokendoc-writeskill andCLAUDE.mddescribe the tab set that actually exists