Skip to content

Agency AI Access Model

If the iframe does not render, open the standalone version at /diagrams/agency-model.html.

The Automation Engineer builds the skills. Everyone else consumes them through a channel that is scoped to what they are allowed to do. Access control is enforced at the channel, not inside each skill.

RoleWhat they do
Owner (James)Full access to every repo, every skill, every channel. Can do anything.
Automation EngineerBuilds new skills, edits CLAUDE.md and hooks, ships updates. No client-data access by default.
Content LeadReviews and merges content PRs. Uses scoped local CLI. Gatekeeper on what gets published.
Content WriterWrites blog posts, social copy, article drafts. Local CLI on content repos, or Discord-only.
Account ManagerPulls client reports, runs CRM-scoped skills, drafts client-facing updates. Discord-only.
Junior / Client-FacingMinimal access. One Discord channel. Can only run what their channel exposes.

2. Access Interfaces (where scope is enforced)

Section titled “2. Access Interfaces (where scope is enforced)”
InterfaceWhat it looks likeWho uses it
Full Local CLIClones every repo, runs Claude Code with no restrictions, pushes directly to main.Owner, Automation Engineer
Scoped Local CLI + PRsClones only approved repos. Pushes go to feature branches. Merging requires pull request review.Content Lead, Content Writer (power users)
Discord BotNo local clone, no git, no CLI. Just a bot in specific Discord channels. Each channel exposes a different subset of skills.Everyone, depending on what channels they are in

3. Shared Capability Layer (the same for everyone)

Section titled “3. Shared Capability Layer (the same for everyone)”
ComponentWhat it holds
GitHub ReposThe source of truth for every skill, hook, and CLAUDE.md rule.
Claude Code RuntimeExecutes any skill loaded from the repo. Does not enforce role-based access itself — it trusts the calling channel’s permission check.
External APIsGHL, Gmail, GSC, Stripe, Cloudflare, Supabase. Credentials live in shared-secrets.env, never in any role’s hands directly.
  1. Owner opens Claude Code CLI.
  2. Runs /create-blog-post with raw content pasted in.
  3. The runtime invokes the skill.
  4. The skill edits a markdown file in ~/apps/themarketingshow/content/.
  5. Owner commits and pushes to main.
  6. Vercel auto-deploys.

Content Writer writes a blog post via Discord

Section titled “Content Writer writes a blog post via Discord”
  1. Writer types /write-post about X in #content-writing.
  2. The Discord bot checks the channel’s permission list. /create-blog-post is on the allow list.
  3. The bot invokes the skill in the runtime.
  4. The runtime opens a pull request against the TMS content repo.
  5. The Content Lead reviews the PR.
  6. On approval, the Lead merges and Vercel deploys.
  1. Engineer clones the cc repo locally.
  2. Writes ~/.claude/commands/new-skill.md.
  3. Tests it via full local CLI.
  4. Commits and pushes to main.
  5. Next time anyone’s runtime reloads, the new skill is available to whichever channel has been granted it in the bot config.
  1. AM types /hottest-prospect gkp in #client-ops.
  2. The Discord bot checks the channel’s permission list. /hottest-prospect is on the allow list, and the gkp scope matches what the channel is allowed to see.
  3. The runtime hits the GHL API, pulls contact data.
  4. Result returns to the channel as a message.
  5. AM never touches a repo, never sees credentials, never sees any client the channel is not scoped to.
  • Accidental pushes to main. A Discord-only user cannot do it because they have no git access.
  • Token leakage. Credentials live in shared-secrets.env on Google Drive and on the VPS. No role below Owner / Automation Engineer ever sees them directly.
  • Scope creep. A scoped CLI user cannot bypass the PR process, because main push access is gated on the repo side.
  • Cross-client leakage. An Account Manager in #client-ops for client A cannot see client B’s data unless a channel for client B exists and they are invited to it.
  • Rogue skill execution. The Automation Engineer can write a new skill, but until someone grants a channel the right to invoke it, no non-Owner can run it.

Open questions (things to decide before building this for real)

Section titled “Open questions (things to decide before building this for real)”
  • Where does the Discord bot run? VPS. Which runtime — Node, Python, or a managed service like discord.py + a systemd unit?
  • How are channel permissions stored? Probably a YAML file in the cc repo: channel name → allowed skills list. Bot reads on startup, hot-reloads on file change.
  • How are scopes passed to skills? The bot adds a --scope=<channel> argument when invoking a skill. Skills that touch client data read the scope and filter.
  • How is the audit trail logged? Every bot invocation writes to ~/apps/claude-log/ with channel, user, skill, args, outcome. Same pattern as the existing conversation log.
  • What if a skill wants to run for multiple clients? Only the Owner’s channel can invoke the “all clients” variant. Scoped channels always pass their single-client scope.
  • What if the GitHub repo splits? Over time, moving tool/skill repos to a separate org from content repos would make the access model cleaner — no more “clone this one subdirectory only.”

Today, every agency I know is either (a) giving everyone full access and praying, or (b) blocking everyone from using AI at all because they are afraid of what will happen. Neither is the answer. The answer is scoping access at the interface level, not inside the AI itself — trust the bot, not the LLM.