Skip to content

01 — Architecture Assessment

Status: complete — populated from adventive-admin repo analysis, April 2026.

Current state

Attribute Value
Application version 3.3.9 (composer.json)
Language PHP ≥ 7.3
Framework CodeIgniter 3.1.* (last release 3.1.13, 2022; no active maintenance)
Deploy target EC2 (Linux) — direct server push via AWS CodeDeploy
CI/CD Bitbucket Pipelines — three branch-triggered pipelines (development, staging, production)
Environments development, staging, production (called "ops" in pipeline)
Authentication JumpCloud LDAP (application/libraries/Jc_auth.php) + Duo 2FA (duosecurity/duo_universal_php) — Duo currently disabled (see pain points)
Data stores MySQL: console DB (accounts, campaigns, plans), billing DB (invoices, profiles), aggregate DB (stats); AWS Redshift (impression events)
File storage AWS S3 — invoice PDFs (billing-*.adventivecdn.com), override files (override-*.adventivecdn.com)
Email Mailgun (application/libraries/Mailgun.php)
Integration endpoints Internal billing_service API, AWS Cognito, AWS CodeDeploy, Bitbucket OAuth, DoubleVerify

Traffic profile: Internal only. Low volume, concentrated around month-end billing operations. Peak: ~10 concurrent operators.

Code inventory

Top-level folder tree

adventive-admin/
├── application/
│   ├── config/           (21 files — routes, database, adventive.php, mailgun.php, rest.php)
│   ├── controllers/      (12 controllers + 2 API sub-controllers)
│   ├── models/           (13 models, 11,172 total lines)
│   ├── libraries/        (9 libraries: Jc_auth, Mailgun, AmazonS3, AwsCognito,
│   │                       AwsAthena, AppDeploy, REST_Controller, Duo wrapper)
│   ├── views/            (56 view files across 11 directories)
│   ├── helpers/          (csv, download, file helpers)
│   ├── hooks/
│   ├── data/
│   └── cache/
├── www/                  (public web root, index.php)
├── tests/                (test directory — no test files found)
├── logs/
├── composer.json         (version 3.3.9, PHP ≥ 7.3)
├── appspec.yml           (AWS CodeDeploy — currently inactive, replaced by shell repo)
└── bitbucket-pipelines.yml

Route map (controllers → URL patterns)

Controller URL prefix Method count Lines
Dashboard.php /dashboard 1 42
Auth.php /auth 4 205
Account.php /account 10 460
Billing.php /billing 18 960
Campaign.php /campaign 3 111
Report.php /report 22 445
Reporting.php /reporting 25 997
Tools.php /tools 14 451
Override.php /override 8 167
Chart.php /chart 4 98
ManagedServiceJob.php /managedservicejob 2 104
Utility.php /utility (CLI only) 2 58
api/Campaign_Controller.php /api/campaign 2 117
api/Managedservices_Controller.php /api/managedservices 2 99

Route config (application/config/routes.php) has minimal explicit routes — most routing is CodeIgniter's default controller/method/param convention.

Models → database tables

Model File Lines Tables Primary operations
Account_model Account_model.php 1,321 account, users, account_executive SELECT, UPDATE, INSERT
Billing_model Billing_model.php 933 billing_invoice, billing_invoice_account, billing_invoice_usage, billing_profiles, account_plan, account_plan_sub, account_plan_usage SELECT, INSERT, UPDATE
Campaigns_model Campaigns_model.php 665 campaign, client, ad_html5, ad_format SELECT, INSERT
Report_model Report_model.php 5,333 campaign, ad_html5, stats_aggregate_*, impressions (Redshift) SELECT (complex analytical)
ManagedService_model ManagedService_model.php 505 managed_service_jobs, managed_service_jobs_ads, managed_service_impression_log, managed_service_job_log SELECT, INSERT, UPDATE
Stats_model Stats_model.php 1,286 stats_* aggregate tables SELECT, INSERT
Override_model Override_model.php 280 override_file_descriptions + S3 S3 + DB
Dashboard_model Dashboard_model.php 121 account SELECT
Advertiser_model Advertiser_model.php 112 client SELECT, INSERT
Tools_model Tools_model.php 298 various SELECT
Utility_model Utility_model.php 89 account, api_keys UPDATE
Pdo_model Pdo_model.php 124 (generic PDO wrapper for Redshift) Generic
Uuid_model Uuid_model.php 105 UUID generation utility

Views → operator workflow identification

Directory File count Operator surface
views/account/ 12 Account list, detail, creation wizard, activation, cancellation
views/billing/ 11 Billing dashboard, invoice list, processing forms, delinquent, managed services
views/campaign/ 1 Campaign + ad list
views/dashboard/ 1 Main dashboard
views/report/ 5 Report listing, queue status
views/tools/ 14 Lookup tools, ad types, benchmarks, permissions, deployment
views/override/ 2 Override file editor + version viewer
views/auth/ 2 Login form, Duo 2FA prompt
views/common/ 7 Header, footer, sidebar, JS includes
views/errors/ 10 Error pages

Third-party libraries (composer.json)

Package Version Status Notes
codeigniter/framework 3.1.* ⚠️ End-of-life No active maintenance since 2022
aws/aws-sdk-php 3.324.* ✓ Active S3, Cognito, Athena
chriskacerguis/codeigniter-restserver ^3.1 ⚠️ Community Not officially maintained; REST framework for CI3
phpoffice/phpexcel 1.7.9 🚨 Abandoned Archived 2019; no security patches. Used by Billing::getMonthSummary
phpoffice/phpspreadsheet 1.25.* ✓ Active Successor to PHPExcel — both installed simultaneously
mk-j/php_xlsxwriter ^0.39 ✓ Active Third Excel library; three overlapping Excel dependencies
duosecurity/duo_universal_php ^1.0 ✓ Active Currently disabled in application
paragonie/sodium_compat ^1.21 ✓ Active Libsodium polyfill
firebase/php-jwt ^6.4 ✓ Active JWT support
phan/phan ^1 (dev) ⚠️ Outdated Static analysis tool; current major is v5

Scheduled tasks / cron jobs

Reporting.php (997 lines) is the cron-facing controller. Its 25 public methods are called by server cron jobs via HTTP — no auth check in the constructor (the class extends CI_Controller without the jc_auth->is_authenticated() guard present in all other controllers). Each method fetches data from Report_model, generates a CSV/Excel file, and emails it to a partner contact via Mailgun.

Partner reports delivered on schedule:

Method Schedule (implied) Recipient
runRefineryDaily Daily Refinery Media
runSfGateXMLWeekly Weekly SF Gate
runBridgetowerMonthly Monthly BridgeTower Media
runHerCampusDaily Daily Her Campus
runHerCampusMonthlySite Monthly Her Campus (per site)
runRefineryMonthlySite Monthly Refinery (per site)
runSagaCityQuarterlySite Quarterly Saga City
runGoYuitMonthly Monthly GoYuit
runMansuetoDoubleVerifydaily Daily Mansueto / DoubleVerify
runMoveDoubleVerifydaily Daily Move / DoubleVerify
runFarmJournalMonthly Monthly Farm Journal
runMorganMurphyMonthly Monthly Morgan Murphy Media
runSonomaWeekly Weekly Sonoma Media
runHighSnobietyMonthly Monthly HighSnobiety
runAfarMonthly Monthly Afar
runNPRMonthly Monthly NPR
runAtlanticMonthly Monthly The Atlantic
runPulpoMonthly Monthly Pulpo
runPulpoWeekly Weekly Pulpo
runPulpoDaily Daily Pulpo
runPulpoPreviousDay Daily Pulpo

Utility.php (CLI only) provides resetApiRequestCounts and updateLiveCampaigns — system maintenance tasks run from CLI, not HTTP.

Integration endpoints

Integration Usage Library / mechanism
Internal billing_service API Invoice generation, payment processing, ACH info, remittance file_get_contents() + curl POST (Billing_model.php)
AWS S3 Invoice PDFs, override files, report exports application/libraries/AmazonS3.php (aws-sdk-php)
AWS Cognito User sync, OAuth application/libraries/AwsCognito.php
AWS Redshift Impression analytics queries application/models/Pdo_model.php (PDO direct)
AWS Athena Events querying application/libraries/AwsAthena.php
Mailgun Invoice emails, report delivery application/libraries/Mailgun.php
JumpCloud LDAP Operator authentication application/libraries/Jc_auth.php
Duo Security 2FA (currently disabled) duosecurity/duo_universal_php
DoubleVerify Ad verification report data Direct HTTP calls in Report_model.php
Bitbucket OAuth App deployment Tools.php + curl
AWS CodeDeploy Application deployments application/libraries/AppDeploy.php

Domain decomposition

Domain Current surface Planned API seam Stripe Billing Transition impact Notes
Account / customer management Account.php, Account_model.php, Dashboard.php admin-api /customers, /dashboard None — accounts stay in legacy DB Core operator workflow; cohort 1
Service-level management Account.php (settings), ManagedServiceJob.php, Override.php admin-api /service-levels, /managed-jobs, /overrides None Cohort 2
Campaigns & ads Campaign.php, api/Campaign_Controller.php, Campaigns_model.php admin-api /campaigns, /ads None Cohort 3; ad copy is super-admin only
Billing — invoice view Billing.php (view methods), Billing_model.php admin-api /invoices → reads Stripe Billing transitions to Stripe as SoR Admin becomes a Stripe-read view
Billing — payment processing Billing::processPayment Replaced by Stripe Checkout / PaymentIntent Stripe owns payment collection Manual payment entry via Stripe Dashboard
Billing — invoice PDF + email billing_service API → S3 → Mailgun invoice-renderer Worker → R2 → Mailgun Custom PDF retained (ADR) Webhook-driven off invoice.finalized
Billing — month-end rollup Billing::getMonthSummary, generateInvoices Retired — Stripe accumulates InvoiceItems as-they-happen Rollup job fully retired Excel summary report may be reimplemented as a Stripe-read report
Operator auth / sessions Auth.php, Jc_auth.php, CI sessions Cloudflare Access (edge-terminated) None Directory choice deferred
Partner reports (scheduled) Reporting.php, Report_model.php Cloudflare Cron Trigger Workers None 25 methods → Cron Triggers; deferred to Phase 4
Partner reports (on-demand) Report.php, Report_model.php Deferred — dedicated analytics service evaluation None 5,333-line god-model; not in admin-api scope
Tooling (deploy, Cognito) Tools.php admin-api /tools None App deployment UI; Cognito sync stays
Chart / metrics data Chart.php admin-api /charts None Simple aggregates off existing DB

Strengths

  • Auth is already isolated: Jc_auth.php and Duo libraries are clean seams — dropping them for Cloudflare Access requires only removing the jc_auth->is_authenticated() guard in each controller constructor. No auth logic is scattered through controllers.
  • Models are well-separated from controllers: The PHP model/controller split means DB queries can be read and ported without reverse-engineering view logic.
  • Billing DB is already separate: billing database is physically separate from console — Stripe Billing Transition can take over the billing DB tables without schema conflict.
  • Deployment is environment-parameterized: Bitbucket Pipelines to CodeDeploy pattern is understood; Cloudflare Workers + Pages CI/CD mirrors the same branch→environment model.
  • Mailgun is already library-isolated: application/libraries/Mailgun.php is a clean seam — invoice-renderer Worker can call the same Mailgun API without touching the admin-api codebase.
  • No test debt to preserve: the tests/ directory is empty — no legacy test suite to port.

Pain points

P1 — 🚨 CRITICAL: Production secrets hardcoded in source application/config/adventive.php contains AWS access keys, S3 secrets, and Bitbucket OAuth credentials for all three environments in plaintext. Production key AKIAIAPRP6JG4QQK3YZA is visible in the source history. The file comment reads "to be removed upon inclusion of reading from aws-secretsmanager" — never completed. Remediation is required before migration begins.

P2 — ⚠️ CSRF protection globally disabled application/config/config.php:320'csrf_protection' => false. Mutations (account creation, billing operations, settings changes) have no per-request token protection. The admin relies entirely on session auth. Cloudflare Access + the new API's JWT validation replaces this at the architectural level, but the gap is live today.

P3 — ⚠️ Duo 2FA currently bypassed Most recent auth commit: config(auth): temporarily disable the DUO. Current operator auth is JumpCloud username/password only — no second factor. Live until Cloudflare Access is in place.

P4 — ⚠️ MD5-based payment link security Billing.php:561$payNowUrl .= md5($invoice->payment_customer_id . $invoice->inv_uuid . $invoice->acct_id). MD5 is cryptographically broken for integrity purposes. If payment_customer_id is known or guessable, the link can be forged. Replaced by Stripe Checkout Sessions in the target architecture.

P5 — 📦 phpoffice/phpexcel 1.7.9 — abandoned library Archived 2019. No security patches. Billing::getMonthSummary depends on it directly. Both PHPExcel and its successor phpspreadsheet are installed simultaneously — three Excel libraries total with mk-j/php_xlsxwriter. The month-end summary generator must migrate to phpspreadsheet (or be retired when Stripe owns the billing data).

P6 — 📊 Report_model.php at 5,333 lines — god-model 24 functions, each 100–300 lines of partner-specific SQL with hardcoded account IDs, campaign IDs, and date arithmetic. Analytical queries span Redshift and the MySQL console DB. This is not portable to a standard Hono CRUD route — it requires a dedicated analytics service decision. Recommend Phase 4 deferral with explicit evaluation.

P7 — 🔒 Reporting.php cron controller is unauthenticated HTTP The Reporting controller has no jc_auth->is_authenticated() guard. Any HTTP client that knows the route can trigger a report delivery. In the serverless architecture these become Cloudflare Cron Triggers — auth is handled by the Workers runtime, not application code.

P8 — 🗄️ Raw SQL strings in models Billing_model.php constructs SQL as interpolated strings in multiple places (e.g., lines 33, 86, 101, 148, 193, 256, 329, 347, 432). While values are mostly bound via CodeIgniter's query-builder binds, the pattern mixes interpolated and bound queries, making injection audits difficult. All queries rewritten as parameterized Hono/Drizzle queries in the target.

P9 — 📦 Deploy process requires SSH + AWS CLI in CI runner bitbucket-pipelines.yml bootstraps aws-cli and openssh in an Alpine container on each deploy, clones the private shell repo for deploy scripts, then calls proprietary updateApp.sh. This is fragile and environment-specific. Target: wrangler deploy from GitHub Actions — a one-command deploy with no shared-state scripts.

P10 — 📝 No test coverage tests/ directory is empty. Zero test coverage. phan/phan (static analysis) is in require-dev at version 1 (current: 5) and shows no evidence of active use. Target architecture has unit tests (Vitest), contract tests (generated OpenAPI types), and Playwright E2E.

Target architecture (decided)

┌──────────────────────────────────────────────────────────────┐
│  Operators (~10) behind Cloudflare Access                    │
│  (directory: JumpCloud or Google Workspace, deferred)        │
└───────────────┬──────────────────────────────────────────────┘
                │  HTTPS + CF-Access-Jwt-Assertion
┌──────────────────────────────────────────────────────────────┐
│  admin.adventive.<tld>  — Cloudflare Pages (React SPA)       │
│  - shadcn/ui + Tailwind, brand-guide-aligned                 │
│  - TanStack Router + TanStack Query                          │
│  - Calls admin-api exclusively                               │
│  - Edge-cached static bundle; deep-link via _redirects       │
└───────────────┬──────────────────────────────────────────────┘
                │ HTTPS, OpenAPI contract
                │ JWT validated per request
┌──────────────────────────────────────────────────────────────┐
│  admin-api Worker (TypeScript / Hono)                        │
│  - /customers /accounts /invoices /service-levels /jobs ...  │
│  - RBAC: super-admin, billing, read-only                     │
│  - Reads Stripe for billing; legacy MySQL for everything else│
│  - MySQL via Cloudflare Hyperdrive (connection pooling)      │
└───────────────┬──────────────────────────────────────────────┘
        ┌───────┼──────────────┬──────────────┐
        ▼       ▼              ▼              ▼
  [Legacy DB  [Stripe API]  [Mailgun]  [Public API
   via                       (invoices   Worker]
   Hyperdrive]                via
                              invoice-
                              renderer]

Parallel-run topology during transition:

  Cloudflare Access (shared auth edge)
        │                      │
        ▼                      ▼
  admin.legacy.*         admin.adventive.*
  (CodeIgniter)          (Pages SPA, new)
        │                      │
        ▼                      ▼
  CodeIgniter DAL        admin-api Worker
        │                      │
        └──────────┬───────────┘
          Shared billing / customer DB

Both admins read/write the same database during the transition. Operators migrate cohort-by-cohort; rollback is trivial (return to legacy URL).

Serverless viability assessment

Dimension Current state Serverless fit Notes
Statefulness PHP sessions, CI session library ✅ Excellent Cloudflare Access JWT replaces sessions entirely; admin-api is stateless
Cold-start tolerance Persistent PHP process (EC2) ✅ Good Internal tool — ~10 operators; occasional cold starts are acceptable; Hono is sub-10ms startup
Request duration Typical < 500ms; month-end Excel generation can run 5–30s ⚠️ Caution Excel/CSV generation moves to async (Queues) or client-side; Stripe-read views are fast
Memory footprint PHP process ~64–128 MB typical ✅ Good Hono Worker runs at ~10–20 MB; no full PHP runtime overhead
Dependency surface 10 composer packages; PHPExcel abandoned ✅ Better npm ecosystem; no abandoned-library risk; Stripe SDK, Hono, Zod
PDF rendering External billing_service generates PDF to S3 ⚠️ Constrained Cloudflare Browser Rendering (leading candidate) or external service for invoice-renderer Worker; out of admin-api scope
Secrets / config Hardcoded in adventive.php 🚨 ✅ Much better Wrangler secret / Cloudflare environment variables; no committed secrets
Observability Server logs on EC2; no structured metrics ✅ Better Workers Analytics Engine + Logpush → existing log sink
Cost at current scale EC2 instance + RDS (always-on) ✅ Lower Workers: pay-per-request; Pages: free tier; Hyperdrive: ~$0.001/query at this scale
Scheduled jobs Server cron → unauthenticated HTTP endpoints ✅ Better Cloudflare Cron Triggers — authenticated, managed, observable

Recommendation: Proceed. Cloudflare Workers + Pages is a strong fit for this workload. The only constrained dimension is the billing PDF renderer — this is deliberately out of admin-api scope (handled by the invoice-renderer Worker in the Stripe Billing Transition project). All operator-facing request durations fit comfortably within the 30-second Worker limit with substantial headroom.

Operator workflow catalog

34 distinct operator-facing workflows, organized by cohort migration order:

# Workflow Controller::method URL pattern RBAC tier Frequency Cohort
1 Login / 2FA Auth::login /auth/login All Daily Pre-1
2 Dashboard overview Dashboard::index /dashboard All Daily 1
3 List/search accounts Account::index /account All Daily 1
4 View account detail Account::detail /account/:id All Daily 1
5 Create account Account::newAccount + validateNewAccount /account/newAccount Super-admin Weekly 1
6 Activate trial account Account::activateAccount /account/activateAccount Super-admin Weekly 1
7 Cancel account Account::cancelAccount /account/cancelAccount/:id Super-admin Rare 1
8 Unlock user Account::unlockUser /account/unlockUser/:id Super-admin Weekly 1
9 Toggle account settings Account::changeSetting /account/changeSetting Super-admin Weekly 1
10 Change account admin Account::updateAccountAdmin /account/updateAccountAdmin Super-admin Rare 1
11 User lookup by email Tools::userlookup /tools/userlookup All Daily 1
12 Preview link lookup Tools::lookup + previewSearch /tools/lookup All Weekly 1
13 Billing dashboard Billing::index /billing Billing Daily 2
14 Invoice list / filter Billing::invoices /billing/invoices Billing Daily 2
15 Generate invoices (batch) Billing::generateInvoices /billing/generateInvoices Billing Monthly 2
16 Email invoice batch Billing::emailInvoice /billing/emailInvoice Billing Monthly 2
17 Send invoice reminder Billing::sendInvoiceReminder /billing/sendInvoiceReminder Billing Monthly 2
18 Process payment Billing::processPayment /billing/processPayment Billing Weekly 2
19 Update billing profile Billing::updateBillingProfile /billing/updateBillingProfile Billing Rare 2
20 View delinquent invoices Billing::viewDelinquentInvoices /billing/viewDelinquentInvoices Billing Monthly 2
21 Revenue history Billing::history /billing/history Billing Monthly 2
22 QuickBooks CSV export Billing::generateQuickbooksCSV /billing/generateQuickbooksCSV Billing Monthly 2
23 Commission report Billing::generateCommissionReport /billing/generateCommissionReport Billing Monthly 2
24 Month-end summary (Excel) Billing::getMonthSummary /billing/getMonthSummary Billing Monthly 2
25 Managed services billing overview Billing::managedServices /billing/managedServices Billing Monthly 2
26 Campaign list (account) api/Campaign_Controller::get /api/campaign?resource=accountCampaigns All Weekly 3
27 Ad list (account) api/Campaign_Controller::get /api/campaign?resource=accountAds All Weekly 3
28 Copy ad/template Campaign::copyAdOrTemplate /campaign/copyAdOrTemplate Super-admin Rare 3
29 Managed service jobs list api/Managedservices_Controller::get /api/managedservices?resource=jobs Billing Weekly 3
30 Create/edit managed service job ManagedServiceJob::create/updateManagedServiceJob /managedservicejob/* Billing Weekly 3
31 Override file versioning Override::* /override/* Super-admin Rare 3
32 Ad types / video benchmarks Tools::adtypes, videoBenchmarks /tools/adtypes All Rare 4
33 Special permissions management Tools::specialPermissions /tools/specialPermissions Super-admin Rare 4
34 Application deployment Tools::appDeploy, deploy /tools/appDeploy Super-admin Weekly 4

Out of scope for admin-api (deferred / separate): - Partner report downloads (22 methods in Report.php) — Phase 4 evaluation: dedicated analytics Worker or self-serve Stripe reporting - Partner report scheduled deliveries (25 methods in Reporting.php) — Cloudflare Cron Trigger Workers, separate from admin-api

Data model — billing tables fate

Table Database Fate Owned by
billing_invoice billing Retire — Stripe Invoice becomes SoR Stripe Billing Transition
billing_invoice_account billing Retire — Stripe InvoiceItem Stripe Billing Transition
billing_invoice_usage billing Retire — Stripe usage records Stripe Billing Transition
billing_profiles billing Partial retire — address/ACH fields move to Stripe Customer; stripe_customer_id column stays Stripe Billing Transition
account_plan console Retain during transition; long-term map to Stripe Products/Prices Admin project (read-only in new admin)
account_plan_sub console Retain during transition; migrates to Stripe Subscription Stripe Billing Transition
account_plan_usage console Retain during transition; migrates to Stripe Prices (graduated) Stripe Billing Transition
account console Retain — customer record mirrors Stripe Customer via stripe_customer_id Admin project
users console Retain Admin project
campaign, client, ad_html5 console Retain Admin project
managed_service_jobs console Retain Admin project
override_file_descriptions console Retain — file references migrate from S3 to R2 Admin project

Decisions logged

  • 2026-04-23 — Operator auth: Cloudflare Access. Directory integration (JumpCloud vs. Google Workspace) deferred. See decisions/2026-04-23-cloudflare-access-for-operator-auth.md.
  • 2026-04-23 — UI stack: React + Vite + TypeScript SPA on Cloudflare Pages. shadcn/ui + Radix + Tailwind + TanStack Query + TanStack Router. See decisions/2026-04-23-ui-stack-react-spa.md.
  • 2026-04-23 — Deployment strategy: parallel run, cohort migration, no CodeIgniter retrofit. See decisions/2026-04-23-ship-together-no-codeigniter-retrofit.md.
  • 2026-04-23 — Data store: existing DB stays, admin-api connects directly via Hyperdrive during transition. No data migration in this project scope.
  • 2026-04-23 — Invoice PDF renderer retained (Stripe Billing Transition ADR). Custom Handlebars renderer moves to a dedicated invoice-renderer Worker. See sibling project decisions/2026-04-23-custom-invoice-pdf-retained.md.
  • 2026-04-23 — Stripe owns billing source of truth (Stripe Billing Transition ADR). Month-end rollup retired. Admin billing surface becomes a Stripe-read view. See sibling project decisions/2026-04-23-stripe-billing-scope.md.

Open architectural questions

  1. Partner report surface — 5,333-line Report_model.php with 24 hardcoded partner SQL queries. Options: (a) port to dedicated analytics Worker, (b) retire as partners migrate to Stripe self-serve, (c) thin wrapper exposing Redshift queries via admin-api. Needs evaluation in Phase 4.
  2. R2 vs. S3 for override files — Current override files live in S3 (override-*.adventivecdn.com). Natural migration target is Cloudflare R2 (no egress fees, Workers-native). Decision needed before Phase 3 implementation.
  3. Cohort migration order (confirmed) — Workflows 1–12 (accounts/lookup) → Workflows 13–25 (billing, coordinated with Stripe transition) → Workflows 26–31 (campaigns, managed services, overrides) → Workflows 32–34 + partner reports.
  4. App deployment toolingTools::appDeploy currently orchestrates CodeDeploy via Bitbucket OAuth. Once the admin moves to Workers/Pages, deployment is via wrangler deploy from GitHub Actions. The legacy CodeDeploy tooling in the admin UI may be retired rather than ported.
  5. Month-end Excel summaryBilling::getMonthSummary generates an Excel workbook for billing team review. After Stripe transition, the underlying data comes from Stripe. Should this be rebuilt as a Stripe-read report, or replaced by Stripe's native reporting? Needs Jeffrey + Patrick input.
  6. UI component library — shadcn/ui + Radix + Tailwind confirmed as leading candidate. Formally validate against brandguide.adventive.com/dashboard before Phase 1 implementation begins.