A multi-tenant social media management SaaS that goes from "what should we post?" to "it's live on nine networks" without the operator touching a calendar.
Project: S0cial Master (a.k.a. S0cial-Master)
Path: /Users/hollyiq600/projects/S0cial-Master
Dev server: runs on port 5001 (Express + Vite middleware in one process)
Status at capture: live, real dev DB, one brand ("My Brand") with real AI-generated posts in flight
S0cial Master is a single-tenant-per-brand social media management platform built around three content-generation modes that share the same AI quality spine:
generateFullContent flag upgrades each idea to a full draft with quality flags per post.Every generated draft passes through the same quality layer before it reaches the UI: hook scoring, banned-phrase match, length sanity, CTA presence, and hook uniqueness against the last 30 hooks for that brand. The hook is auto-regenerated up to a configurable number of retries if the score is below threshold.
package.json and AGENTS.md)| Layer | Tech |
|---|---|
| Runtime | Node 20+, TypeScript 5.6 strict, tsx |
| Server | Express 5, Drizzle ORM, multer, ws |
| Client | React 18, Vite 7, Tailwind 3, Radix UI, TanStack Query, Wouter (hash routing) |
| Database | SQLite locally (data/postflow.db), PostgreSQL when DATABASE_URL is set, dual-mode schema bootstrap |
| LLM providers | Perplexity (default), OpenRouter, MiniMax (Anthropic-compatible at api.minimax.io/anthropic) |
| Image generation | MiniMax image-01 (default) or OpenRouter, Cloudinary for hosting |
| Auth | Single-user, SHA-256 hash, in-memory session set, dev auth bypass |
| Deployment | Railway (Procfile), health check at /api/health |
| Tests | Playwright e2e (tests/*.spec.ts) |
S0cial-Master/
├── client/src/
│ ├── App.tsx
│ ├── pages/ # 19 page components, one per feature
│ ├── components/ # 17 feature components
│ ├── components/ui/ # 47 Radix-wrapped primitives
│ ├── components/inbox/ # 5 inbox components
│ ├── components/previews/ # 5 post-preview helpers
│ ├── components/studio/ # PostCard
│ ├── hooks/ # use-auth, use-brand, use-breadcrumbs, use-toast
│ └── lib/ # queryClient, utils
├── server/
│ ├── index.ts # Entry: .env, migrations, routes, scheduler, workers, listen
│ ├── routes/ # 17 route files (largest: content.ts, autopilot.ts, brands.ts, analytics.ts)
│ ├── lib/ # 30 files: brand-context, slop-check, system-prompt,
│ │ # scheduling-engine, publish-orchestrator, providers/, search/
│ ├── adapters/ # 8 per-platform publish adapters
│ ├── services/ # apify, reddit, youtube
│ ├── workers/ # publishWorker, syncWorker
│ ├── scheduler.ts # 5 timers, 60s intervals
│ ├── storage.ts # single DatabaseStorage class (60+ methods)
│ ├── db.ts # SQLite CREATE TABLEs
│ ├── image_gen.py # FastAPI microservice on :5050
│ └── static.ts / vite.ts # Prod vs dev serving
├── shared/
│ ├── schema.ts # 30+ pgTable definitions
│ └── quality-rules.ts # 8 quality rules + 24 default banned phrases
├── tests/ # 13 Playwright specs + helpers + global-setup
├── docs/ # ENV, SCHEMA, META_ANALYTICS_PLAN, metrics_learning_v1, plan-social-listening
├── scripts/ # 12 migration/maintenance scripts
├── .agents/skills/ # Project-local skills
├── AGENTS.md
├── CHANGELOG.md # 280KB of dated Problem, Fix, Verification, Files Modified entries
└── package.json
All content generation runs through three modules. New generation endpoints are expected to call all three.
server/lib/brand-context.tsRenders a token-safe block for any brand: basics, voice examples (capped at 3), knowledge base (capped at 12, 60 percent of maxChars budget), image preferences, news sources, watch URLs. Default cap 6000 chars. Each section togglable.
server/lib/system-prompt.tsRenders the system-role prompt for content generation. Layers in order: role statement, BRAND block, VOICE EXAMPLES, QUALITY_RULES (8 rules), BANNED PHRASES (24 defaults plus brand override plus extras).
8 quality rules:
server/lib/slop-check.tscheckDraftQuality(input) returns a QualityReport with five checks: length sanity, banned-phrase match, hook presence, CTA presence (info flag), hook uniqueness (Jaccard similarity above 0.7 against last 30 brand hooks, 5min TTL cache).
Score: 1 minus blocks*0.5 minus warns*0.1, clamped to [0, 1]. passed=false only on block severity.
| Module | What it does |
|---|---|
top-performers.ts |
Pulls top 3 plus worst 2 historical posts, renders as BEST EXAMPLES / AVOID block |
reader-persona.ts |
Per-post persona LLM call: real human (name, role, pain point), or deterministic fallback from audienceProfile |
meeting-research.ts |
Per-post Google News RSS fetch, top 5 headlines for topic plus brand industry |
hook-scorer.ts |
Deterministic 0-1 hook strength scorer, auto-iterate weak hooks via LLM with feedback |
kb-retrieval.ts |
RAG-style keyword-overlap retrieval of top 7 KB entries relevant to a post idea |
meeting-content.ts |
The orchestrator. generateFullDraftForPostIdea composes all five in parallel, calls LLM, runs hook-scorer and slop-check, returns structured result with skippedSources so the UI can explain which context sources were empty |
Each helper is independent and v1-simple with a clean upgrade path. Deterministic swap-in for embeddings, LLM-as-judge, Tavily/Perplexity.
server/lib/publish-orchestrator.ts (single chokepoint)For each publish action:
auto_publish / notification_publish / manual_completion_required) from platform-capabilities.ts.post_now calls the per-platform adapter in server/adapters/schedule stores the post with scheduledForqueue adds to position-based queuesend_for_approval creates approval requestposted / partially_published / needs_attention / notification_required / failed / ready.facebook, instagram, linkedin, x, tiktok, bluesky, youtube, placeholder. Each implements publishToplatform(post): Promise<{ published, platformPostId?, url?, error? }>.
Adapter availability at capture:
adapterAvailable: true: facebook, instagram, linkedin, blueskyadapterAvailable: false: tiktok, x, youtube, reddit, threads (publish paths stubbed or manual_completion_required)getOptimalSlots(brandId, platform, pillar?, options) returns scored TimeSlots from: best practice (built-in peak-hours table), analytics-based (currently returns empty, TODO), pillar preferences (educational Tue-Thu, promotional Friday, etc.), premium boost (x1.2 if peak hour for strategy-created posts).
server/scheduler.ts): 5 timers on 60s intervals (publisher check, queue processor, inbox sync check, analytics sync check, daily news check).server/workers/): publishWorker (10s poll), syncWorker (60s poll). Both atomically claim pending jobs from the jobs table, exponential backoff (1min, 5min, 30min, 2hr).A public landing page at / lives outside the auth wall (added 2026-06-09). Behind auth, the dashboard moved to /dashboard. Sections on the landing page, all in client/src/pages/index.tsx: Nav, Hero, MCPDemo, Bento, HowItWorks, Platforms, CalendarShot, CodeSection, Pricing, APIKeyForm, Footer.
Three JSON-LD schemas in client/index.html: SoftwareApplication with full featureList matching the product, Organization, FAQPage with 4 questions. OG/Twitter card uses dashboard.png as og:image.
All product visuals in the landing are real captures from the running app in client/public/screenshots/. No div-based fake mockups. Capture scripts in scripts/capture-*.mjs use Playwright with a fresh context (no cookies, no auth) at deviceScaleFactor: 2.
Default admin for the login dialog on the landing page is admin@example.com / changeme, env-overridable via ADMIN_EMAIL and ADMIN_PASSWORD.
The project ships its own MCP server at mcp-server.py (repo root). Hermes loads it via ~/.hermes/config.yaml and exposes 13 tools, including:
list_brands, get_brand, list_posts, get_postget_brand_kb, get_top_performerscreate_post, update_post, delete_postgenerate_post (THE USP, wraps POST /api/meeting/save-as-plan with the orchestrator and image gen)regenerate_post (wraps POST /api/posts/:id/regenerate)research_news, summarize_contextSame data model for the MCP server and the app. Drop the MCP server into Claude or Cursor and ship the same day.
| Area | Status |
|---|---|
| Twitter/X publish | adapter exists, auth flow unwired, deliveryMode: "manual_completion_required" |
| TikTok publish | same |
| YouTube publish | same |
| Reddit, Threads publish | manual_completion_required |
| Engagement metrics | documented stub in docs/metrics_learning_v1.md, plausible random numbers (comment-marked in server/routes/engagement.ts) |
| Meta demographics | March 2024 deprecation, listed fixable items were already fixed |
| Social listening | only Reddit + Bluesky have direct adapters, everything else is LLM-mediated web search |
| Brand timezone | hardcoded to GMT (TODO in scheduling-engine.getBrandTimezone()) |
| Analytics-based scheduling | getAnalyticsBasedSlots() returns empty (TODO) |
engagement_events.brand_id column |
missing from SQLite bootstrap. Result: top-performers.ts returns empty silently via the safeGet wrapper. Documented as known issue. |
All screenshots below are real captures of the running dev server at localhost:5001 taken on 2026-06-18 with Playwright at 1440x900 viewport, deviceScaleFactor: 2. Files saved to this folder.
| File | Description |
|---|---|
live-landing.png |
Full marketing landing page, top to bottom (2880 x 18202 px). Hero, MCP demo, bento, how-it-works, platforms, calendar shot, code section, pricing, API key form, footer. |
live-landing-features.png |
Features anchor section on the landing page. |
live-landing-workflow.png |
Workflow anchor section, full walkthrough page. |
live-landing-pricing.png |
Pricing anchor section. |
marketing-landing-hero.png |
Hero crop only. |
marketing-landing-features.png |
Bento + MCP demo section crop. |
marketing-landing-mcp.png |
MCP demo card crop. |
marketing-landing-workflow-teaser.png |
Workflow teaser crop. |
marketing-landing-platforms.png |
Platforms strip crop. |
marketing-landing-pricing.png |
Pricing card crop. |
| File | Description |
|---|---|
live-dashboard.png |
LCARS dashboard with S0CIAL tab, recent posts, stats. |
live-autopilot.png |
Chat-first autopilot with chat thread, quick-action chips, 3-column kanban (Ideas, Drafts, Ready to Schedule). |
live-calendar.png |
Content calendar with scheduled and failed posts visible (LinkedIn and X). |
live-meeting.png |
Monthly Meeting wizard. |
live-research.png |
Research Hub: web search, YouTube search, saved research. |
live-studio.png |
Content Studio: five-column kanban (Suggestions, Approved, Defining, Ready, Posted). |
live-analytics.png |
Analytics dashboard with charts. |
live-inbox.png |
Unified inbox for comments across connected networks. |
live-create.png |
Create-post modal surface. |
live-accounts.png |
Connected accounts management (Meta, LinkedIn, etc.). |
client/public/screenshots/)| File | Description |
|---|---|
marketing-autopilot.png |
Autopilot empty state. |
marketing-autopilot-with-input.png |
Autopilot with chat input visible. |
marketing-monthly-meeting.png |
Monthly Meeting main panel. |
marketing-strategy.png |
Strategy Hub surface. |
marketing-workflow-step-01.png, marketing-workflow-step-02.png, marketing-workflow-step-03.png |
Walkthrough step crops. |
brand-context.ts, system-prompt.ts, and slop-check.ts. No mode ships "raw LLM output to UI."meeting-content.ts returns skippedSources: string[] so the UI can render "drafted from KB + news (no top performers yet)" instead of hiding empty context sources.meeting/save-as-plan.DATABASE_URL is set.platform-capabilities.ts.mcp-server.py exposes 13 tools over stdio using the low-level mcp.server.stdio pattern (not FastMCP, which dies silently under Hermes supervisor).s0cial-master SKILL.md §17)engagement_events is missing the brand_id column. Top-performers silently returns empty for every brand. safeGet wrapper masks the bug at runtime.posts table field names are non-obvious. The orchestrator uses body and hook internally; the DB columns are content and hookTitle. Route layer does the mapping.document.getElementById("root") empty. Fix: kill -9 $(lsof -ti:5001) && npm run dev.NavAnchor pattern in client/src/pages/index.tsx./.models.ts are Perplexity-Computer-specific; the provider abstraction maps them for OpenRouter and MiniMax.# S0cial Master
A multi-tenant social media management SaaS that goes from "what should we post?" to "it's live on nine networks" without the operator touching a calendar.
**Project:** S0cial Master (a.k.a. S0cial-Master)
**Path:** `/Users/hollyiq600/projects/S0cial-Master`
**Dev server:** runs on port 5001 (Express + Vite middleware in one process)
**Status at capture:** live, real dev DB, one brand ("My Brand") with real AI-generated posts in flight
---
## At a glance
S0cial Master is a single-tenant-per-brand social media management platform built around three content-generation modes that share the same AI quality spine:
- **Manual (Research + Studio)**: operator pulls research, picks a hook, generates a full draft, approves it through a five-column kanban (Suggestions, Approved, Defining, Ready, Posted).
- **Monthly Meeting (semi-auto)**: operator states monthly goals, the AI returns a full strategy plus roughly 12 post ideas, distributed across the calendar in one click. Optional `generateFullContent` flag upgrades each idea to a full draft with quality flags per post.
- **Autopilot (full auto)**: chat-style AI that pulls research (news RSS, YouTube, Reddit, social listening), proposes ideas, expands them into drafts, generates images, and schedules them, with a configurable autonomy level (low / medium / high / full).
Every generated draft passes through the same quality layer before it reaches the UI: hook scoring, banned-phrase match, length sanity, CTA presence, and hook uniqueness against the last 30 hooks for that brand. The hook is auto-regenerated up to a configurable number of retries if the score is below threshold.
---
## Stack (verified from `package.json` and `AGENTS.md`)
| Layer | Tech |
|---|---|
| Runtime | Node 20+, TypeScript 5.6 strict, `tsx` |
| Server | Express 5, Drizzle ORM, multer, ws |
| Client | React 18, Vite 7, Tailwind 3, Radix UI, TanStack Query, Wouter (hash routing) |
| Database | SQLite locally (`data/postflow.db`), PostgreSQL when `DATABASE_URL` is set, dual-mode schema bootstrap |
| LLM providers | Perplexity (default), OpenRouter, MiniMax (Anthropic-compatible at `api.minimax.io/anthropic`) |
| Image generation | MiniMax `image-01` (default) or OpenRouter, Cloudinary for hosting |
| Auth | Single-user, SHA-256 hash, in-memory session set, dev auth bypass |
| Deployment | Railway (Procfile), health check at `/api/health` |
| Tests | Playwright e2e (`tests/*.spec.ts`) |
---
## Repository layout (251 files, verified)
```
S0cial-Master/
├── client/src/
│ ├── App.tsx
│ ├── pages/ # 19 page components, one per feature
│ ├── components/ # 17 feature components
│ ├── components/ui/ # 47 Radix-wrapped primitives
│ ├── components/inbox/ # 5 inbox components
│ ├── components/previews/ # 5 post-preview helpers
│ ├── components/studio/ # PostCard
│ ├── hooks/ # use-auth, use-brand, use-breadcrumbs, use-toast
│ └── lib/ # queryClient, utils
├── server/
│ ├── index.ts # Entry: .env, migrations, routes, scheduler, workers, listen
│ ├── routes/ # 17 route files (largest: content.ts, autopilot.ts, brands.ts, analytics.ts)
│ ├── lib/ # 30 files: brand-context, slop-check, system-prompt,
│ │ # scheduling-engine, publish-orchestrator, providers/, search/
│ ├── adapters/ # 8 per-platform publish adapters
│ ├── services/ # apify, reddit, youtube
│ ├── workers/ # publishWorker, syncWorker
│ ├── scheduler.ts # 5 timers, 60s intervals
│ ├── storage.ts # single DatabaseStorage class (60+ methods)
│ ├── db.ts # SQLite CREATE TABLEs
│ ├── image_gen.py # FastAPI microservice on :5050
│ └── static.ts / vite.ts # Prod vs dev serving
├── shared/
│ ├── schema.ts # 30+ pgTable definitions
│ └── quality-rules.ts # 8 quality rules + 24 default banned phrases
├── tests/ # 13 Playwright specs + helpers + global-setup
├── docs/ # ENV, SCHEMA, META_ANALYTICS_PLAN, metrics_learning_v1, plan-social-listening
├── scripts/ # 12 migration/maintenance scripts
├── .agents/skills/ # Project-local skills
├── AGENTS.md
├── CHANGELOG.md # 280KB of dated Problem, Fix, Verification, Files Modified entries
└── package.json
```
---
## The AI / quality spine
All content generation runs through three modules. New generation endpoints are expected to call all three.
### `server/lib/brand-context.ts`
Renders a token-safe block for any brand: basics, voice examples (capped at 3), knowledge base (capped at 12, 60 percent of maxChars budget), image preferences, news sources, watch URLs. Default cap 6000 chars. Each section togglable.
### `server/lib/system-prompt.ts`
Renders the system-role prompt for content generation. Layers in order: role statement, BRAND block, VOICE EXAMPLES, QUALITY_RULES (8 rules), BANNED PHRASES (24 defaults plus brand override plus extras).
**8 quality rules:**
1. Open with a strong specific hook (question, stat, quote, contrarian, story).
2. Never use a banned phrase.
3. Use the brand's voice.
4. Reference 1 to 2 concrete facts or examples.
5. End with a CTA that fits intent.
6. Respect platform char limit and hashtag policy.
7. Output ONLY the post (no preamble, no markdown fences).
8. Vary hooks, do not repeat recent ones.
### `server/lib/slop-check.ts`
`checkDraftQuality(input)` returns a QualityReport with five checks: length sanity, banned-phrase match, hook presence, CTA presence (info flag), hook uniqueness (Jaccard similarity above 0.7 against last 30 brand hooks, 5min TTL cache).
**Score:** `1 minus blocks*0.5 minus warns*0.1`, clamped to [0, 1]. `passed=false` only on `block` severity.
### Six content-quality helpers (added 2026-06-08 sprint)
| Module | What it does |
|---|---|
| `top-performers.ts` | Pulls top 3 plus worst 2 historical posts, renders as BEST EXAMPLES / AVOID block |
| `reader-persona.ts` | Per-post persona LLM call: real human (name, role, pain point), or deterministic fallback from `audienceProfile` |
| `meeting-research.ts` | Per-post Google News RSS fetch, top 5 headlines for topic plus brand industry |
| `hook-scorer.ts` | Deterministic 0-1 hook strength scorer, auto-iterate weak hooks via LLM with feedback |
| `kb-retrieval.ts` | RAG-style keyword-overlap retrieval of top 7 KB entries relevant to a post idea |
| `meeting-content.ts` | The orchestrator. `generateFullDraftForPostIdea` composes all five in parallel, calls LLM, runs hook-scorer and slop-check, returns structured result with `skippedSources` so the UI can explain which context sources were empty |
Each helper is independent and v1-simple with a clean upgrade path. Deterministic swap-in for embeddings, LLM-as-judge, Tavily/Perplexity.
---
## The publishing pipeline
### `server/lib/publish-orchestrator.ts` (single chokepoint)
For each publish action:
1. Validates (validation-engine: connected accounts, target readiness).
2. Checks quota per brand per platform (quota-tracker).
3. Determines delivery mode per target (`auto_publish` / `notification_publish` / `manual_completion_required`) from `platform-capabilities.ts`.
4. For each target:
- `post_now` calls the per-platform adapter in `server/adapters/`
- `schedule` stores the post with `scheduledFor`
- `queue` adds to position-based queue
- `send_for_approval` creates approval request
5. Derives parent status from per-target results: `posted` / `partially_published` / `needs_attention` / `notification_required` / `failed` / `ready`.
### Per-platform adapters
`facebook`, `instagram`, `linkedin`, `x`, `tiktok`, `bluesky`, `youtube`, `placeholder`. Each implements `publishToplatform(post): Promise<{ published, platformPostId?, url?, error? }>`.
**Adapter availability at capture:**
- `adapterAvailable: true`: facebook, instagram, linkedin, bluesky
- `adapterAvailable: false`: tiktok, x, youtube, reddit, threads (publish paths stubbed or `manual_completion_required`)
### Scheduling engine
`getOptimalSlots(brandId, platform, pillar?, options)` returns scored TimeSlots from: best practice (built-in peak-hours table), analytics-based (currently returns empty, TODO), pillar preferences (educational Tue-Thu, promotional Friday, etc.), premium boost (x1.2 if peak hour for strategy-created posts).
### Background processes
- **Scheduler** (`server/scheduler.ts`): 5 timers on 60s intervals (publisher check, queue processor, inbox sync check, analytics sync check, daily news check).
- **Workers** (`server/workers/`): `publishWorker` (10s poll), `syncWorker` (60s poll). Both atomically claim `pending` jobs from the `jobs` table, exponential backoff (1min, 5min, 30min, 2hr).
---
## The marketing surface
A public landing page at `/` lives outside the auth wall (added 2026-06-09). Behind auth, the dashboard moved to `/dashboard`. Sections on the landing page, all in `client/src/pages/index.tsx`: Nav, Hero, MCPDemo, Bento, HowItWorks, Platforms, CalendarShot, CodeSection, Pricing, APIKeyForm, Footer.
Three JSON-LD schemas in `client/index.html`: SoftwareApplication with full `featureList` matching the product, Organization, FAQPage with 4 questions. OG/Twitter card uses `dashboard.png` as `og:image`.
All product visuals in the landing are real captures from the running app in `client/public/screenshots/`. No div-based fake mockups. Capture scripts in `scripts/capture-*.mjs` use Playwright with a fresh context (no cookies, no auth) at `deviceScaleFactor: 2`.
Default admin for the login dialog on the landing page is `admin@example.com` / `changeme`, env-overridable via `ADMIN_EMAIL` and `ADMIN_PASSWORD`.
---
## The MCP layer
The project ships its own MCP server at `mcp-server.py` (repo root). Hermes loads it via `~/.hermes/config.yaml` and exposes 13 tools, including:
- `list_brands`, `get_brand`, `list_posts`, `get_post`
- `get_brand_kb`, `get_top_performers`
- `create_post`, `update_post`, `delete_post`
- `generate_post` (THE USP, wraps `POST /api/meeting/save-as-plan` with the orchestrator and image gen)
- `regenerate_post` (wraps `POST /api/posts/:id/regenerate`)
- `research_news`, `summarize_context`
Same data model for the MCP server and the app. Drop the MCP server into Claude or Cursor and ship the same day.
---
## What's stubbed vs real (verified against code)
| Area | Status |
|---|---|
| Twitter/X publish | adapter exists, auth flow unwired, `deliveryMode: "manual_completion_required"` |
| TikTok publish | same |
| YouTube publish | same |
| Reddit, Threads publish | `manual_completion_required` |
| Engagement metrics | documented stub in `docs/metrics_learning_v1.md`, plausible random numbers (comment-marked in `server/routes/engagement.ts`) |
| Meta demographics | March 2024 deprecation, listed fixable items were already fixed |
| Social listening | only Reddit + Bluesky have direct adapters, everything else is LLM-mediated web search |
| Brand timezone | hardcoded to GMT (TODO in `scheduling-engine.getBrandTimezone()`) |
| Analytics-based scheduling | `getAnalyticsBasedSlots()` returns empty (TODO) |
| `engagement_events.brand_id` column | missing from SQLite bootstrap. Result: `top-performers.ts` returns empty silently via the `safeGet` wrapper. Documented as known issue. |
---
## Screenshots
All screenshots below are real captures of the running dev server at `localhost:5001` taken on 2026-06-18 with Playwright at 1440x900 viewport, `deviceScaleFactor: 2`. Files saved to this folder.
### Marketing landing (public, no auth)
| File | Description |
|---|---|
| `live-landing.png` | Full marketing landing page, top to bottom (2880 x 18202 px). Hero, MCP demo, bento, how-it-works, platforms, calendar shot, code section, pricing, API key form, footer. |
| `live-landing-features.png` | Features anchor section on the landing page. |
| `live-landing-workflow.png` | Workflow anchor section, full walkthrough page. |
| `live-landing-pricing.png` | Pricing anchor section. |
| `marketing-landing-hero.png` | Hero crop only. |
| `marketing-landing-features.png` | Bento + MCP demo section crop. |
| `marketing-landing-mcp.png` | MCP demo card crop. |
| `marketing-landing-workflow-teaser.png` | Workflow teaser crop. |
| `marketing-landing-platforms.png` | Platforms strip crop. |
| `marketing-landing-pricing.png` | Pricing card crop. |
### Authenticated app (default brand "My Brand")
| File | Description |
|---|---|
| `live-dashboard.png` | LCARS dashboard with S0CIAL tab, recent posts, stats. |
| `live-autopilot.png` | Chat-first autopilot with chat thread, quick-action chips, 3-column kanban (Ideas, Drafts, Ready to Schedule). |
| `live-calendar.png` | Content calendar with scheduled and failed posts visible (LinkedIn and X). |
| `live-meeting.png` | Monthly Meeting wizard. |
| `live-research.png` | Research Hub: web search, YouTube search, saved research. |
| `live-studio.png` | Content Studio: five-column kanban (Suggestions, Approved, Defining, Ready, Posted). |
| `live-analytics.png` | Analytics dashboard with charts. |
| `live-inbox.png` | Unified inbox for comments across connected networks. |
| `live-create.png` | Create-post modal surface. |
| `live-accounts.png` | Connected accounts management (Meta, LinkedIn, etc.). |
### Marketing crops (reused from `client/public/screenshots/`)
| File | Description |
|---|---|
| `marketing-autopilot.png` | Autopilot empty state. |
| `marketing-autopilot-with-input.png` | Autopilot with chat input visible. |
| `marketing-monthly-meeting.png` | Monthly Meeting main panel. |
| `marketing-strategy.png` | Strategy Hub surface. |
| `marketing-workflow-step-01.png`, `marketing-workflow-step-02.png`, `marketing-workflow-step-03.png` | Walkthrough step crops. |
---
## What stands out technically
1. **Three content-generation modes, one quality spine.** Manual, Monthly Meeting, and Autopilot all run through `brand-context.ts`, `system-prompt.ts`, and `slop-check.ts`. No mode ships "raw LLM output to UI."
2. **The orchestrator surfaces why a draft is weaker.** `meeting-content.ts` returns `skippedSources: string[]` so the UI can render "drafted from KB + news (no top performers yet)" instead of hiding empty context sources.
3. **Six v1-simple content-quality helpers.** Each is independent, has a clean upgrade path to embeddings / LLM-as-judge / Tavily, and is wired through `meeting/save-as-plan`.
4. **Single-process dev server.** Vite runs in middleware mode inside Express on port 5001, no separate dev port. SQLite locally, PostgreSQL when `DATABASE_URL` is set.
5. **One publish orchestrator, eight platform adapters.** Validation, quota, delivery mode, and per-target handling live in one place. Adding a platform means writing one adapter and updating `platform-capabilities.ts`.
6. **A real MCP server.** Not a marketing claim. `mcp-server.py` exposes 13 tools over stdio using the low-level `mcp.server.stdio` pattern (not FastMCP, which dies silently under Hermes supervisor).
7. **Real screenshots in marketing.** Every visual on the landing page is a Playwright capture of the actual running app at 2x DPR. No div-based mockups.
---
## Known issues worth knowing about (from `s0cial-master` SKILL.md §17)
- `engagement_events` is missing the `brand_id` column. Top-performers silently returns empty for every brand. `safeGet` wrapper masks the bug at runtime.
- `posts` table field names are non-obvious. The orchestrator uses `body` and `hook` internally; the DB columns are `content` and `hookTitle`. Route layer does the mapping.
- Vite middleware can go stale after a sequence of file changes. Symptom: correct HTML head, `document.getElementById("root")` empty. Fix: `kill -9 $(lsof -ti:5001) && npm run dev`.
- Wouter hash routing collides with in-page anchor scrolling. Use the `NavAnchor` pattern in `client/src/pages/index.tsx`.
- Auth in dev is bypassed. Logout must explicitly navigate to `/`.
- Perplexity model IDs in `models.ts` are Perplexity-Computer-specific; the provider abstraction maps them for OpenRouter and MiniMax.