A headless Claude Code agent that answers "what's actually testable in dev right now?" for products with distributed deploy signals.
It watches a Slack deploy channel, walks the affected routes in a logged-in browser via Playwright, and posts a verification report distinguishing shipped-and-working, wired-but-broken, wired-but-mocked, permission-walled, and not-shipped.
Two modes ship together; they share the same product profile.
| Mode | When to use | Entry point |
|---|---|---|
Interactive skill (/dev-status) |
Ad-hoc — "what should I test right now?" | skill/SKILL.md |
| Headless monitor | Continuous — every 5 min, posts when something ships | monitor/ |
- A pattern for headless
claude -p+ MCPs (Slack + Playwright) doing real verification work, not just notification. - Profile-driven: one YAML file per product describes the deploy channel, the routes, and the service→route map.
- Honest about uncertainty: outputs a five-state evidence table, not a green check.
- Not a deploy notifier. It doesn't duplicate the deploy channel; it only posts when an actual route walk happened.
- Not uptime monitoring. Routes are checked after a relevant deploy, not on a fixed cadence.
- Not exhaustive. Failed deploys / unmapped services between walks are absorbed into state and not separately surfaced. If signal is being lost, iterate by adding a daily summary.
- Not human-in-the-loop. The Slack message is the final output. No review step.
┌─ launchd (every 5 min)
│
└─→ monitor.sh
│
└─→ claude -p (with prompt.md)
│
├─→ Slack MCP — read deploy channel since last-seen
├─→ profile YAML — map shipped services → routes
├─→ Playwright MCP — walk routes (auth held in browser context)
└─→ Slack MCP — post results to output channel
State (last-seen timestamp) is written only on a clean run — failures retry next tick.
- macOS (the headless monitor uses launchd; adapt for Linux/cron as needed)
- Claude Code installed and authenticated
- Slack MCP and Playwright MCP configured for Claude
- A Playwright MCP browser session already authenticated to your dev environment (Okta/Auth0 sessions persist across runs)
cp config.example.yaml config.yaml
# Edit config.yaml — set output channel, profile path, state dirCopy profiles/example.yaml to profiles/<your-product>.yaml and fill it in. See profiles/README.md for the schema. Profiles take ~30 minutes to author for a new product (the slow step is walking the app once to enumerate routes and services).
Point config.yaml's profile: at your new file.
Before automating, run the interactive skill once to make sure the profile produces sane output:
/dev-status <profile-name>
Iterate on the profile until it categorizes routes correctly.
./monitor/install-launchd.shThis installs a launchd agent running every 5 minutes. First run will likely surface "Okta session expired" — log in via the Playwright browser to refresh.
launchctl unload ~/Library/LaunchAgents/com.claude-dev-status.plist
rm ~/Library/LaunchAgents/com.claude-dev-status.plist- Auth. Browser auth is interactive on first walk; the MCP browser context holds the session for subsequent walks. Most SSO sessions last 8–24h. For unattended operation across long windows you'd need a service account with relaxed MFA — out of scope here.
- State file. A single file holds the last-seen Slack message timestamp. Delete it to reprocess from scratch; back it up if you need exactly-once semantics.
- Lockfile. A second tick won't start while a first is still running — the Playwright MCP browser is single-instance.
- Concurrency with other Claude sessions. If you also use Claude Code interactively with the same Playwright MCP, the monitor will skip ticks while you're using the browser. That's by design.
See profiles/README.md for the full schema. The minimum a profile needs:
dev_url— base URL of the dev environmentauth.type—okta/auth0/basic/nonediscovery.deploy_channel.id— Slack channel where deploy events postdeploy_signals— regexes that extract service names from deploy bot messagesroutes[]— list of routes, each with the services that, when shipped, justify a re-walk
MIT — see LICENSE.