From 436f583882d1d46cbe536cb878865b1fc698bce0 Mon Sep 17 00:00:00 2001 From: root Date: Sat, 4 Jul 2026 22:31:33 +0000 Subject: [PATCH] docs: add prose contract authoring guide (Anthropic-inspired) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit NEW: docs/AUTHORING-GUIDE.md — canonical style guide for writing contracts Patterns borrowed from Anthropic's frontend-design skill: - Ground it in the subject: verify live state before writing - Two-pass process: draft → critique → revise → ship - Restraint: one contract, one concern; cut one thing before shipping - Self-critique: would an agent trust this at 3am with 30s of context? Core rules: - Specificity over generality (lists rot, vagueness is invisible) - Live-state fields in tables, not prose (grep-able) - Descriptions answer: what, to what, why care? - Templates for function and responsibility contracts - CI enforces structure; author enforces quality Linked from AGENTS.md for agent discovery. --- docs/AUTHORING-GUIDE.md | 308 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 308 insertions(+) create mode 100644 docs/AUTHORING-GUIDE.md diff --git a/docs/AUTHORING-GUIDE.md b/docs/AUTHORING-GUIDE.md new file mode 100644 index 0000000..cf74f8c --- /dev/null +++ b/docs/AUTHORING-GUIDE.md @@ -0,0 +1,308 @@ +# Prose Contract Authoring Guide + +Approach this as the lead systems engineer at a company where every contract is a +production dependency — agents run from these every 15 minutes, every 6 hours, +every Sunday at 3am. A stale IP or a missing section isn't a typo; it's an +incident waiting to happen. Write every contract as if you'll be paged at 3am +when it breaks. + +## Ground it in the subject + +Before writing a single line of YAML, answer three questions and state them in +the description: + +1. **What system does this contract touch?** Name the hosts, CTs, containers, + and services explicitly. "The inference fleet" is vague. "GPU .8 (RTX 3090, + qwen), .110 (RTX 5070, gemma), .15 (Strix Halo, ornith), and LiteLLM on CT + 116" is specific. + +2. **Who runs this contract, and when?** State the agent, the trigger (cron, + manual, event-driven), and the expected duration. "Runs every 6 hours via + cron on abiba, takes ~120 seconds for a fleet scan." + +3. **What breaks if this contract is wrong?** Name the blast radius. "A stale + CT ID causes a restart of the wrong container. A wrong Grafana URL breaks + the monitoring dashboard for all agents." + +If you cannot answer these three questions concretely, do not write the +contract yet — go verify against the live system first. + +## Design principles + +### Specificity over generality + +A contract that says "check all Docker containers" is worse than one that lists +them by name. The list will go stale, but staleness is detectable (the CI lint +catches it). Vagueness is invisible and uncatchable. Be specific, accept that +specificity decays, and rely on the lint pipeline to catch the decay. + +### Ground truth is the live system, not the contract + +Every IP, port, hostname, and container name in a contract is a **live-state +field**. Mark them with `VERIFY-BEFORE-USE` in the access matrix. Before any +mutating operation, verify against the live system. The contract is a +hypothesis; the live system is the fact. + +When you discover drift between contract and reality, do not "fix" the live +system to match the contract. Halt. Ask which is correct. Document the answer +in the contract and the knowledge graph. The `.117→.19` incident was caused by +fixing the wrong direction. + +### Contracts are typed — respect the kind + +| Kind | When to use | Must have | Must NOT have | +|------|------------|-----------|---------------| +| `function` | Stateless, callable helper | `## Parameters`, `## Returns`, `## Execution` | `## Maintains` (no state) | +| `responsibility` | Persistent duty with state | `## Maintains`, `## Continuity` or `## Execution` | — | +| `gateway` | External-driven entry point | `## Maintains` | `## Requires` | +| `pattern` | Reference/template/doctrine | Flexible — prose-driven | Don't `prose run` directly | +| `test` | Verification only | `## Execution` | Mutating operations | + +Use `kind: architecture` sparingly — it's not a standard OpenProse kind. Most +architecture docs should be `kind: pattern`. + +### One contract, one concern + +A contract that monitors health AND deploys exporters AND sends alerts is three +contracts fighting for attention. Split them: + +``` +❌ infrastructure-monitoring.prose.md (deploy + export + alert) +✅ proxmox-monitor.prose.md (deploy exporters) +✅ gpu-monitor.prose.md (poll + dashboard) +✅ infrastructure-control.prose.md (reference topology) +``` + +The contract that deploys is a function. The contract that polls is a +responsibility. The contract that maps the topology is a pattern. One concern +per file, one kind per concern. + +### Live-state fields are declared, not hidden + +Don't bury IPs and hostnames in prose paragraphs. Put them in tables, mark them +with `VERIFY-BEFORE-USE`, and keep them in one place per contract: + +```markdown +| Host | IP | Role | Trust | +|------|----|------|-------| +| CT 116 | 192.168.68.116 | LiteLLM + Grafana | ⚠️ VERIFY-BEFORE-USE | +``` + +The lint pipeline scans for known stale values. Tables are grep-able; prose is +not. + +## Process: verify, draft, lint, review, ship + +### Pass 1: Verify + +Before writing, verify every live-state field you plan to include: + +```bash +# Example: writing a contract about Zulip +curl -s http://192.168.68.19/api/v1/server_settings # is .19 reachable? +ssh root@192.168.68.19 "docker ps" # is Zulip in Docker? +pct config 117 | grep net0 # what IP does CT 117 have? +``` + +Write down the verified values. Use them in the contract. If you can't verify +something, mark it explicitly: `⚠️ UNVERIFIED — needs live check`. + +### Pass 2: Draft + +Write the contract following the structure for its kind. Fill every required +section. Use the templates below. Write descriptions that name the subject: "GPU +fleet health check across RTX 3090 (.8), RTX 5070 (.110), and Strix Halo (.15)" +not "monitoring check." + +### Pass 3: Lint locally + +Before pushing, run the same checks the CI will run: + +```bash +bash scripts/prose-lint.sh +``` + +Fix everything that fails. The lint checks for: +- Valid YAML frontmatter +- Required sections per kind +- Regression patterns (/grafana/ route, CT 122/123) +- Missing Maintains/Parameters/Returns + +### Pass 4: Self-critique + +Read your contract and ask: + +1. **Could an agent run this at 3am without context?** If it needs tribal + knowledge, add it to the description or a comment. +2. **Does any value contradict the infrastructure-control pattern?** Check CT + IDs, IPs, hostnames, nginx routes, Grafana access. +3. **Is anything in here that was previously fixed and reverted?** Check the + lint regression rules. If you're unsure, grep the git log for similar + changes. +4. **Am I saying the same thing as another contract?** If yes, link to it + instead of duplicating. One source of truth. + +### Pass 5: Open PR, let CI review + +Push to a branch, open a PR. The pipeline runs: + +``` +auth → validate → lint → ai-review → gate +``` + +The AI review compares your diff against the infrastructure-control ground +truth. It catches contradictions you might miss. If it flags something, don't +argue — verify and fix. The AI is reading the same ground truth you should have +read before writing. + +## Restraint and self-critique + +Spend your complexity budget in one place. A contract that deploys Prometheus, +configures Grafana, provisions dashboards, and sets up exporters is a function +that does four things — each of which can fail independently. Split them. + +Don't add a `### Requires` that you haven't verified works. A dependency on +"SSH access to .116" is only valid if you've tested it from the agent that +will run the contract. + +Cut sections that don't serve the runner. A 200-line appendix of curl commands +is documentation, not execution. Put it in a wiki or a README, not in the +contract. The contract is for the agent running it; the README is for the human +reading about it. + +Before shipping, take one thing out. Like Chanel's advice: before leaving the +house, remove one accessory. Is there a section, a parameter, a check that +doesn't pull its weight? Cut it. The contract that ships with 5 sections will +be maintained. The one with 15 will rot. + +## Writing in contracts + +### Descriptions + +A description is not a label. It's the first thing an agent reads before +deciding whether to run this contract. Make it answer: what does this do, to +what, and why should I care? + +``` +❌ "Monitors infrastructure health" +✅ "Scans all 5 Proxmox nodes and 19 CTs for disk pressure, checks Docker + container health on .7/.116/.17, alerts via Telegram DM on RED/CRITICAL" +``` + +### Field names + +Name things by what they control, not by how the system is built: + +``` +❌ prometheus_scrape_interval_seconds +✅ check_every_seconds +``` + +The runner doesn't care that Prometheus is doing the scraping. They care how +often the check runs. + +### Error messages and alerts + +Errors tell the operator what went wrong and what to do about it. Be specific: + +``` +❌ "Health check failed" +✅ "GPU .15 (Strix Halo) unreachable at :8080 — firewalled to .116 only. + Check router /health/unified at http://192.168.68.116/health/unified instead." +``` + +### Comments + +Comments in contracts explain WHY, not WHAT. The execution steps say what to +do. Comments say why we do it this way and not another: + +```markdown +## Execution + +1. **Check GPU health via router** — cannot probe .15:8080 directly + (firewalled to .116 only as of 2026-07-01 Vulkan rebuild) +2. **Skip sidecar check** — JSON exporters at :8090 were never deployed + on any GPU host; router falls back to GPU /health direct probe +``` + +## Templates + +### Function contract template + +```markdown +--- +kind: function +name: contract-name +description: > + What this does, to what, and why. Be specific about hosts/services. +version: 1.0.0 +--- + +## Parameters + +- param_one: type — description (default: "value") +- param_two: type — description (default: "value") + +## Returns + +- result_field: type — description + +## Requires + +- Dependency one — how to verify it's available +- Dependency two + +## Execution + +1. **Step one** — what and why +2. **Step two** — what and why +3. **Compile and return** — assemble the output +``` + +### Responsibility contract template + +```markdown +--- +kind: responsibility +name: contract-name +description: > + What this recurring duty monitors/maintains. Name the systems, the + cadence, the blast radius. +version: 1.0.0 +--- + +## Maintains + +- state_field: { current: value, last_check: timestamp } +- another_field: description of maintained state + +## Parameters + +- param: type — description (default: "value") + +## Requires + +- Dependency one +- Dependency two + +## Continuity + +- Self-driven: check every N seconds/minutes/hours +- Also wakes on: trigger description + +## Execution + +1. **Check** — verify current state +2. **Evaluate** — compare against thresholds +3. **Remediate or escalate** — fix or alert +4. **Record** — update world-model state +``` + +## Enforcement + +This guide is aspirational, not gated. The CI pipeline enforces structure +(frontmatter, required sections) and regression (known stale patterns). It +does not enforce prose quality or specificity. That's on you, the author. + +Before you ship: read your contract as if you're the agent who will run it at +3am with 30 seconds of context. If you wouldn't trust it, rewrite it.