Evidence Report · Software Valuation

Marketology

AI-powered marketing and ad-automation engine — the "Marketologist" brain. Generates ad proposals with per-persona copy, enforces 8 hard ad-standards via a pre-launch lint gate that blocks non-compliant launches, manages promos / verified targeting / creative uploads, and integrates directly with the Meta Ads API. Deployed live on akconnect at /var/www/marketology.

$89K – $164K Traditional build cost
·
0.9 person-years Engineering effort
·
Built in ~2 weeks Calendar span (file-mtime basis)

The Defensible Quartet

Traditional Build Cost

$89K – $164K

Low: $88,875  ·  Mid: $126,000  ·  High: $164,250
What a normal ad-tech shop charges to design, integrate, and harden a rules-engine of this scope against a live ad platform.

Engineering Effort

0.9 person-years

Mid estimate, two independent models (productivity + COCOMO II post-architecture). Represents the human capital compressed into a two-week build.

Team & Schedule Replaced

~2 × 7 mo

1–2 engineers working for 7 months — the conventional staffing to design, build, test, and integrate an ad-standards enforcement engine against a live Meta API.

Build Speed

~14.4× faster

Shipped in ~2 calendar weeks vs the 7-month conventional schedule. 1 regression test suite, 15 test runs, 13 defects tracked and iterated against.

The Hard Evidence — counts straight from the codebase

Source: cloc on /var/www/marketology, project console DB — reproducible on demand. No git repo; calendar from file mtimes (see footer).
Code (LOC, non-blank, non-comment) 4,285
↳ Python 2,420
↳ JavaScript 1,569
↳ CSS 1,152
↳ HTML 648
↳ Docs / specs 150
↳ Shell 8
Source LOC (all text files) 5,797
Files 13
Git commits n/a — not a git repo (file-mtime calendar basis)
Real calendar span ~2 weeks (file mtimes; see footer)
Defects tracked / resolved 13 tracked  /  5 resolved (38%)
Regression tests 1 test suite (smoke_local.py)
Test runs (total) 15

What's Actually in It — feature enumeration from the source

Proposal Generation & Management

  • Ad proposal CRUD — create, read, update, delete per project (mkt_api.py: mkt_create_proposal / mkt_update_proposal / mkt_delete_proposal)
  • Per-proposal ad items — primary text, headline, slug, CTA, persona, R code stored per ad
  • Auto-generated unique slug per ad to avoid naming collisions (_mkt_unique_slug)
  • Campaign blueprint generation — structured launch plan from a finalized creative (mkt_campaign_blueprint)
  • Proposal list with project-scoped filtering and full-detail dict serialization
  • Multi-project support — BKFK, 6IX, AP Agency, etc. all managed in one engine

Pre-Launch Lint Gate — 8 Ad Standards

  • Single gated endpoint /api/mkt/proposal/<id>/lint — blocks launch on any blocker (mkt_proposal_lint)
  • Rule 1: unique R tracking code required per ad — missing R = hard blocker
  • Rule 1b: R code must be registered into admin.bkfk.ca lead tracking — un-synced = warning
  • Rule 2: promo-forward copy enforcement — if a live promo is active the copy must lead with it; burying the promo = blocker
  • Rule 3: verified targeting IDs only — unverified IDs or name-mismatch (the "Soccer-guard") = hard blocker
  • Rule 4: CTA must never be UNKNOWN or unset — each ad must name its call-to-action
  • Rule 5: pixel / dataset attached — even awareness campaigns must fire the pixel (blocker if missing)
  • Rule 5b: event tracking flag must be ON — explicit OFF is a blocker regardless of pixel
  • Structured response — {ok, blockers:[], warnings:[]} so the UI can surface the exact violation
  • Shared lint gate also wired into the BKFK Admin dashboard via its marketing module (separate dashboard code; only the app dir counted)

Promo Management

  • Promo CRUD — create, update, delete, list per project (mkt_promo_create / mkt_promo_update / mkt_promo_delete / mkt_promos_list)
  • Admin promo sync — pulls live promo from admin.bkfk.ca as the single source of truth (_mkt_sync_admin_promo)
  • Primary promo resolver — selects the canonical active promo for a project (_mkt_primary_promo)
  • 5-minute TTL cache on admin promo reads — keeps copy generation cheap without serving stale data (promo_client.py)
  • Graceful fallback — static 50%→25%→15% default used when admin API is unreachable; response is flagged source:"default" so the UI can warn
  • Project-scoped promo routing — projects without a membership promo return active:0 and a benefit-led copy path

Targeting Catalog (Rule 3)

  • Verified Meta targeting-ID library — stores interest / behavior / demographic IDs with confirmed name, type, path, and audience-size range (targeting_catalog.py)
  • Never-guess enforcement — IDs must be in the verified catalog before they can be used in a proposal; unverified = hard blocker at lint
  • Soccer-guard name-mismatch detection — validator checks meta_id ↔ name parity; a mismatched ID is a lint blocker with the claimed vs real name surfaced
  • Harvest query library per project — BKFK (30+ boxing/fitness/parenting queries), 6IX (talent/fashion/agency queries), more — HARVEST_QUERIES dict in targeting_catalog.py
  • Targeting validate endpoint — caller submits an ID list; returns {ok, mismatch, unknown} (mkt_targeting_validate)
  • Targeting harvest-queries endpoint — returns the per-project query set so the UI can drive a live targetingsearch session (mkt_targeting_harvest_queries)
  • Targeting CRUD — upsert and delete verified IDs in the catalog (mkt_targeting_upsert / mkt_targeting_delete)

Copy Studio

  • AI-generated promo-forward copy per ad — tone-aware, persona-tuned, leads with live promo (mkt_copy_suggest)
  • Copy quality check — validates that copy is benefit-led and promo-forward before it can pass lint (mkt_copy_check)
  • Per-persona copy optimization — BKFK personas include parents, fighters, general; each gets distinct angle (mkt_personas)
  • R-code auto-sync on copy generation — creates and registers the tracking code when ad copy is confirmed (_mkt_register_r via _mkt_r_prefix + _mkt_next_r)
  • R-code backfill — batch endpoint rebuilds missing R codes across all ads in a proposal and re-registers them (mkt_sync_r)

Meta Ads Integration

  • Meta ad account list and upsert — stores account IDs, tokens, and project mapping (mkt_list_ad_accounts / mkt_upsert_ad_account)
  • Live Meta campaign list — fetches active campaigns from the Meta API for a given ad account (mkt_list_campaigns)
  • Resumable multi-part video upload to Meta — chunked upload session, progress tracking (mkt_meta_upload / _meta_resumable_upload)
  • Upload registry — tracks every upload with status, file path, and Meta asset ID (mkt_list_uploads)
  • Attribution passthrough — R-code landing-page attribution synced to admin.bkfk.ca so leads trace back to the specific ad (mkt_attribution / _lp_attribution)

Creative Analysis Pipeline

  • ffmpeg scene-change keyframe extraction from uploaded video ads — up to 20 frames, downscaled to 512px (creative_analysis.py)
  • Audio extraction and OpenAI Whisper transcription of ad audio (whisper-1 API)
  • Claude vision structured breakdown of winning creatives — hook, story arc, visual technique, emotional trigger (claude-sonnet-4-6 vision)
  • Replication recipe generation — actionable production notes extracted from the analysis
  • Per-analysis cost tracking — Whisper + Claude token costs summed and stored as cost_usd
  • Analysis store and retrieval — get endpoint returns the full structured breakdown (mkt_get_analysis)

Recommendations & Attribution

  • AI recommendations engine — stores ranked suggestions per project with status tracking (mkt_recommendations)
  • Recommendation workflow — accept / dismiss / re-open per recommendation (mkt_update_recommendation)
  • Landing-page attribution endpoint — maps inbound lead source r= URL params back to the originating ad and proposal (mkt_attribution)
  • R-code registration into admin.bkfk.ca — new R codes POST into the admin lead-source registry so dashboards show which ad drove each lead (_mkt_register_r)

How We Costed It — methodology

Model. The same model with the same constants is applied to every project in this portfolio — there are no per-project complexity multipliers and no schedule compression applied. The generator is console/backend/tools/evidence_merge.py.

Low bound. Productivity model — 4,285 lines of production code ÷ 25 finished LOC per developer-day ÷ 21.7 work-days per month = 7.9 person-months (0.7 person-years). The 25 LOC/day figure is the conservative industry baseline for finished, tested, reviewed code (McConnell, Code Complete); it excludes generated scaffolding and build artifacts.

High bound. COCOMO II post-architecture — PM = 2.94 × (4.29 KLOC)1.0997 = 14.6 person-months (1.2 person-years). Applied nominally, with no effort-adjustment multipliers, so the ceiling is not inflated.

Mid (reported). The arithmetic mean of the two models: 11.2 person-months = 0.9 person-years. This is the figure quoted throughout this report.

Rate assumption. $135,000/year fully-loaded blended developer (salary, benefits, overhead) = $11,250 per person-month. Applied uniformly to all three effort bounds: low $89K, mid $126K, high $164K.

Calendar. This repository carries a single commit, so its git span is zero and the model applies its 0.5-month floor instead of a measured calendar. COCOMO's schedule equation gives the calendar a conventional team would need: TDEV = 3.67 × 11.20.28 = 7.2 months at a sustainable 1.6 engineers. Speed advantage = 7.2 ÷ 0.5 = 14.4×. Because the calendar is floored rather than measured, this speed multiple is a conservative figure — the raw span would produce a larger one.