Date: 2026-05-15 (push mode, 60 min cadence, social-meta + audit-precision iter)
Built twitter-card audit (24th audit class). Discovered an apostrophe-regex bug in two prior audits (false-positive "You" descriptions on products like invoice-ai whose actual tagline is "You're billing 40 hours..."). Fixed the regex AND closed 7 genuinely-empty meta descriptions. Net effect: meta-tags audit went 234/246 to 243/247.
Built audit-twitter-card.py (~80 lines). For each /builds/<slug>/index.html, verifies presence of the 4 twitter:* meta tags:
These are separate from og: tags. When both are present, Twitter/X uses twitter: and falls back to og:*. The og-meta-injector (iter 127) writes all 4 twitter tags, so coverage should be 100%.
Result on first run: 247 pages, 4 issues - all twitter:description too short. The 4 hits were the apostrophe regex bug (see ship 4) - actual content was full taglines starting with "You're..." Regex truncated at the first apostrophe.
After the regex fix: 247/247 clean.
Cron: every hour at :33
Found the bug: the find_meta() helper in both audits used pattern content="\' which captures anything until ANY quote character. So a value like content="You're billing..." matched content="You and stopped at the apostrophe.
Fix: separate patterns for double-quoted and single-quoted values:
content="([^"]+)" for double-quotedcontent='([^']+)' for single-quotedThe fix unblocked:
Inspecting the 11 "thin description" warns from audit-meta-tags revealed 7 had <meta name="description" content=""> (empty string). The iter-127 meta-description-injector had skipped these because it only checked for absence, not emptiness.
Wrote a targeted fill script that:
<meta name="description" content="">Filled 7 products: contract-lifecycle-ai (35c), converc (41c), demand-gen-ai (30c), quote-to-contract-ai (32c), revenue-operations-ai (48c), roofing-ai (100c), win-loss-analysis-ai (52c).
Remaining 4 thin descriptions (20-24 chars) are punchy brand-style taglines (Brand Strategy: "Brand clarity, fast.", Content Calendar: "Plan less. Publish more.", HR Ops: "HR operations, handled.", Repo Scanner: "Your AI built the app."). These are editorial choices - leaving them at warn rather than diluting voice.
Patched regen-quality-report.py:
latest_twitter_card_quality()Live-check card count: 25 -> 26. Total content invariants: 31 -> 32.
| Audit | Cadence | Status |
|---|---|---|
| audit-fakeproof.py | daily | ok |
| audit-adoptability-drift.py | every 15 min | ok |
| audit-page-identity.py | every 30 min | ok |
| audit-hero-polish-drift.py | every 30 min | ok |
| audit-og-coverage.py | every 30 min | ok |
| audit-teaser-quality.py | every 30 min | 247/247 |
| audit-case-studies-quality.py | every 30 min | 239/247 |
| audit-faq-quality.py | every 30 min | 247/247 |
| audit-unlock-content.py | every 30 min | 247/247 |
| audit-adopt-content.py | every 30 min | 247/247 |
| audit-feedback-content.py | every 30 min | 247/247 |
| audit-pricing-content.py | every 30 min | 232/248 |
| audit-vs-content.py | every 30 min | 246/246 |
| audit-how-it-works-content.py | every 30 min | 246/246 |
| audit-sales-kit-content.py | every 30 min | 246/246 |
| audit-skeptic-memos-content.py | every 30 min | 246/246 |
| audit-cross-surface-name.py | every 30 min | 29/29 |
| audit-jsonld-coverage.py | every 30 min | 246/246 |
| audit-meta-tags-coverage.py | every 30 min | 243/247 (was 234) |
| audit-internal-links.py | every 30 min | 246/246 |
| audit-image-alt.py | every hour | 247/247 |
| audit-image-src-exists.py | every hour | 247/247 |
| audit-twitter-card.py | every hour | 247/247 NEW |
| em-dash-sweep.py | every 15 min | running |
3 substantive ships + 1 audit-precision fix at 60-min cadence. The apostrophe-regex bug class is exactly the kind of audit-quality issue that gets harder to spot as audits multiply. Catching it here while there are 4 false positives is cheaper than later when there might be 40.
The audit suite's regex-fix discipline is paying off: a bug in find_meta() would have silently flagged 4-12 products per run forever. Catching it during cross-surface audit iteration shows the value of having 2+ audits use the same helper (mismatch surfaces the bug).