Model-generated docs fail when the prompt has too little evidence and too much freedom. A ledger-first workflow puts the facts in a human-curated file and narrows the model's job to rewriting that file into coherent paragraphs. The result is fewer fabricated API names and invented commands, without losing the speed of a first draft.
Why free-tier drafting changes the economics
When you have access to free model calls, the natural instinct is to paste more repository context and let the model "figure out" what matters. That approach produces paragraphs that sound correct but mix real symbols with plausible ones. The cost shows up later, when a reader runs a suggested command and gets command not found.
The underlying issue is not model quality; it is the absence of a verifiable boundary between known repository facts and training-data guesses. A ledger is that boundary. It is a Markdown file with YAML front matter that enumerates every external fact allowed in one documentation section.
The evidence ledger template
Create docs/ledgers/setup.md for the documentation section you plan to generate. The file lists the exact source paths, symbols, commands, and observed outputs that the model may cite.
---
doc: docs/setup.md
owner: platform
evidence:
- file: src/config.rs
symbol: Config::from_env
note: reads DATABASE_URL
- cmd: cargo run -- --help
output_file: tmp/help.txt
- file: examples/quickstart.rs
note: verified on Linux at commit 4f2c
required: [intro, quickstart, troubleshooting]
---
The evidence list is the model's allowed fact set, and the required list names the sections the draft must include. A human maintainer fills this file during a short triage pass, before any prompt is written. If an evidence item does not exist in the repository, the ledger should be rejected instead of sent to the model.
Validating the ledger before drafting
Use a small Python script to check that every referenced file exists and that required section names are well-formed. This script is intentionally minimal, but it inserts a machine-checkable gate between evidence collection and prompt construction.
#!/usr/bin/env python3
import argparse
from pathlib import Path
import yaml
def main() -> int:
with open(args.ledger, "r") as f:
data = yaml.safe_load(f)
root = Path(args.root or ".")
errors = []
for item in data["evidence"]:
if "file" in item:
p = root / item["file"]
if not p.is_file():
errors.append(f"Missing file: {item['file']}")
if "output_file" in item:
p = root / item["output_file"]
if not p.is_file():
errors.append(f"Missing output: {item['output_file']}")
for section in data["required"]:
if not isinstance(section, str) or len(section) < 2:
errors.append(f"Invalid required section: {section}")
if errors:
print("\n".join(errors))
return 1
print("Ledger OK")
return 0
if __name__ == "__main__":
parser = argparse.ArgumentParser()
parser.add_argument("--ledger", required=True)
parser.add_argument("--root", default=".")
args = parser.parse_args()
raise SystemExit(main())
Run it before drafting with python validate_ledger.py --ledger docs/ledgers/setup.md --root .. The script does not verify that Config::from_env exists inside src/config.rs; a simple grep in the same loop can add that check if you need stricter enforcement.
The drafting step with a constrained prompt
Now that the evidence is curated, build the prompt from the ledger rather than from the whole repository. Send only the evidence rows and ask for the three required sections: an intro, a quickstart, and a troubleshooting list. Tell the model to use [LEDGER-ONLY] if it feels an important fact is missing, which gives reviewers a clear signal to update the ledger.
A minimal prompt template looks like this:
Given the evidence below, write docs/setup.md.
- You may only mention facts listed in the evidence.
- Do not add commands that are not in the evidence.
- If you believe an important fact is missing, write [LEDGER-ONLY] in the paragraph.
- Use Markdown headings for: intro, quickstart, troubleshooting.
Evidence:
- src/config.rs -> Config::from_env
- cargo run -- --help -> tmp/help.txt
- examples/quickstart.rs
For this step, you can use MonkeyCode's free model access and its free server option without changing the rest of your toolchain. Disclosure: This article was prepared as part of MonkeyCode's product outreach. The prompt above is editor-agnostic; if you prefer another endpoint, the constraint rules stay exactly the same.
Reviewing the draft against the ledger
After the model returns the draft, run the validation script again and then perform a manual review that takes less time than a full documentation rewrite. Ask three yes/no questions for every paragraph:
- Does each fact map to one row in the
evidencelist? - Are warnings, recommendations, or conclusions absent unless the ledger explicitly notes them?
- Are commands written exactly as they appear in the observed output file?
If any answer is no, either add the missing fact to the ledger or delete the sentence. In the pull request, a review comment such as "Added tmp/help.txt evidence for the quickstart command, removed the unverified --force flag" keeps the human as the owner of decisions while letting the model own sentence structure.
Limitations and who should not use this workflow
A ledger-first workflow does not protect you from an inaccurate ledger. If you record the wrong command or the wrong symbol, the model will happily turn that error into fluent prose. The workflow also adds overhead to trivial documentation tasks; a one-sentence note should not need a ledger and a script. Teams that need a broad narrative overview of an entire system will find evidence curation too slow, because the ledger must be complete before any drafting can start. The method works best for reference-heavy docs such as setup guides, migration notes, API examples, and troubleshooting pages where each sentence can be traced to code or to an observed output.
Start with one ledger for a section that has already caused a support ticket or a wrong user action. After two or three iterations, you will know how much evidence each section actually needs. That measurement, not the model's output, is the real deliverable.
Top comments (0)