DEV Community

Alex Zhu
Alex Zhu

Posted on

Write a Demo Contract: A Wiki Run Before a Clickable Agent Page Gets Treated as Shipped

You opened the review thread and found a public URL where a teammate had asked an agent to rebuild the onboarding page overnight. The page looked finished, the buttons worked, and a stakeholder had already forwarded it to a customer success lead. Nobody had written what the page promised, what data it stored, or when the server should disappear. You need a demo contract before the next clickable agent page gets treated as shipped work.

Why a finished look is not a handoff

A clickable page compresses a lot of unfinished decisions into one link that feels safe to forward. People assume uptime, data handling, and accessibility because the layout resembles the product they already trust. Your wiki run should force those assumptions into named fields before anyone outside the author shares the URL. The card is the handoff, and the page is only the prop that the card is allowed to describe.

Roles you name before the first share

You assign four short roles, and you keep them as names rather than team aliases that nobody checks. The author builds the page and fills the first draft of the card from the agent transcript. The contract owner accepts or rejects the promises, including the stop date and the data rule. The audience liaison is the only person who may forward the URL, and the teardown owner deletes the server when the date hits.

The one-page run you paste into the wiki

You can paste the numbered run below under a heading called Demo contract, then link it from your agent lane page. Each step names an owner, an input, and a proof you can attach without scheduling a long meeting. Stop the run if any proof is missing, because a missing proof means the URL stays private. Do not treat a green local preview as permission to share the URL outside the author channel.

1. Author writes the question and saves a private card before any server starts.
2. Contract owner sets data_rule, claims_refused, stop_date, and teardown_owner.
3. Author drafts the page in the model lane and commits the page with the card.
4. Contract owner runs python3 check_demo_contract.py demo-contract.yaml.
5. Audience liaison forwards the URL only when status is shareable and exit code is 0.
6. Teardown owner deletes the server on stop_date and sets status to expired.
Enter fullscreen mode Exit fullscreen mode

1. Capture the question

You write one sentence that states the question the demo answers, and you reject vague goals like explore the new flow. The author pastes that sentence at the top of the card before any model call is made. If you cannot state the question in one sentence, you do not start the server at all. A demo without a question becomes a second product by accident, and the wiki cannot reverse that later.

2. Mark the boundary

You list what the page may show and what it must not collect, store, or imply about production behavior. The contract owner checks that list against your data handling notes before the audience liaison sees the link. Sample data stays synthetic, and any real identifier is a reason to tear the run down immediately. You also note which claims the page is not allowed to make, such as uptime or accessibility completion.

3. Bind the stop date

You set a stop date that is soon enough to force a decision, and you name the teardown owner on the same line. The audience liaison may extend the date once, and only by editing the card before the old date passes. A silent extension is a failed handoff, even if the server is still healthy and the page still loads. When the date passes, the teardown owner removes the process and writes the outcome back into the card.

4. Attach the proof

You attach the card path, the commit that generated the page, and a short note on which model lane produced the draft. The contract owner signs by adding their name and the time, using the checker below so the fields are actually present. The audience liaison forwards only after the checker exits cleanly and the card status says shareable. You keep the transcript link next to the card so a later reader can see why a sentence was chosen.

Failures this run is meant to catch

Three failures show up often when a clickable agent page escapes the author channel and lands in a stakeholder thread. The first is a forwarded URL with no stop date, which turns a short favor into an unpaid hosting promise. The second is a data rule written as be careful, which nobody can test and nobody can honestly reject. The third is one person holding author, contract, and teardown, so the share decision was never actually reviewed.

A checker you can run before anyone forwards the URL

You do not need a platform team to enforce the first version of this habit on the team. A small Python script can reject a missing owner, a past stop date, or a shareable rule that is not synthetic. Label the script below as an unexecuted example until you run it against a card you wrote. You run it in the same directory as the card, and you paste the exit status into the wiki comment.

# Replace every placeholder before you share anything.
question: Can a stakeholder finish onboarding without asking what the next button does?
status: draft
author: riley
contract_owner: jordan
audience_liaison: casey
teardown_owner: devon
stop_date: 2026-10-15
data_rule: synthetic fixtures only; no customer identifiers
claims_refused: uptime, accessibility completion, production parity
server_name: onboarding-demo-041
transcript_url: https://example.internal/transcripts/041
Enter fullscreen mode Exit fullscreen mode
#!/usr/bin/env python3
"""Local demo-contract checker.

Unexecuted example: run it yourself. It reads a local file and does not call a network.
"""

from __future__ import annotations

import sys
from datetime import date
from pathlib import Path

REQUIRED = [
    "question",
    "status",
    "author",
    "contract_owner",
    "audience_liaison",
    "teardown_owner",
    "stop_date",
    "data_rule",
    "claims_refused",
    "server_name",
]

ALLOWED_STATUS = {"draft", "shareable", "blocked", "freeze", "expired"}


def parse_flat_card(text: str) -> dict[str, str]:
    data: dict[str, str] = {}
    for line_no, raw in enumerate(text.splitlines(), start=1):
        line = raw.split("#", 1)[0].rstrip()
        if not line.strip():
            continue
        if line[0].isspace() or line.startswith("-"):
            raise SystemExit(f"line {line_no}: nested values are not allowed in v1")
        if ":" not in line:
            raise SystemExit(f"line {line_no}: expected key: value")
        key, value = line.split(":", 1)
        data[key.strip()] = value.strip().strip('"').strip("'")
    return data


def check(card: dict[str, str], today: date) -> list[str]:
    errors: list[str] = []
    for key in REQUIRED:
        if not card.get(key):
            errors.append(f"missing {key}")
    status = card.get("status", "")
    if status and status not in ALLOWED_STATUS:
        errors.append(f"unknown status {status}")
    stop_on = None
    stop = card.get("stop_date", "")
    if stop:
        try:
            stop_on = date.fromisoformat(stop)
        except ValueError:
            errors.append("stop_date must be YYYY-MM-DD")
    if stop_on and stop_on < today and status not in {"expired", "freeze"}:
        errors.append("stop date has passed; set expired and tear the server down")
    author = card.get("author", "")
    if author and author == card.get("contract_owner", ""):
        errors.append("contract_owner must differ from author")
    if author and author == card.get("teardown_owner", ""):
        errors.append("teardown_owner must differ from author")
    if author and author == card.get("audience_liaison", ""):
        errors.append("audience_liaison must differ from author")
    if status == "shareable":
        rule = card.get("data_rule", "").lower()
        if not rule.startswith("synthetic"):
            errors.append("shareable data_rule must start with synthetic")
        if not card.get("transcript_url"):
            errors.append("shareable cards need transcript_url")
    return errors


def main() -> int:
    path = Path(sys.argv[1] if len(sys.argv) > 1 else "demo-contract.yaml")
    card = parse_flat_card(path.read_text(encoding="utf-8"))
    errors = check(card, date.today())
    if errors:
        print(f"blocked: {path}")
        for err in errors:
            print(f"- {err}")
        return 1
    print(f"ok: {path} status={card['status']} stop={card['stop_date']}")
    return 0


if __name__ == "__main__":
    raise SystemExit(main())
Enter fullscreen mode Exit fullscreen mode
python3 check_demo_contract.py demo-contract.yaml
echo "exit=$?"
Enter fullscreen mode Exit fullscreen mode

You treat a zero exit as permission to ask the audience liaison for a forward, not as permission to call the page production. A failing exit means you fix the card, and you do not negotiate the missing field in chat. If the stop date is already past, the script should fail even when every other field looks tidy. That failure is the point, because tidy cards can still describe a server that should already be gone.

The sample card uses a stop date of 2026-10-15 so you can see the field shape, not so you can copy that day forever. You should replace that date, the names, and the server name before you trust the printed result. The parser accepts only flat key lines, which keeps the first version easy to review in a wiki diff. Nested lists belong in a later version, after the team actually follows the flat card for a few runs.

The handoff comment after each step

You leave a four-line comment on the wiki page so the next person does not reconstruct the run from chat. The comment names the role that just finished, the proof path, the next role, and the command they should run. You do not add praise, guesses, or a second URL in that comment, because extras hide the missing proof. A short comment is easier to audit than a paragraph that tries to retell the whole demo from memory.

Role finished: contract owner
Proof: demo-contract.yaml exit 0
Next: audience liaison
Command: python3 check_demo_contract.py demo-contract.yaml
Enter fullscreen mode Exit fullscreen mode

A decision table for the contract owner

You will get pressure to keep a popular demo alive because a stakeholder liked the click path. Use a small table so the decision stays tied to the question, the data rule, and the date. The table does not replace judgment, but it stops the team from inventing a new exception in the thread. Read the row that matches your evidence, then write that row name into the card status field.

Evidence you can point to Status to write Next owner
Question still open, synthetic data, stop date in the future shareable audience liaison
Question already answered, no new stakeholder ask freeze teardown owner
Real identifiers, or a data rule nobody can test blocked contract owner
Stop date has passed, whatever the page still shows expired teardown owner

You copy one row into the handoff comment so the status change has a reason sitting beside it. You do not add a fifth row during the meeting, because a new row is a new policy and belongs in a separate edit. If two rows seem true, you pick the stricter one and you say so in the comment. Stricter here means blocked before shareable, and expired before freeze, so old servers do not linger under a polite name.

Where free model access and a free server fit

You can draft the card and the page copy with free model access, then host that page on MonkeyCode's free server option. Disclosure: This article was prepared as part of MonkeyCode's product outreach. Those two options fit this run because the demo should stay cheap enough to delete, not precious enough to defend. You still fill the owners yourself, because model access does not choose your stop date or your data rule.

Limitations and who should skip this

This checker only reads a local card file, and it cannot see whether the server is actually down or who can still log in. You should not use the run for production incidents, regulated data, or any page that already holds customer records. A team with no wiki and no named owners will turn the card into another unread document on a shelf. If you cannot name a teardown owner who can actually delete the server, skip the share step entirely.

Free model access and a free server option can change, so you should not write quotas, hardware sizes, or permanence into the wiki card. The script does not call a vendor API, and it will not catch a secret that someone pasted into the page source. You still need your normal review if the demo later becomes a candidate for the product repository. People who only build private throwaway sketches, with no shared URL, do not need this ceremony at all.

Close the loop without keeping the URL

After teardown, you leave the card in place and you change the status to expired rather than deleting the history. The next author can copy the question and the data rule, but they must set a new date and a new server name. That copy is a fresh run, not an extension of the old URL or the old transcript. If you try the drafting step, keep the card private and use free model access plus the free server option there.

Top comments (0)