Skip to content
This document describes intent at the time it was written. It is part of the design record, not user documentation, and may not reflect the current behaviour of the code.

021 - ai rewrite and ai index commands

Purpose: Two user-facing AI commands built on the S20 client: mdd ai rewrite and mdd ai index.

Status: Implemented (2026-05-08)

mdd ai rewrite proposes tone/clarity rewrites of one or more markdown files. mdd ai index generates or refreshes a directory INDEX.md listing every page with a one-sentence summary.

Bundled in one spec because they share infrastructure (caching, output handling, frontmatter awareness) and are similarly small.

Originates from research note R03.

mdd ai rewrite <path>... [--style <prompt-file>] [--apply] [--model MODEL]
mdd ai index <dir> [--depth 1|all] [--apply] [--model MODEL]

Behaviour

  • For each input file, send the body (frontmatter excluded) plus a tone-of-voice system prompt to the model (default: ai.models.default from S20).
  • Default output: a <file>.rewrite.md sibling alongside the source (e.g. notes.mdnotes.md.rewrite.md; the full source filename is preserved so multi-dot names like RFC.draft.md round-trip to RFC.draft.md.rewrite.md without losing information). The user reviews with git diff or in an editor. No source file is mutated without --apply — the candidate-sibling pattern is the primary safety mechanism.
  • --apply overwrites in place (atomic *.tmp → rename). The prior file content is preserved in git history; no separate backup.

Tone-of-voice prompt

The rewrite system prompt has two blocks composed at call time:

  • Tone block — bundled at src/mdd/ai/prompts/tone-of-voice.md. Encodes a concise house voice (clear, professional, low-jargon). --style <file> overrides this block per run; the file is plain markdown used as the tone half of the system prompt.
  • Constraints block — bundled at src/mdd/ai/prompts/rewrite-constraints.md. Carries the load-bearing rules (preserve placeholder tokens, don’t alter substance, return only the rewritten body). Never overridable. --style swaps only the tone block; the constraints block is always appended after the user’s tone so the placeholder- preservation rule survives, regardless of how aggressive the custom style is.

Both digests participate in the cache key (body hash + tone digest

  • constraints digest + model id + mdd version per S20). A bundled-prompt upgrade therefore invalidates stale rewrites correctly.

Pass-through rules (the AI is allowed to rewrite body prose only)

  • Frontmatter — passed through unchanged.
  • Tables, code blocks, fenced {=confluence} blocks, fenced {=html} blocks — passed through unchanged.
  • Image references and links — preserved; the model is instructed not to reword link text in ways that break the document’s structure.
  • The Confluence export header (S09) — passed through unchanged.
  • The MDD footer (S09) — passed through unchanged.

The pass-through is enforced by extraction-and-restitch, not by trusting the model. Protected regions are replaced with placeholder tokens before the model call and substituted back afterwards. If a placeholder is missing from the response, fail loudly (model hallucinated away a protected region; output is unsafe).

Cache behaviour

  • Cache key includes: file body hash, tone digest, constraints digest, model id, mdd version (per S20 default).
  • Re-running on an unchanged file with the same tone is a no-op (cache hit, no API call).

Run summary

  • Per file: cached, rewritten, error.
  • End of run: total tokens / cost / count rewritten.

Output

  • Multiple files are processed concurrently, bounded by ai.concurrency (default 4 — see S20). Each call is small; the bound exists to honour gateway rate limits without serialising unnecessarily.
  • Errors per file are reported and continue; exit non-zero if any failed.

Behaviour

  • Walks <dir> recursively, finding .md files (ignoring already- generated INDEX.md files at any level).
  • For each .md: compute body hash; if frontmatter has a matching mdd.ai.summary_input_hash, reuse the cached summary (free). Else, summarise via the model (default: ai.models.summarise) and save the result back into the file’s frontmatter.
  • Generate or refresh <dir>/INDEX.md:
    # Index
    *Auto-generated by `mdd ai index`. Re-run to refresh.*
    ## Architecture
    - **[Platform Topology](Architecture/Platform-Topology.md)**
    overview of the platform's main runtime components and how
    they connect.
    - **[Deployment Pipeline](Architecture/Deployment-Pipeline.md)**
    end-to-end flow from PR merge to production rollout.
    ## Process
    - **[Onboarding](Process/Onboarding.md)** — checklist for new joiners.
  • --depth 1 lists files only (flat list, no grouping). --depth all lets the model cluster pages into topic groups (one extra LLM call); the cluster names become H2 headings.
  • --apply writes INDEX.md (default) and writes summaries back into source-file frontmatter. Without --apply, prints the proposed INDEX.md to stdout. Same safety pattern as rewrite: nothing on disk changes without explicit opt-in.
  • The generated INDEX.md carries its own frontmatter:
    ---
    ai_generated: true
    generated_by: mdd ai index
    generated_at: 2026-05-08T10:30:00Z
    ---

Per-file summary frontmatter

  • Stored on the source .md:
    mdd:
    ai:
    summary: "Overview of the platform's main runtime components and how they connect."
    summary_input_hash: 9f86d081...
    summary_model: claude-haiku-4-5
    summary_at: 2026-05-08T10:30:00Z
  • The summary_input_hash is what makes reruns a no-op.

Topic clustering (only at --depth all)

  • After per-file summaries, send the list (file + one-line summary) to the default model with a clustering prompt.
  • Model returns [{topic_title, file_paths[]}, ...].
  • Render each topic as an H2 with the listed files under it.

Default prompts are bundled in templates/prompts/ (one each for rewrite, summarise, cluster). They are plain markdown so users can read and override them.

Cache keys (all on top of S20’s base key):

  • Rewrite: (body_hash, style_hash, model_id, mdd_version, "rewrite")
  • Index per-file summary: (body_hash, model_id, mdd_version, "summary")
  • Index clustering: (joined_summaries_hash, model_id, mdd_version, "cluster")

Safety design — the --apply vs candidate-sibling distinction is deliberate. Rewrites are suggestions; the default behaviour must never silently mutate the user’s source. INDEX.md follows the same rule: stdout by default, file write only with --apply.

  • Multi-file rewrite that reasons about cross-file consistency (that’s S22’s review territory).
  • Translation. The default style is monolingual.
  • Generating frontmatter fields beyond mdd.ai.*.
  • Customising the index header / styling.

Site built 2026-07-30.