You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Three landing pieces:
1. Schema-validated LLM outputs (the "no false results" guarantee)
- New lib/llm-structured.ts: llmCallStructured() wraps any LLM call
with Zod schema validation + one automatic retry-on-fail that
re-prompts the same model with the validation errors so it can
self-correct. Throws StructuredLlmError on second failure rather
than letting bad shapes crash the renderer.
- GeneratorShell now honors an optional schema on GeneratorConfig.
When the streamed JSON fails Zod parse, the shell sends one
non-streaming corrective round-trip behind a "ai output
auto-corrected" badge. If that also fails, the user sees an
actionable error instead of a UI crash from undefined.map().
- Schemas wired for the three highest-traffic structured generators:
lib/schemas/creative-score.ts
lib/schemas/content-calendar.ts
lib/schemas/campaign-kit.ts
- 10 new vitest cases cover clean parse, fenced JSON, wrong-shape
retry success/fail, total non-JSON retry, maxAttempts=1.
2. /research/competitors honesty
- The LLM can't fetch live ads; previously, submitting just a
competitor name produced plausible-but-invented teardowns.
- Form now requires >= 120 chars of pasted ad copy and shows an
inline "the AI has no live access to ad libraries" note.
- Prompt has a CRITICAL clause: if the paste block is empty or
trivial, emit an explanatory error JSON instead of inventing ads.
- Renderer detects that error JSON and shows a clear nudge to use
the Meta Ads Library / Google Transparency / TikTok Top Ads /
LinkedIn Ad Library buttons above.
3. Accessibility + mobile pass (from punch-list audit)
- Skip-to-content link in app/layout.tsx
- MobileNav: h-11 hamburger + close, role="dialog" + aria-modal,
Esc handler, focus moved into drawer on open + returned to
trigger on close
- StatusBar: flex-wrap so 8-cell row reflows under 380px
- CopyButton: py-2 mobile / py-1 desktop (44px touch), aria-live
so screen readers announce "copied"
- GeneratorShell FieldRenderer: stable inputId + htmlFor on every
label, aria-required on required fields — fixes a11y across ALL
generators in one place
- File input now has aria-label
- Stop buttons in GeneratorShell, competitors page, launch wizard
all gained aria-label="Stop ..."
- Settings ToggleRow: role="switch" + aria-checked
- CommandPalette: tabIndex=-1 on option buttons traps Tab focus on
the search input so keyboard users can't escape behind the modal
Build: tsc clean, 53/53 unit tests pass, next build clean (76 routes,
+0 kB on shared chunks, +5 kB on competitors due to honesty branch).
0 commit comments