# Architecture Research

**Domain:** Multi-audience documentation site with agent accessibility (Jekyll + Just the Docs)
**Researched:** 2026-03-20
**Confidence:** HIGH

## Standard Architecture

### System Overview

```
┌─────────────────────────────────────────────────────────────────────┐
│                         Authoring Layer                              │
│  ┌──────────────────┐  ┌──────────────────┐  ┌──────────────────┐   │
│  │  docs/guide/     │  │  docs/dev/       │  │  docs/           │   │
│  │  (User Guide)    │  │  (Dev Guide)     │  │  (Root pages +   │   │
│  │  MD + nav        │  │  MD + nav        │  │   llms.txt)      │   │
│  └────────┬─────────┘  └────────┬─────────┘  └────────┬─────────┘   │
└───────────┼─────────────────────┼─────────────────────┼─────────────┘
            │                     │                     │
┌───────────┴─────────────────────┴─────────────────────┴─────────────┐
│                        Build Layer (Jekyll)                           │
│  ┌─────────────────────────────────────────────────────────────┐    │
│  │  Just the Docs remote theme                                 │    │
│  │  nav built from parent: / nav_order: front matter           │    │
│  │  _config.yml — search, aux_links, excludes                  │    │
│  └─────────────────────┬───────────────────────────────────────┘    │
│                        │                                            │
│  ┌─────────────────────┴───────────────────────────────────────┐    │
│  │  scripts/gen_llms_txt.py                                    │    │
│  │  (Pre-build: walks docs/ tree, writes docs/llms.txt)        │    │
│  └───────────────────────────┬─────────────────────────────────┘    │
│                              │                                      │
│  ┌───────────────────────────┴─────────────────────────────────┐    │
│  │  scripts/build_docs_index.py                                │    │
│  │  (Pre-package-build: writes src/ztlctl/data/docs_index.json)│    │
│  └─────────────────────────────────────────────────────────────┘    │
└──────────────────────────────────────────────────────────────────────┘
            │
┌───────────┴──────────────────────────────────────────────────────────┐
│                          Output Layer                                 │
│  ┌──────────────┐  ┌──────────────────┐  ┌──────────────────────┐   │
│  │  HTML site   │  │  /llms.txt       │  │  docs_index.json     │   │
│  │  (GitHub     │  │  (static file,   │  │  (embedded in Python │   │
│  │   Pages)     │  │   served by GH   │  │   package as data)   │   │
│  └──────────────┘  │   Pages)         │  └──────────────────────┘   │
│                    └──────────────────┘                              │
└──────────────────────────────────────────────────────────────────────┘
            │                          │                │
┌───────────┴──────────────────────────┴────────────────┴─────────────┐
│                         Consumer Layer                                │
│  ┌──────────────┐  ┌────────────────────┐  ┌──────────────────────┐ │
│  │  Human users │  │  MCP clients /     │  │  ztlctl docs <query> │ │
│  │  (browser)   │  │  AI agents         │  │  (CLI command reads  │ │
│  │              │  │  (llms.txt +       │  │   embedded index,    │ │
│  │              │  │   docs:// resource)│  │   no network needed) │ │
│  └──────────────┘  └────────────────────┘  └──────────────────────┘ │
└──────────────────────────────────────────────────────────────────────┘
```

### Component Responsibilities

| Component | Responsibility | Implementation |
|-----------|----------------|----------------|
| `docs/guide.md` | User Guide section root — rendered as collapsible nav parent | `nav_order: 2`, `has_children: true` front matter |
| `docs/guide/` | User Guide content pages — tutorials, concepts, workflows, recipes, Obsidian | Each page: `parent: User Guide`, sequential `nav_order:` |
| `docs/dev.md` | Developer Guide section root — plugin authors and contributors | `nav_order: 3`, `has_children: true` front matter |
| `docs/dev/` | Developer Guide content pages — plugin API, hookspecs, architecture, contributing | Each page: `parent: Developer Guide`, sequential `nav_order:` |
| `docs/index.md` | Landing page — audience routing, quick links to both sections | `nav_order: 1`, no parent |
| `docs/llms.txt` | Agent/LLM discovery file following llms.txt spec | Generated by `scripts/gen_llms_txt.py`, committed |
| `docs/_config.yml` | Jekyll config — excludes internal files from public build | Add `plans/` to `exclude:` list |
| `scripts/gen_llms_txt.py` | Walks `docs/` tree, extracts frontmatter, emits `docs/llms.txt` | Standalone Python, no ztlctl imports |
| `scripts/build_docs_index.py` | Walks `docs/` tree, strips frontmatter, writes `src/ztlctl/data/docs_index.json` | Run before `uv build`; output committed |
| `src/ztlctl/commands/docs.py` | `ztlctl docs <query>` Click command — keyword search over embedded index | Reads `docs_index.json` via `importlib.resources` |
| `src/ztlctl/mcp/resources.py` | Adds `docs://search` MCP resource — agent-queryable documentation | Shares `_docs_search_impl()` with CLI command |

---

## Recommended Project Structure

```
docs/
├── _config.yml                      # Modified: exclude plans/, verify excludes
├── index.md                         # Landing page — audience routing, nav_order: 1
│
├── guide.md                         # User Guide parent, nav_order: 2, has_children: true
├── guide/
│   ├── installation.md              # parent: User Guide, nav_order: 1
│   ├── quickstart.md                # parent: User Guide, nav_order: 2
│   ├── tutorial.md                  # parent: User Guide, nav_order: 3
│   ├── concepts.md                  # parent: User Guide, nav_order: 4
│   ├── paradigms.md                 # parent: User Guide, nav_order: 5
│   ├── obsidian.md                  # parent: User Guide, nav_order: 6
│   ├── commands.md                  # parent: User Guide, nav_order: 7
│   ├── configuration.md             # parent: User Guide, nav_order: 8
│   ├── mcp.md                       # parent: User Guide, nav_order: 9
│   ├── agentic-workflows.md         # parent: User Guide, nav_order: 10
│   └── troubleshooting.md           # parent: User Guide, nav_order: 11
│
├── dev.md                           # Developer Guide parent, nav_order: 3, has_children: true
├── dev/
│   ├── architecture.md              # parent: Developer Guide, nav_order: 1
│   ├── contributing.md              # parent: Developer Guide, nav_order: 2
│   ├── plugin-guide.md              # parent: Developer Guide, nav_order: 3
│   ├── hookspecs.md                 # parent: Developer Guide, nav_order: 4
│   ├── api-reference.md             # parent: Developer Guide, nav_order: 5
│   └── action-registry.md           # parent: Developer Guide, nav_order: 6
│
├── llms.txt                         # Generated; committed; served at /ztlctl/llms.txt
└── plans/                           # Excluded from build; internal planning only
    └── *.md

scripts/
├── gen_llms_txt.py                  # New: generates docs/llms.txt from docs/ tree
└── build_docs_index.py              # New: generates src/ztlctl/data/docs_index.json

src/ztlctl/
├── commands/
│   └── docs.py                      # New: `ztlctl docs <query>` Click command
├── data/
│   └── docs_index.json              # Generated artifact; committed; included as package data
└── mcp/
    └── resources.py                 # Modified: add docs://search resource (7th resource)
```

### Files Removed from docs/ Root (After Move)

| File | Disposition |
|---|---|
| `installation.md` | Moved to `docs/guide/installation.md` |
| `quickstart.md` | Moved to `docs/guide/quickstart.md` |
| `tutorial.md` | Moved to `docs/guide/tutorial.md` |
| `concepts.md` | Moved to `docs/guide/concepts.md` |
| `paradigms.md` | Moved to `docs/guide/paradigms.md` |
| `obsidian.md` | Moved to `docs/guide/obsidian.md` |
| `commands.md` | Moved to `docs/guide/commands.md` |
| `configuration.md` | Moved to `docs/guide/configuration.md` |
| `mcp.md` | Moved to `docs/guide/mcp.md` |
| `agentic-workflows.md` | Moved to `docs/guide/agentic-workflows.md` |
| `troubleshooting.md` | Moved to `docs/guide/troubleshooting.md` |
| `development.md` | Moved to `docs/dev/contributing.md` (rename on move) |
| `backlog.md` | Removed from public docs; archive in `.planning/` or delete |
| `research-mapping.md` | Removed from public docs; archive in `.planning/` or delete |
| `roadmap.md` | Removed from public docs; archive in `.planning/` or delete |

### Structure Rationale

- **`docs/guide.md` + `docs/guide/`:** Just the Docs determines hierarchy from `parent:` front matter, not from directory structure. The parent page must be a standalone file (`guide.md`), not a directory index. Child pages in `docs/guide/` declare `parent: User Guide` to attach to it.
- **`docs/dev.md` + `docs/dev/`:** Same pattern for Developer Guide.
- **New section pages written fresh:** The user-facing content for plugin guides, hookspecs, and API reference does not exist yet and will be written as new files in `docs/dev/`.
- **`docs/plans/` excluded from build:** Add to `_config.yml` `exclude:` list. GitHub Pages currently serves these files publicly, which is unintended.
- **`docs/llms.txt` committed:** Generation takes under 1 second for a ~20-page site. Committing the output means it is always present when GitHub Pages builds. No GitHub Actions complexity needed.
- **`scripts/` at repo root:** Keeps build tools in Python without requiring Jekyll plugins or Ruby. No new project dependencies.

---

## Architectural Patterns

### Pattern 1: Just the Docs Parent/Child Navigation via Front Matter

**What:** Every page in a section declares its parent with `parent: [exact title]`. The parent page declares `has_children: true`. Three levels use `grand_parent:`. Ordering within a section uses `nav_order:`.

**When to use:** All pages in `docs/guide/` and `docs/dev/`. This is the only navigation mechanism Just the Docs supports — there is no sidebar config file, no `_data/navigation.yml`.

**Trade-offs:** Title matching is string-based and case-sensitive. Renaming a parent page's `title:` breaks all children until they are updated. Keep parent page titles stable.

**Example front matter:**

```yaml
# docs/guide.md  — section root (parent page)
---
title: User Guide
nav_order: 2
has_children: true
---

# docs/guide/concepts.md  — child page
---
title: Core Concepts
parent: User Guide
nav_order: 4
---

# docs/guide/workflows/index.md  — grandchild section root
---
title: Workflows
parent: User Guide
has_children: true
nav_order: 10
---

# docs/guide/workflows/research-capture.md  — grandchild page
---
title: Research Capture Workflow
parent: Workflows
grand_parent: User Guide
nav_order: 1
---
```

Note: `has_children: true` is technically redundant in Just the Docs >= 0.10.0 (inferred from children declaring `parent:`), but keep it explicit on section root pages for clarity.

### Pattern 2: llms.txt Generated from Docs Tree

**What:** `scripts/gen_llms_txt.py` walks `docs/`, reads YAML frontmatter for `title`, `description`, `parent`, and `nav_order`, groups pages by section, sorts by `nav_order`, filters internal files, and writes `docs/llms.txt` following the [llms.txt spec](https://llmstxt.org/): H1 project name, blockquote summary, H2 sections with markdown links and descriptions.

**When to use:** Run this script whenever docs content changes. Commit the output to `docs/llms.txt`. Do not maintain the file by hand.

**Trade-offs:** A generation step must be run manually (or added as a pre-commit hook). The alternative — hand-maintenance — produces drift within weeks. For a ~20-page site, generation is trivially fast.

**Output format following the llms.txt spec:**

```markdown
# ztlctl

> A local knowledge operating system for human users and AI agents.
> CLI and MCP tool for Zettelkasten knowledge management.

## User Guide

- [Installation](https://thatdevstudio.github.io/ztlctl/guide/installation): Install via pip, uv, or Homebrew
- [Quick Start](https://thatdevstudio.github.io/ztlctl/guide/quickstart): Your first vault in 9 commands
- [Core Concepts](https://thatdevstudio.github.io/ztlctl/guide/concepts): Content types, lifecycle states, vault structure

## Developer Guide

- [Plugin Guide](https://thatdevstudio.github.io/ztlctl/dev/plugin-guide): Writing plugins with hookspecs and custom note types
- [API Reference](https://thatdevstudio.github.io/ztlctl/dev/api-reference): Plugin contracts, hook signatures, ActionRegistry

## Optional

- [Command Reference](https://thatdevstudio.github.io/ztlctl/guide/commands): Full CLI options and flags reference
- [Troubleshooting](https://thatdevstudio.github.io/ztlctl/guide/troubleshooting): Common issues and solutions
```

### Pattern 3: Embedded Doc Index for CLI and MCP Search

**What:** `scripts/build_docs_index.py` walks `docs/`, strips YAML frontmatter, extracts section headings and body text, builds an array of page records, and writes `src/ztlctl/data/docs_index.json`. The Python package includes this file as package data. At runtime, `ztlctl docs <query>` and the MCP `docs://search` resource both read the index via `importlib.resources` and perform keyword matching against title, headings, and body.

**When to use:** Run before `uv build` (package build). Commit the generated file so it is always current relative to the source docs.

**Trade-offs:** The index is static relative to the installed package version. An older install has older docs — this is intentional and correct behavior. Index size for ~20 pages is negligible (< 100 KB uncompressed).

**`docs_index.json` record shape:**

```json
[
  {
    "id": "guide/concepts",
    "title": "Core Concepts",
    "section": "User Guide",
    "url": "https://thatdevstudio.github.io/ztlctl/guide/concepts",
    "description": "Content types, lifecycle states, vault structure",
    "headings": ["Content Types", "Lifecycle States", "Vault Structure"],
    "body": "ztlctl manages three durable authored artifact types..."
  }
]
```

### Pattern 4: Shared `_docs_search_impl()` for CLI and MCP

**What:** Both the CLI command (`commands/docs.py`) and the MCP resource (`mcp/resources.py`) call a shared helper function `_docs_search_impl(query: str, limit: int = 10) -> list[dict]`. This helper loads `docs_index.json` via `importlib.resources` (cached in a module-level variable after the first call) and returns matching records.

**When to use:** This is the existing `_impl` pattern already used across all 25 MCP tool functions. Apply the same pattern here.

**Trade-offs:** None. This is the established project convention — separating testable logic from Click/MCP wiring.

```python
# src/ztlctl/mcp/_docs_search_impl.py  (or inline in resources.py)
import importlib.resources
import json

_index: list[dict] | None = None

def _load_docs_index() -> list[dict]:
    global _index
    if _index is None:
        data = importlib.resources.files("ztlctl.data").joinpath("docs_index.json")
        _index = json.loads(data.read_text())
    return _index

def _docs_search_impl(query: str, limit: int = 10) -> list[dict]:
    index = _load_docs_index()
    q = query.lower()
    results = []
    for page in index:
        score = 0
        if q in page["title"].lower():
            score += 3
        if any(q in h.lower() for h in page["headings"]):
            score += 2
        if q in page["body"].lower():
            score += 1
        if score > 0:
            results.append((score, page))
    results.sort(key=lambda x: x[0], reverse=True)
    return [r[1] for r in results[:limit]]
```

---

## Data Flow

### 1. `ztlctl docs <query>` CLI Command

```
User: ztlctl docs "plugin hooks"
    |
    v
src/ztlctl/commands/docs.py  (Click command)
    | calls _docs_search_impl("plugin hooks")
    v
_load_docs_index()
    | importlib.resources.files("ztlctl.data").joinpath("docs_index.json")
    | cached after first call
    v
[docs_index.json]  (embedded in installed package)
    | keyword match: title (+3), headings (+2), body (+1)
    | sort by score, truncate to --limit
    v
[matched page records]
    v
Rich table: Title | Section | Excerpt | URL
```

### 2. MCP `docs://search` Resource

```
MCP client: read resource "docs://search?q=plugin+hooks"
    |
    v
src/ztlctl/mcp/resources.py  (7th resource)
    | calls _docs_search_impl("plugin hooks")
    v
[same _load_docs_index() call — cached module-level]
    | keyword match (same logic as CLI)
    v
[matched page records]
    v
MCP resource response:
  { "results": [{ "title": "...", "section": "...", "url": "...", "excerpt": "..." }] }
```

### 3. `llms.txt` Generation (Pre-Commit / Manual)

```
scripts/gen_llms_txt.py
    | walks docs/ tree  (os.walk or glob)
    | for each .md file (excluding plans/, backlog.md, research-mapping.md, roadmap.md):
    |   parse YAML frontmatter: title, description, parent, nav_order
    | group pages: no parent -> root, parent=="User Guide" -> User Guide section, etc.
    | sort by nav_order within each group
    | emit llms.txt following spec: H1, blockquote, H2 sections, markdown links
    v
docs/llms.txt  (committed to repo)
    |
    v (GitHub Pages build)
https://thatdevstudio.github.io/ztlctl/llms.txt  (served as static file)
```

### 4. `docs_index.json` Generation (Pre-Package-Build)

```
scripts/build_docs_index.py
    | walks docs/ tree (same exclusions as gen_llms_txt.py)
    | for each .md file:
    |   strip YAML frontmatter
    |   extract: title (from frontmatter), section (from parent: field),
    |            headings (## lines), body (non-heading text)
    |   construct URL: baseurl + derived path from file location
    v
src/ztlctl/data/docs_index.json  (committed to repo)
    |
    v (uv build)
[wheel]  includes ztlctl/data/docs_index.json as package data
    |
    v (pip install / uv add)
[installed package]  importlib.resources can locate docs_index.json
```

### 5. Jekyll Navigation Rendering

```
docs/guide.md          (title: "User Guide", nav_order: 2, has_children: true)
docs/guide/concepts.md (title: "Core Concepts", parent: "User Guide", nav_order: 4)
    |
    v (Jekyll build with just-the-docs remote theme)
Left navigation panel:
  Home
  User Guide         (collapsed unless current page is a child)
    Installation
    Quick Start
    Tutorial
    Core Concepts    <- highlighted (current page)
    ...
  Developer Guide    (collapsed)
    Architecture
    Contributing
    ...
```

---

## Integration Points

### Jekyll Config Changes

`docs/_config.yml` requires one addition — `plans/` in the exclude list to prevent GitHub Pages from serving internal planning files publicly:

```yaml
exclude:
  - Gemfile
  - Gemfile.lock
  - plans/          # NEW: prevent public serving of internal planning files
```

The existing `search_enabled: true` setting uses Just the Docs' built-in Lunr.js search, which automatically indexes all non-excluded pages. No search configuration changes are needed — the restructured hierarchy is picked up automatically.

### Python Package Data Registration

`src/ztlctl/data/docs_index.json` must be included as package data. Add `src/ztlctl/data/__init__.py` (empty) so `importlib.resources` can locate the directory, and verify `pyproject.toml` includes data files:

```toml
# pyproject.toml (verify this section exists or add it)
[tool.hatch.build.targets.wheel]
packages = ["src/ztlctl"]
# hatch includes all package data by default; the data/ subpackage
# is included because src/ztlctl/data/__init__.py makes it a subpackage
```

If the project uses standard setuptools rather than hatch, add to `MANIFEST.in`:

```
include src/ztlctl/data/docs_index.json
```

### MCP Resource Addition

`src/ztlctl/mcp/resources.py` adds a seventh resource. The existing 6 resources are: `vault://info`, `vault://stats`, `vault://schema`, `note://[id]`, `tags://list`, `config://current`. The new resource:

| Resource URI | Query | Returns |
|---|---|---|
| `docs://search` | `q` (keyword string) | `{ "results": [{ "title", "section", "url", "excerpt" }] }` |

The resource handler calls `_docs_search_impl(q)`. The `_docs_search_impl` function lives in a shared location that both `commands/docs.py` and `resources.py` can import — either as a standalone module or as a function within `mcp/resources.py` that `commands/docs.py` imports from there.

### Internal Boundaries

| Boundary | Communication | Notes |
|---|---|---|
| `scripts/gen_llms_txt.py` -> `docs/` | Reads YAML frontmatter with PyYAML or python-frontmatter | No ztlctl imports; standalone script |
| `scripts/build_docs_index.py` -> `docs/` | Reads YAML frontmatter + body | Same standalone approach |
| `commands/docs.py` -> `data/docs_index.json` | `importlib.resources` at runtime | Works in editable installs and wheels |
| `mcp/resources.py` -> `data/docs_index.json` | Same `importlib.resources` load via shared `_docs_search_impl` | Module-level cache prevents re-reads during MCP server lifetime |
| `docs/llms.txt` -> GitHub Pages | Static file, no special Jekyll handling | Jekyll serves it as-is; no frontmatter added |
| `docs/plans/` -> Jekyll | Excluded in `_config.yml` | Not rendered or indexed |

---

## Build Order and Dependencies

The four new components have a strict dependency ordering:

```
Step 1: docs/ restructure
        Move files, update front matter (parent:, nav_order:),
        create guide.md and dev.md parent pages,
        create new dev/ content files
        |
        v
Step 2: _config.yml update
        Add plans/ to exclude list
        Verify navigation renders correctly (local Jekyll serve)
        |
        +--------------------------------+
        |                                |
        v                                v
Step 3a: scripts/gen_llms_txt.py    Step 3b: scripts/build_docs_index.py
         Run script                           Run script
         Commit docs/llms.txt                 Commit src/ztlctl/data/docs_index.json
         Verify served at /llms.txt
        |                                |
        |                                v
        |                        Step 4: commands/docs.py + mcp/resources.py
        |                                (CLI search command + MCP resource)
        |                                Requires docs_index.json (Step 3b)
        v
Step 5: Verify llms.txt served at
        https://thatdevstudio.github.io/ztlctl/llms.txt
        after GitHub Pages deployment
```

Steps 3a and 3b are independent. Step 4 depends on Step 3b — the CLI command and MCP resource need `docs_index.json` to exist for tests to pass.

---

## Anti-Patterns

### Anti-Pattern 1: Relying on Directory Structure for Navigation Hierarchy

**What people do:** Move files into `docs/guide/` and `docs/dev/` subdirectories but do not update `parent:` front matter, expecting Just the Docs to infer the hierarchy from the directory layout.

**Why it is wrong:** Just the Docs determines navigation hierarchy entirely from `parent:` front matter, not from filesystem structure. A page at `docs/guide/concepts.md` without `parent: User Guide` renders as a top-level page, not under User Guide. The directory structure is invisible to the theme.

**Do this instead:** Every file moved into a subdirectory must have its `parent:` field set to the exact `title:` value of the intended parent page. Update all 16 files as part of the restructure — leaving stale or missing `parent:` fields silently breaks the navigation.

### Anti-Pattern 2: Manually Maintained `llms.txt`

**What people do:** Write `docs/llms.txt` by hand when creating a new doc page, or edit it after moving files.

**Why it is wrong:** The file drifts immediately. A new page added under time pressure skips the manual llms.txt update. After two or three such occurrences, llms.txt contains dead links and missing entries. Agents receiving stale discovery data make worse tool selections.

**Do this instead:** Generate `docs/llms.txt` from the docs tree with `scripts/gen_llms_txt.py`. The generation is deterministic and fast (<1 second). Add a note to CONTRIBUTING.md: "Run `python scripts/gen_llms_txt.py` before committing new or renamed doc files."

### Anti-Pattern 3: Network-Dependent CLI Doc Search

**What people do:** Implement `ztlctl docs <query>` by fetching from the live GitHub Pages site at query time — either fetching `llms.txt` and parsing it, or hitting a search endpoint.

**Why it is wrong:** (1) Fails offline (CI, air-gapped systems, no network in subprocess). (2) Fetched content may not match the installed version. (3) Adds 200-500ms latency to every doc query. (4) Makes the command fail silently when GitHub Pages is down or rate-limiting.

**Do this instead:** Embed `docs_index.json` in the Python package as package data. Read it with `importlib.resources`. Zero network dependency, zero latency, guaranteed version match.

### Anti-Pattern 4: Duplicating Doc Search Logic in CLI and MCP

**What people do:** Write keyword search logic directly in `commands/docs.py` and write it again independently in `mcp/resources.py`.

**Why it is wrong:** The project has a clear established convention — MCP tool handlers call `_impl` functions that are independently testable (25 existing examples in `mcp/tools.py`). Duplicating logic violates this convention, creates divergence risk, and splits test coverage across two code paths that must remain identical.

**Do this instead:** Extract the search logic to `_docs_search_impl(query, limit)`. Both `commands/docs.py` and `mcp/resources.py` import and call it. Test it directly. One function, two callers, one set of tests.

### Anti-Pattern 5: Leaving Internal Files Served Publicly

**What people do:** Keep `backlog.md`, `research-mapping.md`, and `roadmap.md` in `docs/` after removing them from the navigation, relying on "if it is not linked, no one will find it."

**Why it is wrong:** GitHub Pages serves every file in `docs/` unless explicitly excluded. These files are indexed by search engines, discoverable by crawlers, and — most relevantly — included in the Just the Docs Lunr.js search index. A user searching for "MCP tools" might surface the internal backlog before the MCP guide.

**Do this instead:** Add `plans/` to `_config.yml` `exclude:` and physically remove `backlog.md`, `research-mapping.md`, and `roadmap.md` from `docs/`. Archive them in `.planning/` if the content has historical value.

---

## Scaling Considerations

This is a documentation site for a local CLI tool. Site delivery scaling is irrelevant (GitHub Pages handles all traffic). The relevant dimension is content volume and search quality:

| Content Scale | Architecture Adjustments |
|---|---|
| ~20 pages (v2.1 launch) | Keyword search in JSON array is sufficient. Full scan completes in < 5ms. |
| ~50 pages (6-12 months) | No changes needed. Consider adding `--section` filter flag to `ztlctl docs`. |
| ~200+ pages | Switch embedded index to SQLite FTS5 (ztlctl already has SQLite infrastructure). Index built same way, queried with `fts5` virtual table. |

For `llms.txt` specifically: the spec's `Optional` section is the right escape valve at larger scale. Pages that are detailed reference material (commands list, full API reference) move to `## Optional` so agents can skip them if context is constrained.

---

## Sources

- [Just the Docs — Page Levels](https://just-the-docs.com/docs/navigation/main/levels/) — `parent:`, `has_children:`, `nav_order:` front matter, verified 2026-03-20 (HIGH)
- [Just the Docs — Ancestry](https://just-the-docs.com/docs/navigation/main/ancestry/) — `grand_parent:`, `ancestor:` fields, verified 2026-03-20 (HIGH)
- [llms.txt specification](https://llmstxt.org/) — H1/blockquote/H2 format, `## Optional` section semantics, verified 2026-03-20 (HIGH)
- [Python importlib.resources](https://docs.python.org/3/library/importlib.resources.html) — package data access for embedded files (HIGH)
- Existing ztlctl codebase — `mcp/tools.py` `_impl` pattern, `mcp/resources.py` 6 existing resources, `docs/_config.yml`, `docs/index.md` — read directly 2026-03-20 (HIGH)

---

*Architecture research for: ztlctl v2.1 documentation site restructure*
*Researched: 2026-03-20*
