Developer docs
Logs

Update log

The board: what is green, what is broken, what is next.

docs/STATUS.md

The board. Update at the end of a work session — this is what a fresh session reads first.

Last updated: 2026-08-05 (Pass 8 · after batch 20 wrap — 135/135 = 100% shipped)

Where we are

Pass 1 ("docs first") is done: the two-deployable monorepo, the docs set, the seeded board, the landing page and /dashboard.

Pass 2 shipped the first vertical slice — a company signs up, creates a project, picks what kinds of marketing it wants, and puts a budget behind each one. Auth pages, the app shell, and the project-membership scoping that makes an agency login safe came with it.

Pass 3 shipped OAuth sign-in (Google + GitHub, with encryption at rest) and Stripe billing (four plans, entitlements, and caps enforced where they apply).

Pass 4 settled the infrastructure: RabbitMQ for short jobs, trigger.dev for durable agent runs, a Baileys container for WhatsApp, and the scraping rules — all recorded in INFRASTRUCTURE.md with the trade-offs written down rather than implied.

Pass 5 rebuilt the front door (F-089). It was prose over a static list; it is now seven interactive sections over the same data/dashboard/*.json the board renders — including a working approval-gate simulator and a ⌘K palette across every feature, experiment and doc.

Pass 6 (2026-07-31 → 2026-08-03) rebuilt auth on Better-Auth and set up the marketing-work foundations: PR #1–3 replaced the hand-rolled JWT flow with Better-Auth (parallel mount → schema

  • bcrypt bridge → frontend cutover → password reset + email verification), PR #4 added the Campaign model + UI, then E1 (Post + PlatformPost + 11-state machine) and E2 (PostingSlot
  • Queue + QueueEntry + timezone-correct next-available-slot resolution) shipped as engines with tests, and finally the IA restructure landed the full per-project sidebar shape from docs/IA.md — the whole target sitemap is walkable in the browser now, ~5 of ~20 sections have real behaviour behind them and the rest are placeholders that name the checklist slice that ships each one. Test suite grew 105 → 201/201 across the pass.

Pass 7 (2026-08-03) shipped the first end-to-end publishing loop for a real platform. Five PRs in sequence: E9.1 — a ChannelAdapter framework with registry, credential vault (AES-256-GCM via core/crypto.ts), and a generic OAuth harness (POST /oauth/:adapterKey/start + GET /oauth/:adapterKey/callback with signed state and optional PKCE) that dispatch by URL segment so no route imports a specific platform. E9.2 — real Instagram + LinkedIn OAuth adapters plug into the harness (Meta short→long token exchange with the Page-walk that finds the linked IG Business account; LinkedIn OIDC /v2/userinfo); registry auto-registers whichever platforms have client credentials set at boot; GET /channels powers a "Sign in with X" primary CTA on every organic hub; plus Option A — connected_by_user_id on SocialAccount so token-death alerts reach the teammate whose grant powers the account. E9.3 — media pipeline (POST /uploads → MinIO, GET /uploads/:key gateway), full LinkedIn publish (single & multi-image, single video, link preview, visibility), a composer rewrite with drop-zone file picker + drag-drop + preview grid, and Modal primitive gains a footer slot so action bars stay pinned. E3.1 — publisher orchestrator: a tick loop (modules/publisher/, default 30s) claims due scheduled PlatformPosts via the sole-writer transition (safe under N replicas), dispatches through the same code path as the "Publish now" button, marks accounts disconnected on token_revoked/token_expired, drains gracefully on SIGTERM. E5.1 — LinkedIn post-metrics ingestion: PostMetricSnapshot time-series model + modules/metrics/ worker (15-min tick, 1-hour per-post min-interval, 30-day retention) + LinkedIn getPostMetrics against /v2/socialActions, + GET /posts/:id/metrics + a live metric strip on every published PostRow that refetches every 60s. Test suite grew 201 → 255/255 across the pass. LinkedIn is production-usable today: connect → compose with media → schedule or publish now → the worker fires on time → the analytics roll in.

Pass 8 (2026-08-03 → 2026-08-05, batches 8–20 · 20 PRs merged) closed everything that Pass 7 listed as "still specced but unbuilt" — and then some. In order of impact:

  • Full channel-adapter parity (E9): Instagram, Facebook, Threads, Pinterest, X (OAuth2+PKCE), TikTok (Content Posting API), YouTube (video publish scaffolded, real path guarded until resumable upload wired) — six adapters on top of the E9.1 framework, each mock-first with config-toggle for real creds. Reply-through-adapter shipped for LinkedIn / IG / FB / Threads.
  • Full publisher hardening (E3.1–3.4): orchestrator + exponential-backoff retry queue (5 attempts, 20→300s) + per-account rate-limit tracking (SocialAccountRateLimit) + full request/response audit with token redaction (PublishAttemptAudit).
  • Full inbound content (E4): signed-webhook framework + InboxItem model + Meta/LinkedIn/Threads webhook adapters + 10-min polling fallback for the rest + Inbox UI. Pinterest/X/TikTok webhook adapters remain the only red row (blocked on platform push-API access — polling covers).
  • Full analytics stack (E5, E10): per-day PostMetricDaily rollup + AccountMetricSnapshot + GA4 + Google Search Console ingestion + cross-source reconciliation with drift ribbon + project-day KPI rollup engine (ProjectKpiSnapshot) + read API + dashboard UI.
  • Full ad platform (E6): AdAccount / AdCampaign / AdGroup / Ad / Creative / Budget models + status machine + budget-engine auto-pause + bid strategies + ad-metrics ingestion + Google + Meta Ads adapter shells + D6 optimisation-proposal engine + creative asset pipeline (sharp → 3 variants) + attribution engine (touchpoints + 4 models + KPI rollup + public tracker.js SDK with SPA hooks + sendBeacon fallback) + spend reconciliation vs invoice CSV.
  • Full SEO / AEO (E7): site audit + recurring CrawlSchedule + TrackedKeyword + KeywordRank + backlink monitor + AI citation tracker (daily tick vs 4 LLMs) + competitor tracker + share-of-voice + full D1/D2/D3/D4/D5/D6/D7 UI hubs.
  • Full approvals (E8): multi-stage workflow + stage definitions + threaded comments (internal_only) + per-stage decisions + reminder scheduler + delegation rules + client-portal magic-link engine + login page + workflow template editor UI.
  • Full notifications (E11): email (nodemailer) + SMS (Twilio-shaped) + phone verification + Web Push (VAPID) + service worker + daily/weekly digest engine + Slack outbound (Block Kit) + generic outbound webhook (HMAC-signed, auto-disable at 10 fails) + notification-preferences UI.
  • Full auth extensions (E12): invitations flow + session listing + scoped API keys (ak_live_…) + policy-based RBAC (Role/Permission/RolePermission/UserRoleAssignment) + TOTP MFA + recovery codes + step-up gate + WebAuthn/passkeys (second-factor + first-factor sign-in) + GDPR export + right-to-erasure with 7-day grace.
  • Full billing (H2–H6): plans compare/change + usage meter view (UsageMeter model + reconciler + overage notification) + native invoices list + payment methods + dunning UX banner.
  • Full team surface (I1–I3, J1–J6): project members list + invitations + Roles & Permissions UI + Workspace settings + Notification prefs + API keys + Security (sessions + MFA + passkeys) + Audit logs (with chip filters + preview drawer + CSV export as of batch 20).
  • Full per-project UI closure (batch 20): every sidebar link in the per-project IA now resolves to real UI. Batch 20 T2 converted 8 sections to optional-catch-all [[...tab]]/page.tsx so ~50 child slugs stop hitting the "Building this next" placeholder; batch 20 T1 shipped the two truly-missing pages (/integrations, /activity) + /kpis redirect; batch 20 T3 shipped <ConnectionHealthPill> + audit-feed polish.

Feature spreadsheet: 135/135 = 100% shipped (docs/FEATURE_STATUS.xlsx). Test suite grew 255/255 → ~1050/1050 API + ~514/514 web + ~500 helper cases across the pass. The sole remaining red row is E4 Pinterest/X/TikTok inbound webhook adapters, blocked on each platform's push-API access — polling already covers them.

Gate

CheckCommandState
Typesnpm run typecheck✅ api · web · contracts clean
Lintnpm run lint✅ 0 errors (pre-existing warnings only)
Unit / integrationnpm test --workspace @verjson/api✅ ~1050/1050 (API), no skips
Web testsnpm test --workspace @verjson/web✅ 514/514 (helpers + hooks)
E2Enpm run test:e2e⚠️ smoke still asserts old landing copy — rewrite pending
Buildnpm run build✅ api + web build

In flight

ItemStateBlockerNext step
F-001 monorepo split✅ done——
F-088 dashboard✅ done—wire the optional dev write-back so remarks can be added from the browser
F-089 landing✅ done—12 e2e cases; smoke needs refresh after IA restructure
F-020 projects + F-025 mix + F-026 budgets✅ done——
F-005 RBAC / F-006 agency logins✅ done——
E12 policy-based RBAC (roles + permissions + assignments)🟡 partial—engine + CRUD endpoints landed; ONE endpoint migrated to requirePermission as demo (POST /billing/checkout → billing:manage), rest carry TODO(rbac) for follow-up
E12 step-up gate🟡 partial—permission-based gate proven out via requirePermission; per-request re-auth for sensitive actions still to come
F-015 OAuth · F-016 Stripe✅ done——
F-015 OAuth · F-016 Stripe✅ done—GitHub OAuth temporarily hidden until wired to Better-Auth (OAuth-migration slice)
F-096 queue + worker✅ done—verified against a real broker
F-073/074/075 agent runs + approval gate✅ done——
F-097 trigger.dev✅ done—falls back to our worker when unconfigured
F-023 campaigns✅ done—model + org-wide UI + per-project view (PR #4 + IA restructure)
Auth — Better-Auth adoption✅ done—4 PRs (parallel mount → schema → cutover → reset/verify). Legacy /api/v1/auth/* still mounted; retire in cleanup slice
E1 content lifecycle engine✅ done—Post + PlatformPost + 11-state machine + CRUD. Version history / normaliser / policy checks are E1.2–E1.4
E2 scheduling engine✅ done—PostingSlot + Queue + QueueEntry + next-available-slot resolution. Optimal-time + cadence rules are E2.2/E2.3
B1 calendar UI✅ done—Monthly view at /projects/[id]/calendar. Weekly + drag-to-schedule is B3
IA restructure (A2–A7)✅ done—Full walkable IA — top nav + project sidebar + placeholder catch-all
F-082 CI gate🟡—npm run verify works locally; put it in GitHub Actions
F-083 mock testing🟡—add the MSW layer for web; see TESTING.md
E9 channel-adapter framework🔲—Highest-leverage next slice — see docs/CHECKLIST.md

Next up

Every feature in the original spec has shipped. Remaining candidates, ordered by dependency-free first:

  1. Pinterest / X / TikTok inbound webhook adapters — the only pending row in the spreadsheet. Blocked on each platform's push-API access (Pinterest partner review, X paid tier, TikTok business review). The polling fallback already covers these end-users; the push path is a latency + cost improvement, not a capability gap.
  2. E2E smoke rewrite — apps/web/e2e/smoke.spec.ts still asserts the pre-cutover template landing copy. Small slice, unblocks the E2E suite.
  3. CI gate in GitHub Actions (F-082) — npm run verify runs locally; wiring it into CI is what stops the "someone remembered to run it" failure mode.
  4. TODO(rbac) sweep — ~7 role checks in projects/, agents/, billing/ still carry requireRoles rather than the batch-12 requirePermission engine. Endpoint-by-endpoint swap, deliberately deferred to keep tenant-scope 404 semantics on a few.
  5. Cross-worktree test-DB deadlock — every parallel batch hit 40P01 on the shared TRUNCATE in apps/api/src/test/setup.ts. Canonical fix: per-worktree DB name (app_test_<slug>). Not urgent (agents work around with scratch DBs) but would remove a recurring speed-bump.
  6. Reviewer sweep on batches 10–20 — the parallel-delivery push was time-boxed to spawn-merge-repeat; a formal reviewer pass across those ~40 PRs hasn't been run.

Everything else is polish behind these six.

Security findings

All 19 findings from the 2026-07-29 review are closed, including both blockers. The full register — what each one allowed, and what closed it — is in SECURITY_FINDINGS.md. Rows are kept after closure on purpose: knowing what was once possible is what tells the next person which assumptions to re-check.

Known broken

IssueImpactOwner
apps/web/e2e/smoke.spec.ts still asserts the old template's landing copyE2E suite will fail until rewritten—
No CI pipelineThe gate only runs when someone remembers to run it. This is what let lint sit at 14 errors under a documented "0 errors" (B-014), and it is the common cause behind all four bugs found on 2026-08-05—
Legacy POST /auth/signup still mounted alongside POST /auth/signup-with-orgTwo signup paths, and src/test/helpers.ts exercises the legacy one — so the suite proves the path users don't take. Directly caused B-011. Retire the legacy route, or repoint the helper—
The API suite reads the developer's root .env (vitest.config.ts loads it for TEST_DATABASE_URL)A legitimate local setting can fail tests: a WEB_URL other than http://localhost:3000 fails 4 oauth.test.ts cases. Pin the values the tests depend on in vitest.config.ts#test.env instead—

Reference apps still in-tree

archive/brightbean-studio/, archive/adwords-adsense/, archive/brightbean-studio-app/ — excluded from the workspaces and from lint. Read-only sources for the port; delete once PORTING.md is fully worked through.