{% extends "base.html" %} {% block title %}mdship · Documentation that knows when it is stale{% endblock %} {% block body_class %}landing{% endblock %} {% block content %}
Docs-as-code for the AI era · v{{ facts.version }}

Know exactly which paragraph went stale, and why.

mdship tracks the sources behind every AI-written and generated section of your Markdown. When the code changes, it tells you what to regenerate, and leaves everything else alone.

docs — zsh

$ mdship ai-list docs/api.md

[{"name": "api-summary", "line": 3, "status": "up_to_date"}]

$ $EDITOR src/api.py # add farewell()

$ mdship ai-check docs/api.md

Error: docs/api.md: AI placeholder 'api-summary': dep ../src/api.py has changed since last generation

$ mdship ai-list docs/api.md

[{"name": "api-summary", "line": 3, "status": "needs_update"}]

$

Works with
The problem

AI-written docs rot silently.

The code changes. The paragraph that describes it does not. Nothing tells you which paragraph is now wrong, so you either regenerate everything, rewriting text that was fine, or you ship docs that lie.

`greet(name)` returns a friendly greeting…

…and there is nothing else in the module.

silently wrong since last Tuesday
The mdship way

Treat prose like a build artifact.

Every generated section declares its inputs: the prompt, the source files or slices it depends on, and a shared writing brief. mdship checksums all of them, so staleness is a fact you can check, not a feeling.

  • up_to_date nothing it depends on changed
  • needs_update this dependency, prompt, or brief changed
  • edited someone changed the generated text by hand
How it works

Declare. Generate. Detect.

Three steps, and the loop runs itself from then on.

  1. 1

    Declare the inputs

    An AI placeholder names its prompt and the sources it depends on.

    <!--AI
    name: "api-summary"
    prompt: |
        Summarize the public functions.
    deps:
      - path: ../src/api.py
    -->
    <!--/AI-->
  2. 2

    Generate through the gate

    ai_context says whether anything relevant changed and, only if something did, hands the agent exactly the source slices, previous text, and brief. ai_update installs the result and records every checksum.

    # in Claude Code
    /ai-placeholder docs/api.md
  3. 3

    Detect drift in CI

    Change a source file and ai-check fails with the precise reason. Regenerate only what is stale.

    Error: docs/api.md:
    AI placeholder 'api-summary':
    dep ../src/api.py has changed
    since last generation
What you get

Everything a living document needs.

🔗

Dependency tracking for prose

Depend on whole files or slices by line range, regex markers, or heading. Know which input changed.

🚦

A gate before every generation

ai_context answers "does anything need regenerating?" before a single token is spent on writing.

🛡️

Hand edits don't vanish silently

Generated regions carry a checksum. Edit one by hand and mdship update stops instead of overwriting your work.

✂️

Surgical edits for agents

get_section, replace_section, list_headings and friends: agents change the part they need, not the whole file.

🧩

Deterministic content, too

INCLUDE code, generate a TOC, load variables from JSON/YAML/TOML, render Jinja2 and Mermaid, run project scripts.

👀

Invisible when rendered

All control lives in HTML comments. GitHub and every renderer show clean, ordinary Markdown.

Honest by design

Detection, enforced in CI.

mdship detects problems; it doesn't prevent them. Checksums are compared when a check runs, so put the check where it can't be skipped:

  • ✓ a changed dependency fails the build
  • ✓ a changed prompt or brief fails the build
  • ✓ a hand-edited generated section fails the build
  • ! a never-generated placeholder passes; find those with ai-list
See the CI cookbook →
.github/workflows/docs.yml
jobs:
  docs:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: verhas/mdship@v1
        with:
          command: ai-check
          files: README.md docs/**/*.md
Placeholders

A toolbox of managed regions.

Each one is protected by the same checksum model.

Ship docs that stay true.

Install, run mdship init, add one placeholder, and let ai-check tell you the moment it goes stale.

Start the quickstart →
{% endblock %}