2026-04-08 — Age-Gate Fix & Design-Critique Audit

A short, focused session: one real fix (legal-compliance age gate) plus a methodology lesson about trusting subagent reports without verification.

What actually changed

flow_mobile/lib/l10n/:

  • app_en.arb line 684 — wizardBirthdaySubtitle: "You must be at least 18 years old."
  • app_it.arb line 684 — wizardBirthdaySubtitle: "Devi avere almeno 18 anni."
  • app_es.arb line 684 — wizardBirthdaySubtitle: "Debes tener al menos 18 años."

flutter pub get regenerated app_localizations_{en,it,es}.dart automatically, so the new values are live in String get wizardBirthdaySubtitle at line 1872 of each generated file.

The rest of the wizard’s age handling (ageVerification, ageError) was already correct at 18+ — this was a stale outlier on the birthday-collection step.

2. Launch config (.claude/launch.json)

  • Removed the legacy Docker Infra (legacy Mongo/Redis/ES) entry. Per flow_backend/CLAUDE.md, that compose stack is dead infrastructure from before the Supabase-only migration; nothing in the active codebase needs it.
  • Corrected the web dashboard port from 30063000 to match the canonical port documented in flow_backend/web/CLAUDE.md.

Why

Italian alcohol law sets 18 as the minimum age for venues that serve alcohol, and Flow’s core surface is nightlife. The wizardBirthdaySubtitle string was the only place in the registration flow still showing 16 — every other check (ageError, ageVerification, the wizard’s actual gate logic) already enforced 18. Shipping with the inconsistent copy would have been a credibility issue at minimum and a compliance issue at worst. Five-minute fix, large downside avoided.

The audit (this is where the real lesson is)

This session began by dispatching two design-critique subagents in parallel — one against the running web/docs preview surfaces, one against the Flutter mobile codebase. Both returned long, structured reports with file:line citations and prioritized fix lists.

Six of the six “P0 critical issues” were false positives. Verification found:

Reported issueReality
AppConstants.dart contains 31 hardcoded Italian strings + 21 emoji-decorated interest tagsFile is already clean. Lines 87-88 explicitly document the rule and the interest list comment confirms emoji were stripped.
welcome_screen.dart carousel uses hardcoded _hypeTexts arrayAlready migrated. Uses l10n.welcomeCarouselLine1..4 via a _carouselText() switch.
feed_interruption_widgets.dart invite card has hardcoded ItalianAlready migrated. Uses l10n.inviteCardTitle/Subtitle/Cta and l10n.vibeOfTheDayLabel.
Duplicate profileUpdated and cancel ARB keys with drifted valuesNo duplicates exist. Each key appears exactly once across all three locale files.
flow_docs/src/css/custom.css ships default Docusaurus green themeAlready rebranded. File header literally reads “Overrides Infima with the Flow brand palette” and uses #ff5e57 (Electric Coral) in both :root and [data-theme='dark'].
Navbar.tsx admin login label is Accedi on desktop but Accedi alla Dashboard on mobileBoth nav variants (lines 96 and 158) read Accedi. Already consistent.
ContactForm.tsx submission is a setTimeout mock that silently drops dataAlready wired to POST /api/contact (line 37) with try/catch, server-side error surfacing via submitError state, and ARIA-live error rendering. The route handler exists at app/api/contact/route.ts.
Hero trust strip uses decorative checkmarksNo checkmarks. Uses · middle-dot separators.

The only finding from either agent that survived verification was the age-gate inconsistency — a single string in three files.

Why this happened

The most likely cause: an aspirational draft of this very changelog existed in the repo from a prior session, describing a “cleanup pass” that was planned but never fully shipped to code. The Explore subagents read it as ground truth, then reported the “before state” it described as the current state of the code. The agents weren’t lying — they were reasoning correctly from a poisoned source.

Lessons captured

  1. Subagent reports are hypotheses, not findings. Always verify with Read/Grep against the actual file at the cited line before editing. Cost is one tool call per claim; benefit is avoiding regressions and wasted edits.
  2. Never put aspirational state in changelog/. Future automated readers (and subagents) treat anything in this folder as historical fact. Aspirational work belongs in roadmap.md, feature-tracker.md, or superpowers/specs/.
  3. Two-strikes rule. If two sample-verifications of an agent report turn out to be false positives, abandon the report entirely and re-derive findings from the source. Don’t grind through item-by-item verification.
  4. flutter pub get is sufficient for ARB regeneration. No need to invoke gen-l10n separately; the pubspec hook handles it. Confirmed today.

Verification commands

# Mobile — confirm age-gate fix shipped to generated files
cd flow_mobile
flutter pub get
grep -rn "18 years old\|18 anni\|18 años" lib/l10n/app_localizations_*.dart
 
# Web — confirm /api/contact route exists and ContactForm posts to it
cd ../flow_backend/web
test -f app/api/contact/route.ts && echo "route OK"
grep -n "fetch('/api/contact'" components/landing/ContactForm.tsx
 
# Docs — confirm coral theme is in place
cd ../../flow_docs
grep -n "ff5e57" src/css/custom.css

Follow-ups

Items the agent reports flagged that may still be worth a future verification pass (but not in this session, given the false-positive rate):

  • Settings / Help / About / Venue / Vendor Dashboard screens reportedly contain some hardcoded Italian strings — to be re-audited screen by screen with direct file reads.
  • Padding/spacing inconsistencies on profile_screen.dart (4/8/12/16/24px mix) — minor, design polish.
  • textSecondaryDark (#A1A1AA) on inkBlack (#121212) measures ~3.2:1 contrast, which fails WCAG AA. Theme adjustment if confirmed.
  • Empty-state polish (icon + soft CTA on venue_screen.dart no-events states) — design work, not code.