# Executive Morning Brief: five rules and a five-check script (free sample)

Version 1.0, 2026-10-29. By PaperChaseWebb. For owners who ask an agent for a morning brief, in Grok Bot, Claude Code, Codex, Hermes or a mix, and want to be able to check it. One Markdown file and one Python 3 script, standard library only. The script was tested on its worked example (5 findings, then 0) and on a missing file (exit 2). It was not run inside any agent harness or against a real inbox or calendar.

The usual morning brief reads well and cannot be checked. A meeting appears that was never booked. A figure is close to the real one. The inbox failed to load and the brief looks calm because of it. Then one day the prompt says "reply to anything simple" and a reply goes out that nobody read. These five rules cover the gaps that cost the most, and the script checks them in one small file.

## Install in one line

Save this page as FREE-SAMPLE.md, then:

```
python3 -c "import re;t=open('FREE-SAMPLE.md').read();open('brief_mini.py','w').write(re.search(r'~~~python\n(.*?)~~~',t,re.S).group(1))"
python3 brief_mini.py brief-lite.json
```

## Five rules

1. **The brief has a human owner.** A person reads it and decides. Not "TBD", and not the agent writing it.
2. **Every line has a source you can open, and a quote from it.** If you cannot click through to the event or the message, the line is a guess.
3. **Figures are copied, not retold.** Every money figure, percentage and large number in a line appears in the quote behind it, character for character.
4. **Drafts wait for the owner.** Replies and calendar changes are drafts until you say yes. Payments and deletions are never drafted at all.
5. **Failed sources are named.** If the inbox did not load, the brief says so. A calm brief built on missing data is worse than no brief.

## The five checks

| Id | Check |
|---|---|
| M1 | the brief names a human owner |
| M2 | every line has a source and a quote from it |
| M3 | every figure in a line appears in its quote |
| M4 | every draft is waiting for the owner, and no draft is a payment or a deletion |
| M5 | every source that failed is named in missing |

Exit 0 clean, 1 findings, 2 missing or unreadable file. The script only reads.

## Worked example

A made-up print shop. The job board export failed this morning, an invoice figure was retold wrong, one line is advice with no source, and a reply was marked sent. Before (brief-lite.json):

```json
{
 "owner": "TBD",
 "sources": {"calendar": true, "inbox": true, "payments": true, "jobs": false},
 "lines": [
  {"text": "Lark Cafe wants a quote for 400 menus by Friday.", "source": "gmail:msg_18c2", "quote": "asks for a quote on 400 menus by Friday"},
  {"text": "Northside Paper invoice is $1,420, due Nov 3.", "source": "gmail:msg_18c3", "quote": "invoice NP-2207 for $1,240, due Nov 3"},
  {"text": "Busy afternoon, protect your focus time.", "source": "", "quote": ""}
 ],
 "drafts": [
  {"kind": "reply", "to": "orders@lark-cafe.example", "status": "sent"}
 ],
 "missing": []
}
```

Output:

```
M1 the brief has no human owner
M2 line 3 has no source to open, or no quote from it
M3 line 2: figure $1,420 is not in the quote from gmail:msg_18c3
M4 draft reply to orders@lark-cafe.example is 'sent'; drafts wait for the owner and pay or delete is never drafted
M5 source jobs failed but the brief does not say so
5 checks, 5 findings
```

The fixes: Noor is named as the owner, the invoice line is corrected to the $1,240 in the message, the focus-time advice is replaced by a real conflict from the calendar with its event id, the reply goes back to waiting for owner, and the failed job board gets a missing line. After:

```
5 checks, 0 findings
```

## One prompt to try

Paste into your agent: "Write this morning's brief as brief-lite.json. For every line give the source id I can open and a short quote from it, and copy figures exactly. List every source you tried and whether it loaded. Put replies under drafts with status waiting for owner. Do not send anything."

## What the full pack adds

The Executive Morning Brief (pp-executive-morning-brief, $24) turns these rules into a brief folder your agents build every morning. Six plain files: the setup, the sources (read-only, with personal, family, health and legal scopes excluded), the day's run, today's records with refs and a freshness limit, the brief lines and the drafts, plus an approvals file that only brief.py writes. brief.py gates what the agent may do (read and draft alone, reply or move only with a yes, pay and delete never), renders the brief only when the checker is clean, and records the owner's yes at their own terminal, tied to the sha256 of that day's brief and refused inside an agent or on piped input. Drafts can only be aimed at people and events that appear in the records. Fifteen read-only checks, 67 tests, a skill file for Claude Code, Codex and Hermes, an AGENTS.md snippet and a Grok Bot rules block. If you want it set up with you on your own accounts, that is the Install sprint on paperchasewebb.ai.

## The script

~~~python
#!/usr/bin/env python3
"""brief_mini.py: five read-only checks on a one-file morning brief (brief-lite.json).
Usage: python3 brief_mini.py brief-lite.json
Exit 0 clean, 1 findings, 2 missing or unreadable file. Standard library only. Writes nothing."""
import json, re, sys

PLACEHOLDERS = {"", "tbd", "todo", "owner", "someone", "agent", "ai", "assistant", "team", "default"}
FIGURE = re.compile(r"\$\d+(?:,\d{3})*(?:\.\d+)?|\d+(?:\.\d+)?%|\b\d{1,3}(?:,\d{3})+(?:\.\d+)?\b")
NEVER = ("pay", "delete")


def main(a):
    if len(a) < 2:
        print(__doc__); return 2
    try:
        b = json.load(open(a[1]))
    except Exception as e:
        print(f"cannot read {a[1]}: {e}", file=sys.stderr); return 2
    F = []
    if str(b.get("owner", "")).strip().lower() in PLACEHOLDERS:
        F.append("M1 the brief has no human owner")
    for n, line in enumerate(b.get("lines", []), 1):
        src, quote = line.get("source", "").strip(), line.get("quote", "").strip()
        if not src or not quote:
            F.append(f"M2 line {n} has no source to open, or no quote from it")
            continue
        for fig in FIGURE.findall(line.get("text", "")):
            if fig not in quote:
                F.append(f"M3 line {n}: figure {fig} is not in the quote from {src}")
    for d in b.get("drafts", []):
        if d.get("kind") in NEVER or d.get("status") != "waiting for owner":
            F.append(f"M4 draft {d.get('kind', '?')} to {d.get('to', '?')} is '{d.get('status', '')}'; drafts wait for the owner and pay or delete is never drafted")
    named = " ".join(b.get("missing", []))
    for s, ok in b.get("sources", {}).items():
        if not ok and s not in named:
            F.append(f"M5 source {s} failed but the brief does not say so")
    F.sort(key=lambda x: x[:2])
    for f in F:
        print(f)
    print(f"5 checks, {len(F)} findings")
    return 1 if F else 0


if __name__ == "__main__":
    sys.exit(main(sys.argv))
~~~

Made-up example; no real business, people or figures are described.

PaperChaseWebb 💸
