DEV Community

Avery Lin
Avery Lin

Posted on

Record Human Owners Before a Model Drafts Reference Policy Blocks

Start with the ownership result

A reference page should not leave a model until every policy block has a named human owner and a recorded sign-off. Models can expand frozen schema fields into plain explanations, but they cannot own support promises, security boundaries, or compatibility rules. This workflow freezes a section ownership contract, drafts only allowed blocks, and rejects a page when a human-owned block is blank or unsigned. The checker below is a local proposal you can run yourself, and it is not a measured production benchmark.

Documentation generators fail quietly when a polished draft looks complete while an unowned operational decision remains hidden inside it. A missing owner is not a style issue, because readers treat published reference text as an operational commitment. The contract separates blocks the model may draft from blocks a human must write, date, and sign. Shipping without that split turns a helpful mechanical draft into an unsigned policy statement for later readers.

Classify headings before prose exists

Start from the page outline, not from generated prose, because ownership is a property of the section rather than the sentence. Each heading in the outline receives exactly one lane, either model_draft or human_own, before drafting begins. A model_draft block may restate facts already present in a frozen schema, example file, or captured command transcript. A human_own block contains a decision that the schema, example file, or captured transcript does not encode by itself.

Use the table as a decision record for a configuration reference, then change the headings to match your page. The lane does not describe writing quality, because it only records who may introduce a new claim. If a heading mixes a schema fact with a support promise, split that heading before the model runs. Mixed headings are the usual route by which an unsigned promise enters an otherwise mechanical reference page unnoticed.

Heading Lane Allowed input Human must record
Field meanings model_draft Frozen schema property text Source file name and digest
Captured examples model_draft Version-pinned command transcript Confirmation the capture is still current
Defaults model_draft Schema default only Rejection when the default is absent
Support window human_own No model input Owner, date, and window wording
Security boundary human_own No model input Owner, date, and boundary wording
Compatibility promise human_own No model input Owner, date, and version range
Escalation path human_own No model input Owner, date, and contact route

Follow six steps in order

1. Freeze the source bundle

Copy the schema, the example file, and the command transcript into a directory that the draft job cannot modify. Record a digest for each file, and store those digests in the contract before any prose is requested. A later edit to the schema invalidates the draft, even when the generated sentences still look fluent. Treat the recorded digest as the hard boundary between captured evidence and any later composition step.

mkdir -p build/doc-sources
cp schema.json examples/config.yaml transcripts/help.txt build/doc-sources/
sha256sum build/doc-sources/* | tee build/source-digests.txt
Enter fullscreen mode Exit fullscreen mode

The commands above are a reproducible setup sketch, not a captured log from a finished documentation release. Replace those sample filenames with the schema, example file, and transcript that your page actually cites. If you cannot name a file for a claim, that claim does not belong in a model_draft block. Move the claim to a human_own heading, or delete it from the outline before generation starts.

2. Write the ownership contract

Store one JSON object per heading, including the lane, the source name, and the required human fields. Leave owner and signed_on empty for human_own blocks until a person fills them after review. Do not let the draft job write those two fields, because a model signature is not a human signature. Keep the contract in the same pull request as the page, so a heading change stays visible to reviewers.

{
  "page": "docs/config-reference.md",
  "blocks": [
    {
      "heading": "Field meanings",
      "lane": "model_draft",
      "source": "build/doc-sources/schema.json",
      "source_sha256": "PASTE_DIGEST"
    },
    {
      "heading": "Support window",
      "lane": "human_own",
      "owner": "",
      "signed_on": ""
    }
  ]
}
Enter fullscreen mode Exit fullscreen mode

The source_sha256 value PASTE_DIGEST is a placeholder, and the checker treats that exact token as a missing digest. Replace it with the digest printed by sha256sum before you treat any exit code as a pass. An empty owner string is also intentional in the sample, because the human has not signed the policy block yet. Running the sample unchanged should fail, and a failure on these placeholders is the correct first result.

3. Let the model draft only allowed headings

Give the model the frozen sources, the contract, and an instruction that forbids human_own headings in its output. Ask for one markdown fragment per model_draft heading, and require a source comment under each heading. Reject any fragment that mentions support windows, security contacts, compatibility ranges, or escalation routes at all. Those four topics stay empty until the named human writes them into the assembled reference page.

A useful prompt constraint is short and mechanical, and it should quote the lane names from the contract. Tell the model to copy default values only when the schema contains them, and to mark a missing default explicitly. Tell it to refuse rather than invent a version, a date, a quota, or a contact address. Save the model output as a fragment file, not as the final page that reviewers will approve.

4. Assemble the page without overwriting human blocks

Build the published page by concatenating approved model fragments and the human-written blocks in contract order. If a human_own section is missing, insert an explicit placeholder that the checker will fail, rather than a fluent guess. The unowned placeholder should remain impossible for a later reviewer to mistake for finished policy text. A suggested marker is the token DRAFT_UNOWNED, followed by the heading name and no additional policy wording.

## Support window
<!-- lane: human_own owner: signed_on: -->
DRAFT_UNOWNED Support window
Enter fullscreen mode Exit fullscreen mode

5. Run the ownership checker

The checker reads the contract and the assembled markdown, then exits non-zero when any ownership rule fails. It checks heading presence, empty human fields, the unowned marker, and a small forbidden-phrase list inside model_draft sections. It requires a recorded source digest for every model_draft block and compares that digest with the file on disk. It does not judge whether a human support window is correct, because that judgment stays with the named owner and reviewer.

#!/usr/bin/env python3
"""Unexecuted proposal: block a reference page until human-owned sections are signed."""

import argparse
import hashlib
import json
import re
import sys

FORBIDDEN = re.compile(
    r"\b(we guarantee|supported until|contact security|service level|sla)\b",
    re.IGNORECASE,
)
HEADING = re.compile(r"^## (.+)$", re.MULTILINE)


def section_map(text):
    matches = list(HEADING.finditer(text))
    found = {}
    for index, match in enumerate(matches):
        start = match.end()
        end = matches[index + 1].start() if index + 1 < len(matches) else len(text)
        found[match.group(1).strip()] = text[start:end]
    return found


def file_sha256(path):
    digest = hashlib.sha256()
    with open(path, "rb") as handle:
        for chunk in iter(lambda: handle.read(65536), b""):
            digest.update(chunk)
    return digest.hexdigest()


def main():
    parser = argparse.ArgumentParser(description="Check reference-page ownership lanes.")
    parser.add_argument("--contract", required=True)
    parser.add_argument("--page", required=True)
    args = parser.parse_args()
    with open(args.contract, encoding="utf-8") as handle:
        contract = json.load(handle)
    with open(args.page, encoding="utf-8") as handle:
        sections = section_map(handle.read())
    errors = []
    for block in contract.get("blocks", []):
        heading = block.get("heading", "")
        lane = block.get("lane", "")
        body = sections.get(heading)
        if body is None:
            errors.append(f"missing heading: {heading}")
            continue
        if lane == "human_own":
            owner = block.get("owner", "").strip()
            signed_on = block.get("signed_on", "").strip()
            if not owner or not signed_on:
                errors.append(f"unsigned human block: {heading}")
            if "DRAFT_UNOWNED" in body or not body.strip():
                errors.append(f"empty human block: {heading}")
            if owner and owner not in body:
                errors.append(f"owner token missing from page: {heading}")
        elif lane == "model_draft":
            if not body.strip():
                errors.append(f"empty draft block: {heading}")
            if FORBIDDEN.search(body):
                errors.append(f"policy phrase in draft block: {heading}")
            expected = block.get("source_sha256", "").strip()
            source = block.get("source", "").strip()
            if not expected or expected == "PASTE_DIGEST":
                errors.append(f"digest not recorded: {heading}")
            elif not source:
                errors.append(f"source path missing: {heading}")
            else:
                try:
                    actual = file_sha256(source)
                except OSError as exc:
                    errors.append(f"source unreadable: {heading}: {exc}")
                else:
                    if actual != expected:
                        errors.append(f"source digest mismatch: {heading}")
        else:
            errors.append(f"unknown lane: {lane}")
    if errors:
        print("\n".join(errors))
        return 1
    print(f"ownership gate passed: {len(contract.get('blocks', []))} blocks")
    return 0


if __name__ == "__main__":
    sys.exit(main())
Enter fullscreen mode Exit fullscreen mode
python3 ownership_gate.py --contract contract.json --page docs/config-reference.md
Enter fullscreen mode Exit fullscreen mode

Read a non-zero exit as a blocked publish, not as a suggestion to regenerate the missing policy text. Fix the contract or the human block, then run the same command again on the same page path. Keep the script free of network calls so a documentation job cannot fetch a new claim during the check. The example is unexecuted in this article, so run it on your own files before you trust the exit code.

6. Record the human sign-off in the same change

The human owner writes the policy block, fills owner and signed_on, and mirrors the owner token in the page comment. A reviewer checks that the owner is a real maintainer for that decision, not an account created for the draft job. The date in signed_on should be the review date, not a date the model guessed from earlier text. Merge only after the checker prints a pass line for that exact page path and contract file.

Place drafting help only in the model lane

Disclosure: This article was prepared as part of MonkeyCode's product outreach.

The operator supplied two availability facts for this workflow, and no further product limits were provided with them. Free model access is relevant only for drafting model_draft blocks from the frozen source bundle supplied by the writer. A free server option is relevant for running the ownership checker as a repeatable job, not for inventing missing policy text. This draft states no model names, quotas, hardware sizes, durations, or benchmark numbers, because none were supplied.

Remove the product name, and the contract, the checker, and the sign-off rule remain useful without it. Do not send human_own headings to a model merely because generation is inexpensive or already configured nearby. Cheap drafting does not change who is allowed to make a support, security, or compatibility promise. If a heading has no frozen source, it stays with the human even when a free drafting lane is available.

Limitations of the gate

The checker is syntactic, so a signed block can still contain a wrong support window or an outdated contact route. Digest comparison confirms a recorded hash, but only when you pass the source path and keep that file unchanged. A person with write access can place a sign-off token if review does not require a second maintainer. Forbidden phrases catch a few policy leaks, and they miss paraphrases that avoid those exact strings.

The sample filenames and empty owner tokens are illustrations, not records from a shipped documentation release. Use a real maintainer identity from your own team, and do not publish a placeholder person as the owner. This workflow assumes that each English heading matches the contract exactly, including capitalization and internal spacing. If your generator rewrites headings, normalize both sides before comparison or the gate will fail for formatting.

Skip the workflow in these cases

Skip this workflow when the page is entirely policy, legal text, or an incident promise, because no model_draft lane is safe. Skip it when you have no frozen schema, transcript, or example file, because the model would be inventing the evidence. Skip it when one author owns every sentence and will not keep the contract updated, because the matrix becomes ceremony. Skip it when the repository cannot enforce review on the contract file, because the sign-off token would be decorative.

A team that needs semantic review, legal approval, or accessibility review still needs those reviews after this gate passes. The gate answers a narrower question, which is whether each heading has the correct author type and a recorded signature. It does not answer whether the explanation is clear, complete, or safe for every reader of the page. Treat a passing exit code as permission to begin human review, not as permission to publish the page unattended.

Close on the contract, not on the draft

Copy the contract skeleton, name real owners for the policy headings, and run the checker against one configuration reference. If the checker fails, repair the unsigned block instead of asking a model to complete the missing promise. After one page passes under review, decide whether a free drafting lane and a free checker job belong in that pipeline. The page is ready only when every human_own block has a person, a date, and text that person will defend.

Top comments (0)