# AGENTS.md — Penn Relations

Penn Relations is an agent-first PR system. Penn is the agent at the
press desk — handles inbound press inquiries, drafts outbound pitches
and releases, maintains the journalist corpus, keeps a tamper-evident
audit log.

This file is the contract for **agent integrators** calling Penn from
outside the system (other Workers, MCP clients, internal CF tooling).
Humans use the web UI at `/`.

---

## Quick start

```bash
# MCP server (preferred when your host speaks MCP)
claude mcp add penn npx -y @capitalthought/penn-mcp \
  --env PENN_API_KEY=<your-key>

# Or direct HTTP (typed JSON, Bearer auth)
curl -X POST https://pr.pennrelations.com/mcp \
  -H "Authorization: Bearer $PENN_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"jsonrpc":"2.0","id":1,"method":"tools/list"}'
```

MCP discovery: <https://pennrelations.com/.well-known/mcp/server.json>
OpenAPI:        <https://pennrelations.com/v1/openapi.json>

> **Status:** Penn is currently invite-only. The production endpoints at
> `pr.pennrelations.com` are a planned alias for the live `pr.capitalfactory.com`
> system; until that DNS swap lands, use `pr.capitalfactory.com` as the
> hostname. The MCP package name is also pending publication.

---

## Tools (verb-first)

Releases (Phase 1):
- `penn_draft_release` — generate a release draft from typed facts.
- `penn_add_release_quote` — typed `Source` + attestation status required.
- `penn_add_release_stat` — typed `Source` + `verified_by_human_ref` required.
- `penn_legal_review` — counsel ack (bar number, jurisdiction, basis ≥ 200 chars, 24h cooling-off).
- `penn_approve_release` — refuses on any unmet legal / attestation / substantiation gate.
- `penn_publish_release` — publish to selected channels.

Media lists (Phase 2):
- `penn_search_journalists` — byline-grounded; defaults `require_recent_byline=true` (90d window).
- `penn_get_journalist` — `refresh=true` to re-crawl bylines (rate-capped 1/hr per journalist).
- `penn_verify_journalist` — on-demand byline corpus refresh.

Outreach (Phase 3):
- `penn_draft_pitch` — refuses on opt-out / blocklist / cooldown / stale bylines / unapproved release.
- `penn_request_pitch_approval` — HMAC-bound approval token; tier-3 requires TOTP.
- `penn_send_pitch` — atomic transactional send via Service Binding worker; re-validates approval state.
- `penn_record_response` — opt-out / decline / interested / bounce_hard; suppression-list propagation.

Inbound (Phase 4):
- `penn_intake_inquiry` — Postmark inbound webhook handler.
- `penn_draft_inquiry_response` — drafts a response; never auto-sends.

Operational:
- `penn_overview` — Inspectable State (queues, caps, recent activity, circuit state).
- `penn_prep` — session-start prep gates (creds, Supabase, Postmark, audit-chain integrity).
- `penn_pitch_timeline` — forensic timeline for a pitch, joinable by `correlation_id`.
- `penn_recent_refusals` — last N safety-system refusals with reason codes.

---

## Mandatory contract for callers

1. **Every quote needs a Source.** Releases ship with a typed `Source`
   discriminated union (`person.email`, `person.title`, `attestation_status`,
   `granted_at`). No anonymous quotes, no magic-link "ok to use."

2. **Every numeric claim needs verification.** The numeric classifier flags
   any claim it parses. `approve_release` refuses if `verified_by_human_ref`
   is missing on a flagged stat.

3. **Every send carries an AI-disclosure footer.** Not optional. Tier-3
   sends carry an additional transparency block.

4. **Tier-3 sends require TOTP step-up.** Per-approver TOTP secret stored
   per-env-binding. Lower tiers gated by approval-reason specificity check.

5. **Opt-outs propagate.** Adding an email to `suppression_list` blocks
   future pitches across all releases (per `propagates: true`).

6. **The audit log is append-only.** Postgres triggers REVOKE everything
   except INSERT. Don't try to UPDATE or DELETE — you'll just get a 500.

---

## Errors

All errors return `{ ok: false, error: "<code>", message: "<human>" }`.
Common codes:
- `refused:suppression_list_hit` — recipient on suppression list
- `refused:cooldown` — recipient in post-bounce cooldown window
- `refused:stale_bylines` — journalist hasn't bylined in 90d
- `refused:unapproved_release` — pitching against a release that isn't approved
- `refused:tier3_requires_totp` — tier-3 send without TOTP step-up
- `refused:approval_state_drift` — re-validation found state changed since approval
- `rate_limited` — per-domain send cap exceeded

---

## Versioning

Single hosted instance. MCP-tool schema is the contract; we will not
break it. Adding tools / fields is safe. Removing requires deprecation
notice in this file + 30d window.

---

## Source

<https://github.com/capitalthought/penn-relations>. Operated by
Capital Thought, LLC.
