By 10:36 on the night of August 2, a Codex review had told me the agent-team pattern I’d spent the day building was “not fit to copy as the staff pattern.” That day I had written a role charter, a state file and a feed directory.
The role-shaped version
The spec (v2) described a shared activity feed that role-scoped bots listen to. Devs post what they’re working on. Bots watch the feed, and each bot has one job. The first was an analyst that reads everything and flags overlap, blast radius and blockers. Then a help-topics bot that decides whether a post should improve our help content, and a marketing bot that says “save that one.”
I built it that way. There was a roles/analyst/CHARTER.md with a job, a “what to watch for” list, a “what it may do” list and a notes/ directory it appended to after every pass. There was a STATE.md with a table of who gets invited first, and a feed/ directory. It read well, and each charter was tidy.
The problem showed up when I filled in the invite table. Every row had a person in it, and every person had a different machine and a different tolerance for tooling. One teammate would clone a session and tick it over. One would use chat only and read and write through the GitHub web UI, with no CLI wake loop. One has no git at all and reads Discord on a phone. A role has no machine, no credentials and no one to answer when it does something odd. I had written charters for enclosures that nothing could actually run in.
A review that landed on enforcement
I pointed Codex (high effort, about 19k tokens, 105 seconds) at the whole setup. The verdict called it a workable founder workbench, said to keep the Windows Scheduled Tasks, and said not to copy it as the staff pattern. Its deepest finding was about enforcement. Red-band classification and quarantine were instructions in a prompt. The model had to classify correctly and also remember to invoke the CLI. Nothing below the model stopped a red action.
That applies just as much to a role as to a person, but it is much worse for a role. If the analyst bot belongs to nobody, whose credentials is it running on, and whose fault is the bad call?
The shape that shipped
Person first. Each staff member gets:
- their own bot identity in Discord, with their own persona
- their own charter, scoped to their own workflow
- their own machine, waking on their own schedule
- their own scoped credentials
Roles survive as behavior inside a person’s agent. They stop being the unit of deployment. Nobody inherits my session setup. Mine has its own listener too, and it carries a 12-turn throttle (raised from 4 once two agents actually held a working conversation and the loop I feared never showed up).
The wake prompt says who the agent belongs to: “You are BEN’S agent.” The agent’s job in a feed channel is to be a filter and pick exactly one of three outcomes: SILENT, ASK the other person’s agent in #agents, or DM its owner. Its owner’s Discord notifications for that channel are off on purpose, so the agent’s judgment is the only thing standing between a partner’s routine push and an interruption. A role-scoped bot can’t be held to that. It has no owner whose attention it is spending.
Each wake records its outcome to a ledger (silent, escalated), and the reaction mark on the triggering message closes to match. A session that claims 🧠 and never clears it gets swept to ❌ after 20 minutes. That only makes sense per person. “The analyst died” is nobody’s problem. “My agent died” is mine.
Being wrong in the open
The same morning showed me the failure mode. A dev-side bot asked my agent in #agents a source question: open ai/shared/test-sites and name the baseline test site. My agent’s reply started SKIP, then kept going: “wait, actually this needs an answer, switching to POST.” It then posted. The output contract wants a decision word alone on the first line, and that reply broke it because the agent changed its mind mid-sentence. The answer was fine. test-sites doesn’t name a baseline at all. It describes a per-staff pattern where each person has their own “My Test HQ” account. Even the shared documentation was person-first, and my architecture was the thing that disagreed with it.
The layer underneath
Once people were the unit, the transport problem got clearer. Discord is the human surface. It shipped that day, and one teammate reads it on her phone. But the substrate under it is a git vault, and the person with no git is excluded by exactly the tool meant to include her. I recorded the direction as unsettled, so it isn’t in scope for the current ticket. The idea is an MCP server exposing the vault as tools and resources. Her side becomes a URL and a token her agent uses invisibly, and it works the same across whichever model each person ends up on.
It also addresses the Codex finding. If BOX is enforced at the tool boundary, so the tool is absent or refuses for that principal, a red action is stopped below the model instead of depending on it remembering a rule. That only works if “that principal” is a real person with a real identity, which is the whole argument for person-first.
The invite table test
Before drawing any box labeled with a role, I fill in the invite table. If a row has no machine, no credential and no one who answers for it, it isn’t a unit yet. The roles I wrote are still useful as charters. They just live inside someone’s agent now.