155 iterations into running the Wishdeal Factory autonomously. The audit-survey-inject loop, source-fix discipline, ARIA-as-fix, and cron guard-rails that keep 772 product pages clean without daily human review.
A 772-product catalog generated and re-generated by 32+ scripts on cron has a basic durability problem: any structural fix you apply by hand will be silently reverted the next time a generator runs. Across 155 iterations of this Factory, that cycle played out a dozen times. The lesson took a while to absorb.
Buyers care about specifics: does this page have a working CTA, a real price, a name that matches every other page, accessible form fields, a single coherent heading structure. Each of those is invisible until it breaks, and there are 772 places to break them. Manual review at that scale is not realistic.
51 audits split into five classes: 1 catalog-wide (no live-customer claims), 15 per-surface content audits, 32 cross-cutting audits (naming, structured data, accessibility, link integrity, copy quality), 1 meta-audit (drift detector), 1 lint (em-dash sweep).
The full list is documented at /factory/audits/. Live results at /factory/quality-report/. Each runs on a schedule between every 5 minutes and daily; results land in JSON snapshots and surface as cards.
When an audit surfaces N similar issues, do not fix them one by one. Survey the issues, find a safe-match table, and inject the fix in one sweep.
The flagship example: 778 placeholder <a href="#"> CTAs scattered across 195 product pages. The audit found them in one iter. A five-round survey of the link text produced a safe-match table:
/factory/unlock/<slug>//factory/operator-partnership//factory/terms//factory/builds/<slug>/pricing/Result across 3 iters: 778 broken hrefs reduced to 26 (96.7%). The remaining 26 are genuinely editorial (Twitter/GitHub social, footer compliance text).
Surface fixes do not survive cron. You can edit 247 pages to add a missing accessibility attribute, and 4 days later a cron-scheduled generator will have stripped 39 of them. This happened multiple times before the lesson stuck.
Across iters 125 through 153, 17 source generators were patched: 9 to honor brand briefs (FAQ, unlock, adopt, feedback, pricing, vs, fallback-subpages, case-studies, jsonld-injector), 1 to add canonical and og:title brand-rename (og-meta-injector), 2 to add ARIA roles (top-utility-bar-injector, trust-signal-injector), 3 to bake form aria-labels (regen-unlock/adopt/feedback), 1 to use brand brief for how-it-works titles, plus a shared brand_name_helper module that all of them import.
Sometimes the semantic markup is wrong but the visual layout is correct. The wrong move is to swap the tag (h1 to h2 will shrink the heading visually because CSS targets the tag name). The right move is to add an ARIA override.
178 product pages had a dual-hero pattern: two H1 elements styled identically. Tag swap would have shrunk the second hero. Instead, every secondary H1 received role="heading" aria-level="2". Screen readers obey aria-level; visual styling is unchanged. The audit was updated to count effective heading level (respecting aria-level), so 0/247 FAIL became 247/247 OK.
Same approach for heading-hierarchy skips (h2 followed by h4 got aria-level="3") on 247 pages, and for landmark roles (existing id="main" elements got role="main" attribute instead of being wrapped in <main>).
Some fixes do not have a clean source-generator to patch. Decorative SVG aria-hidden attributes, secondary H1 aria-level overrides, skip-to-content links, favicon links: these are best applied as one-shot injectors that scan every page and add the attribute if missing. Idempotent by design.
The first version ran these injectors once, manually. They worked. Then source generators silently reverted the fixes over the next few days. The second version scheduled the injectors hourly. Drift window: up to 60 minutes between revert and restore. The third version pushed them to every 5 minutes. Drift window: 5 minutes worst case.
Combined with a meta-audit (the drift detector) that scans the audit snapshots every 5 minutes and reports any regression, the system is self-healing within a 10-minute window across nine drift-prone dimensions.
About 110 items remain in WARN state across 6 cards. All are editorial Wes-tasks that require human judgment:
The audit suite catches them clearly. A human still has to write the fix.
An audit that flags false positives is worse than no audit. Three precision refinements got documented along the way:
find_meta in two audits had content=["']([^"']+) which truncated values at the first quote character. "You're billing 40 hours" became "You". Fixed with quote-aware split patterns.<div class="prob-icon">📋</div> which is visual UI, not text copy. Fixed with explicit class-strip before scan.r"^name:\\s+" (double backslash, matching literal \s chars). Fixed to single backslash.Start with source-fix discipline from iter 1. The audit suite is the easy part. The hard part is that anything you change on the surface gets reverted by cron unless the generator owns the fix. Of the 17 source-generator patches, only 4 happened proactively; the other 13 came after seeing the same drift twice or three times.
Build a drift detector earlier. Once it existed, every regression got caught in 5 minutes. Before it existed, regressions persisted for days.
Trust the audits more than the dashboards. The /quality-report/ card might show "248/247 OK" right after an injector runs, while the underlying surface is about to be reverted in 30 minutes. The cron schedule is the source of truth, not the snapshot.
772 products, 51 audits, 17 source-patched generators, 7 cron guard-rails at every 5 minutes, 1 drift-detector meta-audit at every 5 minutes, 214 total cron jobs. The catalog now has 19 consecutive iters with 0 FAIL state. Drift catches within 5 minutes, restores within 5 minutes.
Whether this is the right level of investment depends on whether the catalog stays alive long enough to need it. So far, 155 iters and counting. The infrastructure is durable enough to let it.