Skip to content

01 — Architecture Assessment

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

AttributeValue
Application version3.3.9 (composer.json)
LanguagePHP ≥ 7.3
FrameworkCodeIgniter 3.1.* (last release 3.1.13, 2022; no active maintenance)
Deploy targetEC2 (Linux) — direct server push via AWS CodeDeploy
CI/CDBitbucket Pipelines — three branch-triggered pipelines (development, staging, production)
Environmentsdevelopment, staging, production (called “ops” in pipeline)
AuthenticationJumpCloud LDAP (application/libraries/Jc_auth.php) + Duo 2FA (duosecurity/duo_universal_php) — Duo currently disabled (see pain points)
Data storesMySQL: console DB (accounts, campaigns, plans), billing DB (invoices, profiles), aggregate DB (stats); AWS Redshift (impression events)
File storageAWS S3 — invoice PDFs (billing-*.adventivecdn.com), override files (override-*.adventivecdn.com)
EmailMailgun (application/libraries/Mailgun.php)
Integration endpointsInternal 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.

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
ControllerURL prefixMethod countLines
Dashboard.php/dashboard142
Auth.php/auth4205
Account.php/account10460
Billing.php/billing18960
Campaign.php/campaign3111
Report.php/report22445
Reporting.php/reporting25997
Tools.php/tools14451
Override.php/override8167
Chart.php/chart498
ManagedServiceJob.php/managedservicejob2104
Utility.php/utility (CLI only)258
api/Campaign_Controller.php/api/campaign2117
api/Managedservices_Controller.php/api/managedservices299

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

ModelFileLinesTablesPrimary operations
Account_modelAccount_model.php1,321account, users, account_executiveSELECT, UPDATE, INSERT
Billing_modelBilling_model.php933billing_invoice, billing_invoice_account, billing_invoice_usage, billing_profiles, account_plan, account_plan_sub, account_plan_usageSELECT, INSERT, UPDATE
Campaigns_modelCampaigns_model.php665campaign, client, ad_html5, ad_formatSELECT, INSERT
Report_modelReport_model.php5,333campaign, ad_html5, stats_aggregate_*, impressions (Redshift)SELECT (complex analytical)
ManagedService_modelManagedService_model.php505managed_service_jobs, managed_service_jobs_ads, managed_service_impression_log, managed_service_job_logSELECT, INSERT, UPDATE
Stats_modelStats_model.php1,286stats_* aggregate tablesSELECT, INSERT
Override_modelOverride_model.php280override_file_descriptions + S3S3 + DB
Dashboard_modelDashboard_model.php121accountSELECT
Advertiser_modelAdvertiser_model.php112clientSELECT, INSERT
Tools_modelTools_model.php298variousSELECT
Utility_modelUtility_model.php89account, api_keysUPDATE
Pdo_modelPdo_model.php124(generic PDO wrapper for Redshift)Generic
Uuid_modelUuid_model.php105UUID generation utility

Views → operator workflow identification

Section titled “Views → operator workflow identification”
DirectoryFile countOperator surface
views/account/12Account list, detail, creation wizard, activation, cancellation
views/billing/11Billing dashboard, invoice list, processing forms, delinquent, managed services
views/campaign/1Campaign + ad list
views/dashboard/1Main dashboard
views/report/5Report listing, queue status
views/tools/14Lookup tools, ad types, benchmarks, permissions, deployment
views/override/2Override file editor + version viewer
views/auth/2Login form, Duo 2FA prompt
views/common/7Header, footer, sidebar, JS includes
views/errors/10Error pages
PackageVersionStatusNotes
codeigniter/framework3.1.*⚠️ End-of-lifeNo active maintenance since 2022
aws/aws-sdk-php3.324.*✓ ActiveS3, Cognito, Athena
chriskacerguis/codeigniter-restserver^3.1⚠️ CommunityNot officially maintained; REST framework for CI3
phpoffice/phpexcel1.7.9🚨 AbandonedArchived 2019; no security patches. Used by Billing::getMonthSummary
phpoffice/phpspreadsheet1.25.*✓ ActiveSuccessor to PHPExcel — both installed simultaneously
mk-j/php_xlsxwriter^0.39✓ ActiveThird Excel library; three overlapping Excel dependencies
duosecurity/duo_universal_php^1.0✓ ActiveCurrently disabled in application
paragonie/sodium_compat^1.21✓ ActiveLibsodium polyfill
firebase/php-jwt^6.4✓ ActiveJWT support
phan/phan^1 (dev)⚠️ OutdatedStatic analysis tool; current major is v5

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:

MethodSchedule (implied)Recipient
runRefineryDailyDailyRefinery Media
runSfGateXMLWeeklyWeeklySF Gate
runBridgetowerMonthlyMonthlyBridgeTower Media
runHerCampusDailyDailyHer Campus
runHerCampusMonthlySiteMonthlyHer Campus (per site)
runRefineryMonthlySiteMonthlyRefinery (per site)
runSagaCityQuarterlySiteQuarterlySaga City
runGoYuitMonthlyMonthlyGoYuit
runMansuetoDoubleVerifydailyDailyMansueto / DoubleVerify
runMoveDoubleVerifydailyDailyMove / DoubleVerify
runFarmJournalMonthlyMonthlyFarm Journal
runMorganMurphyMonthlyMonthlyMorgan Murphy Media
runSonomaWeeklyWeeklySonoma Media
runHighSnobietyMonthlyMonthlyHighSnobiety
runAfarMonthlyMonthlyAfar
runNPRMonthlyMonthlyNPR
runAtlanticMonthlyMonthlyThe Atlantic
runPulpoMonthlyMonthlyPulpo
runPulpoWeeklyWeeklyPulpo
runPulpoDailyDailyPulpo
runPulpoPreviousDayDailyPulpo

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

IntegrationUsageLibrary / mechanism
Internal billing_service APIInvoice generation, payment processing, ACH info, remittancefile_get_contents() + curl POST (Billing_model.php)
AWS S3Invoice PDFs, override files, report exportsapplication/libraries/AmazonS3.php (aws-sdk-php)
AWS CognitoUser sync, OAuthapplication/libraries/AwsCognito.php
AWS RedshiftImpression analytics queriesapplication/models/Pdo_model.php (PDO direct)
AWS AthenaEvents queryingapplication/libraries/AwsAthena.php
MailgunInvoice emails, report deliveryapplication/libraries/Mailgun.php
JumpCloud LDAPOperator authenticationapplication/libraries/Jc_auth.php
Duo Security2FA (currently disabled)duosecurity/duo_universal_php
DoubleVerifyAd verification report dataDirect HTTP calls in Report_model.php
Bitbucket OAuthApp deploymentTools.php + curl
AWS CodeDeployApplication deploymentsapplication/libraries/AppDeploy.php
DomainCurrent surfacePlanned API seamStripe Billing Transition impactNotes
Account / customer managementAccount.php, Account_model.php, Dashboard.phpadmin-api /customers, /dashboardNone — accounts stay in legacy DBCore operator workflow; cohort 1
Service-level managementAccount.php (settings), ManagedServiceJob.php, Override.phpadmin-api /service-levels, /managed-jobs, /overridesNoneCohort 2
Campaigns & adsCampaign.php, api/Campaign_Controller.php, Campaigns_model.phpadmin-api /campaigns, /adsNoneCohort 3; ad copy is super-admin only
Billing — invoice viewBilling.php (view methods), Billing_model.phpadmin-api /invoices → reads StripeBilling transitions to Stripe as SoRAdmin becomes a Stripe-read view
Billing — payment processingBilling::processPaymentReplaced by Stripe Checkout / PaymentIntentStripe owns payment collectionManual payment entry via Stripe Dashboard
Billing — invoice PDF + emailbilling_service API → S3 → Mailguninvoice-renderer Worker → R2 → MailgunCustom PDF retained (ADR)Webhook-driven off invoice.finalized
Billing — month-end rollupBilling::getMonthSummary, generateInvoicesRetired — Stripe accumulates InvoiceItems as-they-happenRollup job fully retiredExcel summary report may be reimplemented as a Stripe-read report
Operator auth / sessionsAuth.php, Jc_auth.php, CI sessionsCloudflare Access (edge-terminated)NoneDirectory choice deferred
Partner reports (scheduled)Reporting.php, Report_model.phpCloudflare Cron Trigger WorkersNone25 methods → Cron Triggers; deferred to Phase 4
Partner reports (on-demand)Report.php, Report_model.phpDeferred — dedicated analytics service evaluationNone5,333-line god-model; not in admin-api scope
Tooling (deploy, Cognito)Tools.phpadmin-api /toolsNoneApp deployment UI; Cognito sync stays
Chart / metrics dataChart.phpadmin-api /chartsNoneSimple aggregates off existing DB
  • 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.

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.

┌──────────────────────────────────────────────────────────────┐
│ 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).

DimensionCurrent stateServerless fitNotes
StatefulnessPHP sessions, CI session library✅ ExcellentCloudflare Access JWT replaces sessions entirely; admin-api is stateless
Cold-start tolerancePersistent PHP process (EC2)✅ GoodInternal tool — ~10 operators; occasional cold starts are acceptable; Hono is sub-10ms startup
Request durationTypical < 500ms; month-end Excel generation can run 5–30s⚠️ CautionExcel/CSV generation moves to async (Queues) or client-side; Stripe-read views are fast
Memory footprintPHP process ~64–128 MB typical✅ GoodHono Worker runs at ~10–20 MB; no full PHP runtime overhead
Dependency surface10 composer packages; PHPExcel abandoned✅ Betternpm ecosystem; no abandoned-library risk; Stripe SDK, Hono, Zod
PDF renderingExternal billing_service generates PDF to S3⚠️ ConstrainedCloudflare Browser Rendering (leading candidate) or external service for invoice-renderer Worker; out of admin-api scope
Secrets / configHardcoded in adventive.php 🚨✅ Much betterWrangler secret / Cloudflare environment variables; no committed secrets
ObservabilityServer logs on EC2; no structured metrics✅ BetterWorkers Analytics Engine + Logpush → existing log sink
Cost at current scaleEC2 instance + RDS (always-on)✅ LowerWorkers: pay-per-request; Pages: free tier; Hyperdrive: ~$0.001/query at this scale
Scheduled jobsServer cron → unauthenticated HTTP endpoints✅ BetterCloudflare 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.

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

#WorkflowController::methodURL patternRBAC tierFrequencyCohort
1Login / 2FAAuth::login/auth/loginAllDailyPre-1
2Dashboard overviewDashboard::index/dashboardAllDaily1
3List/search accountsAccount::index/accountAllDaily1
4View account detailAccount::detail/account/:idAllDaily1
5Create accountAccount::newAccount + validateNewAccount/account/newAccountSuper-adminWeekly1
6Activate trial accountAccount::activateAccount/account/activateAccountSuper-adminWeekly1
7Cancel accountAccount::cancelAccount/account/cancelAccount/:idSuper-adminRare1
8Unlock userAccount::unlockUser/account/unlockUser/:idSuper-adminWeekly1
9Toggle account settingsAccount::changeSetting/account/changeSettingSuper-adminWeekly1
10Change account adminAccount::updateAccountAdmin/account/updateAccountAdminSuper-adminRare1
11User lookup by emailTools::userlookup/tools/userlookupAllDaily1
12Preview link lookupTools::lookup + previewSearch/tools/lookupAllWeekly1
13Billing dashboardBilling::index/billingBillingDaily2
14Invoice list / filterBilling::invoices/billing/invoicesBillingDaily2
15Generate invoices (batch)Billing::generateInvoices/billing/generateInvoicesBillingMonthly2
16Email invoice batchBilling::emailInvoice/billing/emailInvoiceBillingMonthly2
17Send invoice reminderBilling::sendInvoiceReminder/billing/sendInvoiceReminderBillingMonthly2
18Process paymentBilling::processPayment/billing/processPaymentBillingWeekly2
19Update billing profileBilling::updateBillingProfile/billing/updateBillingProfileBillingRare2
20View delinquent invoicesBilling::viewDelinquentInvoices/billing/viewDelinquentInvoicesBillingMonthly2
21Revenue historyBilling::history/billing/historyBillingMonthly2
22QuickBooks CSV exportBilling::generateQuickbooksCSV/billing/generateQuickbooksCSVBillingMonthly2
23Commission reportBilling::generateCommissionReport/billing/generateCommissionReportBillingMonthly2
24Month-end summary (Excel)Billing::getMonthSummary/billing/getMonthSummaryBillingMonthly2
25Managed services billing overviewBilling::managedServices/billing/managedServicesBillingMonthly2
26Campaign list (account)api/Campaign_Controller::get/api/campaign?resource=accountCampaignsAllWeekly3
27Ad list (account)api/Campaign_Controller::get/api/campaign?resource=accountAdsAllWeekly3
28Copy ad/templateCampaign::copyAdOrTemplate/campaign/copyAdOrTemplateSuper-adminRare3
29Managed service jobs listapi/Managedservices_Controller::get/api/managedservices?resource=jobsBillingWeekly3
30Create/edit managed service jobManagedServiceJob::create/updateManagedServiceJob/managedservicejob/*BillingWeekly3
31Override file versioningOverride::*/override/*Super-adminRare3
32Ad types / video benchmarksTools::adtypes, videoBenchmarks/tools/adtypesAllRare4
33Special permissions managementTools::specialPermissions/tools/specialPermissionsSuper-adminRare4
34Application deploymentTools::appDeploy, deploy/tools/appDeploySuper-adminWeekly4

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
TableDatabaseFateOwned by
billing_invoicebillingRetire — Stripe Invoice becomes SoRStripe Billing Transition
billing_invoice_accountbillingRetire — Stripe InvoiceItemStripe Billing Transition
billing_invoice_usagebillingRetire — Stripe usage recordsStripe Billing Transition
billing_profilesbillingPartial retire — address/ACH fields move to Stripe Customer; stripe_customer_id column staysStripe Billing Transition
account_planconsoleRetain during transition; long-term map to Stripe Products/PricesAdmin project (read-only in new admin)
account_plan_subconsoleRetain during transition; migrates to Stripe SubscriptionStripe Billing Transition
account_plan_usageconsoleRetain during transition; migrates to Stripe Prices (graduated)Stripe Billing Transition
accountconsoleRetain — customer record mirrors Stripe Customer via stripe_customer_idAdmin project
usersconsoleRetainAdmin project
campaign, client, ad_html5consoleRetainAdmin project
managed_service_jobsconsoleRetainAdmin project
override_file_descriptionsconsoleRetain — file references migrate from S3 to R2Admin project
  • 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.
  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.