# Changelog

> Everything shipped on Unformal, reverse chronological.

**URL:** https://unformal.ai/changelog
**Updated:** 2026-08-20
**Topics:** changelog, releases, shipped features

## Summary

Reverse-chronological log of features and fixes shipped on Unformal. Covers 11 dated releases, most recently 2026-04-18. Synced from the canonical changelog in the unformal-workspace repo.

---

Shipped features and fixes, reverse chronological. For the roadmap and upcoming features, see Roadmap.

---

## 2026-04-18

### CLI Documentation — @unformal/cli Now Surfaced Everywhere
The `@unformal/cli` npm package has existed for a while but wasn't documented on the public site. Now surfaced in all four entry points: the `/agents` "Built for Agents" page, the `/docs/api` reference, the `UNFORMAL.md` skill file, and `llms.txt`.
- Install: `npm i -g @unformal/cli` (or `npx @unformal/cli <command>`)
- Every API endpoint exposed as a subcommand — `unformal create`, `list`, `get`, `conversations`, `resonance`, `analytics`, `export`, `usage`
- `--json` flag on every command for piping structured output to other agents
- Source at github.com/Unformalai/unformal-skills/tree/master/cli

### Notification Routine — Claude Code Desktop Routines (Primary Path, Corrected)
Recommend Claude Code desktop routines (SKILL.md files at `~/.claude/scheduled-tasks/<name>/`) as the primary notification path — these appear in the Claude Code desktop sidebar alongside other routines, run locally with access to local secrets, and let the user pick the schedule visually in the desktop UI.
- Initial attempt used the `schedule` skill (remote triggers at claude.ai/code/scheduled) which was the wrong surface — those don't appear in the desktop sidebar
- Corrected: `unformal-notifications` SKILL.md (in unformal-skills repo) now describes the desktop-routine pattern with the exact SKILL.md template and tempfile-based Python parsing (avoids heredoc stdin collision)
- `/agents` page section updated: "Claude Code Desktop Routine" with the corrected copy-paste prompt
- `UNFORMAL.md` skill updated to match
- Created working example at `~/.claude/scheduled-tasks/unformal-new-responses/SKILL.md` for WAT feedback monitoring
- Disabled the (wrong) remote trigger `trig_01R77BAkEBZehYSdYc5FvpRK`

---

## 2026-04-17

### Desktop Notifications — Real-time Pulse Response Alerts
New `unformal-notifications` Agent Skill in the [unformal-skills repo](https://github.com/Unformalai/unformal-skills) that gives Claude Code (or any agent) real-time awareness of new Pulse responses.
- New `unformal-notifications/SKILL.md` — teaches Claude to summarize inbox events on demand, fall back to `completedSince` API polling when no listener is running, archive processed events, and act on new responses
- New `unformal-listen.sh` shell script — connects to the SSE stream, shows native macOS notifications (Linux: `notify-send` fallback), writes each event to `~/.unformal/inbox/*.json`
- Script also hosted at unformal.ai/unformal-listen.sh for quick one-liner install
- New "Desktop Notifications" section on `/agents` page with install + demo output
- UNFORMAL.md skill extended with the same notification patterns

### HR Use Case — Refocused on Feedback Surveys
Shifted the HR use case page away from "exit interviews" (which felt heavy and odd to productize as a survey) toward feedback surveys and interviews — the actual sweet spot for a conversational form tool.
- Hero: "What if feedback surveys captured what people really think?"
- Use case table now leads with engagement pulse checks, onboarding feedback, team feedback interviews, performance reviews, 360 feedback, and training/event feedback
- Nav dropdown, landing page card, and OG metadata updated to match
- Exit interviews removed as a headline use case

### Interviewer-style Thinking Animation (replaces token-by-token streaming)
Replaced the slow character-by-character streaming of AI responses with a rotating shimmer animation that reflects the interviewer role — words like "Listening carefully", "Taking that in", "Following your thread", "Curious to dig deeper", "Framing a follow-up", "Jotting that down". When streaming completes, the full message reveals at once with a smooth fade-in. This directly addresses the UX friction Florian flagged in WAT beta feedback — the perceived lag between questions.
- New `ThinkingAnimation` component with gradient shimmer, dark-mode aware, rotates every 1.8s
- Shows for the entire `isLoading` duration (was previously only before first token, then switched to live streaming)
- No more slow-typing feel between questions

### Public Changelog
- New public `/changelog` page at unformal.ai/changelog
- Inline markdown renderer (no new dependencies)
- Synced from workspace via `skills/changelog/sync-to-app.sh`
- Added "Changelog" link to marketing footer
- `/ship` skill updated: mandatory changelog entry before every commit, auto-sync to app

---

## 2026-04-15

### Automated Reminders + Onboarding Drip via Resend Automations
End-to-end event-driven email automation for survey reminders and new user onboarding.
- New `convex/resendEvents.ts` — shared helper to fire Resend Automation events (`recipient.invited`, `conversation.completed`, `user.created`, `pulse.created`)
- New `convex/resendAutomations.ts` — idempotent setup actions that create templates and automations via Resend API
- Reminder sequence: 2-day delay → check for completion → send reminder → repeat once more
- Onboarding drip: 1-day delay → check for pulse creation → nudge → 2-day delay → personal help email
- Templates created and published in Resend dashboard programmatically
- Auto-reminders toggle on pulse Distribution tab (saves `autoRemindersEnabled`)
- Schema: `systemConfig` table for storing automation IDs, `autoRemindersEnabled` field on pulses

### Admin Dashboard + LLM Cost Tracking
Super admin view of platform-wide metrics and real-time cost visibility.
- Role-based admin access via `users.role: "admin"` (replaced email whitelist)
- Token usage logging added to all 5 previously untracked backend actions: resonance, extractEcho, insightExchange, pulseOptimizer, research
- New `convex/llmHelpers.ts` — shared cost calculation and usage extraction
- New `/studio/admin` page: platform stats (users, workspaces, pulses), LLM costs (today/7d/30d/all-time), 14-day trend chart, cost breakdown by model and by source, top workspaces by cost
- Added `source` field to `usageEvents` schema
- Backfilled token costs for 20 historical completed conversations using transcript char count estimation
- Fixed auth race condition in admin page (skip queries until `useConvexAuth` is loaded)

### Insight Exchange — Respondent Reward Mechanism
Respondents who complete a conversation unlock personal insights + anonymized group intelligence.
- Schema: `insightExchange` toggle on pulses, `insightExchangeObservation` field on conversations
- New public query `resonanceHelpers.getForInsightExchange` — returns anonymized aggregate data (requires `insightExchange: true` and 3+ completions)
- New `CollectiveInsightsSection` component renders below personal InsightsScreen: group sentiment bar, top themes with frequency bars, consensus/divergence cards
- Phase 2: New `convex/insightExchange.ts` generates personalized AI observation contextualizing respondent's answers within group (Claude Sonnet, ~1K tokens)
- Phase 3: Welcome screen shows respondent count ("14 people have shared their thoughts. Complete yours...")
- API: `insightExchange` field on POST/PATCH/GET pulse endpoints, `llms.txt` docs, `UNFORMAL.md` example
- Cleanup: deleted 90 abandoned test conversations
- See Insight Exchange

### Email Distribution List + Automated Reminders (P0)
Full workflow infrastructure for sending invitations, tracking completion, and reminding non-completers.
- Schema: `pulseRecipients` table with status tracking (pending → sent → started → completed)
- Mutations: `addRecipients`, `removeRecipient`, `sendInvitations`, `sendReminders`, `_updateRecipientStatus`, `_markReminded`
- Actions: `sendInvitations` and `sendReminders` via Resend with branded HTML templates
- Studio Distribution tab: recipient list with status badges, bulk add, send invitations, reminder settings (24h/48h/72h delays, 1/2/3 max reminders)
- Conversation flow integration: `recipientToken` URL param (`?t=xxx`) tracked through create → started → completed
- Automatic status updates via scheduler when conversations complete
- See Email Distribution List, Reminders & Nudges

### CSV/JSON Export (P0)
Data portability — download conversations from any Pulse.
- `GET /api/v1/pulses/:id/export?format=csv|json` — public API endpoint
- Studio InboxTab: Export dropdown (CSV or JSON) with client-side generation via Blob
- CSV: flattens echo fields as columns, one row per conversation
- JSON: includes full transcript, echo, and metadata
- New `convex/exports.ts` + `exports_queries.ts` for action/query separation

### Pulse Creation — Custom Slug Support
- Added optional `slug` field to `create` and `_createFromApi` mutations with validation (format, reserved list, uniqueness)
- Shared `resolveSlug` helper replaces duplicate slug generation logic
- Studio `/studio/new`: slug input with `unformal.ai/p/` prefix and debounced availability check
- API: `slug` field on POST /pulses (falls back to auto-generation if omitted)
- Agents get branded URLs instead of `understand-what-a-new-clie-x7k2m9`

### Built for Agents Page + Navbar
- New `/agents` route (renamed from `/integrate`) — full integration guide for AI agents
- Uses shared `MarketingHeader` / `MarketingFooter` for consistency across marketing pages
- Added "Built for Agents" link to desktop and mobile navbar (between Docs and Sign in)
- `/integrate` redirects to `/agents` for backward compatibility
- Landing page CTA updated

### Completion UX Polish
- Removed full-screen "Analyzing" takeover that replaced the conversation — now shows inline "Wrapping up..." spinner instead
- Input field and textarea form hidden immediately when completion is detected (was still showing under "View insights" button due to form being outside the conditional block)
- Reduced completion delay from 3s → 1.5s → 0.8s for snappier transition
- Fixed: `onFinish` callback now uses `conversationIdRef.current` instead of `conversationId` state, ensuring the first AI message is always saved to the transcript (fixes "first message missing" bug)

### Agent Behavior Fixes
- AI now skips questions in extract mode that were already answered in the initial brain dump (was re-asking for URL after user included it in Q1 dump)
- Strengthened "one question per message" instruction to a CRITICAL RULE (AI was firing 2-3 questions per response)
- Instructed AI to never write internal status notes like "starts researching..." — added frontend regex safety net
- Research mode instructions updated to not re-ask for URL if one was already provided in conversation history
- Set `FIRECRAWL_API_KEY` and `EXA_API_KEY` in Convex env — research was silently failing before
- Bumped `100in-pitch` maxQuestions from 10 to 12

### Storage ID Alias System (Image Attachments)
- Root cause: AI was truncating long 32-character Convex storageIds when reproducing them in `image_select` ui_hint JSON output (e.g. dropping `gwcqa0n` from the middle of the 4th image's ID)
- Fix: system prompt now passes short aliases (`img1`, `img2`, ...) instead of real storageIds
- Frontend resolves aliases back to real IDs via `pulse.questionFramework`, with label-based fuzzy match fallback for already-broken conversations
- Also fixed broken attachment images in PulseSettingsForm — the `isStorageId` heuristic required a `:` that Convex IDs don't contain
- Fixed `getFileUrl` crash on invalid IDs (restored `v.id("_storage")` for optimized performance after root cause fix)

### Dynamic Pricing Spec (Documentation Only)
- Added Dynamic Pricing feature spec — token-based credit consumption model with conversation-length tiers
- Recommended approach: Option B (tiered based on `maxQuestions`: 3Q=1cr, 5Q=2cr, 8Q=3cr, 12Q=5cr, 20Q=8cr)
- Margin analysis, credit pack impact, three implementation phases

### Workspace Agent OS Improvements
- Added `.claude/launch.json` for preview servers
- Pre-commit hooks in `settings.json` to catch secrets before git commit
- New `/rollback` command for emergency production reverts
- Activated `/learn` system (`data/learnings.jsonl`)
- Expanded `/user-support` command to 15 structured steps across 5 phases
- Created `docs/troubleshooting.md` runbook for Convex/Vercel/auth/Stripe/AI failures

### Miscellaneous Fixes
- Removed hardcoded "WAT" reference from Insight Exchange welcome text — now generic
- Fixed build error: added `mode` to chat route pulseConfig type
- Enabled token cost tracking end-to-end: frontend now sends `workspaceId` and `pulseId` to chat API (was silently missing)
- Set `jonas@sparkcollective.be` as admin role

---

## 2026-04-12

### Pulse Attachments + Image Select Component
Creator-uploaded attachments on Pulses. The AI can reference and display images during conversations.
- Schema: `attachments` array on pulses table (storageId, name, filename, mimeType, type)
- Mutations: `addAttachment`, `removeAttachment` (+ internal variants for API)
- API: `attachments` in PATCH whitelist and GET response
- New adaptive input: `image_select` — grid of clickable image cards with checkmark overlay, single/multi select
- Prompt templates bumped to V2 with `image_select` documentation
- Chat route injects pulse attachments into system prompt
- Studio: attachment upload section in PulseSettingsForm with thumbnail grid and delete
- See Pulse Attachments

### Email Distribution List + Completion Tracking
Send Pulse invitations to named recipients and track who completed.
- Schema: `pulseRecipients` table (pulseId, name, email, linkToken, status, conversationId, sentAt, completedAt)
- Mutations: `addRecipients`, `removeRecipient`, `sendInvitations`, `sendReminders`
- Actions: `sendInvitationEmails`, `sendReminderEmails` via Resend
- Pulse schema: `reminderEnabled`, `reminderDelayHours`, `maxReminders` fields
- Conversation page: `recipientToken` support for tracking named respondents
- See Email Distribution List, Reminders & Nudges

### CSV/JSON Export API
- `GET /api/v1/pulses/:id/export?format=csv` — download conversations as CSV
- `GET /api/v1/pulses/:id/export?format=json` — download as JSON
- `convex/exports.ts` — generateCsv and generateJson internal actions

### Website: 5 New Pages
- `/use-cases/accelerators` — "Screen 200 applicants without reading 200 forms"
- `/use-cases/agencies` — "Briefs that actually brief"
- `/use-cases/hr` — "What if exit interviews captured what people really think?"
- `/compare` — Unformal vs Typeform comparison with pricing table
- `/blog/why-forms-give-garbage-data` — SEO blog post about form pain points

### Navigation Redesign (React Bits Pro)
- New `MarketingLayout.tsx` shared component (header, CTA, footer) used across all marketing pages
- Glass-effect rounded navbar with expanding card dropdown for use cases (AnimatePresence from motion/react)
- Mobile hamburger menu with full use case cards
- Landing page and pricing page refactored to use shared MarketingHeader/MarketingFooter
- React Bits Pro setup: `components.json`, license, `cn` utility, Navigation-2 block installed

### FAQ Redesign (Pricing Page)
- Replaced bordered boxes with clean divider lines
- Plus/cross toggle icon with filled circle on active state
- Smooth AnimatePresence height animation
- Consistent `max-w-3xl` width matching rest of page

### Ranking Drag-and-Drop Fix
- Fixed page-level file upload drop zone intercepting Ranking component's internal drag events
- Added `e.dataTransfer.setData()`, `e.preventDefault()` on drop, and `e.stopPropagation()` on all drag events
- Added `dataTransfer.types.includes("Files")` check to page-level handlers

### WAT Beta Feedback Pulse Improvements
- Updated WALDO persona: three pivots origin story, celebrates WAT before critiquing
- Reduced maxQuestions 12 → 8
- Added meme picker opening question with 4 AI-generated meme images (via XAI Grok Imagine)
- Updated context: celebration-first tone, meta-awareness of being an Unformal demo

### Form Pain Index Campaign Pulse
- Created market research Pulse at `/p/understand-the-deepest-frustrations-peop-9c3muf`
- Casual tone, 8 questions, coverage-based completion, insights shown to respondent
- 3 LinkedIn post drafts in campaign plan

---

## 2026-04-11

### Question Framework Field + Smart Completion Algorithm
**Smart Completion:** Replaced hard `maxQuestions` limit with coverage-based completion. Three modes: balanced (default, 80% coverage target), coverage/thorough (90%, extends freely), speed/quick (70%, respects question target). `buildSmartCompletionPrompt()` in chat route, `completionPriority` field on Pulse schema, UI toggle in Studio. See Smart Completion Algorithm.

### Question Framework Field
Clean architectural separation: `persona` = personality only, `questionFramework` = structured questions with `ui_hint` definitions.
- Schema: `questionFramework` array on pulses table
- Backend: `generateConfig` and fallback prompt builder use it cleanly
- Studio UI: JSON editor in settings and create flow with validation
- Chat route: `persona` now properly forwarded

---

## 2026-04-04

### Auto-Improving Pulses (V1)
Self-optimizing conversation flows — analyze past conversations and propose config improvements. See Auto-Improving Pulses for full spec.

---

## 2026-04-03

### Response Limit per Pulse + Closed/Archived Pulse Page
- `maxResponses` field on pulses (Unlimited / 10 / 25 / 50 / 100)
- `ClosedPulseScreen` for archived/paused, `ResponseLimitScreen` for limit-reached
- Studio inbox shows "X / Y responses"
- API: `maxResponses` in POST/PATCH/GET

---

## 2026-03-31

### ui_hint JSON Flash Fix
`parseUIHint` now detects ui_hint-shaped JSON in both code block types. Partial blocks stripped during streaming.

### Resonance Compute Race Condition Fix
`lastComputedAt` + `_shouldCompute` + `_markComputing` — transactional lock prevents duplicate API calls.

### Adaptive Input AI Reliability
Strengthened system prompt: MUST use structured inputs for finite options. Dutch language quality instructions added.

### Docs — Shipped
`/docs` index, `/docs/concepts` guide, `/docs/best-practices` playbooks.

### Terms + Privacy Date Update
Updated "Last updated" dates.

---

## 2026-03-30

### Pulse Settings Tab — Now Inline
Settings form embedded directly in the "Settings" tab on Pulse detail page, no longer a separate page.

### Workspace Not Auto-Created — Fallback Fix
`getOrCreate` mutation in Studio layout now reliably creates a workspace if the `afterUserCreatedOrUpdated` callback fails. Not a root cause fix (Convex Auth callback is unreliable), but eliminates user-facing impact.

### Model Fallback + Budget Monitoring
Claude Sonnet → GPT-4o → Gemini Flash failover. Token usage tracking. Usage dashboard. `X-Unformal-Model` header.

### Session Recovery
localStorage persistence. "Welcome back" banner with Continue/Start Over. Resume via URL params.

### DNS Email Deliverability
SPF + DMARC records. Login spam warning + resend button.

### Landing Page Updates
"Try it yourself" demo section. Trust/transparency section. Pricing tiers. Footer links.

### Multi-Workspace Support — Phase 1
`workspaceMembers` table, roles, workspace switcher, scoped queries, `getOrCreate` guard.

### Extract Mode
New `mode` field (`interview` | `extract`). Casual tone. Dump-first strategy with field coverage tracking.

### Credit System + Stripe
Stripe Checkout, webhook handler, credit packs (€9/100 → €249/10K), Studio settings integration.

### Agent Neutrality Rules
Universal rules: never praise, stay neutral, repeat proper nouns, position as preparation not replacement.

### Welcome Screen Customization
`welcomeTitle` + `welcomeDescription` fields on Pulses.

---

## 2026-03-29

### REST API
Full API: create/list/get/update/delete Pulses, list conversations, get conversation, usage stats, agent signup.

### Agent-Friendly Distribution
`llms.txt`, `UNFORMAL.md` skill file, `ai-plugin.json`, SSE stream, integration docs page.

### Templates
4 pre-built: Client Onboarding, User Research, Lead Qualification, Brain Dump Intake.

### File Uploads + Link Sharing
Image upload, drag & drop, Convex file storage, URL detection, link previews, `FileUpload` + `LinkInput` adaptive inputs.

### Security Hardening (7 Critical + 3 Important)
Session tokens, rate limiting, webhook secrets, email verification protection, signup rate limiting, SSRF protection, file upload auth. See `SECURITY_FIXES.md`.

### Dark Mode
Full coverage: Studio, conversation page, login, privacy/terms.

### Legal Pages
GDPR-compliant privacy policy + terms of service (Belgian law, Gent).

### Email Templates
Coinbase-inspired design for verification + completion notification emails.

---

## 2026-03-28

### Echo + Resonance
Primary + fallback Echo extraction. Resonance: themes, consensus, divergence, sentiment, recommended actions.

---

## User Feedback Log

### Sil (Shift & Season, 2026-03-30)
1. Login email in spam → Fixed (SPF + DMARC)
2. No demo/preview without building → Fixed (demo Pulse on landing page)
3. Free tier trust / GDPR concerns → Fixed (transparency section, business model explanation)
4. Conversation crash before completion → Session recovery shipped
5. No session recovery → Fixed
6. Privacy concern about voice input → Needs clearer explanation
7. "What happens with my answers?" → Needs post-completion explanation
8. Auto-generate Pulse from website URL → Future feature idea

---

*Related: Roadmap, Product Spec*
