Skip to main content

Portfolio Audit & Scorecard — 2026-05-19

Founder /goal: complete audit of all website properties, detailed scorecard, fix all issues. Run by an orchestrator dispatching 5 parallel read-only audit agents (3 per-repo + cross-cutting + live-health), then parallel fix agents, with the orchestrator as QA reviewer.

Audit scope (what this run DID and did NOT cover)

Covered: production build + TypeScript typecheck + ESLint + unit tests per repo; quality-principle violation scan (165-bug corpus); marketing/pricing copy accuracy vs canonical sources; SEO metadata/canonicals; knowledge derive drift; validate_product_knowledge(); pricing consistency across yaml/md/ts/db; Stripe product hygiene; open founder action items; moderation SLA; live HTTP health of ~50 production URLs across 8 properties.

NOT covered this run (recommended follow-up): live journey-runner 5-Question journey evaluations; viewport Playwright suites (desktop/tablet/mobile); the 8 never-run critical_path: true registry specs flagged in the v7 baseline (stripe-live-checkout, voice-live-call, voice-routing-integration, user-login-cwa, etc.). Those remain open and are the highest-value next step after these fixes land.


Portfolio Scorecard

Status key: ✅ PASS · ⚠ WARN · ❌ FAIL · — N/A / not exercised

PropertyCode healthCopy/knowledge accuracySEO/metaBilling configLive URLsDrift/issuesVerdict
cwa (churchwiseai.com)GOOD
sermonwiseGOOD
sharewise (coming soon)N/A — coming soon
pro-websiteGOOD
pewsearchNEEDS WORK
itw (illustratetheword.com)GOOD
funeralwiseaiNEEDS WORK
vetwiseaiNEEDS WORK
wiseaiagencyNEEDS WORK

Build/test status (3 repos):

  • churchwiseai-web — build ✅ exit 0 (all 7 properties' routes compile), tsc ✅ 0 errors, tests 426/430 pass (3 brittle snapshot, 1 real vocabulary mismatch), lint 183 errors all in e2e/tests/scripts — 0 in shipped src/.
  • pewsearch — build ✅, tsc ✅ 0 errors, tests ✅ 57/57, lint ❌ exit 1 (1 error + 97 warnings).
  • sermon-illustrations (ITW) — build ✅, tsc ✅ 0 errors, no unit-test framework, lint ❌ exit 1 (2 errors + 2 warnings).

Live production: all 8 properties + ~50 URLs return HTTP 200 with valid content, sitemaps, robots.txt. No outages.


Counts

SeverityCountDefinition
P01Customer-facing breakage / billing / security / data loss / safety / build failure
P116Broken non-critical flow / wrong pricing-or-copy shown / missing tier gating / a11y / significant SEO miss
P223Polish / minor copy / code smell / missing test / backlog cleanup

P0 — fix immediately

#PropertyFindingFix
P0-1cross-cuttingproduct_knowledge row "How much does ChurchWiseAI cost?" quotes Voice Pro at $79.95 (archived Apr-10 price; canonical $99.95) and references a non-existent "Voice Suite" tier. The chatbot + voice agent inject this row at runtime → AI actively misquotes prices to prospects.Correct the row to canonical pricing (Voice Starter $49.95 / Pro $99.95, no Suite), write a migration file, run validate_product_knowledge().

P1 — fix this pass

churchwiseai-web

  • P1 [DRIFT] src/lib/outreach-types.ts:5-22OutreachStatus type uses 'link_clicked' / 'pro_website_clicked' which do not exist in the outreach_contacts_status_check DB constraint. Two incompatible status vocabularies coexist.
  • P1 [ANALYTICS] src/lib/outreach-queries.ts:547-560FUNNEL_STATUSES omits provisioned (1,344 live rows) and clicked (25 rows) and includes non-existent statuses → buildFunnelStats silently drops 1,369 rows; the founder outreach funnel undercounts.
  • P1 [COPY] src/app/wiseaiagency/pricing/PricingOverviewClient.tsx:78 — FuneralWiseAI setup shown as $999; canonical is $745 (2026-05-02 revision).
  • P1 [COPY] src/app/wiseaiagency/pricing/page.tsx:27 — page metadata description says "FuneralWiseAI $999 setup" — stale.
  • P1 [SEO] src/app/funeralwiseai/page.tsx, vetwiseai/page.tsx, wiseaiagency/page.tsx (+ demo home) — no alternates.canonical; vertical sites have no canonical URL.
  • P1 [MARKETING] src/app/wiseaiagency/pricing/PricingOverviewClient.tsx:57-83 — VetWiseAI (a shipped, live marketing site) is missing from the hub pricing comparison.

pewsearch

  • P1 [SEO/DATA] src/lib/queries.ts:798searchChurchesByAccessibilityDirect uses .ilike('about->>Accessibility', …); about->'Accessibility' is a nested JSON object, not text → query errors at runtime → /churches-with/[feature] accessibility pages render 0 churches despite 25,347 matching rows. Fix: call the existing search_churches_by_accessibility RPC.
  • P1 [SEO] src/app/directory/[state]/page.tsx:14-24 — state landing pages (~50+) have no alternates.canonical.
  • P1 [SEO] src/app/directory/[state]/[slug]/page.tsx:89-120 — city + state-denomination pages (thousands) have no alternates.canonical.

sermon-illustrations (ITW)

  • P1 [LINT] src/components/pricing/PricingInteractive.tsx:318react-hooks/set-state-in-effect error fails pnpm lint.
  • P1 [LINT] src/app/api/stripe/webhook/route.ts:127@typescript-eslint/no-explicit-any error fails pnpm lint.
  • P1 [COPY] "50,000+" illustration count drifts from the real count — appears in page.tsx:31, help/page.tsx:65,121, PricingInteractive.tsx:28, layout.tsx:34.
  • P1 [DOCS] src/app/help/page.tsx:164-166 — account-deletion FAQ says "email support" but a self-service /api/account/delete route exists.

cross-cutting

  • P1 [PRICING] product_knowledge duplicate "Can I upgrade from Premium…" row says Premium $9.95/mo; canonical is $4.95 since 2026-04-16.
  • P1 [DRIFT] knowledge/INDEX.mdpnpm derive --check reports DRIFT (stale counts, missing "Reference Data" section).
  • P1 [PRICING] knowledge/data/pricing.yamlstarter_voice / pro_voice / *_both .live.monthly_price_id fields point to archived Apr-10 Stripe price IDs (price_1T99…); canonical live IDs are price_1TLDX…. The cad_prices block is correct, so the file contradicts itself.

ops (investigated, not code)

  • P1 [OPS] ops_errors d70dce84 — P0 voice-health failure 2026-05-19 03:31 UTC. Investigated: transient. One fetch failed reaching the LiveKit control API; trunk/schema/Telnyx checks all passed same run; single occurrence; LiveKit API confirmed reachable now (agent redeployed 17:06 UTC). Recommend marking the ops_error resolved. Real fix below (P2).
  • P1 [OUTREACH] founder_action_items 27b485c3 — "429 outreach drafts await approval (>48h)". Founder action — sends require founder blessing; not an agent fix.

P2 — polish / backlog

churchwiseai-web

  • P2 [BOUNDARY] src/lib/voice-queries.ts:1 — imports ./supabase but lacks import 'server-only'; 5 'use client' components import types from it (build passes only because type imports are elided). Add the guard + move types to a *-shared.ts.
  • P2 [DOCS] vet-checkout/route.ts:38 + vetwiseai/checkout/page.tsx:12 + CheckoutFormClient.tsx:10 — "PLACEHOLDER TODO" price-ID comments are stale; vet-pricing.ts has real live IDs.
  • P2 [DB] src/app/api/founder/action-items/route.ts:15.select('*') with no .limit()/.range() (unbounded). ~40 other src/ files use .select('*') (mostly non-sensitive, founder-internal) — tracked as backlog, not fixed this pass (40-file mechanical refactor; regression risk outweighs value per "no refactor beyond task").
  • P2 [TEST] webhook-outreach-attribution.test.ts:169-194 — test encodes the wrong status vocabulary (1 failing subtest); realign with the DB constraint.
  • P2 [TEST] test_chatbot_snapshots.test.ts — 3 failing subtests are live-LLM marker brittleness; the actual chatbot responses are correct and on-protocol. Broaden allowed markers.
  • P2 [LINT] 183 ESLint errors in e2e/, tests/, scripts/, tmp/0 in shipped src/. Backlog cleanup, not launch-blocking.
  • P2 [SEO/UX] churchwiseai.com/security redirects to /privacy (generic privacy doc), but the route table + robots.txt treat /security as a real dedicated security page. Investigate; either restore the page or reconcile docs.

pewsearch

  • P2 [CODE-HEALTH] src/components/CrossPromoBanner.tsx:14react-hooks/set-state-in-effect error makes pnpm lint exit 1 (blocks any CI lint gate).
  • P2 [SECURITY] src/lib/rate-limit.ts — in-memory rate limiter resets on every serverless cold start; effectively unthrottled. Deferred — needs an Upstash/Supabase-RPC infra decision (founder).
  • P2 [CODE-SMELL] src/lib/queries.ts:262-267getFeaturedChurches joins premium_churches with no .limit(), filters in memory.
  • P2 [SEO] src/app/directory/page.tsx:59-88 — filtered directory variants share the bare canonical (OK) but emit no robots: noindex (optional cleanliness).
  • P2 [LINT] 97 ESLint warnings, mostly no-html-link-for-pages (raw <a> for internal nav) — convert the clean ones to next/link.
  • P2 [CODE-HEALTH] ~25 stray temp files at repo root (temp_church_*.json, etc.) — gitignore the untracked ones.

sermon-illustrations (ITW)

  • P2 [SECURITY] src/lib/csrf.tsALLOWED_ORIGINS omits www. (apex-only today; harmless future-proofing).
  • P2 [WARN] src/app/signup/page.tsx:48react-hooks/exhaustive-deps missing from.
  • P2 [WARN] src/components/illustrations/ChurchWiseAIBlogCTA.tsx:46 — unused themes prop.
  • P2 [DB] src/lib/queries/lectionary.ts (6 sites) — .select('*') on small RCL reference tables; tighten to explicit columns.
  • P2 [DATA] src/app/api/account/delete/route.ts:60-64tablesToClean omits user_collections / user_collection_items; verify FK cascade or add them.

cross-cutting

  • P2 [OPS] /api/cron/voice-health connectivity check has no retry — one transient network blip raises a P0 (root cause of P1 ops finding above). Add retry/backoff.
  • P2 [PRICING] product_knowledge Pro Website rows — verify "12 tools" / "200 conversations/mo" against features.yaml.
  • P2 [STRIPE] Legacy "PewSearch Pro Website" test product (prod_UAQlkScRcaeT4q) lingers post-decouple — archive once confirmed unused.
  • P2 [DOC] PRICING.md — missing a VetWiseAI section; still says FuneralWiseAI "$999 setup"; header date "April 11, 2026" predates all recent changes.
  • P2 [DOC] Root C:\dev\CLAUDE.md Pricing line still says FuneralWiseAI "$999 setup" — should be $745.
  • P2 [DOC] FOUNDER_ACTIONS.md (3 open P1) vs founder_action_items DB table (1 open P1) are out of sync.

Fix plan

TrackOwnerScope
pewsearchfix agent (running)accessibility RPC, state/city canonicals, lint error, unbounded join, gitignore
itwfix agent (running)2 lint errors, illustration-count copy, help-doc FAQ, warnings, lectionary columns, delete-route tables
cwa outreachfix agentoutreach-types.ts + outreach-queries.ts + webhook-outreach-attribution.test.ts realign to DB constraint
cwa pricing/SEOfix agentwiseaiagency $999→$745 (×2), VetWiseAI hub entry, vertical-page canonicals, vet-checkout stale comments, /security investigation
cwa boundary/testsfix agentvoice-queries.ts server-only + type extraction, chatbot snapshot test markers, action-items .limit()
cross-cuttingorchestratorproduct_knowledge P0+P1 (migration + validate), pricing.yaml live IDs, pnpm derive, PRICING.md, root CLAUDE.md, FOUNDER_ACTIONS.md sync

Deferred (founder decision): pewsearch rate-limiter infra; the 429 outreach drafts; the 40-file select('*') refactor; restoring a dedicated /security page (content/legal).

Next step after fixes: run the journey-runner + viewport Playwright suites and the 8 never-run critical-path specs — none were exercised this run.


Resolution — fixes applied 2026-05-19

All fixes below were made on branch feat/portfolio-audit-fixes-2026-05-19 in each repo (churchwiseai-web, pewsearch, sermon-illustrations) and on a branch in the knowledge repo. Verification: tsc clean + production build green for all 3 code repos; validate_product_knowledge() 0 CRITICAL after DB edits; every contested Stripe price ID verified against live Stripe.

Fixed — P0

  • product_knowledge "How much does ChurchWiseAI cost?" — Voice Pro $79.95→$99.95, removed phantom "Voice Suite" tier. (DB + migration 2026-05-19-product-knowledge-voice-pro-price-fix.sql.)
  • knowledge/products/churchwiseai/help.md pricing table — Voice Pro $79.95→$99.95, deleted phantom Voice Suite row, corrected all 3 bundle annual prices ($604/$1319/$1539 → $599/$1299/$1499 — they used a monthly×11 estimate, not the real Stripe prices) and the 3 chat annuals to canonical .50 values.
  • product_knowledge 49d6f6a4 — FuneralWiseAI setup $999→$745.

Fixed — P1

  • churchwiseai-web — outreach status vocabulary realigned to the DB outreach_contacts_status_check constraint across 7 files; funnel now counts the 1,369 previously-dropped provisioned/clicked rows; wiseaiagency FuneralWiseAI $999→$745 (component + metadata); VetWiseAI added to the hub pricing comparison (with the hub-link path-doubling bug caught + fixed in QA review); alternates.canonical added to funeralwiseai / vetwiseai / wiseaiagency pages.
  • pewsearchsearchChurchesByAccessibilityDirect rewritten to use the working search_churches_by_accessibility RPC (the /churches-with/[feature] pages now render churches — RPC verified returns 20,171+ rows; previously 0); alternates.canonical added to state + city/denomination directory pages.
  • ITW — 2 ESLint errors fixed (lint now 0/0); account-deletion help FAQ corrected to point at the real /profile delete action.
  • cross-cuttingpricing.yaml per-product + env_var_mapping Voice/Bundle live IDs corrected from archived T99* (pre-overhaul $39.95–$99.95) to canonical TLDX* IDs — every replacement verified against live Stripe; pnpm derive data/pricing.yaml re-run clean (PRICING.md UP TO DATE, confirming pricing.ts was already correct).
  • KB rows — 3 Pro Website rows de-stale'd off the 2026-04-18 PewSearch decouple (d0e8cd59, 8df803fc, bec0e0fc); 6 stale Pro Website references removed from the rendered PewSearch help page.
  • opsops_errors d70dce84 (P0 voice-health) investigated → transient single-occurrence LiveKit-API blip, self-resolved, no outage; marked resolved.

Fixed — P2

  • churchwiseai-web: voice-queries.ts server-only boundary closed (types extracted to new voice-shared.ts, 7 importers repointed); /api/founder/action-items unbounded query bounded with .limit(500); 3 brittle chatbot snapshot tests de-flaked (markers broadened — responses were already correct); stale "PLACEHOLDER TODO" vet-checkout comments removed.
  • pewsearch: CrossPromoBanner lint error fixed (CI lint gate unblocked); getFeaturedChurches join bounded with .limit(200).
  • ITW: 2 ESLint warnings cleared; 6 lectionary.ts select('*') tightened to explicit columns; csrf.ts ALLOWED_ORIGINS future-proofed.
  • KB: voice-agent LLM-stack row de-stale'd (Groq→Haiku/Gemini); internal plan-key strings removed from 2 customer-facing answers.
  • docs: root C:\dev\CLAUDE.md — FuneralWiseAI $999→$745 + PewSearch Premium corrected to $4.95.

Findings reclassified during QA review (NOT bugs)

  • "$9.95 Premium" in product_knowledge — false positive. Every $9.95 reference is to ITW Premium, which IS $9.95. No PewSearch-Premium price error exists.
  • KB row "9 team roles"correct. TeamRole in premium-shared.ts genuinely has 9 members (spiritual_leader + care_leader are real, RBAC-wired). It is churchwiseai-web's CLAUDE.md ("7 roles") that is stale — logged as a doc-drift item below.
  • ITW "50,000+" illustration countaccurate. Real dir_illustrations count is 50,739; "50,000+" is correct (the "53K+" target in qa-checklist.md is itself stale).

Still open — founder action required

  • RESOLVED 2026-05-20pnpm derive --all crash fixed (getSourceHash virtual-source guard extended to match loadSourceData); the stale INDEX.md was regenerated.
  • RESOLVED 2026-05-20 — the 5 old T99* Voice/Bundle prices archived in live Stripe (founder-approved). Each of the 5 products had its default_price still pointing at the pre-overhaul price — repointed to the canonical TLDX* price as part of the archival. Grandfathered subs on T99LM/T99MH are unaffected (archiving never touches existing subscriptions).
  • VERIFIED 2026-05-20 — all 5 Vercel production STRIPE_PRICE_{STARTER,PRO}_VOICE / _{STARTER,PRO,SUITE}_BOTH env vars hold the canonical TLDX* IDs. Archiving the old T99* prices did not affect live checkout.
  • /securitychurchwiseai.com/security is an intentional redirect('/privacy'); either build a real security page or correct the route-table docs (content/legal call).
  • churchwiseai-web CLAUDE.md "7 roles" → 9 (+ data-access table needs the 2 new roles); pewsearch/CLAUDE.md still documents Pro Website as a PewSearch product.
  • 429 outreach drafts awaiting founder approval; FOUNDER_ACTIONS.mdfounder_action_items table out of sync.
  • Deliberately deferred (refactor risk / infra decision): pewsearch in-memory rate-limiter; 40-file select('*') cleanup; 183 e2e/test/script ESLint errors; voice-health cron retry-on-transient.