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
| Property | Code health | Copy/knowledge accuracy | SEO/meta | Billing config | Live URLs | Drift/issues | Verdict |
|---|---|---|---|---|---|---|---|
| cwa (churchwiseai.com) | ✅ | ⚠ | ⚠ | ✅ | ✅ | ⚠ | GOOD |
| sermonwise | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | GOOD |
| sharewise (coming soon) | ✅ | ✅ | — | — | ✅ | ✅ | N/A — coming soon |
| pro-website | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | GOOD |
| pewsearch | ⚠ | ✅ | ❌ | ✅ | ✅ | ⚠ | NEEDS WORK |
| itw (illustratetheword.com) | ⚠ | ⚠ | ✅ | ✅ | ✅ | ✅ | GOOD |
| funeralwiseai | ✅ | ⚠ | ❌ | ✅ | ✅ | ⚠ | NEEDS WORK |
| vetwiseai | ✅ | ⚠ | ❌ | ⚠ | ✅ | ⚠ | NEEDS WORK |
| wiseaiagency | ✅ | ❌ | ❌ | ✅ | ✅ | ⚠ | NEEDS 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 shippedsrc/.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
| Severity | Count | Definition |
|---|---|---|
| P0 | 1 | Customer-facing breakage / billing / security / data loss / safety / build failure |
| P1 | 16 | Broken non-critical flow / wrong pricing-or-copy shown / missing tier gating / a11y / significant SEO miss |
| P2 | 23 | Polish / minor copy / code smell / missing test / backlog cleanup |
P0 — fix immediately
| # | Property | Finding | Fix |
|---|---|---|---|
| P0-1 | cross-cutting | product_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-22—OutreachStatustype uses'link_clicked'/'pro_website_clicked'which do not exist in theoutreach_contacts_status_checkDB constraint. Two incompatible status vocabularies coexist. - P1
[ANALYTICS]src/lib/outreach-queries.ts:547-560—FUNNEL_STATUSESomitsprovisioned(1,344 live rows) andclicked(25 rows) and includes non-existent statuses →buildFunnelStatssilently 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) — noalternates.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:798—searchChurchesByAccessibilityDirectuses.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 existingsearch_churches_by_accessibilityRPC. - P1
[SEO]src/app/directory/[state]/page.tsx:14-24— state landing pages (~50+) have noalternates.canonical. - P1
[SEO]src/app/directory/[state]/[slug]/page.tsx:89-120— city + state-denomination pages (thousands) have noalternates.canonical.
sermon-illustrations (ITW)
- P1
[LINT]src/components/pricing/PricingInteractive.tsx:318—react-hooks/set-state-in-effecterror failspnpm lint. - P1
[LINT]src/app/api/stripe/webhook/route.ts:127—@typescript-eslint/no-explicit-anyerror failspnpm lint. - P1
[COPY]"50,000+" illustration count drifts from the real count — appears inpage.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/deleteroute exists.
cross-cutting
- P1
[PRICING]product_knowledgeduplicate "Can I upgrade from Premium…" row says Premium$9.95/mo; canonical is$4.95since 2026-04-16. - P1
[DRIFT]knowledge/INDEX.md—pnpm derive --checkreports DRIFT (stale counts, missing "Reference Data" section). - P1
[PRICING]knowledge/data/pricing.yaml—starter_voice/pro_voice/*_both.live.monthly_price_idfields point to archived Apr-10 Stripe price IDs (price_1T99…); canonical live IDs areprice_1TLDX…. Thecad_pricesblock is correct, so the file contradicts itself.
ops (investigated, not code)
- P1
[OPS]ops_errorsd70dce84— P0 voice-health failure 2026-05-19 03:31 UTC. Investigated: transient. Onefetch failedreaching 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./supabasebut lacksimport '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.tshas real live IDs. - P2
[DB]src/app/api/founder/action-items/route.ts:15—.select('*')with no.limit()/.range()(unbounded). ~40 othersrc/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 ine2e/,tests/,scripts/,tmp/— 0 in shippedsrc/. Backlog cleanup, not launch-blocking. - P2
[SEO/UX]churchwiseai.com/securityredirects to/privacy(generic privacy doc), but the route table + robots.txt treat/securityas a real dedicated security page. Investigate; either restore the page or reconcile docs.
pewsearch
- P2
[CODE-HEALTH]src/components/CrossPromoBanner.tsx:14—react-hooks/set-state-in-effecterror makespnpm lintexit 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-267—getFeaturedChurchesjoinspremium_churcheswith no.limit(), filters in memory. - P2
[SEO]src/app/directory/page.tsx:59-88— filtered directory variants share the bare canonical (OK) but emit norobots: noindex(optional cleanliness). - P2
[LINT]97 ESLint warnings, mostlyno-html-link-for-pages(raw<a>for internal nav) — convert the clean ones tonext/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.ts—ALLOWED_ORIGINSomitswww.(apex-only today; harmless future-proofing). - P2
[WARN]src/app/signup/page.tsx:48—react-hooks/exhaustive-depsmissingfrom. - P2
[WARN]src/components/illustrations/ChurchWiseAIBlogCTA.tsx:46— unusedthemesprop. - 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-64—tablesToCleanomitsuser_collections/user_collection_items; verify FK cascade or add them.
cross-cutting
- P2
[OPS]/api/cron/voice-healthconnectivity check has no retry — one transient network blip raises a P0 (root cause of P1 ops finding above). Add retry/backoff. - P2
[PRICING]product_knowledgePro Website rows — verify "12 tools" / "200 conversations/mo" againstfeatures.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]RootC:\dev\CLAUDE.mdPricing line still says FuneralWiseAI "$999 setup" — should be $745. - P2
[DOC]FOUNDER_ACTIONS.md(3 open P1) vsfounder_action_itemsDB table (1 open P1) are out of sync.
Fix plan
| Track | Owner | Scope |
|---|---|---|
| pewsearch | fix agent (running) | accessibility RPC, state/city canonicals, lint error, unbounded join, gitignore |
| itw | fix agent (running) | 2 lint errors, illustration-count copy, help-doc FAQ, warnings, lectionary columns, delete-route tables |
| cwa outreach | fix agent | outreach-types.ts + outreach-queries.ts + webhook-outreach-attribution.test.ts realign to DB constraint |
| cwa pricing/SEO | fix agent | wiseaiagency $999→$745 (×2), VetWiseAI hub entry, vertical-page canonicals, vet-checkout stale comments, /security investigation |
| cwa boundary/tests | fix agent | voice-queries.ts server-only + type extraction, chatbot snapshot test markers, action-items .limit() |
| cross-cutting | orchestrator | product_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 + migration2026-05-19-product-knowledge-voice-pro-price-fix.sql.)knowledge/products/churchwiseai/help.mdpricing 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 amonthly×11estimate, not the real Stripe prices) and the 3 chat annuals to canonical.50values.product_knowledge49d6f6a4— FuneralWiseAI setup $999→$745.
Fixed — P1
- churchwiseai-web — outreach status vocabulary realigned to the DB
outreach_contacts_status_checkconstraint across 7 files; funnel now counts the 1,369 previously-droppedprovisioned/clickedrows; 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.canonicaladded to funeralwiseai / vetwiseai / wiseaiagency pages. - pewsearch —
searchChurchesByAccessibilityDirectrewritten to use the workingsearch_churches_by_accessibilityRPC (the/churches-with/[feature]pages now render churches — RPC verified returns 20,171+ rows; previously 0);alternates.canonicaladded 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
/profiledelete action. - cross-cutting —
pricing.yamlper-product +env_var_mappingVoice/BundleliveIDs corrected from archivedT99*(pre-overhaul $39.95–$99.95) to canonicalTLDX*IDs — every replacement verified against live Stripe;pnpm derive data/pricing.yamlre-run clean (PRICING.mdUP TO DATE, confirmingpricing.tswas 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. - ops —
ops_errors d70dce84(P0 voice-health) investigated → transient single-occurrence LiveKit-API blip, self-resolved, no outage; marked resolved.
Fixed — P2
- churchwiseai-web:
voice-queries.tsserver-onlyboundary closed (types extracted to newvoice-shared.ts, 7 importers repointed);/api/founder/action-itemsunbounded 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:
CrossPromoBannerlint error fixed (CI lint gate unblocked);getFeaturedChurchesjoin bounded with.limit(200). - ITW: 2 ESLint warnings cleared; 6
lectionary.tsselect('*')tightened to explicit columns;csrf.tsALLOWED_ORIGINSfuture-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.95reference is to ITW Premium, which IS $9.95. No PewSearch-Premium price error exists. - KB row "9 team roles" — correct.
TeamRoleinpremium-shared.tsgenuinely has 9 members (spiritual_leader+care_leaderare 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 count — accurate. Real
dir_illustrationscount 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-20 —
pnpm derive --allcrash fixed (getSourceHashvirtual-source guard extended to matchloadSourceData); the staleINDEX.mdwas regenerated. - ✅ RESOLVED 2026-05-20 — the 5 old
T99*Voice/Bundle prices archived in live Stripe (founder-approved). Each of the 5 products had itsdefault_pricestill pointing at the pre-overhaul price — repointed to the canonicalTLDX*price as part of the archival. Grandfathered subs onT99LM/T99MHare unaffected (archiving never touches existing subscriptions). - ✅ VERIFIED 2026-05-20 — all 5 Vercel production
STRIPE_PRICE_{STARTER,PRO}_VOICE/_{STARTER,PRO,SUITE}_BOTHenv vars hold the canonicalTLDX*IDs. Archiving the oldT99*prices did not affect live checkout. /security—churchwiseai.com/securityis an intentionalredirect('/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.mdstill documents Pro Website as a PewSearch product. - 429 outreach drafts awaiting founder approval;
FOUNDER_ACTIONS.md↔founder_action_itemstable 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.