# mini-docs — Development Plan
Coverage target: **≥ 80%** line coverage, with the security test (sanitize-before-safe)
and the path-escape test treated as non-negotiable regardless of overall percentage.
## Phase 0.1.0 — M0: MVP
*Exit criteria: `input.md` + a template produces byte-correct, sanitized, escape-guarded
`output.html`.*
1. **Commit: `Builder` + template loading + input walk.** `Cargo.toml` (pulldown-cmark,
tera, serde, ammonia default), `src/lib.rs`. Primary test: walk a fixture input dir
with two nested `.md` files, assert the output dir mirrors the same structure with
`.html` extensions — file existence and path shape, not yet content.
2. **Commit: `split_frontmatter` + serde value.** Primary test: a fixture file with a
frontmatter block containing `title`, a string, and a list; assert the parsed serde
value matches an independently-constructed expected value field-by-field.
3. **Commit: `render_markdown` via pulldown-cmark + title/template resolution.** Primary
test: render a fixture `.md` with a `#` heading and no frontmatter `title`, assert
`page.title` falls back to that heading text exactly.
4. **Commit: sanitize-before-context, `{{ page.content | safe }}` wiring.** Primary
test (the crate's most important test): a fixture `.md` containing
`<script>alert(1)</script>` and an `onerror=` attribute, run the full `build()`
pipeline including template rendering, assert neither string appears anywhere in the
written output file.
5. **Commit: output-path escape guard.** Primary test: a fixture filename engineered to
traverse (`../../evil`), assert `build()` returns `Err(DocError::Escape)` and no file
is written outside `output_dir`.
6. **Commit: full boundary test.** One `input.md` + one `page.html` template → exact
expected `output.html`, with the expected string derived by hand from the CommonMark
spec and the template — not copied from a prior run.
## Phase 0.2.0 — M1: DX
*Exit criteria: `watch()` works across both `.md` and template changes; clean-URL link
rewriting and heading anchors are live; `DocError` is complete.*
7. **Commit: clean-URL link rewriting (`[x](x.md)` → `/x`).** Primary test: a fixture
linking to another page by its `.md` filename, assert the rendered output link is the
rewritten clean URL exactly.
8. **Commit: heading anchors/slugs.** Primary test: a heading with punctuation, assert
the generated anchor slug matches an independently-specified slugification rule.
9. **Commit: `watch()` — mtime polling over `.md` and templates, template-change fans
out to all dependents.** Primary test: modify a base template that a page extends,
assert `watch`'s next tick rebuilds that page's output file (mtime changes), not just
pages whose own `.md` mtime changed.
10. **Commit: `DocError::Template` + `err` feature mapping table.** Primary test: a
template referencing an undefined block, assert exactly `DocError::Template` (not a
generic error) and, under `--features err`, the correct `mini_err` variant/code.
## Phase 0.3.0 — M2: Performance
*Exit criteria: incremental rebuild only rewrites affected files; parallelism (if added)
has a fixed worker count.*
11. **Commit: render cache keyed on `(md_mtime, template_mtime)`.** Primary test: build
once, touch only one `.md` file, rebuild, assert only that file's output changed
(via mtime or content hash comparison on the others).
12. **Commit (optional): bounded parallel build.** Only if a real build-time budget
(stated as a number, per the manifesto's "measure before optimizing") motivates it.
Primary test: assert the worker count never exceeds the configured fixed bound under
a large fixture set.
## Phase 0.4.0 — M3: Features (demand-gated)
*Not built speculatively — each sub-item becomes its own commit only once a real
consumer needs it.*
13. ~~Two-pass build → `pages` collection in the context (index/nav generation).~~
**Superseded (`v0.3.0`)** by a real consumer need: `Builder::data_json(name)`, an
opt-in `data.json` page index (`id`/`title`/`date`/`updated`/`version`/`url`/
`summary`/`tags`/`pinned`) written for an *external* consumer (search index, TOC,
recent-items list) rather than exposed as an in-template `pages` variable. Also
landed: `draft: true` frontmatter (excludes a page from both the index and the
HTML build) and bare `true`/`false` frontmatter values parsing as real JSON
booleans. Regenerated by both `build()` and `Watcher::tick()`. See README's
`data.json` section. The original `pages`-in-Tera-context idea is still
unimplemented and would still need the two-pass build described above if a
consumer ever needs in-template iteration specifically.
14. TOC generation from collected headings.
15. `frontmatter-yaml`, `watch-notify`, `highlight` flags.
## Phase 0.5.0 — Publish
16. **Commit: crate metadata + doc comments.**
17. **Commit: CI** — `cargo test`, `cargo test --all-features`, `cargo clippy -- -D warnings`,
`cargo llvm-cov --fail-under-lines 80`, plus a Docker-free build-only check confirming
MSRV 1.75 (or Tera's actual MSRV, whichever is higher — resolve before this commit).
18. **Publish `0.5.0`** to crates.io (name confirmed available).