feat: Shared Business Infrastructure — Phase 1 (Websites + Marketing)
Shared Business Infrastructure — Phase 1: Websites + Marketing
Enhancement Summary
Deepened on: 2026-03-07 Agents used: 13 (architecture-strategist, security-sentinel, performance-oracle, agent-native-reviewer, code-simplicity-reviewer, kieran-typescript-reviewer, pattern-recognition-specialist, deployment-verification-agent, agent-native-architecture-skill, create-agent-skills, frontend-design-skill, cloudflare-researcher, seo-researcher)
Key Improvements from Original Plan
- Simplified structure — Dropped
tooling/,packages/config/,packages/tailwind-config/. Brand configs moved toapps/<slug>/brand.config.ts. Singletsconfig.base.jsonat root. - Agent-native architecture — Added discovery skills (
/biz-list,/biz-status), Wrangler CLI project creation in skills, social draft persistence, CRUD completeness. - Security hardened — Pin GitHub Actions to SHA, validate CI matrix inputs, scope Cloudflare token, add CSP headers, gitignore PDFs immediately.
- Performance optimized — Single-job Turborepo CI (no matrix), remote caching, self-hosted fonts, Astro image pipeline.
- Design system — Flat CSS custom properties (~12 vars per site) as single source of truth for visual theming.
- TypeScript improvements —
as const satisfies, slim BrandConfig (~30 lines),zodimported directly (notastro/zod).
Key Tension Resolved: Simplicity vs. Architecture
The simplicity reviewer argued for dropping Turborepo and all shared packages. The architecture and performance reviewers validated them. Resolution: Keep Turborepo (one config file, earns its keep via --filter and caching) and packages/ui (minimal: BaseLayout + types + schemas + tokens.css). Drop packages/config, packages/tailwind-config, tooling/, BrandStyle, aesthetic presets, three-tier CSS tokens, SocialLinks, and modules?. CSS is the single source of truth for visual properties; TypeScript holds only non-visual metadata. Components built inline in site 1, extracted to shared during Phase 1D.
Overview
Set up a Claude Code skills monorepo that serves as shared operational infrastructure for 4-6 businesses (mixed services + products). Phase 1 establishes the foundation: monorepo scaffolding, shared Astro component library, per-business branding, Cloudflare Pages deployment, and core Claude Code skills for website management and marketing content.
The architecture is skills-first and agent-native — Claude Code’s native skill system is the “operating system.” Skills are shortcuts on top of primitives, not gates. The agent can always fall back to direct file operations for novel requests.
Problem Statement / Motivation
Aaron manages 4-6 businesses with mostly manual/ad-hoc tooling. Each business needs a marketing website, content creation, SEO, and social media — but building these independently per business means duplicated effort and inconsistent quality. A shared infrastructure with AI-powered skills lets one operator manage all businesses efficiently from a single repo.
Proposed Solution
A pnpm + Turborepo monorepo with:
- Astro static sites per business (zero JS by default, component sharing, TypeScript)
- Cloudflare Pages hosting (300+ edge locations, generous free tier)
- GitHub Actions CI/CD with Turborepo-native change detection
- Claude Code skills as shortcuts over primitives — for scaffolding, content creation, and discovery
- Config-driven branding via TypeScript
BrandConfig(non-visual metadata) + flat CSS custom properties (visual theming)
Technical Approach
Architecture
sendy-admin/ # Renamed from sendy-adventures-admin
├── CLAUDE.md # Root conventions (~50-100 lines)
├── package.json # private: true, workspace scripts
├── pnpm-workspace.yaml
├── turbo.json
├── tsconfig.base.json # Shared TypeScript base config
├── # Tailwind v4: CSS-first config via @tailwindcss/vite (no JS preset)
├── .gitignore # Expanded for Node/Astro/Wrangler/PDFs
│
├── .claude/
│ └── skills/ # Shared Claude Code skills
│ ├── site-scaffold/
│ │ └── SKILL.md
│ ├── content-create/
│ │ └── SKILL.md
│ └── biz-list/
│ └── SKILL.md
│
├── apps/ # One Astro site per business
│ └── zendee-adventures/
│ ├── package.json # depends on @repo/ui
│ ├── brand.config.ts # BrandConfig — THE keystone contract
│ ├── astro.config.mjs
│ ├── tailwind.config.mjs # extends ../../tailwind.preset.js
│ ├── tsconfig.json # extends ../../tsconfig.base.json
│ ├── CLAUDE.md # Business-specific AI context
│ └── src/
│ ├── pages/
│ │ └── index.astro
│ ├── content/
│ │ ├── config.ts # Astro content collection config
│ │ └── blog/ # Blog posts (Markdown, not MDX)
│ ├── assets/ # Images processed by Astro pipeline
│ ├── layouts/
│ │ └── SiteLayout.astro # Imports BaseLayout + brand.css
│ ├── components/ # Site-specific components (override shared)
│ └── styles/
│ └── brand.css # CSS custom property overrides
│
├── packages/
│ └── ui/ # Shared Astro components
│ ├── package.json # name: "@repo/ui"
│ └── src/
│ ├── types.ts # BrandConfig interface (~30 lines)
│ ├── schemas.ts # Zod schemas (blog, etc.)
│ ├── layouts/
│ │ └── BaseLayout.astro # Only shared layout to start
│ └── styles/
│ └── tokens.css # Default CSS custom properties (~12 vars)
│
├── .github/
│ └── workflows/
│ └── deploy.yml # Single-job Turborepo deploy
│
└── docs/
├── brainstorms/
└── plans/
Research Insights: Architecture
Simplifications from original plan:
packages/config/eliminated — brand configs live atapps/<slug>/brand.config.ts. Skills globapps/*/brand.config.tsfor discovery. This keeps the config package purely definitional and collapses the three-step resolution to one directory.packages/tailwind-config/eliminated — singletailwind.preset.jsat repo root. Each site’stailwind.config.mjsdoespresets: [require('../../tailwind.preset.js')].tooling/directory eliminated — singletsconfig.base.jsonat root. ESLint deferred to when it is needed.packages/ui/starts minimal: BaseLayout + types + schemas + tokens.css. Components (Header, Footer, etc.) built inline in site 1, extracted to shared during Phase 1D when a second site reveals what’s truly shared.
Per-site component override convention: Not needed yet — components start per-site. When extracting to packages/ui/, document the import convention in root CLAUDE.md.
BrandConfig Schema
The keystone contract. Lives at apps/<slug>/brand.config.ts per business.
// packages/ui/src/types.ts — shared type definitions (~30 lines, zero runtime)
export interface BrandConfig {
name: string; // "Zendee Adventures"
slug: string; // "zendee-adventures" (must match directory)
domain: `https://${string}`; // Full origin, no trailing slash
type: 'service' | 'product';
brand: {
tagline: string;
voice: string; // Free-text tone description for content generation
audience: string; // Target audience description
};
meta: {
title: string;
description: string;
ogImage?: string;
};
deploy: {
projectName: string; // Cloudflare Pages project name
};
}
CSS is the single source of truth for visual properties (colors, fonts, spacing, shapes). TypeScript holds only non-visual metadata (voice, audience, slug, domain, deploy). This eliminates the dual-source-of-truth problem where BrandConfig colors and CSS vars must stay in sync.
Research Insights: BrandConfig
Simplification rationale:
- Dropped
BrandColors,BrandFonts,BrandStyle— all visual properties live in CSS custom properties (brand.css) only. - Dropped
SocialLinks— not needed for Phase 1 (add when social skills mature). - Dropped
modules?— YAGNI; add when Phase 2 begins. - Dropped
HexColorbranded type — colors are in CSS now, not TypeScript. - Use
as const satisfies BrandConfigin brand config files — preserves literal types while validating structure. brand.config.tslives per-app, making discovery trivial:glob apps/*/brand.config.ts.- Content-create skill reads
voiceandaudiencefrom TypeScript, visual context from CSS.
Example brand config:
// apps/zendee-adventures/brand.config.ts
import type { BrandConfig } from '@repo/ui/types';
export default {
name: 'Zendee Adventures',
slug: 'zendee-adventures',
domain: 'https://zendee-adventures.com',
type: 'service',
brand: {
tagline: 'Adventure awaits in the wild',
voice: 'Energetic, outdoorsy, approachable. Short punchy sentences. Active voice.',
audience: 'Young adults (25-40) seeking outdoor adventures and nature experiences',
},
meta: {
title: 'Zendee Adventures | Outdoor Tours & Experiences',
description: 'Guided outdoor adventures for thrill-seekers and nature lovers.',
},
deploy: {
projectName: 'zendee-adventures',
},
} as const satisfies BrandConfig;
Skill Invocation Convention
All shared skills accept the business slug as the first argument:
/site-scaffold zendee-adventures
/content-create zendee-adventures blog
/biz-list
Skills resolve the business by:
- Validating slug matches
/^[a-z0-9-]+$/andapps/<slug>/exists - Loading the brand config from
apps/<slug>/brand.config.ts - Using the CLAUDE.md in
apps/<slug>/for business-specific context - On invalid slug: listing available businesses (discovery fallback)
This convention is documented in the root CLAUDE.md.
Research Insights: Skill Design (Agent-Native)
Skills are shortcuts, not gates. The agent can always fall back to direct file operations. Skills exist for convenience and consistency, not as the only way to accomplish a task.
CRUD completeness check:
| Entity | Create | Read | Update | Delete |
|---|---|---|---|---|
| Site | /site-scaffold | /biz-list, /biz-status | Direct file edit | Manual (deferred) |
| Content | /content-create | ls/read apps content | Direct file edit | Direct file delete |
| Brand Config | Via /site-scaffold | Direct read brand.config.ts | Direct file edit | Manual |
| Deploy | Push to main / /site-deploy (ad-hoc) | wrangler pages deployment list | N/A | N/A |
Key agent-native additions:
/biz-listskill — returns all businesses with status summary. Critical for agent autonomy./site-scaffolduseswrangler pages project create— no manual Cloudflare dashboard step.- Social drafts persist to
apps/<slug>/content/social/<platform>-<date>.mdfor retrieval. - Build errors are captured and presented to Claude for diagnosis.
- Per-business CLAUDE.md files should include: business description, brand voice, content guidelines, available images list.
- Content-create skill reads
voice/audiencefrom BrandConfig TypeScript; visual context frombrand.css.
Content Collection Schema
// Shared blog schema — importable from @repo/ui/schemas
// packages/ui/src/schemas.ts
import { z } from 'zod'; // Direct import, NOT astro/zod
export const blogSchema = z.object({
title: z.string(),
description: z.string(),
pubDate: z.coerce.date(),
updatedDate: z.coerce.date().optional(),
author: z.string().default('Team'),
heroImage: z.string().optional(),
tags: z.array(z.string()).default([]),
draft: z.boolean().default(false),
});
export type BlogFrontmatter = z.infer<typeof blogSchema>;
Research Insights: Content
- Use
.md(Markdown), not.mdxfor generated content. MDX allows arbitrary JSX/JavaScript, which is an XSS vector if content generation ever produces embedded scripts. Standard Markdown has no script execution. Use MDX only when you explicitly need interactive components. - Import
zoddirectly, notastro/zod. This keeps the schema framework-agnostic and reusable outside Astro. - Export inferred types (
BlogFrontmatter) alongside schemas so components can type-check without callingz.infer. - Images go in
apps/<slug>/src/assets/(notpublic/). Astro’s<Image>component processes these at build time for WebP/AVIF conversion, responsivesrcset, and automaticwidth/heightfor CLS prevention.
CSS Custom Properties (Flat, ~12 Variables)
CSS is the single source of truth for all visual properties. Each site overrides brand.css with its own values. The shared tokens.css provides sensible defaults.
/* packages/ui/src/styles/tokens.css — Default brand tokens */
:root {
--brand-font-heading: 'Inter', sans-serif;
--brand-font-body: 'Inter', sans-serif;
--brand-color-primary: #2563eb;
--brand-color-accent: #f59e0b;
--brand-color-bg: #ffffff;
--brand-color-text: #1e293b;
--brand-color-border: #e2e8f0;
--brand-radius: 8px;
--brand-shadow: 0 1px 3px rgba(0,0,0,0.1);
--brand-container-max: 1200px;
--brand-content-max: 720px;
--brand-section-padding: 6rem;
}
/* apps/zendee-adventures/src/styles/brand.css — Per-site overrides */
:root {
--brand-font-heading: 'Montserrat', sans-serif;
--brand-font-body: 'Inter', sans-serif;
--brand-color-primary: #2d5016;
--brand-color-accent: #f59e0b;
--brand-color-bg: #fafaf5;
--brand-color-text: #1a1a1a;
--brand-color-border: #e5e7d0;
--brand-radius: 16px;
--brand-shadow: 0 1px 2px rgba(0,0,0,0.05);
}
Research Insights: Design System
Keep it flat. Start with ~12 CSS variables. Add more when you need them, not before. Two sites feel different through color, font, and border-radius alone — you don’t need three-tier token hierarchies or aesthetic presets yet.
Self-host fonts via @fontsource — eliminates render-blocking Google Fonts requests:
pnpm add @fontsource-variable/montserrat @fontsource-variable/inter --filter=zendee-adventures
Build components inline first. Header, Footer, HeroSection, etc. start in apps/zendee-adventures/src/components/. Extract to packages/ui/ during Phase 1D when a second site reveals what’s truly shared vs. site-specific.
CI/CD Pipeline
Redesigned based on performance and security reviews.
# .github/workflows/deploy.yml
name: Deploy Sites
on:
push:
branches: [main]
pull_request:
branches: [main]
workflow_dispatch:
inputs:
site:
description: 'Site slug to deploy (or "all")'
required: true
jobs:
build-and-deploy:
runs-on: ubuntu-latest
env:
TURBO_TOKEN: ${{ secrets.TURBO_TOKEN }}
TURBO_TEAM: ${{ secrets.TURBO_TEAM }}
steps:
# Pin all actions to SHA (supply chain security)
- uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4
with:
fetch-depth: 2 # Need HEAD^1 for turbo change detection
- uses: pnpm/action-setup@fe02b34f77f8bc703a5f83f2ec0b1d17f6cfbf1f # v4
- uses: actions/setup-node@39370e3970a6d050c480ffad4ff0ed4d3fdee5af # v4
with:
node-version-file: '.node-version'
cache: 'pnpm'
- run: pnpm install --frozen-lockfile
# Lint and type-check on all PRs
- name: Lint & Type Check
if: github.event_name == 'pull_request'
run: pnpm turbo check lint
# Build only changed sites (Turborepo-native detection)
- name: Build changed sites
run: pnpm turbo build --filter='./apps/*[HEAD^1]'
# Deploy each site that produced a dist/
- name: Deploy to Cloudflare Pages
if: github.ref == 'refs/heads/main'
run: |
for site_dir in apps/*/; do
if [ -d "$site_dir/dist" ]; then
site_name=$(basename "$site_dir")
# Validate slug format
if [[ ! "$site_name" =~ ^[a-z0-9-]+$ ]]; then
echo "Invalid site slug: $site_name"
exit 1
fi
echo "Deploying $site_name..."
npx wrangler pages deploy "$site_dir/dist" \
--project-name="$site_name"
fi
done
env:
CLOUDFLARE_API_TOKEN: ${{ secrets.CLOUDFLARE_API_TOKEN }}
CLOUDFLARE_ACCOUNT_ID: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}
Research Insights: CI/CD
Single-job Turborepo build replaces matrix strategy (from performance oracle):
- Pays
pnpm installcost once instead of N times. - Turborepo handles parallelism internally (better than GitHub Actions matrix).
- Avoids GitHub Actions concurrent job limits (5 for private repos).
--filter='./apps/*[HEAD^1]'uses Turborepo’s native change detection — nodorny/paths-filterdependency.
Turborepo remote caching from day one (free Vercel tier):
- After first build, unchanged sites complete in 1-3 seconds (cache hit).
- Set
TURBO_TOKENandTURBO_TEAMin GitHub Secrets.
Security hardening (from security sentinel):
- All third-party actions pinned to commit SHA (tags are mutable —
tj-actions/changed-fileswas compromised via tag in 2025). - Slug validated against
/^[a-z0-9-]+$/before use in shell commands. CLOUDFLARE_API_TOKENscoped to only “Cloudflare Pages: Edit” permission..node-versionfile pins exact Node.js version (not just22).- Added lint/check job on PRs for early error detection.
Performance:
- With remote cache, a shared package change that normally rebuilds 6 sites completes in ~30 seconds (5 cache hits + 1 rebuild).
- Without remote cache, all 6 builds run but Turborepo parallelizes them on available CPU cores (~2-3 minutes).
Implementation Phases
Phase 0: Security Prerequisites (BLOCKING — Do Before Anything Else)
- Add
.gitignoreprotection for PDFs and sensitive files immediately:*.pdf Sendy Adventures/ .env* node_modules/ dist/ .astro/ .wrangler/ .turbo/ - Move the 10 legal PDFs to separate private repo. (PDFs were never committed to git — local only.
.gitignorenow prevents accidental commits.)
Phase 1A: Foundation (Do First)
Scaffolding, tooling, and one deployable site.
Tasks:
- Rename repo or update README to reflect multi-business scope
- Initialize pnpm workspace —
pnpm-workspace.yaml, rootpackage.json(private: true) - Add
.node-versionfile with exact Node.js version (22.22.1) - Add Turborepo —
turbo.jsonwith build/dev/lint/check tasks - Create
tsconfig.base.jsonat repo root with strict settings -
Create— Not needed: Tailwind v4 uses CSS-first config viatailwind.preset.jsat repo root@tailwindcss/vite - Create
packages/ui/— types.ts (slim BrandConfig ~30 lines), schemas.ts (blog schema), BaseLayout.astro, tokens.css (~12 default CSS vars) - Scaffold
apps/zendee-adventures/:brand.config.ts(usingas const satisfies BrandConfig)astro.config.mjs(with@tailwindcss/vite),tsconfig.json(extends base)src/pages/index.astro,src/layouts/SiteLayout.astrosrc/content.config.tswith blog collection using shared schemasrc/styles/brand.csswith flat CSS custom property overrides (~12 vars)src/assets/directory for images (Astro pipeline)- Self-hosted fonts via
@fontsource
- Create root
CLAUDE.md - Create
apps/zendee-adventures/CLAUDE.md - Verify build works:
pnpm turbo build— 3 pages in 863ms
Success gate: pnpm turbo build produces a working static site for Zendee Adventures.
Phase 1B: Deployment
CI/CD and hosting.
Tasks:
- Create Cloudflare Pages project via CLI:
wrangler pages project create zendee-adventures --production-branch=main - Create Cloudflare API token scoped to Cloudflare Workers/Pages Edit
- Add GitHub Secrets:
CLOUDFLARE_API_TOKEN,CLOUDFLARE_ACCOUNT_ID - Add GitHub Actions workflow —
.github/workflows/deploy.yml(single-job Turborepo design, SHA-pinned actions) - Add
_headersfile to each site’spublic/with security headers:/* X-Frame-Options: DENY X-Content-Type-Options: nosniff Referrer-Policy: strict-origin-when-cross-origin Permissions-Policy: camera=(), microphone=(), geolocation=() Content-Security-Policy: default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline' - Push to main and verify auto-deployment works
- Verify the site is live at
zendee-adventures.pages.dev - Configure custom domain in Cloudflare (if domain is ready)
- Enable Cloudflare Web Analytics (free, no client-side JS needed)
Success gate: Pushing a change to apps/zendee-adventures/ auto-deploys to Cloudflare Pages.
Phase 1C: Core Skills
Three skills for Phase 1. Others are ad-hoc prompts until patterns stabilize.
-
Create
/biz-listskill — Lists all businesses with status.- SKILL.md at
.claude/skills/biz-list/SKILL.md - Globs
apps/*/brand.config.ts, reads each, presents summary table - Shows: name, slug, domain, type, deploy status (via
wrangler pages deployment list) - This is the discovery primitive — every other skill’s error path falls back to it
- SKILL.md at
-
Create
/site-scaffoldskill — Scaffolds a new business site end-to-end.- SKILL.md at
.claude/skills/site-scaffold/SKILL.md disable-model-invocation: true(side effects)- Accepts:
<slug>+ interactive brand info questions - Creates:
apps/<slug>/directory with all files (brand.config.ts, package.json, astro.config.mjs, brand.css, CLAUDE.md, content.config.ts, index.astro, SiteLayout.astro) - Creates Cloudflare Pages project:
wrangler pages project create <slug> - Updates root
CLAUDE.mdbusiness list - Runs
pnpm installto register new workspace - Validates: slug format (
/^[a-z0-9-]+$/), no directory collision
- SKILL.md at
-
Create
/content-createskill — Generates on-brand content.- SKILL.md at
.claude/skills/content-create/SKILL.md - Accepts:
<slug> <content-type>(blog, landing-page, social) - Reads
apps/<slug>/brand.config.tsfor voice, audience, tagline - Reads
apps/<slug>/CLAUDE.mdfor content guidelines - For blog: outputs
.mdfile (not MDX) with correct frontmatter inapps/<slug>/src/content/blog/<date>-<title-slug>.md - For social: outputs to
apps/<slug>/content/social/<platform>-<date>.md(persisted, not stdout-only) - Content generated as local files for operator review — never auto-committed
- On invalid slug: runs
/biz-listfallback
- SKILL.md at
Skills deferred to when patterns stabilize:
/site-deploy—pnpm turbo build --filter=@repo/<slug> && wrangler pages deploy apps/<slug>/dist --project-name=<slug>is a two-line command. Formalize as a skill when deploying frequently enough to forget the syntax./seo-audit— Ask Claude directly: “Audit apps/zendee-adventures for SEO issues.” Formalize when you have a stable checklist you repeat./social-draft— Folded into/content-create <slug> social. Separate skill when platform-specific logic grows complex.
Success gate: Can run /content-create zendee-adventures blog and get a properly formatted, on-brand blog post placed in the correct directory.
Phase 1D: Second Business
Validate the “add a business” flow by onboarding a second business.
- Run
/site-scaffold <second-business>with real business details - Use different CSS custom property values in
brand.cssfor visual distinctiveness - Create initial content with
/content-create - Push to main and verify auto-deployment
- Verify both sites deploy independently (Turborepo change detection)
- Run
/biz-listto see both businesses - Retrospective: Did any components need to be overridden per-site? Extract learnings.
- If common components were copied, extract them to
packages/ui/
Success gate: Two visually distinct sites running from one repo, managed by the same shared skills.
Acceptance Criteria
Functional Requirements
- Monorepo builds all sites with
pnpm turbo build - Each site has unique branding (colors, fonts, border-radius) driven by CSS custom properties
- Shared components render correctly with different brand configs
-
/site-scaffold <slug>creates a deployable new business site including Cloudflare project -
/content-create <slug> bloggenerates an on-brand blog post with correct frontmatter -
/biz-listshows all businesses with status - GitHub Actions deploys only changed sites on push to main
- Adding a new business requires no changes to shared skills or CI pipeline
- Agent can discover all businesses and their configs without human input
Non-Functional Requirements
- Local dev server starts in under 5 seconds per site
- Full build completes in under 60 seconds per site
- Root
CLAUDE.mdstays under 100 lines - All internal packages use
@repo/*namespace - All GitHub Actions pinned to commit SHA
- Security headers on all deployed sites
- Generated content uses
.md(not.mdx) - Fonts self-hosted (no external Google Fonts requests)
Dependencies & Prerequisites
- Cloudflare account with Pages enabled
- Custom domains (optional — Cloudflare provides
.pages.devsubdomains) - pnpm installed locally (
npm install -g pnpm) - Wrangler CLI for local deploys and project creation (
pnpm add -g wrangler) - Node.js 22.x (exact version in
.node-version) - Vercel account for Turborepo remote cache (free tier sufficient)
Risk Analysis & Mitigation
| Risk | Impact | Mitigation |
|---|---|---|
| Legal PDFs accidentally committed | CRITICAL | Phase 0 blocker: gitignore + move immediately |
| CI command injection via dynamic values | CRITICAL | Validate all slugs against /^[a-z0-9-]+$/ regex |
| Third-party GitHub Action compromise | High | Pin all actions to commit SHA |
| Cloudflare API token over-scoped | High | Scope to “Pages: Edit” only |
| Astro breaking changes affect all sites | High | Pin Astro version, update together |
| Shared component change breaks a site | Medium | Build all sites in CI on packages/ change |
| Brand config drift between sites | Low | TypeScript BrandConfig type + as const satisfies |
| Skill produces off-brand content | Medium | Content always generated locally for review |
| Content generation XSS via MDX | Medium | Use .md not .mdx for generated content |
Open Decisions (Resolve During Implementation)
- Repo rename —
sendy-admin?business-ops?ventures-hq? Decide before Phase 1A. - Sensitive docs destination — Separate private repo? Google Drive? Encrypted vault?
- Analytics — Cloudflare Web Analytics (free, no JS) is the default. Revisit if richer analytics needed.
- Image workflow — Images in
apps/<slug>/src/assets/for Astro pipeline. Consider Cloudflare Images or R2 if asset volume grows. - Vitest setup — Add in Phase 1D when there are enough configs and schemas to validate. At minimum: config validation tests, schema snapshot tests.
Future Phases (Out of Scope)
- Phase 2: Accounting + Invoicing skills (invoice-generate, expense-track, report-generate)
- Phase 3: Operations (task management, scheduling, vendor coordination)
- Phase 4: Creative production (design assets, brand management, content pipelines)
- Phase 5: Automated workflows (cron-based content publishing, scheduled reports, monitoring)
- Future agent-native enhancements: Cross-business comparison tools, analytics integration, self-modifying skills, content calendar