Product Spec

PromptCMS

A conversational content management system for community organisations. Members update their website by messaging a bot. No logins, no dashboards, no training required.

๐Ÿ“„ v0.2-concept ๐Ÿ“… Updated 2026-03-09 ๐ŸŸข Live prototype (2 orgs) โ†’ Production SaaS roadmap

PromptCMS โ€” Product Specification

A conversational content management system for community organisations. Members update their website by messaging a bot. No logins, no dashboards, no training required.

Version: 0.2-concept
Last updated: 2026-03-09
Status: Live prototype (2 orgs) โ†’ Production SaaS roadmap


Table of Contents

  1. Product Vision
  2. Core Thesis
  3. Current State (v0.1)
  4. Target Architecture
  5. System Components
  6. Data Model
  7. Persona & Style Guide System
  8. Messaging Layer
  9. Security & Multi-tenancy
  10. Phase Roadmap
  11. Open Decisions
  12. Guiding Principles

1. Product Vision

PromptCMS is a website CMS delivered entirely through messaging โ€” WhatsApp, Telegram, Slack โ€” without any web interface, login, or training for end users.

A sea scout leader wants to update the club's meeting time. They open WhatsApp, message the group bot ("Skipper, change Tuesday meeting to 7pm"), and the site is updated, previewed, and deployed live within seconds. No browser tabs. No passwords. No asking the volunteer webmaster who set the site up three years ago.

The product targets community organisations โ€” scout groups, nature associations, sports clubs, school committees, neighbourhood groups โ€” who have websites they rarely update because the tools are too cumbersome for non-technical volunteers.

The long-term vision is a platform: an operator signs up, provides their organisation's details, and within minutes has a live website with a conversational bot their members can talk to. The bot knows the organisation's visual identity, speaks in the org's voice, and can handle everything from updating event dates to redesigning sections โ€” escalating to a human preview for anything significant.


2. Core Thesis

The problem with existing CMSes is not feature gaps โ€” it's friction. WordPress, Squarespace, and Wix all work, but they require logging in, navigating dashboards, and understanding layout concepts. For a volunteer running a monthly meeting, that friction means the site goes stale.

The PromptCMS thesis: the messaging app is already open. Community groups already coordinate via WhatsApp. If the website bot lives there too, the update cost drops to near zero โ€” and websites actually stay current.

The differentiator: the "persona" approach. The bot isn't a generic assistant โ€” it's Skipper for sea scouts, Ranger for a nature association. That identity makes the bot feel native to the organisation, not like a SaaS tool bolted on. Members engage with it like they would a helpful committee member.

The key technical insight: the hard work (understanding intent, generating valid code, deploying correctly) happens invisibly in the cloud. The user experience is just: type a message, get a preview link, send thumbs-up.


3. Current State (v0.1)

What exists

Two live deployments, both fully operational end-to-end:

Site Persona Stack Status
Brighton 1st/14th Sea Scouts Skipper โš“ Plain HTML/CSS (no build) โœ… Live, tested
Yalukit Willam Nature Association Ranger ๐ŸŒฟ React/Vite/TypeScript โš ๏ธ Configured โ€” bot not yet in WA group

How it works today

WhatsApp message
    โ†’ Twilio webhook
    โ†’ OpenClaw gateway (Mac Mini, Mimisbrunnr)
    โ†’ Mimir AI (Claude Sonnet, adopts persona via SITE.json)
    โ†’ Makes file edits directly on disk
    โ†’ Deploys preview via wrangler CLI
    โ†’ Replies in WhatsApp with preview URL
    โ†’ Admin says "deploy" โ†’ merges to main โ†’ production deploy

Configuration model

Each site is defined by a SITE.json file:

{
  "slug": "brighton-scouts",
  "title": "Brighton 1st/14th Sea Scouts",
  "cfProject": "brighton-sea-scouts",
  "workDir": "/Users/mimisbrunnr/workspace/brighton-scouts",
  "buildCmd": null,
  "distDir": ".",
  "gitRepo": "https://github.com/andrew-julian/brighton-sea-scouts.git",
  "siteUrl": "https://brighton-sea-scouts.pages.dev",
  "channels": [
    { "type": "whatsapp", "id": "120363425375840006@g.us", "admins": ["+61412345678"] }
  ],
  "persona": {
    "name": "Skipper โš“",
    "greeting": "Hi! I'm Skipper...",
    "style": "friendly, concise, community-spirited."
  }
}

Routing: promptcms/index.json maps channel IDs โ†’ site slugs. When a message arrives, Mimir checks this index to know which persona to adopt.

Current limitations


4. Target Architecture

The production system has three tiers that handle different levels of complexity:

User message (WhatsApp / Telegram / Slack)
         โ”‚
         โ–ผ
โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚  TIER 1 โ€” Fast-Path Worker                  โ”‚
โ”‚  Cloudflare Worker (edge, ~100ms)           โ”‚
โ”‚  โ€ข Classify request (simple vs complex)     โ”‚
โ”‚  โ€ข Simple path: extract data โ†’ update D1   โ”‚
โ”‚    โ†’ trigger Pages rebuild โ†’ reply "Done"  โ”‚
โ”‚  โ€ข Complex path: queue Tier 2 job          โ”‚
โ”‚    โ†’ reply "Working on it, few mins..."    โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜
               โ”‚ (complex requests only)
               โ–ผ
โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚  TIER 2 โ€” Async Agentic Engine              โ”‚
โ”‚  Cloud-hosted coding agent                 โ”‚
โ”‚  โ€ข Reads site repo via GitHub API          โ”‚
โ”‚  โ€ข Makes targeted HTML/CSS/JS changes      โ”‚
โ”‚  โ€ข Creates PR โ†’ preview deploy             โ”‚
โ”‚  โ€ข Notifies user with preview URL          โ”‚
โ”‚  โ€ข User approves โ†’ Worker merges โ†’ live    โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜
               โ”‚ (site setup, persona design, schema)
               โ–ผ
โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚  TIER 3 โ€” Operator / Admin (Mimir)          โ”‚
โ”‚  Setup, configuration, style guide gen     โ”‚
โ”‚  โ€ข Onboard new orgs                        โ”‚
โ”‚  โ€ข Design/update templates                 โ”‚
โ”‚  โ€ข Handle edge cases + escalations         โ”‚
โ”‚  Not in any real-time user-facing path     โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜

What each tier handles

Tier 1 (instant, ~seconds)

Tier 2 (async, 2โ€“5 minutes)

Tier 3 (human-in-the-loop, no SLA)


5. System Components

5.1 Cloudflare Worker (Tier 1)

The primary runtime for all user-facing interactions. Runs at the edge globally.

Responsibilities:

Request classification (within Worker):

Simple triggers:
  - "change the meeting time to..."
  - "update the contact email to..."
  - "add an event: [title, date, description]"
  - "remove the [event name] event"
  - "update the announcement to say..."

Complex triggers:
  - "add a photo gallery section"
  - "make the hero more welcoming"
  - "redesign the events section layout"
  - "add a new page for..."
  - "change the colour scheme to..."
  - Any request mentioning layout, design, sections, pages

Classification is a lightweight AI call (cheap model, structured output) at the start of every request.

Worker hardening (Phase 3):

5.2 Agentic Engine (Tier 2)

An async coding agent triggered by the Worker's job queue. Runs in cloud (Cloudflare Durable Object or dedicated serverless function).

Tool registry (6 tools, no more):

Tool Purpose
read_file(path) Read any file in the site repo via GitHub API
write_file(path, content) Create or overwrite a file; auto-commits to working branch
list_files(path?) List directory contents; called at job start to map site structure
search_content(query) Find sections by text or data-section attribute
create_pr(title, body) Open PR from working branch; triggers Cloudflare Pages preview deploy
notify(channel, message) Send WhatsApp/Telegram message to requesting user

The agent cannot: browse the web, run shell commands, access other orgs' repos, modify its own system prompt, or call any API beyond this list.

Execution isolation:

Job serialisation per org:

Execution flow:

  1. Receive job from queue (org ID, repo, raw request, style guide, notify target)
  2. list_files() โ†’ map site structure
  3. read_file() on relevant files โ†’ understand current state
  4. Plan the change internally
  5. write_file() to implement (may loop back to verify)
  6. read_file() on modified files โ†’ verify output
  7. create_pr() โ†’ preview deploy auto-triggers
  8. notify() user with preview URL and approval prompt
  9. Wait for approval webhook (Worker receives thumbs-up โ†’ merges PR โ†’ live)

Preview lifecycle:

5.3 Content Layer (Cloudflare D1 + Pages)

D1 database stores all multi-tenant state:

Cloudflare Pages serves all sites:

5.4 GitHub (Source of Truth)

All site files live in GitHub repos:

5.5 Messaging Layer (Twilio)

5.6 Observability Layer

Instrumented from Phase 3 onwards. All metrics stored in D1 and surfaced in the operator dashboard (Phase 5).

Per-org metrics tracked:

These metrics enable "profile first, then optimise" โ€” routing decisions, model selection, and cost caps should be tuned based on real data, not assumptions.

5.7 Operator Dashboard (future Phase 5)

A web UI for Andrew (or future operators) to:


6. Data Model

Organisation

type Organisation = {
  id: string;                     // "brighton-scouts"
  name: string;                   // "Brighton 1st/14th Sea Scouts"
  status: "active" | "inactive" | "onboarding";
  created_at: string;
  
  // Messaging
  whatsapp_number: string;        // Twilio provisioned number
  telegram_bot_token?: string;
  notify_channels: NotifyChannel[];
  
  // Site
  site_repo: string;              // GitHub repo URL
  cf_project: string;             // Cloudflare Pages project name
  site_url: string;               // Production URL
  preview_url_template: string;   // "{branch}.cf-project.pages.dev"
  build_cmd: string | null;       // null = no build step
  dist_dir: string;               // "." or "dist/public"
  
  // Style
  style_guide: StyleGuide;        // See ยง7
  persona: Persona;               // See ยง7
  
  // Admins
  admin_contacts: string[];       // Phone numbers who can approve deploys
  
  // Limits
  monthly_token_cap?: number;     // Hard spend cap; null = operator default
  tier2_rate_limit?: number;      // Max Tier 2 jobs per day
};

Conversation History (per org)

Design principle: Intra-org shared context is a feature, not a bug. Multiple committee members should be able to interact with the same bot and have it understand what's already been done. Skipper should know that Person A just changed the meeting time, so when Person B asks "what events do we have?", the answer is current.

Conversation context is scoped by org_id only โ€” the whole organisation shares one bot context.

type ConversationMessage = {
  id: string;
  org_id: string;                 // Isolation boundary: between orgs, not between users
  sender_phone: string;           // Attributed for audit, not for isolation
  role: "user" | "assistant";
  content: string;
  created_at: string;
};

Context window policy:

Content Schema (per org)

Rather than the AI editing HTML directly, simple content is stored structurally:

type ContentSchema = {
  org_id: string;
  sections: {
    [sectionName: string]: SectionSchema;
  };
};

type SectionSchema = {
  type: "list" | "single" | "richtext" | "contacts";
  label: string;
  items?: ContentItem[];  // for type=list
  value?: string;         // for type=single or richtext
};

// Example for Brighton Sea Scouts:
// sections.events = { type: "list", label: "Events", items: [...] }
// sections.announcement = { type: "single", label: "Notice board", value: "..." }
// sections.contact_email = { type: "single", label: "Contact", value: "..." }

For Tier 1 simple updates, the Worker updates D1 content directly and triggers a template rebuild โ€” the HTML is generated from the template + content data, not edited directly.

Job (Tier 2)

type AgentJob = {
  id: string;
  org_id: string;
  status: "queued" | "running" | "awaiting_approval" | "approved" | "discarded" | "failed" | "expired";
  
  request: string;                // Raw user message
  requested_by: string;           // Phone number
  queued_at: string;
  started_at?: string;
  completed_at?: string;
  expires_at?: string;            // Set when status=awaiting_approval; 48h from pr creation
  
  pr_url?: string;
  preview_url?: string;
  pr_number?: number;
  
  change_summary?: string;        // Agent's description of what it did
  token_usage?: { input: number; output: number; };  // For billing/observability
  error?: string;                 // If status=failed
  
  approval_token: string;         // Used to verify thumbs-up came from right user
};

7. Persona & Style Guide System

This is PromptCMS's key differentiator. The bot isn't a generic assistant โ€” it has a specific identity that matches the organisation.

Persona

type Persona = {
  name: string;           // "Skipper โš“" / "Ranger ๐ŸŒฟ"
  greeting: string;       // First message sent when bot joins group
  style: string;          // Writing style guidance for the AI
  theme: string;          // "nautical" / "nature / conservation"
  avoid: string[];        // ["corporate", "jargon", "markdown headers"]
  emoji_use: "sparse" | "moderate" | "none";
};

The persona is injected as Layer 1 of the system prompt in all AI interactions. The bot never breaks character, never mentions OpenClaw, and never identifies as an AI unless directly asked.

Style Guide

Generated once at site onboarding. Stored per-org in D1. Injected as Layer 2 of the Tier 2 agent's system prompt โ€” it's the agent's visual bible for that site.

Style Guide safety: before a Style Guide is stored in D1 or injected into a system prompt, it is:

  1. Validated against the StyleGuide schema (structured JSON, not freeform text)
  2. Sanitised to remove any instruction-like patterns (e.g. strings containing "ignore", "system prompt", "instructions")
  3. Tested in a sandbox prompt before being activated for live requests

This prevents both accidental ambiguity and deliberate prompt injection via admin-uploaded style data.

type StyleGuide = {
  colours: {
    primary: string;       // "#1B3A6B"
    secondary: string;     // "#F0A500"
    background: string;
    text: string;
    accent: string;
  };
  typography: {
    heading_font: string;  // "Montserrat"
    body_font: string;     // "Open Sans"
    base_size: string;     // "16px"
    scale_ratio: string;   // "1.25"
  };
  layout: {
    feel: string;          // "clean, outdoorsy, family-friendly"
    whitespace: string;    // "generous"
    imagery: string;       // "nature, community, action"
    mobile_first: boolean;
  };
  brand_voice: {
    tone: string;          // "warm, active"
    avoid: string[];       // ["corporate", "jargon"]
    cta_style: string;     // "imperative"
  };
  technical: {
    stack: string;         // "plain HTML/CSS/JS"
    build_step: boolean;
    css_vars: boolean;
    image_path: string;    // "/images/"
    protected_files: string[];
  };
  site_structure: {
    sections: string[];    // ["hero", "about", "events", "gallery", "contact"]
    nav_style: string;     // "sticky-top"
    footer: boolean;
    section_id_attr: string;  // "data-section"
  };
};

The 5-Layer System Prompt (Tier 2)

Clear separation between what the agent can do (L1, L4, L5 โ€” static, same for all orgs) and how it presents (L2, L3 โ€” dynamic, per org). This consistency is essential for predictable debugging and testing across orgs.

Layer Content Static/Dynamic
L1 Core identity: who the agent is, what its mandate is Static โ€” identical across all orgs
L2 Site context: Style Guide (palette, typography, voice, structure) Dynamic โ€” per org from D1
L3 Technical constraints: stack, protected files, conventions Semi-static โ€” per org
L4 Execution approach: read before writing, minimal changes, verify Static โ€” identical across all orgs
L5 Safety rules: always branch, always preview, never delete without explicit instruction Static โ€” identical across all orgs

Persona Library (future)

A curated library of persona templates organisations can choose from:

Persona Theme Suitable for
Skipper Nautical / sea scouts Scout groups, sailing clubs
Ranger Nature / conservation Environmental groups, nature associations
Coach Active / sporting Sports clubs, fitness groups
Bloom Garden / community Garden clubs, community gardens
Spark Educational / youth School committees, youth groups
Anchor Civic / community Neighbourhood groups, ratepayer associations

Orgs can use a template as-is, customise the name, or (premium) have a fully custom persona designed.


8. Messaging Layer

Supported Channels (current + planned)

Channel Status Notes
WhatsApp (Twilio) โœ… Live Requires Twilio compliance bundle per number
Slack โœ… Live Via OpenClaw โ€” migrates to Worker in Phase 3
Telegram Planned Phase 4 Simpler compliance, no per-number bundle
SMS Future Fallback for orgs without smartphones

WhatsApp Compliance

WhatsApp Business API via Twilio requires:

Current status: Andrew's personal number +61 485 033 211 is provisioned. Compliance bundle BUead167ea... submitted. Once approved, orgs get dedicated numbers provisioned programmatically via Twilio API.

At scale, dedicated numbers are provisioned automatically on org signup via Twilio's REST API:

// Pseudo-code: provision number on org signup
const number = await twilio.incomingPhoneNumbers.create({
  areaCode: '61',
  smsApplicationSid: APP_SID,
  voiceApplicationSid: APP_SID,
});
await db.organisations.update(orgId, { whatsapp_number: number.phoneNumber });

Message Routing

Inbound WhatsApp message
    โ†’ Twilio webhook โ†’ POST /webhook/whatsapp
    โ†’ Worker validates Twilio signature
    โ†’ Lookup org: SELECT * FROM orgs WHERE whatsapp_number = ?
    โ†’ Load org config, style guide, persona
    โ†’ Load conversation history: last 50 messages for this org_id
    โ†’ Process request (Tier 1 or queue for Tier 2)
    โ†’ Reply via Twilio REST API

Admin vs Member Actions

Any group member can: request content changes, ask what the site looks like, ask for help.

Only admins can: approve deploys, discard previews, make admin-level config changes.

Admin detection: phone numbers in org.admin_contacts. Future: any WhatsApp group admin auto-detected via Twilio Group Management API.


9. Security & Multi-tenancy

Isolation Model

PromptCMS achieves multi-tenant isolation through data architecture, not container-per-customer infrastructure. This scales to thousands of orgs without proportional operational overhead.

Between-org isolation (strict):

Within-org context sharing (by design):

Approval Security

The approval flow (thumbs-up โ†’ live deploy) requires:

  1. Message sender is in org.admin_contacts
  2. approval_token matches the pending job (prevents replay attacks)
  3. Job status is awaiting_approval (not already approved/discarded)

Audit Trail

Every AI-generated change:

Rollback

Any admin can request rollback conversationally: "Skipper, undo the last change"

Implementation: git revert on the last merge commit โ†’ new PR โ†’ preview โ†’ approval โ†’ live. The same preview-before-publish flow applies to rollbacks โ€” no changes bypass human review.

Secrets Management


10. Phase Roadmap

Phase 0 โ€” Foundation โœ… COMPLETE

What was built:

Proved: the core thesis works. A non-technical user can update a live website by typing a message in WhatsApp.


Phase 1 โ€” YWNA Activation

Goal: Both sites fully operational. Validate the second deployment proves the config model, not a one-off.

Tasks:

Exit criteria: An YWNA team member (not Andrew) successfully deploys a change end-to-end.


Phase 2 โ€” Robustness

Goal: The current architecture is reliable enough to onboard a third org without Andrew babysitting it.

Tasks:

Exit criteria: Can add a third org by following the playbook, zero OpenClaw debugging required.


Phase 3 โ€” Architecture Migration (Decouple from Mac Mini)

Goal: The Mac Mini and Mimir are removed from the real-time message handling path. All live user interactions run in cloud infrastructure.

This is the most significant engineering phase โ€” it's a full re-architecture, not an extension of the current system.

Part A โ€” Cloudflare Worker (Tier 1)

Build the fast-path Worker:

  1. Twilio webhook handler with signature verification
  2. Org lookup: D1 database (SELECT * FROM orgs WHERE whatsapp_number = ?)
  3. Request classifier: cheap AI call โ†’ "simple" or "complex"
  4. Simple path: AI extracts structured data (event date, text, etc.) โ†’ writes to D1 content store โ†’ triggers Pages rebuild โ†’ replies "Done โœ…"
  5. Complex path: enqueues Tier 2 job โ†’ replies "Working on it, usually a few minutes"
  6. Approval handler: receives thumbs-up โ†’ verifies โ†’ merges PR via GitHub API โ†’ fires production deploy
  7. Worker hardening: rate limiting, cost caps, circuit breakers, and graceful Tier 2 degradation (see ยง5.1)

Part B โ€” D1 Multi-tenant Database

Schema:

Data migration:

Part C โ€” Site Template Architecture

For Tier 1 to work (updating content data without touching HTML), sites need a clean content/template separation:

Note: This requires rebuilding (or adapting) the Brighton and YWNA sites. Brighton (pure HTML) is straightforward โ€” extract content into JSON, generate the HTML via template. YWNA (React/Vite) is more complex โ€” and the strong recommendation is to rebuild it as plain HTML (see ยง11 Open Decisions).

Part D โ€” Cut Over

Exit criteria: Both sites receiving and processing messages with zero Mac Mini involvement. Mimir not required for any user-facing operation.


Phase 4 โ€” Tier 2 Agentic Engine

Goal: Complex layout and design requests (new sections, visual redesign, substantial page changes) handled by an async cloud agent, not Mimir.

What gets built:

  1. Job queue: Cloudflare Queues or Durable Object for async job management, with per-org serialisation (max 1 active Tier 2 job per org)
  2. Agentic runtime: Cloudflare Durable Objects (one DO instance per job, isolated)
  3. GitHub integration: read/write via GitHub API with per-org scoped tokens
  4. Agent system prompt: 5-layer architecture (see ยง7)
  5. Style Guide injection: L2 of system prompt loaded from D1 per org, sanitised before injection
  6. Preview flow: PR creation โ†’ Cloudflare Pages preview โ†’ notify user
  7. Approval webhook: Worker receives thumbs-up โ†’ verifies approval token โ†’ merges PR โ†’ tags last-known-good
  8. Preview expiry: 48h TTL with 24h nudge notification; expired jobs are auto-discarded and branch cleaned up

Style Guide generation at onboarding:

When a new org is set up, Andrew (or eventually a self-service wizard) runs a Style Guide generation step:

  1. Provide the site's existing URL or design brief
  2. AI analyses the site (or brief) and generates a structured Style Guide JSON
  3. Review and adjust
  4. Sanitise and validate
  5. Store in D1 โ€” this is L2 for all future Tier 2 jobs for that org

Exit criteria: A complex request ("add a photo gallery above the events section, make it feel more outdoorsy") is handled end-to-end without Mimir involvement. User receives a preview URL within 5 minutes.


Phase 5 โ€” Scale & Polish

Goal: The system handles multiple orgs reliably. Onboarding a new org is a defined, documented process that doesn't require Andrew's time per site.

Features:

Exit criteria: A new org can be fully onboarded without Andrew writing any code or config.


Phase 6 โ€” Product

Goal: PromptCMS is a sellable SaaS. External operators (orgs, agencies) pay for the service.

Features:

Strategic option โ€” vertical play:

Rather than broad horizontal SaaS, consider approaching a national body (Scouts Australia, Surf Life Saving Australia, Tennis Australia) to endorse PromptCMS to their member clubs. One partnership โ†’ hundreds of orgs, zero marketing spend. This is the fastest path to meaningful scale and a defensible position.

Exit criteria: 10+ paying orgs. Positive unit economics. System runs without Andrew's operational involvement.


11. Open Decisions

Decision Options Recommended Blocker for
YWNA persona name Keep "Ranger" / "Willy Wagtail" / other Decide with YWNA team Phase 1
Site template architecture Separate content/template vs keep editing HTML Content/template separation Phase 3
Tier 2 runtime Cloudflare Durable Objects vs separate VPS Durable Objects (simpler ops, better isolation) Phase 4
Distribution model Horizontal SaaS vs vertical (national body deal) Vertical first Phase 6
Telegram support Twilio vs direct Telegram Bot API Direct Telegram API (simpler) Phase 4
Agency tier pricing Flat per-org vs per-message Flat per-org Phase 6
Style Guide generation Manual (Andrew) vs AI-assisted wizard AI-assisted via Mimir Phase 4
YWNA React complexity Keep React build / rebuild as plain HTML Plain HTML (simpler, faster, more reliable, uniform stack) Phase 3
Conversation history window 50 messages vs 7 days vs both Both (whichever is smaller) Phase 3
Preview expiry duration 24h / 48h / 72h 48h with 24h nudge Phase 4

Note on YWNA: The React/Vite/TypeScript stack was appropriate for a full-stack app but adds significant complexity for a primarily-static community website. Strong recommendation to rebuild YWNA as plain HTML/CSS (like Brighton) during Phase 3 โ€” the Tier 1 content/template model is much simpler without a build step, and keeping a uniform stack means the Tier 2 agent behaves predictably across all orgs with no special-casing.


12. Guiding Principles

Reliability over features. A bot that sometimes fails destroys trust with community volunteers who have little patience for technology issues. Every phase must leave the system more reliable than it found it.

The user experience is the conversation. Every design decision should be evaluated against the experience of a 60-year-old committee member opening WhatsApp and typing a message. If it makes that interaction more confusing, it's wrong regardless of how elegant it is technically.

Preview before publish, always. No AI change should ever go live without a human seeing it first. This is non-negotiable at every tier and every phase. The preview-then-approve flow is the product's trust mechanism. This applies equally to rollbacks.

Persona is identity, not decoration. The fact that the bot is Skipper (not "PromptCMS Bot") is what makes members engage with it naturally. Every product decision that dilutes the persona weakens the product. Protect it.

Mac Mini is the prototype, not the product. The current architecture works and taught us everything we need to know. It is not a foundation to build on โ€” it's a proof of concept to replace. Phase 3 exists to make this clean.

Content and code are different jobs. The AI's job in Tier 1 is to update data. The AI's job in Tier 2 is to edit code. These require fundamentally different architectures. Never conflate them โ€” keep the separation clean.

Isolation through architecture, not infrastructure. Multi-tenancy is achieved via scoped tokens, org_id-filtered queries, and per-job Durable Object instances โ€” not container-per-customer. This scales to thousands of orgs without proportional operational overhead.

Within-org context is shared by design. The bot is a shared resource for a shared website. Intra-org conversation history enables collaboration (Person A changes the meeting time; Person B asks what's on โ€” the bot knows). Isolation boundaries are between orgs, not between members.

Measure before optimising. Instrument observability from Phase 3. Routing decisions, model selection, and cost caps should be tuned on real data โ€” not assumptions. The Tier 1 classifier is the biggest cost lever at scale; you need to see it before you can tune it.

Mimir moves up the stack. In the target architecture, Mimir (and Andrew) do the high-value, high-judgment work: onboarding, persona design, Style Guide generation, edge case handling. Not the repetitive, latency-sensitive work of answering WhatsApp messages. That's what the cloud infrastructure is for.


This document is a living spec. Update it as architectural decisions are made, phases complete, and the product evolves. The goal is that at any point, a developer could read this document and understand exactly what PromptCMS is, where it stands, and what needs to be built next.