tellatribe — Build Plan
Version: 1.0 · Date: October 3, 2026 · Status: Ready for engineering review
This is the document PRD-Activation-Platform.md points at and never had. It reconciles the
two PRDs, fixes the decisions already taken, and sequences the build. It is deliberately
short; the detail lives in three companion documents under build-plan/:
| Track | Document | Covers |
|---|---|---|
| 01 | build-plan/01-ledger-tracking-payouts.md |
Data model, commission engine, attribution, Stripe webhook path, payouts, SDK (PRD phases 1, 2, 5) |
| 02 | build-plan/02-collateral-publishing-teleprompter.md |
The collateral system in its three modes, with current platform-API findings (PRD phase 6) |
| 03 | build-plan/03-reconciliation-and-surfaces.md |
Requirement-by-requirement reconciliation of the two PRDs, the "commercial-shaped" checklist, every v1 screen, first five minutes, admin's week, navigation (PRD phases 3, 4) |
1. Which document governs
PRD.mdv0.1 (Aug 14, updated Oct 3) andPRODUCT.mdare the requirements. Where they disagree with anything else, they win.PRD-Activation-Platform.mdv1.1 is kept, frozen, as the v2 vision of the sellable platform. It is not a requirements document for this build. Track 03 §A.1 maps all 43 of its requirements to a verdict; its status line should be changed to point at that table..impeccable/surfaces/design-portal.mdcarries the chosen visual direction for the affiliate portal (the Title Wall), anddesign/prototype/portal.htmlis its first rendering.DESIGN.mdis written from the built portal at the end of Phase 3, not before.
2. Decisions already made
These are settled. Re-opening one costs a conversation, not a commit.
| Area | Decision | Where |
|---|---|---|
| Shape | Internal first, commercial-shaped. One merchant in v1; the schema, auth boundary, and tokens are built so the SaaS pivot is a feature project, not a rewrite. | PRODUCT.md; 03 §A.2 |
| Tiers | Two levels, permanently. MAX_DEPTH = 2 in the engine with the legal reason in a comment; CHECK (level IN (1,2)) on plan_levels. No third level, no ranks, no volume qualifications. |
PRD §2.1, §5.2, §11 |
| Tenancy | workspaces table with one seeded row; workspace_id NOT NULL on every top-level table from migration one, with composite FKs so the column is a boundary, not a label. |
01 §0.1; 03 §A.2 #1 |
| Ledger | Append-only: money columns frozen by trigger; state advances along the PRD §7.2 edges only; every transition logged. A commission row cannot exist without a positive conversion. | 01 §0.2–0.3, §1.5 |
| Stripe topology | Two webhook endpoints: platform revenue events and connected-account events never share a route. The click id travels in metadata.ac_click_id, with client_reference_id only as a guarded fallback, because HealthyApp's Connect checkout already uses client_reference_id for something else. |
01 §0.4, §4.2–4.3 |
| Payout rail | Stripe Connect Express as primary; PayPal Payouts for countries Stripe can't serve; CSV as the escape hatch. One payout account per workspace — a new tellatribe Stripe account, funded monthly from each product's revenue account (decision 9). | PRD §8; 01 §0.5, §5 |
| Collateral | Three delivery modes: download → publish-for-me → record-with-teleprompter. Mode 1 ships first and must be excellent alone. | PRD §9.3; 02 §0 |
| Mode 2 | Through an aggregator (Ayrshare Business) in v1 so platform approvals are off the critical path; register our own Meta and TikTok apps in parallel as the migration path. Networks at launch: LinkedIn, Instagram, Facebook Page, TikTok. X built but off by default. | 02 §3.2, §6 |
| Mode 3 | Pure web (no Capacitor) with server-side remux of every recording and an in-app-browser fallback card. Ships as v1.5, two to four weeks after v1. | 02 §4.2, §6 |
| Portal direction | The Title Wall: each product a row, each piece of collateral a tile; the focused tile grows and says what it pays and how long it takes; standing lives in the billboard on top. Deep neutral ground, one accent spent only on the focused tile, the primary action, and money. | .impeccable/surfaces/design-portal.md |
| Portal navigation | Four tabs hold every screen: Wall · Links · Studio · Earnings. Team and Payout method live under Earnings; Settings and Notifications behind the code chip in the header. Studio means the affiliate's work in progress (taken, scheduled, takes, connected accounts, rules), not only the recorder. | 03 §B.7; prototype |
| Signup | Open and auto-approved by default, as a per-program toggle; the pending_approval state exists so gating is a setting. |
03 §B.8 D.1 |
| Payout onboarding | Asked after the first commission reaches pending, never at signup; hard-required before the first payout. |
03 §B.5, D.4 |
| Bonuses | None in v1. Promotional actions are counted from day one (asset_uses, publish_jobs, recordings); any future bonus pays through a separate bonus_awards ledger, never as a commissions row. |
PRODUCT.md; 03 D.7 |
3. Phases and gates
Sizes are per track: S ≤ 2 days, M ≤ 1 week, L 2–3 weeks. Gates are bold; nothing after a gate starts until it passes. Phase numbers follow PRD §13.
Phase 1 — Ledger (no UI)
Items 1–11 of 01 §7. Tenancy, programs, affiliates, partitioned tracking tables, the ledger, the pure engine, the ledger service.
Gate 1a: schema tests — a third level is refused; a commission without a conversion is refused; a money-column update is refused; a sponsor cycle is refused. Gate 1b: engine test matrix — incl. property tests and a golden replay file. Gate 1c: ledger integration suite — first sale L1+L2; recurring to month 12 then 13; refund pre- and post-payout; dispute; suspended sponsor; self-referral and duplicate flagged; rate change mid-stream; concurrent duplicate conversion.
Rough size: about half of the 8–10 engineer-weeks in track 01.
Phase 2 — Tracking
Items 12–17 of 01 §7. The click and conversion API, the SDK, the platform Stripe webhook route.
Gate 2: end-to-end tracking — a local HealthyApp (or stub) with the SDK route → click →
Stripe test-mode Checkout with metadata → invoice.paid → ledger rows; refund → reversal.
Verified by reading the tables, not a UI.
Phase 6 (Mode 1) — Collateral, in parallel with Phase 2
Items 6.1–6.9 of 02 §6. Asset schema, token resolver, admin upload and renditions, script editor with read time, vault list and detail.
Gate 6: an affiliate on a phone copies a disclosed, link-substituted caption and saves a correctly cropped JPEG for Instagram in under 30 seconds. Requires the first HealthyApp collateral set (D.9 below) — the vault may not launch empty.
Phase 3 — Affiliate portal
The 20 screens in 03 §B.2, built in the Title Wall world from the direction contract. Order: Wall (with the billboard and product rows), asset sheet, Links, Earnings ledger with commission detail, Studio (taken / scheduled / takes / accounts / rules), Payouts, Team, Payout method, Settings, Notifications, Terms interstitial, Join and Sign-in. Mobile at 390 first; desktop is a wider view of the same thing.
Gate 3: the first five minutes (03 §B.5) run on a real phone by someone who has never seen
the product, ending with a caption on the clipboard and an image in Photos. Then the finish
review, the verdict, and DESIGN.md written from the built world.
Phase 4 — Admin
The 10 screens in 03 §B.3: Queue first, then Collateral authoring (the most-used screen), Payout runs, Affiliates, Programs with the integration panel, Reports with the four guardrail metrics, Audit, Settings. Desktop-first. MFA on from the first login.
Gate 4: a normal week needs zero logins — the queue digest email carries everything, and the empty queue reads "All services running normally."
Phase 5 — Payouts
Items 18–22 of 01 §7. Express onboarding, the two-phase payout run (assemble marks rows paid; release issues transfers), failure handling, statements.
Gate 5: payout suite — below-minimum carried; negative carried; not-onboarded skipped and notified; invalid destination returns rows to approved; insufficient funds halts the run; re-release is idempotent.
Phase 6b (Mode 2) — Publishing
Items 6b.0–6b.7 of 02 §6. 6b.0 starts on day one (Meta Business Verification, app registrations, privacy and terms URLs live) because it is calendar time, not engineering time.
Phase 7 — HealthyApp live
First real affiliates, first payout run. Instrument the activation funnel (03 §A.2 #8) so PRD §14's success criteria are measurable from the first week.
Phase 6c (Mode 3) — Teleprompter, v1.5
Items 6c.1–6c.7 of 02 §6, two to four weeks after v1. The one surface that needs a physical device QA matrix. 6c.7's in-app-browser arrival rate is the data that decides whether a native wrapper is ever worth it.
Phase 8 — Creator Campaigns (shipped Oct 19)
Briefs, applications, deals, deliverables, verification → bounty, disputes, rights, brand funding
(PRD-Creator-Campaigns.md Part B; contract build-plan/04-creator-campaigns.md). Built as four
parallel streams against one schema (0017–0019): engine packages/campaigns, admin Campaigns
section, portal Open calls / Studio → Deals, API funding webhook + 15-minute cron. Gate: every
engine state machine proven on PGlite (37 tests), the admin and portal screens on live rows, the
funding webhook end-to-end with a signed test event. Phase 6 of the PRD (platform marketplaces,
paid amplification, external discovery, storefronts) stays later.
Phase 1 execution plan — agents
Phase 1 is a schema, a pure engine, and a service over both. The spec in track 01 is column-level, so the work splits by file with the spec as the contract; the shared risk is naming drift between parallel migrations, which the test harness catches at merge because it applies every migration in order. Concurrency is capped where merge conflicts would cost more than the parallelism buys.
| Wave | Agents | Work | Gate |
|---|---|---|---|
| 0 | 1 (lead) | Repo, pnpm monorepo, engine type contract (packages/core/src/engine/types.ts), ledger interface, migration 0001_tenancy with tests, PGlite harness, AGENTS.md, baseline commit |
pnpm test green |
| 1 | 4, in parallel worktrees | M1 0002_programs + 0003_affiliates (+ tests) · M2 0004_tracking (partitioned clicks/conversions, customer_attribution with the terms-lock snapshot) (+ tests) · M3 0005_ledger (commissions with guard triggers, fee kind, events, flags, audit) + 0006_webhooks + 0007_payouts (+ tests) · E engine computeCommissions + full §2.4 matrix, property tests, golden replay |
Gates 1a (schema) and 1b (engine): all migration tests green on one tree, engine at 100% branch coverage |
| 2 | 2, in parallel | L ledger service (recordConversion, approveMatured, reverseConversion, adjust) over PGlite · I the gate-1c integration suite written against packages/core/src/ledger/types.ts plus the golden replay through the real tables |
Gate 1c: every §7 item-11 scenario green |
| — | lead | Merge order: M1 → M2 → M3 → E (E is independent of the others), fix drift, run gates; then L + I | Phase 1 ends |
Why not more: a fifth wave-1 agent would have to split a single migration or a single test matrix, and both are cheaper to write whole than to reconcile. The ledger service (L) is one transactional module; two agents inside it conflict on every function.
Environment note. No Docker on this machine, so supabase db reset cannot run locally.
Schema gates run on PGlite (Postgres in WASM) with citext and btree_gist; the migration files
are unchanged and ship to a real Supabase project with supabase db push once one exists
(creating it is a billable step and is Jonathan's call).
Branching. Agents work in worktrees off main and return branches; the lead integrates them
on phase-1. Nothing merges to main without asking.
Phase 1 status log
| When | Event |
|---|---|
| Oct 3 | Wave 0: repo, pnpm monorepo, engine contract, 0001_tenancy, PGlite harness. 7 tests. Vercel project affiliate-coach (sales-boss) created and linked. |
| Oct 4 | Wave 1 merged on phase-1 (M1, M2, M3, E). Stubs removed; the real chain 0001→0007 runs on one tree. Gate 1a green (123 schema tests). Gate 1b green (78 engine tests, 100% branch coverage, golden replay checked in). |
| Oct 4 | Supabase project affiliate-coach created under the sales boss org (us-west-1) with Jonathan's approval; all seven migrations applied with supabase db push — the chain holds on real Postgres, not only PGlite. |
| Oct 4 | Wave 2 started: L (ledger service) and I (gate-1c suite) in parallel worktrees against packages/ledger's Sql port and createLedgerService factory. |
| Oct 4 | Wave 2 merged. Gate 1c green: 14 scenario groups (first sale L1+L2 with the terms lock; recurring to 12 then 13; refund before and after payout, proportional; dispute; suspended sponsor and suspended L1; self-referral at both tiers; duplicate customer; rate change under the lock; idempotency incl. cross-month; serialized concurrency; hold sweep; fees; the 200-conversion golden replay through the real tables). Phase 1 complete: 299 tests — 78 engine (100% branch coverage), 125 schema across 0001–0008, 96 ledger. Live Supabase project matches the tree through 0008. |
| Oct 4 | phase-1 fast-forwarded into main with Jonathan's OK. phase-2 cut. Wave 3 started, four agents in parallel: A1 apps/api (key auth, /v1/clicks, /v1/conversions, cron routes, Postgres Sql adapter) · A2 @affiliate-coach/sdk (/r/[code] handler, cookies, getClickId, stripeCheckoutFields, convert) · A3 @affiliate-coach/stripe-webhooks (platform-event handler with the four topology guards, event ledger, dead letters) · A4 Phase 6 Mode 1: 0009_collateral + the token resolver in core. |
| Oct 4 | Wave 3 merged. Gate 2 green: SDK redirect → /v1/clicks → first-party cookies → Stripe Checkout metadata → signed invoice.paid through the mounted platform webhook → L1 + L2 rows → partial charge.refunded → proportional reversals, on one tree (apps/api/test/gate2.e2e.test.ts). Phase 2 complete: 519 tests (124 core incl. collateral resolver, 149 schema 0001–0009, 96 ledger, 66 SDK, 43 webhooks, 41 API). 0009_collateral live on Supabase. phase-2 → main; phase-3 cut. Integration decisions: RecordConversionInput.mode added (test keys record test rows); API bodies follow the SDK contract (camelCase, outcome); SDK honours the program's cookieWindowDays; HealthyApp mounts the redirect at /go/[code] because app/r/[token] is taken. |
| Oct 4 | Wave 4a started on phase-3: S portal shell (apps/portal: Supabase magic-link auth, Tailwind theme from .impeccable/design.json, tab shell) · D admin app (apps/admin: Queue, Programs with integration panel, Affiliates) · Y payouts (packages/payouts: Express onboarding, two-phase run, statements; API mounts) · B publishing (0010_publishing, Ayrshare adapter, publish worker, composer contract). |
| Oct 4 | apps/api deployed to production: https://api.tellatribe.com (prebuilt from the workspace root; recipe in AGENTS.md). DATABASE_URL verified against the live pooler (the ledger's Postgres adapter test ran green against Supabase); CRON_SECRET, CLICK_SALT set; crons registered. Smoke: unauthenticated click 401, cron 401, unconfigured product slug 404. |
| Oct 4 | HealthyApp seeded in the live database by scripts/seed-product.mts: product (redirect_origin https://healthyapp.coach), evergreen program with terms v1, rates 25% recurring 12 months (L1) / 5% lifetime (L2) — the PRD's suggested plan, applied as a decision so Phase 7 can start; change through set_plan_level(). A live track+convert API key was minted once and lives only in the keychain. Live smoke with that key returns unknown_code 404 — the production stack answers end to end. Agent H started: the documentation hub (apps/docs, code 3003). |
| Oct 4 | Wave 4a Y merged: packages/payouts — Express onboarding ("ask Stripe, don't trust the return"), two-phase payout run (assemble marks paid → check funding → release with payout:{id} idempotency; 4xx returns rows via fail_payout(); insufficient funds halts and fails the rest with run_halted), reconciler, statements, tax-year summary, Connect webhook with its own event-ledger rows. Gate 5 green (35 tests). Mounted and redeployed: /api/stripe/connect, cron payout-run on the 1st at 14:00 UTC. Awaiting from Jonathan: the dedicated tellatribe Stripe account (decision 9) → STRIPE_SECRET_KEY, STRIPE_CONNECT_WEBHOOK_SECRET, and workspaces.payout_stripe_account_id; until then /api/stripe/connect answers 500 (no key) and startOnboarding refuses. 588 tests. |
| Oct 4 | Wave 4a landed on phase-3: S portal shell (Supabase magic-link auth, join with live code check, terms interstitial, Settings sheet, theme from design.json; 0010_affiliate_preferences) · B publishing (0011_publishing: social_connections, publish_jobs, enqueue_publish_job with the X open-balance gate, Ayrshare client + six adapters, preview, worker with backoff; cron publish-worker every 5 min) · D admin (MFA-gated; Queue with exception drawer and reasoned actions; Programs with set_plan_level(), terms, integration panel with reveal-once keys and a test conversion; Affiliates with reparent/suspend/write-off/view-as; 0012_admin_flags) · H documentation hub, live at https://docs.tellatribe.com (code 3003). Migrations renumbered at merge (publishing 0010→0011, admin flags 0011→0012) because Supabase keys migrations by version; 0010–0012 applied live. Wave 4b running: P1 Wall + Studio, P2 Links + Earnings (+ 0013_links). Full suite 693 tests when run unloaded; two ledger scenarios timed out once under a load average of 29 and pass in isolation. |
| Oct 4 | Admin and portal deployed: https://admin.tellatribe.com (MFA-gated sign-in) and https://tellatribe.com (shell; Wall/Links/Studio/Earnings screens landing from wave 4b). Env set on both (Supabase URL + anon key as public config, service role, DATABASE_URL, API URL). scripts/deploy-app.sh does the per-app prebuilt deploy. Supabase Auth still needs the magic-link template pointed at /auth/confirm with {{ .TokenHash }} and the two app origins in the redirect allow-list before anyone can sign in. |
| Oct 4 | Wave 4b landed and the portal is live with all four tabs: P1 Wall (billboard with first-run / under-minimum / needs-payout / negative states, 4:5 tiles, caption rendering per platform with /go/ links, Join-to-promote) + Studio (asset sheet, composer with server-side disclosure check, "Record it" script sheet as Mode 3 v1, posters, signed-rendition redirect) · P2 Links (list, builder, detail; 0013_links) + Earnings (ledger, next payout, commission sheet, payouts list/detail, CSV statement, payout method with Stripe onboarding action). 0013_links applied live. Full suite + typecheck green on phase-3. Portal redeployed: /wall /links /studio /earnings answer 200 behind sign-in. Wave 5 running: D2 admin second half (Collateral authoring, Payout runs, Reports, Audit, Settings) · R @affiliate-coach/media (renditions matrix, sharp/ffmpeg, Storage bucket, cron) · K HealthyApp integration branch in vera-wellness. Interpretations logged in DECISIONS-FOR-REVIEW.md. |
| Oct 4 | Phase 7 prepared — K: HealthyApp integration on branch affiliate-coach-integration in vera-wellness (commit 69b1ec8, not merged, not pushed): SDK vendored to lib/affiliate-coach/, app/go/[code] redirect with .healthyapp.coach cookie domain, /go/ exempted from the auth middleware, stripeCheckoutFormFields spread into startPlatformSubscription only; 13 tests, repo suite 482/482. Go-live checklist in that branch's INTEGRATION.md. Portal: signed-in Join-to-promote flow (/wall/join/[program]) added and deployed; live portal verified signed in as the test affiliate TESTAFF (four tabs render; screenshots in the docs hub). Docs hub status line and Decisions-for-review page live. Agents T (teleprompter, 6c) and C (Connect → publish wiring, 6b) started. |
| Oct 4 | Phase 6 Mode 1 media landed — R: packages/media (17-cell rendition matrix, sharp images / ffmpeg video / PDF cover card, Storage port, claim-with-skip-locked worker; 39 tests), cron /api/cron/renditions every 2 min live and answering (401 unauth, 200 → {attempted:0,…}). Getting it live took four deploys: sharp must be a direct dep of apps/api (else bundled), Vercel runs the API on linux-arm64 so pnpm-workspace.yaml now installs that platform's sharp/ffmpeg beside the Mac's, ffmpeg-static → @ffmpeg-installer/ffmpeg, and the media package resolves ffmpeg lazily with a pnpm-store scan because the installer's symlink cannot be traced. Bucket collateral (private, 500 MiB, 16 mimes) created via the Storage API — supabase config push did not create it. API env gained SUPABASE_URL, SUPABASE_SERVICE_ROLE_KEY. Shell: Links tab count now from my_links. |
| Oct 4 | Checkpoint: main fast-forwarded to phase-3 (809 tests, typecheck clean). Phase 6c teleprompter landed — T and deployed: /studio/record/[asset] (camera gate with in-app-browser detection, 3-2-1 countdown, pace from scriptTiming with 0.8–1.4× speed, 60 s soft / 3 min hard marks, MediaRecorder mp4→webm preference, review with save-to-phone via share sheet, record again, Done → record_completed event + used_at); burn-in opt-in via affiliates.preferences.burn_in_disclosure with canvas compositing when supported; 32 tests. Studio "Takes" copy updated. Remaining agents: D2 (admin half 2) and C (Connect → publish wiring). |
| Oct 4 | Admin complete — D2 (Collateral authoring with token palette, sample-affiliate preview, 150 wpm read time, publish gate incl. claims attestation; Payout runs as Assemble → Check funding → Release with history, run detail, statement CSV; Reports with the four guardrails and row drills + CSV export; Audit with before/after diff; Settings with why/consequence lines; 73 tests). The renditions seam now imports the media package's SQL-only half (@affiliate-coach/media/enqueue, new subpath) so the admin never carries sharp/ffmpeg. Phase 6b wired — C: Connect accounts → Ayrshare link session → /studio/connect/return (HMAC state) → syncConnections; Disconnect/Reconnect (0014_social_reconnect, applied live); Composer preflight + preview parity with the worker; X offered only when the gate passes; honest no-key state. Every module in the build plan is now built. Awaiting AYRSHARE_API_KEY for live posting. |
Decisions that surfaced during Phase 1 integration (recorded here so Phase 2 inherits them):
0008_flag_vocabularyadmitsaffiliate_suspendedincommissions.flag_reasonandfraud_flags.kind; the engine's reason is stored verbatim, never mapped tomanual.- A reused idempotency key returns
duplicateregardless of body; a body mismatch is written onto theapi_requestsmemo (body_mismatch_seen) for the admin and never acted on. The API layer should surface it as a warning, not a 409. - A refund larger than the conversion raises
refund_exceeds_conversion; the webhook route should dead-letter that event rather than retry it. - A suspended converting affiliate is paid into
flagged(held visibly);closed/pendingaffiliates are unattributed and never lock a customer. approveMatured(workspaceId, now)is scoped per call so one cron sweeps every workspace.- Rows born
approved(reversal, fee, adjustment) carryapproved_atat insert; the trigger only stamps it on a transition. - Phase 1 ran on PGlite; the migrations were also applied to the live project, so the chain is proven on real Postgres. True concurrent-request tests (two connections racing on one customer lock) belong to Phase 2's API tests against Supabase.
| Oct 5 | Found while demoing: a conversion in the same second as its click went unattributed (Stripe reports whole seconds) →
CLOCK_SKEW_TOLERANCE_MS(60 s) in the window check; focused caption tiles overlapped their title → two-line clamp. Decisions 53–54. | | Oct 9–13 | Paid social:ads/engine (statics 4:5 / 1:1 / 9:16, two motion MP4s, two carousels) and 14 UGC-style videos (Higgsfield → whisper word timestamps → PNG caption overlays, since the homebrew ffmpeg has no libass); $1,410 demo earnings on the TESTAFF portal screens; docs hub gains the screens gallery and the paid-social page. Executive deck (20 slides, PPTX) with the market-data and Campaigns slides. | | Oct 14 | Creator Campaigns strategy + PRD (PRD-Creator-Campaigns.md, Part A/B, competitive landscape appendix incl. 2025–26 upstarts); Jonathan's six Campaign decisions (55–60). | | Oct 19 | Stripe setup guide for the account administrator (two accounts, hand-off via 1Password). Tax identity shipped (0015_tax_identity):affiliates.tax_statusmirrored fromaccount.updated, payout run and admin preview skiptax_identity_missing, Reports → Paid by recipient · tax year (CSV, drill), portal annual statements per calendar year, payout-method tax line. Decisions 61–66. PayPal Payouts evaluated and parked (guide Part 7). | | Oct 19 | Rebrand: affiliate.coach → tellatribe (0016_rebrand_tellatribe, display values only; package scope unchanged). Domains live: tellatribe.com · admin.tellatribe.com · api.tellatribe.com · docs.tellatribe.com. Ads/screens carry the tellatribe wordmark. | | Oct 19 | Creator Campaigns Part B shipped (0017_campaigns,0018_funding_endpoint,0019_campaigns_vocabulary; build contractbuild-plan/04). Four parallel streams merged:packages/campaignsengine (eligibility, quality score, SOW, escrow, review, verification →bounty, disputes, funding ledger; 37 tests), admin Campaigns section + balance strip + Add funds (Stripe Checkout) + four queue kinds + gift value on the 1099 report, portal Open calls / sheets / Studio → Deals / bounty rows, API/api/stripe/funding+/api/cron/campaigns. Decisions 67–106. Totals: schema 221 · engine 37 · admin 87 · portal 166 · API 53 · payouts 40 · ledger 99. Migrations 0015–0019 live; all three apps + docs deployed. Open: campaign screens not yet exercised with live rows; Stripe account (payments + Connect + funding webhook) and Ayrshare key still awaited. |
What runs in parallel
Phase 1 ──gate──► Phase 2 ──gate──► Phase 3 (portal) ──► Phase 7 (live) ──► 6c (v1.5)
Phase 6 (Mode 1) ─┘ Phase 4 (admin) ─┘
Phase 5 (payouts) ────────────────────────┘
6b.0 platform registrations ─────────────────────────────────► 6b (Mode 2 via aggregator)
Collateral content · counsel ToS review · domains · payout-account confirmation (section 5)
Rough total to v1 (Modes 1 and 2, no teleprompter): four to five months with two engineers, or seven to nine serial. Phase 1 is the half that cannot be compressed and should not be.
4. Non-engineering work that starts now
| Work | Why now | Owner |
|---|---|---|
Register tellatribe and go.tellatribe.com; put privacy and terms pages live |
Meta and TikTok app registration require them; short links need the domain | Jonathan |
| Meta Business Verification; register Meta app and TikTok app (02 §6b.0) | Weeks of calendar time, resets on rejection; the migration path off the aggregator | Jonathan / ops |
| Confirm the payout Stripe account and enable Express on it (01 §8 Q8) | Phase 5 cannot start without it | Jonathan |
| First HealthyApp collateral set: one caption with image renditions, one email swipe, one script, the claims guidelines (03 D.9) | The vault may not launch empty; the Studio design review needs real material | Jonathan / marketing |
| Standard terms-of-service review by counsel | Ordinary contract review; the compensation-plan opinion is not needed at two tiers | Jonathan |
| Decide rates (PRD §15.1) | Seeds plan_levels; the prototype shows 25% / 5% and 20% / 5% as examples only |
Jonathan |
5. Decisions
All fifteen decided with Jonathan on October 3, 2026. Five depart from the plan's defaults (2, 6, 9, 11, 12); each departure's engineering consequence is in its Note.
Decided
| # | Question | Decision | Note |
|---|---|---|---|
| 1 | Commission basis | Net of tax and discounts — amount_paid − tax, after discounts |
Default |
| 2 | Plan terms over a subscription's life | Locked at first conversion. A referred customer keeps paying under the rates in force when they converted, for the life of that customer | Departure from the default. Snapshot plan_level_id per level on customer_attribution (track 01 §8 Q2 names the two nullable columns); the ledger's commission-detail copy must show the locked rate and its date |
| 3 | Partial refunds | Proportional reversal, floored to the cent, per level; disputes always reverse in full | Default |
| 4 | Payout minimum scope | One per affiliate across all products ($50, on the workspace) | Default |
| 5 | Cross-product downline | Yes — a sponsor earns each product's own L2 rate on everything their recruit sells | Default |
| 6 | Caption editing in the Post-for-me composer | Affiliates may edit anything, including the disclosure. No locked span. The composer warns once when the disclosure is missing and logs the removal, then posts | Departure. Liability for an undisclosed endorsement published through the platform sits with the merchant; the claims-guidelines page and the one-time warning are the mitigations |
| 7 | Customers as affiliates | Yes — an "Earn" link inside HealthyApp deep-links to join with customer_ref, so self-referral matching is exact |
Default |
| 8 | Signup | Open, auto-approved, as a per-program toggle | Default |
| 9 | Payout Stripe account | A new tellatribe Stripe account, not HealthyApp's revenue account | Departure. Phase 5 gains a monthly funding transfer from each product's revenue account and a reconciliation step; workspaces.payout_account_ref already keeps this shape open (track 01 §0.5) |
| 10 | Mode 2 aggregator | Ayrshare Business from day one; register own Meta/TikTok apps in parallel as the migration path | Default |
| 11 | X posting | Enabled, gated and self-funded. Post-for-me to X is available only to affiliates with an open balance owed to them (pending + approved > $0), and the per-post link fee (~$0.20) is deducted from that affiliate's payout | Departure. Adds a fee ledger row kind (negative, references the publish_job), netted in the payout run alongside reversals; the composer states the cost before posting ("This post costs $0.20, taken from your next payout"); an affiliate with no open balance sees X greyed with the reason. Fee amount is config, not a constant |
| 12 | Disclosure burn-in on recordings | Off by default; the affiliate can turn it on per take | Departure. The spoken disclosure line stays mandatory in every script and the caption disclosure stays attached; the review screen carries a "Burn in disclosure" toggle, remembered per affiliate. Weaker FTC posture for video than the default; the claims-guidelines page says so plainly |
| 13 | Review queue before publish | None in v1. Posts go out when the affiliate taps Post; controls are the fraud flags, the guidelines page, and the caption-edit log. Built as a v1.5 per-program option | Default |
| 14 | Chargeback velocity (2+ in 30 days) | Flag and queue, never auto-suspend. Open rows move to Held, an exceptions item opens with the evidence, the admin decides | Default |
| 15 | Raw click retention | 13 months, then the monthly partition is dropped | Default |
6. What "done" means for v1
From PRD §14, made checkable:
- A new product integrates in under one working day (Gate 2 proves the mechanics; the integration panel's "last click / last webhook received" proves it in production).
- A normal week requires zero admin logins (Gate 4).
- Zero mis-attributed conversions in the first 90 days (ledger-vs-Stripe reconciliation variance on the Reports screen).
- Any commission dispute answered from the ledger in under five minutes (commission detail shows the rule, the amount basis, and the payable-on reason).
- The median new affiliate visits the wall within 48 hours and takes something from it
(
asset_usesby affiliate, first-week cohort). - v1.5: a first-time video maker gets from script to posted without leaving the portal.
Rendered from BUILD-PLAN.md. Edit the source, not this page.