Working in this repo
Authority. PRD.md + PRODUCT.md are the requirements. BUILD-PLAN.md §2 and §5 are
decisions already made — do not reopen them. build-plan/01-ledger-tracking-payouts.md is the
column-level spec for everything in Phase 1; follow it exactly, and where a §5 decision changed
it (terms lock, fee kind, payout account on the workspace), the decision wins.
Shape. pnpm workspaces. packages/core is pure TypeScript (the engine and shared types; no
I/O). packages/db holds supabase/migrations/*.sql and their tests. Migrations are numbered
NNNN_name.sql, applied in filename order, and never edited once another migration follows them.
Before adding a migration, run supabase migration list --db-url … against the live project:
two sessions once both created a 0016_* (rebrand and campaigns) and the CLI keys migrations by
version, so the second one silently shadowed the first until it was renumbered. Live is at 0019.
Packages: core (engine, pure) · db (migrations + PGlite tests) · ledger (writes, attribution,
holds) · stripe-webhooks (the revenue accounts' platform endpoint) · payouts (Express onboarding,
Connect webhook, the two-phase run, statements, tax year) · media (renditions) · publishing
(aggregator, composer preflight, worker) · campaigns (Creator Campaigns engine: eligibility,
quality score, SOW, escrow, review, verification → bounty, disputes, funding ledger; contract in
build-plan/04-creator-campaigns.md) · sdk (what products install). Apps: api, portal,
admin, docs.
Tests run on PGlite, not a Supabase container (no Docker here): pnpm test from the root.
packages/db/test/harness.ts boots the three Supabase roles, auth.users, auth.uid(), applies
the migrations, and gives you asRole() for RLS tests. Every migration ships with a test file
that proves each constraint it adds by trying to violate it.
House style for SQL. Comment the why, in the voice of vera-wellness/supabase/migrations/0137_stripe_connect.sql:
a header that says what the migration is for and the trap it prevents; comment on for any
column whose meaning isn't obvious from its name; column grants revoked-then-regranted for any
table a browser role can read that also carries a secret.
Money is bigint cents. Rates are basis points. Floor, never round. workspace_id has no
default, anywhere. Composite FKs (workspace_id, parent_id) on every money-path child.
Don't touch files outside your assignment; don't add dependencies without a reason in the
PR description; don't write UI; don't commit secrets. Leave the tree with pnpm test green or
say exactly which tests are red and why.
Secrets. Nothing secret is committed. Local: macOS keychain — affiliate-coach-supabase-db
(postgres password), affiliate-coach-supabase-service-role (service role JWT),
affiliate-coach-database-url, affiliate-coach-CRON_SECRET, affiliate-coach-CLICK_SALT,
affiliate-coach-key-healthyapp. Deployed: Vercel env per project (sales-boss team). .env.example
at the repo root lists every variable an app reads; copy it to .env.local and fill the secret lines
from the keychain. Stripe (all on the dedicated tellatribe payout account; see
STRIPE-SETUP-GUIDE.md): STRIPE_SECRET_KEY is read by api, portal and admin → one team-level
shared variable; STRIPE_CONNECT_WEBHOOK_SECRET (connected-account events) and
STRIPE_FUNDING_WEBHOOK_SECRET (brand top-ups, /api/stripe/funding) live only on the API
project; HEALTHYAPP_WEBHOOK_SECRET is the revenue account's platform endpoint. AYRSHARE_API_KEY
turns on posting and aggregator-backed campaign verification (absent → URL-only mode).
Crons (apps/api/vercel.json, all guarded by CRON_SECRET): approve-matured (hourly) ·
ensure-partitions (daily) · payout-run (1st, 14:00 UTC) · publish-worker (5 min) · replay-webhooks
(15 min) · renditions (2 min) · campaigns (15 min: fund waiting deals, auto-approve stale drafts,
verification sweep + metrics, mark deals paid, flag deleted posts, rights reminders).
Deploying apps/api. The Vercel project affiliate-coach (team sales-boss) has no remote
root-directory setting reachable from this machine, so deploys are prebuilt from the workspace
root: .vercel/project.json carries settings.rootDirectory = "apps/api" locally; run
npx vercel build --prod --yes --scope sales-boss then
npx vercel deploy --prebuilt --prod --yes --scope sales-boss from the repo root. The build
runs here (pnpm workspace resolves), the server only receives the Build Output. Production env
(DATABASE_URL, CRON_SECRET, CLICK_SALT) is set on the project. Live: https://api.tellatribe.com
Live surfaces (sales-boss Vercel team). API https://api.tellatribe.com · portal
https://tellatribe.com · admin https://admin.tellatribe.com ·
docs hub https://docs.tellatribe.com (code 3003). Deploy any Next app with
scripts/deploy-app.sh <app-dir> <project>; the docs hub deploys from apps/docs with
vercel build + vercel deploy --prebuilt. Public env (NEXT_PUBLIC_*) must be added with
--type config.
Storage buckets: supabase config push applies [storage] limits but did not create
[storage.buckets.collateral] on this project (CLI 2.98) — create buckets with a POST to
/storage/v1/bucket using the service-role key, then push the config. Native binaries in
apps/api (sharp, ffmpeg-static) must be direct dependencies of the app or Next bundles
them and the build fails on "could not load the sharp module". Vercel runs the API functions
on linux-arm64; prebuilt deploys from this Mac only work because pnpm-workspace.yaml
supportedArchitectures installs the linux-arm64 sharp/ffmpeg packages too and
apps/api/next.config.ts pins them into the renditions function's trace.
Rendered from AGENTS.md. Edit the source, not this page.