Skip to main content

Projects layer — full roadmap

Status: live development Author: Frederike + Claude Last updated: 2026-06-04 Scope: the complete picture of the Projects layer — what shipped, what's pending, and the order we tackle it.

Companion to projects-layer.md (the Phase 1 spec). This doc is forward-looking; the other is historical record of the foundation.

Concept recap (so it stays out of muscle memory)

User (you)
└── Organization (the company / account) ← "workspace" in UI
└── Project (brand / initiative) ← TIRIDA, Nxtconnect AI, etc.
├── Channels (per-project social accounts)
├── Sources (per-project content ingestion: CDN / URL / Supabase / Notion / ...)
├── Voice (per-project brand voice + style — fed by brand hub OR manual entry)
└── Content (items, packages, generations — all project-scoped)

Plus per-user (NOT per-project — they complement voice):
├── Writing Style (writing_samples, default_image_prompt)
└── Reference Library (logos, reference images)

What's shipped (slice A through Phase 4)

SliceWhat landed
AProject model, /api/projects CRUD, resolveProject middleware
BuseProjectStore + ProjectSwitcher + X-Project-ID header
Cproject_id columns on platform_accounts, sources, items, packages + backfill of 11 channels + filtered list endpoints
C+CORS allow X-Project-ID (hotfix)
C+Sync-worker propagates source.project_id → item.project_id
EOAuth project-stamping (TikTok/YouTube/IG/Twitter/LinkedIn) — new channels land in the active project
Voice phase 2ProjectVoice schema + ProjectStore.GetVoice/SetVoice + ToPromptBlock()
Voice phase 3Brand hub adapter (POST /api/projects/{id}/sync-voice, GET /voice) + configurable BRAND_HUB_BASE_URL
Voice phase 4 (autopilot)autopilot_topics.project_id + ContentGenerator loads voice + voice wins over UserStyle's redundant fields
Fixautopilot Create now includes org_id (pre-existing NOT NULL bug masked until Phase 4 touched the INSERT)

What's pending — the 4-step plan

A Settings tab inside each project. Two reasons we need it:

  • You want to see logos on projects, rename projects (e.g. "Fastbrainer" → "FastBrainer"), tweak voice fields the brand hub got slightly wrong
  • SaaS clients without a brand hub need some way to enter voice manually — that's the only blocker between us and selling this

Fields:

  • Project name (rename)
  • Logo upload (stored in S3, displayed in ProjectSwitcher next to the name)
  • brand_hub_slug override (when project slug ≠ hub slug, e.g. frederike-falkefrederikefalke)
  • "Sync from brand hub" button (calls existing /sync-voice endpoint)
  • Voice editing — see UX approach below

Voice editing UX (the "freeform + AI extraction + ask for missing" approach):

  1. User pastes a freeform brand brief (1-2 paragraphs is fine) OR clicks "Sync from brand hub"
  2. Backend extracts the 9 structured fields via a Claude call (new endpoint POST /api/projects/{id}/voice/extract-from-text)
  3. UI shows extracted fields inline — each editable, marked with a confidence indicator
  4. For fields the AI couldn't extract or marked low-confidence, the UI prompts targeted follow-ups ("Who's your audience?", "What should the AI avoid?")
  5. Final review + Save

Power users can paste a brief and be done in 30 seconds. Completionists can scroll the same form and fill manually. Brand-hub users get pre-filled fields they can tweak.

Doesn't replace Writing Style or Reference Library — those stay per-user and continue to provide concrete writing samples + reference images that complement the per-project voice.

Effort estimate: ~4-6h.

Step 2: Transform pipeline voice integration

Today only the autopilot generator uses project voice. The transform pipeline (worker handlePipelineTransform) processes source-fetched content_items into platform-ready content_packages — this is the pipeline that runs every Tirida character through AI to produce platform variations.

Change: when transforming, load content_item.project_id → project.voice → ToPromptBlock() and inject into the transform prompt. ProjectVoice.image_style_by_type is especially useful here because content_type is known at transform time (story vs character-spotlight vs world).

Effort: ~30 min once we're in the file.

Step 3: Onboarding flow polish

A guided /onboarding route for new users / new projects:

1. Create your first project (project name + optional logo upload)
2. Connect channels (OAuth buttons, scoped to project)
3. Add a source (optional: CDN URL, Supabase, etc.)
4. Set your brand voice (paste a brief OR sync from hub)
5. Create your first autopilot topic (with sane defaults)
6. Done — see your dashboard

Each step has a "Skip for now" — users can come back. The onboarding flow uses the same endpoints as the rest of the dashboard, just packaged with progress UX.

Effort: ~3-4h.

Step 4: Documentation refresh

The Phase 1 spec (projects-layer.md) covers slices A-C only. Out of date.

Action:

  • Update projects-layer.md to mark slices A-E + voice phases as shipped (link to this roadmap for forward-looking work)
  • Add voice-system.md covering the voice schema, brand hub adapter, manual entry flow, and layering with UserStyle/ReferenceLibrary
  • Add brand-hub-integration.md covering the API contract with Creative AI Lab brand hub + how to point at a different hub (env var)
  • Refresh workspace/organizations.md to clarify Org vs. Project (the terms are currently fuzzy in user-facing docs)

Effort: ~1h.

Decisions on file

These were made during implementation and are worth preserving here:

  1. Voice wins for overlapping fields (vs. UserStyle's BrandVoiceDescription / WordsToAvoid / DefaultImagePrompt). UserStyle's WritingSamples + DefaultReferenceImages always included as concrete examples.
  2. Org per company, Project per brand (not Org per brand). One user can have multiple orgs (multi-tenant) but typically has one. Within an org, multiple projects model the brand portfolio.
  3. Topic project-scoping is real (column on autopilot_topics) — not just passed at generation time. Existing topic ("AI in Business") needs manual reassignment.
  4. Source types stay open via source_type (b2_manifest, supabase, github, notion, web_scraper, rss_feed). Clients pointing at their own CDNs work today via b2_manifest with their base_url.
  5. Brand hub is one feed, not the only one. BRAND_HUB_BASE_URL env var lets SaaS clients point at their own hub. Manual entry covers everyone else.
  6. NOT NULL constraint on project_id is deferred (Slice D) until admin@example.com legacy test data is cleaned up.

What's also deferred (but worth tracking)

ItemWhy deferredWhen
Super-admin cross-project dashboardobservability only, not user valuewhen you have multiple clients
Per-project member rolesno team to invite yetwhen you hire someone
brand VARCHAR cleanup on content_packagesmechanical, works fine as legacyindefinitely
Slice D (NOT NULL on project_id)legacy test dataafter admin@example.com cleanup

Open UX questions (not blocking — but track)

  • Logo flow from project → channel posts (decided 2026-06-05):
    • Logo lives at the project level in brand assets (NOT per-user) — every team member working on the project sees the same logo, ready to use.
    • When a SaaS client adds a future team member with scoped access ("can only see TIRIDA project"), they inherit the project's logo + brand voice automatically — no per-user setup.
    • UI toggle per content_type (or per source) for whether to watermark generated visuals with the logo. Off by default for portraits, on for landscape/wide shots — exact defaults TBD when the toggle lands.
    • Implementation: extend project.settings to hold brand_assets.logo_url (auto-populated from brand hub if connected, else uploaded via project settings). Image transformer reads from there when watermark flag is on.
  • primary_color / secondary_color from brand hub — we receive them but don't use them yet. Where? Possibly: dashboard theming per active project, or generated content templates.
  • enabled_content_types — voice has it (e.g. TIRIDA enables story, character-spotlight, world, etc.). Should the autopilot UI surface those as topic-suggestion seeds? Or restrict topic creation to those types?

These get answered as the UI work surfaces them — not designing now.