DEV Community

Avery Lin
Avery Lin

Posted on

Separate Captured Commands from Model-Written Troubleshooting Prose

A troubleshooting page stays accurate when a human owns the command transcript and the model owns only the explanation around it. The model may describe intent, order, and reader context inside a file that the registry marks as draftable. It may not invent flags, exit codes, durations, or allowances that the pinned transcript and fact ledger do not already contain. A checker should enforce that split before generated prose is allowed to reach a human review queue.

Why the transcript has to stay human-owned

Generated troubleshooting pages fail in a familiar way when the evidence and the draft occupy the same file. The model can smooth a failed command into a plausible success, or it can add a retry rule that nobody captured in a shell. A later editor may treat that smoothed paragraph as the procedure, because the page looks finished and the original session is gone. A separate transcript makes the captured evidence something a reviewer can diff without rereading the surrounding narrative.

This split is narrower than a ban on generated documentation, and that distinction matters for teams that already draft prose with a model. Explanations still save time when the same captured session must be introduced to readers who did not run the command. Human-owned files stay hash-pinned, and draftable files remain easy to regenerate whenever the captured pin changes. The method does not ask the model to decide which failures are real, which timeouts are acceptable, or which gaps may be filled.

What each side may write

The table is the editorial contract for a single page, not a company-wide style guide you must adopt unchanged. Add a row only when the owner and the source are already known to a person who can update them. Do not add a row because a draft sounded specific or because a missing cell made the page look thin. Empty cells should stay empty until a human records the evidence that belongs in that row.

Block Owner May contain Must not contain
transcript.log Human Captured stdout, stderr, and exit status Edited success lines or hidden failures
facts.yml Human Retryability, owners, and sourced measurements Model-written numbers or undated allowances
narrative.md Model Order, intent, and reader cues New commands, flags, codes, or quotas
review.md Human Accept, reject, or edit notes Unreviewed claims copied into the page

A command belongs in the transcript only after a person runs it in the environment the page claims to describe. A measurement belongs in the ledger only with a source string and a review date that a human can open again later. Narrative text may quote those registered values, but it may not introduce a number the ledger does not list. Publication status remains human-owned even when every sentence in the generated draft is otherwise clean and specific.

Steps to publish one page

  1. Capture each command, its combined output, and its exit status, then store that log on a path a human owns.
  2. Register retryability, support identifiers, and any numeric allowance in the ledger, each with a source you can open without the model.
  3. Mark the narrative file as the only model-writable path, and limit the prompt to explaining the pinned transcript in that file.
  4. Run the checker so a drifted pin or an unregistered number fails the job before anyone spends review time.
  5. Read the narrative against the log, and accept it only after the checker exits zero and the quoted lines still match.
  6. Commit the transcript, the ledger, and the accepted narrative together, then regenerate the narrative whenever either pinned file changes.

The capture script should fail closed when a command exits non-zero, unless the ledger already marks that status as an expected demonstration. Expected failures still belong in the log, because a page that hides the failing session teaches the reader the wrong recovery. Keep the prompt beside the narrative as a build input, not as the procedure that you publish to readers. If the prompt and the narrative disagree, the transcript and the ledger remain the files that win.

Capture script

The capture helper below is an unexecuted fixture, and it assumes a local demo-job binary that you can replace. It records the command line, appends combined output, and writes the exit status even when the first run fails. Remove the status override once you are capturing a procedure that should stop on the first unexpected exit.

#!/usr/bin/env bash
set -u
out="docs/retry/transcript.log"
: > "$out"
run() {
  printf '$ %s\n' "$*" | tee -a "$out"
  "$@" >>"$out" 2>&1
  code=$?
  printf 'exit: %s\n' "$code" | tee -a "$out"
  return 0
}
run demo-job --once
run demo-job --once
sha256sum "$out"
Enter fullscreen mode Exit fullscreen mode

Fixtures you can copy

The files in this section are unexecuted tutorial fixtures, not a production incident and not a vendor measurement. Replace every source string and every sample command before you point this pattern at a real documentation page. Dates in the fixtures identify the example, and they are not a claim that any vendor published those values on that day. Treat the exit status and the attempt limit as placeholders that your own ledger must replace.

# docs/retry/facts.yml
page: docs/retry
human_owner: docs-team
pins:
  transcript: docs/retry/transcript.log
  transcript_sha256: "replace-with-real-sha256"
draftable:
  - docs/retry/narrative.md
facts:
  - key: expected_exit
    value: 75
    source: "fixture://exit-codes"
    reviewed: "2026-10-13"
  - key: recovered_exit
    value: 0
    source: "fixture://exit-codes"
    reviewed: "2026-10-13"
  - key: max_attempts
    value: 3
    source: "fixture://retry-policy"
    reviewed: "2026-10-13"
Enter fullscreen mode Exit fullscreen mode
# docs/retry/transcript.log
$ demo-job --once
temporary upstream failure
exit: 75
$ demo-job --once
ok
exit: 0
Enter fullscreen mode Exit fullscreen mode
The first invocation exits 75, which this page treats as a temporary failure.
A second invocation is the expected recovery, and it exits 0 in the pinned log.
The ledger allows at most 3 attempts, so a further failure should stop the procedure.
Enter fullscreen mode Exit fullscreen mode

Run the ownership checker

The Python program below is an unexecuted example intended for a repository branch that you already control. It does not call a model, score prose quality, or connect to an external documentation service. Install PyYAML, replace the placeholder hash with the digest of your transcript, and run the script from the repository root. Until you replace the placeholder hash, the hash comparison stays inactive, so do not treat a green run as proof of pin enforcement.

#!/usr/bin/env python3
"""Reject drafts that drift from pins or invent measurements."""

import hashlib
import pathlib
import re
import sys

import yaml

LEDGER = pathlib.Path("docs/retry/facts.yml")
NUMBER = re.compile(r"\b\d+(?:\.\d+)?\b")

def sha256(path: pathlib.Path) -> str:
    return hashlib.sha256(path.read_bytes()).hexdigest()

def main() -> int:
    ledger = yaml.safe_load(LEDGER.read_text(encoding="utf-8"))
    errors = []
    transcript = pathlib.Path(ledger["pins"]["transcript"])
    pinned = ledger["pins"]["transcript_sha256"]
    if pinned != "replace-with-real-sha256" and sha256(transcript) != pinned:
        errors.append(f"transcript hash drift: {transcript}")
    allowed = {str(item["value"]) for item in ledger["facts"]}
    for rel in ledger["draftable"]:
        text = pathlib.Path(rel).read_text(encoding="utf-8")
        for match in NUMBER.findall(text):
            if match not in allowed:
                errors.append(f"unregistered number {match} in {rel}")
    if errors:
        print("\n".join(errors))
        return 1
    print("ownership check passed")
    return 0

if __name__ == "__main__":
    sys.exit(main())
Enter fullscreen mode Exit fullscreen mode
python3 -m pip install pyyaml
sha256sum docs/retry/transcript.log
python3 check_doc_ownership.py
Enter fullscreen mode Exit fullscreen mode

After the happy path passes, append an unregistered duration to the narrative and run the checker a second time. That negative case should exit with status 1, because the added duration is not a registered fact value. Skip that case and a green run on the sample page will not prove that invented durations are blocked. Keep both fixtures in the test directory so a later edit cannot delete the failure mode without a visible diff.

Use a free draft loop without making it the archive

Disclosure: This article was prepared as part of MonkeyCode's product outreach. The operator describes the project as open source and supplies two availability claims for this draft: free model access and a free server option. Those claims do not include a named model, a fixed quota, a hardware profile, or a permanent time window. Narrower commercial details change, and a human should copy them from current product documentation into the ledger only when a page must state them.

Free model access matters at the narrative step, where the draftable file is generated from the pinned transcript and the allowed numbers. The free server option matters when you want a clean shell in which to recapture the log without using a teammate's laptop. Neither option should become the system of record, because an ephemeral machine can disappear and a regenerated draft can drift. The committed transcript, the ledger, and the review note remain the surface that a later editor should trust.

Write the prompt so that it cannot widen its own write permissions while producing the draft. Provide the transcript, the allowed numbers, and the single output path, and instruct the model to leave gaps instead of guessing. Do not ask it to improve the command, choose a timeout, or complete a missing exit code in the captured log. Those requests remain ownership violations even when the resulting paragraph is grammatically clean and easy to skim.

If the model returns a number you did not register, either update the ledger from a source you control or delete the sentence. Do not publish the allowance as a permanent property of the tool, because availability figures need a current primary source. This draft omits a token count for that reason, and a reviewer should treat any remembered quota as unverified.

Four review checks

Apply the same four checks to every page that adopts the split, and record the answers in the human-owned review note. They are mechanical on purpose, so the reviewer does not have to infer intent from a confident tone. A missing answer is a rejection, not a prompt that invites the model to fill the blank. Attach the checker output to the same note so the next editor can see why the draft was allowed through.

  1. Confirm that the transcript hash matches the ledger, and confirm that a person can still reproduce the session in the named environment.
  2. Confirm that every number in the narrative appears as a fact value, including exits, attempt limits, and any published allowance.
  3. Confirm that the change does not edit the transcript or the ledger inside a commit that was supposed to contain only a model draft.
  4. Confirm that a human accepted the narrative in the review note, with the checker output stored beside that acceptance.

Who should skip this gate

This pattern is a file pin plus a lexical number scan, and it is not proof that the explanation is causally right. A narrative can stay inside the allowlist and still describe the recovery steps in the wrong order. Do not use the gate for security advisories, pricing pages, legal terms, or incident reports whose sentences are themselves commitments. Also skip it when nobody on the team can recapture the transcript, because a stale pin is more dangerous than an obvious gap.

A team that publishes model output without a human reviewer will not receive the intended protection from this script. The script does not judge clarity, safety, or whether the advice should be followed outside the captured environment. The free server option does not replace a durable repository, a named owner, or a fresh reading of current product terms. If the page set is small and almost never changes, the automation may cost more effort than the risk it removes.

In that smaller case, keep the same ownership rule without the script and without a scheduled job. Humans edit the transcript and the ledger, and the model never receives write access to either path. Regenerate the explanation only after those pins change, and discard drafts that arrive with new measurements attached. That manual version is slower, but it preserves the same boundary the checker is meant to automate.

Start with one negative test

Choose one troubleshooting page, pin its transcript, and reject the first draft that introduces an unregistered number. That failure is the useful result, because it shows whether the allowlist matches the way the page is actually written. When a person already maintains the ledger, free model access is enough to rehearse the narrative step before the checker joins the default branch.

Top comments (0)