Phase 0 Research: Docs Site IA & Onboarding Overhaul

All items below were either resolved directly with the user during specify/plan discovery (decision IDs referenced where applicable) or grounded in direct repository research. No [NEEDS CLARIFICATION] markers remain.

1. Redirect mechanism for moved/deleted pages

scripts/docs/redirect_map.yaml mechanism, adding one entry per moved/deleted path.

common-docs-consolidation mission (ADR docs/adr/3.x/2026-06-27-1-common-docs-reconciliation.md, decision D4) already solved this exact problem at much larger scale (~1,589 link occurrences) with a generated <meta http-equiv="refresh"> stub emitted at the old path by a post-build step reading a checked-in redirect map. Reusing it is both "use canonical sources" doctrine and the only tested option.

existing, proven infrastructure). Accepting URL churn with no redirects (rejected — breaks NFR-006's zero-orphaned-pages guarantee and regresses external links/SEO).

  • Decision: Reuse the existing scripts/docs/redirect_stub_generator.py +
  • Rationale: DocFX on GitHub Pages has no native alias/redirect mechanism. The
  • Alternatives considered: Building a new redirect mechanism (rejected — duplicates

2. Zone segregation approach (physical move scope)

move only genuinely misplaced content — contributor-only runbooks currently stranded inside docs/guides/ (e.g. pr-landing.md, testing-flakiness.md, recover-from-interrupted-merge.md) — into the existing docs/development/ contributor root. Already-correctly-scoped sections keep their current file locations and are grouped into one of the two zones purely via toc.yml nesting.

rule (specify-phase discovery), but also asked to "reduce overall volume ... not repeat ourselves or focus on minutiae." A full two-root rebuild would touch all ~600 files and regenerate proportionally more redirect-map churn than the actual problem (a 72-file mixed folder) warrants. Confirmed via Engineering Alignment in the plan-phase conversation.

churn for sections research already found structurally sound). Toc.yml-only cosmetic grouping with zero file moves (rejected — doesn't satisfy "STRICT" segregation, since a determined visitor could still browse from an end-user page directly into a contributor-only file at its unchanged path).

  • Decision: Do not rebuild the entire docs/ tree into two fresh top-level roots. Instead,
  • Rationale: The user confirmed "restructure freely" with "STRICT segregation" as a binding
  • Alternatives considered: Full physical two-root rebuild (rejected — disproportionate

3. Glossary term-linker mechanism

run in the same pipeline stage pattern as the existing seo_postprocess.py. Markdown source files are never mutated.

resolved). Keeps authored prose clean and diffable; linking logic lives in one script; re-running it never drifts from hand-edited source. DocFX's native xref system was investigated and rejected (see item 4).

drifts on every text edit). DocFX xref/@-uid cross-references (rejected — see below).

  • Decision: Build-time HTML post-processing pass (new scripts/docs/glossary_linker.py),
  • Rationale: User-confirmed via Decision Moment 01KY03YGX7GQEBKV45Q2Q8FXK3 (plan flow,
  • Alternatives considered: Markdown source rewrite (rejected by user — pollutes prose,

4. Why DocFX xref is not used for glossary linking

this repository. DocFX's xref system expects build-time uid-tagged content (typically generated API-doc YAML), not ad hoc prose-term matching, and the glossary source (kitty-specs/glossary.html) is generated by a separate static-HTML build step (scripts/docs/generate_kitty_specs_docs.py) that is not part of the DocFX markdown content build at all — it's picked up only via docfx.json's resource.files glob. A markdown/HTML post-processing pass is the natural fit, not xref.

  • Finding: grep across docs/ and docs/docfx.json found zero existing xref usage in

5. Canonical-term-usage check placement

— following the structural pattern of tests/architectural/test_no_legacy_terminology.py, but sourcing its term list from .kittify/glossaries/spec_kitty_core.yaml (104 terms) instead of a hardcoded denylist.

existing schema) with full alias/banned-synonym governance deferred to a follow-up GitHub issue (C-003). test_no_legacy_terminology.py checks a small, unrelated denylist (2 legacy terms) — conflating it with a 104-term canonical-form checker would make failures harder to diagnose. A sibling test keeps the two concerns separable while reusing the established pytest.mark.architectural / docs_scoped marker convention so it runs in the same CI job (integration-tests-core-misc) as the existing terminology guard.

conflates two different data sources and failure semantics). A standalone CLI command outside pytest (rejected — no existing precedent for a docs-only CLI gate in this repo; the pytest architectural-test pattern is already CI-wired and understood by maintainers).

  • Decision: New, separate pytest test — tests/architectural/test_glossary_canonical_terms.py
  • Rationale: User confirmed "lighter check now" (canonical spelling/casing only, against the
  • Alternatives considered: Extending test_no_legacy_terminology.py directly (rejected —

6. Hierarchical navigation mechanism (FR-015, added mid-plan)

≤6 unexpanded top-level entries each.

entry already uses nested items: and renders correctly in the site's default theme today. No template, theme, or pipeline change is required, keeping this compliant with C-001.

would require touching DocFX template/theme internals, which C-001 puts out of scope, for no benefit over the native mechanism DocFX already provides).

  • Decision: Use DocFX's native nested toc.yml items: syntax to group both zones into
  • Rationale: Verified by direct read of docs/toc.yml — the existing "Historical Archive"
  • Alternatives considered: A custom hamburger-menu widget via injected JS/CSS (rejected —

7. Doctrine artifact kind vocabulary

procedure, agent_profile, mission_step_contract.

tactic, styleguide, toolguide, paradigm, template, agent_profile, mission_step_contract). Correction (post-plan, during WP06 implementation): CLAUDE.md's table was stale. Independently verified against src/charter/kind_vocabulary.py, src/doctrine/artifact_kinds.py, git history (commit 1e3dc8d2c, "first-class TEMPLATE + loose-contract ASSET doctrine kinds"), and the live installed CLI (spec-kitty charter activate bogus-kind x → lists valid kinds; spec-kitty charter list --all) — the real 8th charter-activatable kind is procedure, not template. template and asset became distinct, non-charter-activatable kinds in a later commit and CLAUDE.md was never updated. This mission's authored content (docs/doctrine/doctrine-kinds.md) uses the code/CLI-verified list; spec.md and data-model.md were corrected to match. Single canonical authority is the live source/CLI, not a cached doc table — consistent with this mission's own FR-007 rationale.

  • Decision: Document exactly 8 kinds — directive, tactic, styleguide, toolguide, paradigm,
  • Rationale: Originally sourced from project CLAUDE.md's documented table (directive,

8. Mission-type vocabulary

— each sourced from its own src/specify_cli/missions/<type>/mission.yaml.

itself uses documentation, confirming the type exists and is distinct from software-dev (which a prior docs mission, spec-kitty-3-2-docs, used instead — noted as a precedent inconsistency but not something this mission needs to correct retroactively).

  • Decision: Document exactly 4 mission types — software-dev, research, documentation, plan
  • Rationale: Verified by directory listing of src/specify_cli/missions/; this mission

9. Reuse vs. rebuild of the abandoned charter-end-user-docs-828 mission's findings

IC-04/IC-05 authoring, never as ground truth (C-006 in spec.md).

validated against the current codebase and may be stale relative to this mission's own, fresher audit.

  • Decision: Treat that mission's gap-analysis.md/research.md as optional input during
  • Rationale: That mission was abandoned with 0 of 10 WPs completed — its analysis was never

10. Auto-generated gap-analysis.md (mechanical tool output)

over the entire repository (605 files, including non-docs/ directories like specify_cli, kernel, runtime, templates that are not part of the published site). Its directory-level heuristic classification is too coarse to drive this mission's IA decisions directly.

one in spec.md's "Documentation Scope → Known gaps" section, built from targeted, verified research (homepage framing, orphaned tutorial, mixed guides/ folder, thin doctrine docs, scattered charter guidance, unlinked glossary) rather than the mechanical tool's per-directory Divio-type matrix.

  • Finding: spec-kitty agent mission setup-plan auto-ran a generic coverage-matrix tool
  • Decision: Treat it as a supplementary data point only. The authoritative gap list is the