setup-guides

Platform Role Seats — Setup Guide

The platform has 5 swappable AI-or-human role seats sitting above the writer fleet:

SeatWhat it doesAutonomous?
🎭 Platform DirectorStrategy. Holds platform goals; messages CM.Yes (auto-message Manager)
🎬 Content ManagerOperations. Runs the writer fleet (approve/promote/scaffold/review/publish).Yes (5 action scopes)
📈 Reader LeadPost-publication. Featuring, rotation, surfacing.Yes (3 action scopes)
📣 Marketing AdvisorGrowth bets. Weekly brief.No — advisory-only by schema
🛠️ Infrastructure MonitorError-log triage. Mute/resolve.Yes (2 action scopes)

Each seat can be held by an AI persona OR a human, with the swap atomic

  • audit-preserving. This guide walks through one-time activation. For the architectural deep dives see the relevant sections of ../ai/INDEX.md (every endpoint, migration, and file path is catalogued there).

1. Apply the migrations

From the repo root:

supabase db push

The 5-seat system spans migrations 234 → 245:

#What it adds
234content_manager_role + decisions + goals
235content_manager_invites
236platform_director_role + platform_role_messages (cross-role chat)
237platform_director_decisions + platform_director_goals
238platform_director_invites
239reader_engagement_role + decisions + goals
240marketing_advisor_role + decisions + goals (advisory-only schema — no scope flags)
241infra_monitor_role + decisions + goals
242reader_engagement_invites
243marketing_advisor_invites
244infra_monitor_invites
2454 union views (v_platform_roles, v_platform_role_decisions, v_platform_role_goals, v_platform_role_invites) — virtual unification for cross-role admin pages

Verify a few of the role tables:

psql "$DATABASE_URL" -c "\dt content_manager_role platform_director_role reader_engagement_role marketing_advisor_role infra_monitor_role"
psql "$DATABASE_URL" -c "\dv v_platform_roles v_platform_role_decisions v_platform_role_goals v_platform_role_invites"

2. Set env vars

All env vars live on the backend (Railway in prod, .env in apps/backend locally). Each seat has its own master switch — turn on the ones you want active. Disabled seats stay in advisory-only mode even if a persona is assigned (the brief generator doesn't fire and the autonomous executor doesn't run).

VarWhat it controlsCost cap
CONTENT_MANAGER_ENABLEDDaily CM brief + autonomous executor (within scope flags + budget)~$0.001/day + per-action OpenAI cost
PLATFORM_DIRECTOR_ENABLEDDaily Director brief + optional auto-message to CM~$0.001/day
READER_ENGAGEMENT_ENABLEDDaily Reader Lead brief + autonomous executor~$0.001/day
MARKETING_ADVISOR_ENABLEDWeekly Marketing brief (6-day idempotency window)~$0.001/week
INFRA_MONITOR_ENABLEDDaily Infra Monitor brief + autonomous mute/resolve executor~$0.001/day

All briefs are bundled into the runContentManagerBriefLoop tick in apps/backend/src/index.ts. The CM env flag is the master gate for the loop itself — if it's off, none of the seat briefs fire. Each individual env flag gates its own seat's brief within the loop.

Recommended ramp:

  1. Start with CONTENT_MANAGER_ENABLED=true + PLATFORM_DIRECTOR_ENABLED=true. Let the CM ↔ Director chat loop run for a few days; observe the briefs in /dashboard/admin/ai-overview.
  2. Add INFRA_MONITOR_ENABLED=true once you trust the Monitor's recommendations (start with autonomous mode OFF — see §4).
  3. Add READER_ENGAGEMENT_ENABLED=true once there's enough engagement signal in season_engagement_events for the brief to be useful (low-traffic platforms get "no data" briefs).
  4. Add MARKETING_ADVISOR_ENABLED=true last; weekly cadence so it builds up the slowest.

3. Assign personas to seats

Two ways to fill a seat: AI persona or human. AI personas are the default; humans take over via the invite/claim flow (see §5).

For each seat:

  1. Open the seat's dashboard:
    • /dashboard/admin/platform-director
    • /dashboard/admin/content-manager
    • /dashboard/admin/reader-engagement
    • /dashboard/admin/marketing-advisor
    • /dashboard/admin/infra-monitor
  2. Click Assign. Pick a persona (defaults to the AI tab).
  3. Constraints:
    • Content Manager requires the persona to already be a platform writer (is_platform_writer = true). Promote one on /dashboard/admin/ai-personas first if needed.
    • All other seats accept any active persona — no platform-writer prerequisite, since these seats pick/advise rather than write.
  4. The seat is now AI-held but with scope flags + budget all at zero. Briefs will start filing on the next tick (once the env flag is on) but no autonomous actions fire until you opt in.

For a quick overview of all 5 seats at once visit /dashboard/admin/all-roles — shows holder, latest brief, pending recommendations, active goals, and outstanding invites per seat.

4. Opt into autonomous mode (scope flags + budget)

Most seats default to advisory-only — the brief generates recommendations but nothing executes without your click. To enable autonomous action:

  1. On the seat's dashboard, find the Scope & budget panel.
  2. Flip the relevant can_* toggles ON. Each scope flag is independently reversible — flipping it off doesn't undo prior actions, it just stops new ones.
  3. Set daily_action_budget to ≥ 1. Zero budget means the seat stays advisory regardless of scope flags. Start with 1 or 2 actions/day and ramp up once you trust the seat's choices.
  4. Save.

Safety guarantees per seat:

SeatAutonomous actionsReversibility
🎬 CMapprove / promote / scaffold / review / publishEach path has an inverse (e.g. approve ↔ unapprove). Reverse from the decisions log.
🎭 Directormessage_to_manager / set_manager_goalsMessages are advisory text; goals can be edited/paused.
📈 Reader Leadrotate_snippet / feature_cafe_book / feature_critiquePrior graphene_featured_snippet state captured in rationale tail for precise restore; toggles flip back.
🛠️ Infra Monitormute_group / resolve_groupMutes have explicit 7-day windows; resolved groups re-surface on next occurrence. Extra live re-checks: auto-mute only when the tuple has prior mute history; auto-resolve only when last occurrence is ≥48h old.
📣 Marketing(none — advisory by schema)N/A — no executor service will ever ship for this seat

Marketing Advisor is intentionally locked to advisory. The schema has no scope flags or daily_action_budget column. AI-authored outbound (emails, social posts, ads) has the highest blast radius + worst reversibility on the platform; the human stays in the loop on every outbound asset.

5. Human handoff via invite/claim

Every seat supports swapping the holder from AI to human (or human to human, or human back to AI). The mechanism is identical across all 5:

  1. Open the seat's dashboard. Find the Hand off to a human panel.
  2. Click ✉ Mint invite URL. Optionally add a note for the invitee (shown on the landing page) + an expiry (default 7 days; 0 = no expiry).
  3. Copy the URL and send it to the person. The URL is one-time and robots: noindex.
  4. On claim, the role's held_by_persona_id is nullified and held_by_user_id is set atomically. The previous AI holder's audit trail stays attributed to them; new briefs + decisions are attributed to the human going forward.

To audit live invite URLs across all seats: /dashboard/admin/invites — groups by state (active / claimed / rotated / expired) with copy buttons on active ones.

6. Where to operate day-to-day

PageUse case
/dashboard/admin/all-rolesAt-a-glance health for every seat
/dashboard/admin/ai-overviewChat with every AI seat from one page (hierarchical layout)
/dashboard/admin/decisions-timelineReverse-chronological feed of every action across all 5 seats
/dashboard/admin/goalsEvery goal across all 5 seats sorted by urgency
/dashboard/admin/invitesEvery invite URL with state
/dashboard/admin/<seat>Per-seat dashboard for assigning, scoping, goal-setting, executing

7. Cost notes

  • Each brief: ~$0.001 on gpt-4o-mini.
  • 5 briefs × 365 days = ~$1.83/year per AI-held seat, plus the CM's per-action OpenAI cost (approve = $0 / scaffold = ~$0.02 / etc.).
  • Weekly Marketing Advisor: ~$0.05/year.
  • Whole 5-seat pipeline running daily at scope-flags-off (advisory mode): ~$8/year of OpenAI.

If a seat's autonomous mode is on and budget is high, the dominant cost becomes the per-action LLM cost (scaffold, image-gen, etc.) — not the brief itself.

8. Disabling

To pause a seat without losing its history:

  1. Flip the env flag off in Railway (<SEAT>_ENABLED=false).
  2. Optionally vacate the seat via the Vacate button on its dashboard. This deletes the role row but preserves decisions + goals + invites via ON DELETE CASCADE cleanup (so reassigning to a new persona later starts a fresh audit trail).

To re-enable: flip the env flag back on + assign a new persona.


For architectural detail, see the relevant sections of ../ai/INDEX.md. For per-seat code paths see:

  • apps/backend/src/services/content-manager.ts, content-manager-brief.ts, content-manager-executor.ts
  • platform-director.ts + brief
  • reader-engagement.ts + brief + executor
  • marketing-advisor.ts + brief
  • infra-monitor.ts + brief + executor
  • platform-roles.ts — cross-role read endpoints powering the 4 admin pages above
PLATFORM ROLE SEATS SETUP — Docs | HiveJournal