DEV Community

Avery Lin
Avery Lin

Posted on

Compile Config Key Tables From a Frozen Schema; Sign Secret and Default-Safety Copy

Configuration documentation fails when a generated key table and human safety copy share one file without a merge contract. A model can inventory keys, types, and declared defaults from a frozen schema, but it cannot own secret handling, default-safety rationale, or production rollout caveats. Treat the schema hash as the only facts input for tables, and treat signed regions as unwritable during generation. The rest of this article is a compile workflow, a decision table, and a CI check you can run on a local tree.

The practical failure is mixed authorship inside CONFIG.md, not a missing prose style guide for contributors. Last week's schema gain a new timeout key, a model rewrites the whole page, and the paragraph that forbade logging tokens disappears from review noise. Teams then argue about tone while the dangerous edit is a deletion of operational constraints that never lived in the schema. Split compile output from signed copy before the next generator run, and review diffs against that split instead of against memory.

A decision table for configuration copy

Use the table as an ownership contract, not as optional writing advice after a model has already drafted the page. If a sentence can be proven from the schema alone, it may be compiled. If a sentence asserts risk, exception handling, or a promise to operators, a human must sign it and the generator must refuse to touch it.

Documentation unit Provenance Owner Generator action
Key name, JSON type, enum values Schema node Compile Emit table row
Declared default literal Schema default Compile Emit cell only if present
Required vs optional flag Schema required Compile Emit yes/no cell
Secret vs non-secret classification Human register Sign Refuse rewrite
Logging, tracing, and redaction rules Policy review Sign Refuse rewrite
Why a default is unsafe in production Incident or review notes Sign Refuse rewrite
Rollout order, dual-running, and abort criteria Release owner Sign Refuse rewrite
Example values in docs Reviewed fixtures Compile after hash pin Emit only pinned samples

The table is the artifact reviewers should quote in pull requests when a model expands a cell into a paragraph. Compiled cells stay short because a schema does not encode blast radius, and signed paragraphs stay long because operators need constraints. Do not let a model “complete” a row by inventing a rationale that is absent from both the schema and the signed register.

Step 1: Freeze a hashed schema as the only facts file

Put a reviewed JSON Schema in docs/facts/config.schema.json and refuse to compile from chat output, ticket comments, or an unpinned OpenAPI snapshot. Compute a digest at freeze time and store it beside the schema so later runs can prove they read the same bytes. Label the following commands as a proposed local workflow, not as production metrics from a live fleet.

# proposed: freeze the reviewed schema and record its digest
install -d docs/facts docs/config/_signed
cp -f path/to/reviewed/config.schema.json docs/facts/config.schema.json
sha256sum docs/facts/config.schema.json > docs/facts/config.schema.json.sha256
Enter fullscreen mode Exit fullscreen mode

A facts file is useful only when it is smaller than the documentation surface it feeds. Strip vendor extensions that describe UI widgets, marketing names, or deprecated aliases you have not agreed to publish. If the schema still contains placeholder secrets or real hostnames, stop; compiled docs should never become a second copy of a credential store.

Step 2: Reserve signed regions for human-owned safety copy

Keep human copy in fragment files, then wrap the published page with begin and end markers that a compiler must treat as opaque. The markers are not documentation flavor; they are the merge API. Proposed marker names below are examples and should be replaced with your own prefix if another tool already owns that syntax.

<!-- SIGNED:BEGIN secret-handling sha256:REPLACE_WITH_FRAGMENT_DIGEST -->
Never log `api_token`, `refresh_token`, or `mtls_key_pem` at any level, including debug.
Treat a default empty token as a boot error in production, not as anonymous access.
<!-- SIGNED:END secret-handling -->

<!-- SIGNED:BEGIN default-safety sha256:REPLACE_WITH_FRAGMENT_DIGEST -->
`timeout_ms` defaults to 30000 in the schema for local fixtures only.
Production must set an explicit budget; failing open on the default has caused queued retries to stampede.
<!-- SIGNED:END default-safety -->
Enter fullscreen mode Exit fullscreen mode

Store the same paragraphs under docs/config/_signed/ so the compiler can re-insert them by name after it rebuilds tables. Hash each fragment at review time and write the digest into the marker. A later model edit that changes a single adverb will fail the digest check even if the surrounding table looks correct.

Step 3: Compile key tables from the schema, never from a chat transcript

The compiler should walk object properties, emit rows, and refuse unknown fields instead of guessing. The Python below is a proposed, unexecuted example: it reads the frozen schema, checks the recorded digest, and prints a Markdown table. It does not call a network model and it does not read signed fragments.

# proposed example: compile_config_table.py
from __future__ import annotations

import hashlib
import json
from pathlib import Path

SCHEMA = Path("docs/facts/config.schema.json")
DIGEST = Path("docs/facts/config.schema.json.sha256")


def load_frozen_schema() -> dict:
    raw = SCHEMA.read_bytes()
    expected = DIGEST.read_text().split()[0]
    actual = hashlib.sha256(raw).hexdigest()
    if actual != expected:
        raise SystemExit(f"schema digest mismatch: {actual} != {expected}")
    data = json.loads(raw.decode("utf-8"))
    if data.get("type") != "object" or "properties" not in data:
        raise SystemExit("schema must describe an object with properties")
    return data


def row_for(name: str, node: dict, required: set[str]) -> str:
    declared_type = node.get("type", "unknown")
    if isinstance(declared_type, list):
        declared_type = "|".join(str(part) for part in declared_type)
    default = node.get("default", "")
    if isinstance(default, (dict, list)):
        default = json.dumps(default, sort_keys=True)
    req = "yes" if name in required else "no"
    enum = node.get("enum")
    values = ", ".join(json.dumps(v) for v in enum) if enum else ""
    return f"| `{name}` | {declared_type} | {req} | `{default}` | {values} |"


def main() -> None:
    schema = load_frozen_schema()
    required = set(schema.get("required", []))
    lines = [
        "| Key | Type | Required | Declared default | Enum |",
        "| --- | --- | --- | --- | --- |",
    ]
    for name, node in sorted(schema["properties"].items()):
        if not isinstance(node, dict):
            raise SystemExit(f"property {name} must be an object schema")
        lines.append(row_for(name, node, required))
    Path("docs/config/_generated/keys.md").parent.mkdir(parents=True, exist_ok=True)
    Path("docs/config/_generated/keys.md").write_text("\n".join(lines) + "\n")


if __name__ == "__main__":
    main()
Enter fullscreen mode Exit fullscreen mode

Run the compiler in CI on every schema change, and keep the model out of this path when the schema is already reviewed. Free-model drafts are useful earlier, when you are proposing the compiler itself or sketching a table layout from a new schema shape. Disclosure: This article was prepared as part of MonkeyCode's product outreach. MonkeyCode's free model access can draft the compiler and the first table layout from the frozen schema, and the free server option can run the compile job without placing secrets on that server; neither should author signed safety copy.

Step 4: Merge generated tables without touching signed regions

Assemble CONFIG.md from three inputs only: a human header stub, the generated table file, and the signed fragments. The merge script should delete any previous generated region and splice signed regions back by name. Proposed shell below assumes csplit or an equivalent marker-aware tool; replace it with a small Python splice if your markers can nest.

# proposed: rebuild CONFIG.md from stub + generated table + signed fragments
python3 compile_config_table.py
{
  cat docs/config/_stubs/header.md
  echo
  echo "<!-- GENERATED:BEGIN keys -->"
  cat docs/config/_generated/keys.md
  echo "<!-- GENERATED:END keys -->"
  echo
  cat docs/config/_signed/secret-handling.md
  echo
  cat docs/config/_signed/default-safety.md
  echo
  cat docs/config/_signed/rollout.md
} > docs/CONFIG.md
Enter fullscreen mode Exit fullscreen mode

Do not ask a model to “refresh the page” after merge, because refresh usually means rewrite. If the table is missing a newly added key, fix the schema freeze and rerun the compiler. If the signed fragment is wrong, open a review that only changes that fragment and its digest, then merge again.

Step 5: Fail CI when a model diff edits a signed hash

Add a check that recomputes fragment digests and also inspects the pull request diff for marker lines. The check is intentionally boring: it should fail closed when a digest is stale, when a marker is missing, or when a generated file was hand-edited. Proposed tests below are local assertions, not a published benchmark.

# proposed example: test_signed_regions.py
from pathlib import Path
import hashlib
import re
import unittest

ROOT = Path("docs")

class SignedRegionTests(unittest.TestCase):
    def test_fragment_digests_match_markers(self):
        page = (ROOT / "CONFIG.md").read_text()
        for path in sorted((ROOT / "config" / "_signed").glob("*.md")):
            body = path.read_text()
            digest = hashlib.sha256(body.encode("utf-8")).hexdigest()
            name = path.stem
            pattern = rf"<!-- SIGNED:BEGIN {re.escape(name)} sha256:([a-f0-9]{{64}}) -->"
            match = re.search(pattern, page)
            self.assertIsNotNone(match, f"missing marker for {name}")
            self.assertEqual(match.group(1), digest)
            self.assertIn(body.strip(), page)

    def test_generated_table_is_reproducible(self):
        generated = (ROOT / "config" / "_generated" / "keys.md").read_text()
        self.assertTrue(generated.startswith("| Key |"))
        self.assertNotIn("TODO", generated)

if __name__ == "__main__":
    unittest.main()
Enter fullscreen mode Exit fullscreen mode

Wire the tests to whatever already gates documentation pull requests, and reject a model diff that so much as reorders a signed paragraph. Reviewers then spend attention on schema deltas and on explicit fragment edits, not on a twelve-hundred-line Markdown rewrite. If your review tool cannot hide generated regions, teach it the GENERATED markers before you scale the compiler to more pages.

What this method cannot cover

A frozen schema does not encode whether a default is safe under saturation, partial outage, or a mis-set replica count. The compiler will faithfully publish a dangerous default if that literal is in the schema and you failed to sign a warning. JSON Schema also cannot prove that a key is a secret; a field named token might be a public pagination cursor, and a field named ref might be a credential. Human classification remains mandatory, and the decision table above is the reminder.

Generated examples are another gap even when types are correct. A syntactically valid default can still be an internal-only hostname, a fixture user, or a region you do not offer. Pin examples to reviewed fixture hashes in a separate compile lane if you need them at all, and keep them out of signed safety copy so a fixture rotation does not look like a policy change. Multi-file configuration overlays, feature flags, and per-tenant exceptions will not fit a single object schema without a facts file that you have not built yet.

Who should not use this workflow

Skip this approach if you do not have a reviewed schema and you are hoping a model will infer keys from scattered runbooks. Skip it if counsel requires every sentence on a page to pass legal review, because a generated table still lands in the same published document. Skip it if the configuration payload contains live secrets, customer identifiers, or undisclosed hostnames that must never be hashed into a public repository. Skip it if nobody will maintain fragment digests; an unsigned CONFIG.md with pretty tables is still a mixed-authorship file.

Teams that should use it are those already compiling API surfaces or fixture-bound examples and now facing the same authorship problem in operations docs. The compiler is small on purpose so a reviewer can read it in one sitting and reject hidden network calls. Free-model help stays upstream of the freeze, and signed copy stays downstream of human review, which is the only split this workflow claims to enforce.

If you want a scratch environment for the compiler and the digest tests, MonkeyCode’s free server option is enough to run the commands above against a sample schema that contains no secrets. Keep production fragments and real tokens off that machine, and treat any drafted compiler as untrusted until the digest checks pass on your own fixtures.

Top comments (0)