Skip to main content
Paul Welty, PhD AI, WORK, AND STAYING HUMAN

2026-03-19 — Dinly: multi-user, meal types, bookmarks, and the new planning flow

What shipped today

The session started with a code quality pass — a /scout found 22 issues across error handling, RLS security, timezone safety, ICS compatibility, and parser edge cases. All 6 grouped issues were prepped and executed: error feedback on all save actions, error boundaries and loading skeletons, an RLS ownership audit (admin client actions now verify household membership), timezone-safe date arithmetic, VTIMEZONE in calendar exports, and divide-by-zero guards in the ingredient parser.

The product then evolved significantly. Meal types became a first-class concept: 28 canonical dinner styles (pasta, tacos, stir fry, etc.) with keyword-based auto-categorization that now covers 95% of the 3,808 imported recipes. The recipe library gained a meal type filter dropdown, cards show type badges, and candidate selection groups recipes by type. During voting, family members can now request dinner types or specific recipes — these feed into the ranking engine with +3/+5 boosts and appear on the results page as “Requests this week.”

Multi-user household support landed as a complete feature chain. Family members got an email field, the cook can send branded invite emails via Brevo, invited users accept and get auto-linked to their household and family member profile, and logged-in members can vote independently (no more cook-as-proxy for adults). Role-based access controls enforce three tiers: shared (dashboard, weeks, recipes), cook (family management, settings), and admin (users, households, invites). The is_primary_cook boolean was replaced with is_cook so multiple family members can have cook-level access. Admin pages show all users, households, and invite status.

The email infrastructure was built on Brevo’s transactional API — a TypeScript client ported from the paulos Python pattern, with 5 dark-themed HTML notification templates (voting open, voting reminder, plan finalized, shopping list, weekly digest) and high-level notify*() functions. Six Supabase auth email templates were also created (confirmation, recovery, invite, email change, magic link, reauthentication). All transactional emails auto-BCC the admin.

Recipe bookmarks shipped as a complete feature: a new recipe_bookmarks table with four types (want to cook, favorite, tried, not interested), toggle buttons on the recipe detail page, heart/bookmark indicators on library cards, a bookmark filter in the library, and bookmark signals feeding into the ranking engine (+2 want to cook, +1 favorite, -2 not interested).

The biggest architectural change was the redesigned planning flow. The old model (pick specific recipes → vote → assign to days) was replaced with a type-aware multi-stage flow: Draft (cook builds a list of types + recipes) → Vote (family votes + suggests new items) → Curate (cook picks winners from ranked results) → Resolve (cook assigns specific recipes to type entries) → Assign to days → Shopping list. The weekly_candidates schema now supports nullable recipe_id with a meal_type_value alternative, plus suggested_by_name for family suggestions during voting. The curate and resolve pages are new additions to the cycle flow.

The project was restructured to web/ subdirectory (matching prakta) and deployed to Vercel at www.getdinly.com with all env vars configured.

Completed

  • #42 Add error feedback to all client-side save actions
  • #43 Add error.tsx and loading.tsx to all route groups
  • #44 Audit all server actions for RLS and household ownership
  • #45 Fix timezone-unaware date handling in weekly cycles
  • #46 Add VTIMEZONE component to .ics calendar export
  • #47 Fix ingredient parser edge cases and add ZIP validation
  • #53 Build USE-CASES.md with 16 documented use cases
  • #56 Add meal type and recipe requests to weekly voting
  • #57 Add meal_type column and auto-categorize existing recipes
  • #58 Show meal type on recipe cards and add meal type filter
  • #59 Group candidate selection by meal type
  • #60 Branded email templates for Supabase Auth
  • #67 Add email field to family members
  • #68 Build invite flow: send invitation email
  • #69 Handle invite acceptance: auto-link account to profile
  • #75 isPrimaryCook helper + vote auto-select
  • #76 Hide cook-only UI elements for non-cook members
  • #77 Add role-based guards to cook-only server actions
  • #79 Rename is_primary_cook to is_cook (multiple cooks)
  • #80 Make sidebar role-aware (shared/cook/admin groups)
  • #81 Build admin users page
  • #82 Build admin households and invites pages
  • #89 Schema: make weekly_candidates support types and suggestions
  • #90 Update candidate selection for types alongside recipes
  • #91 Update voting form for type candidates and family suggestions
  • #92 Build curate step: cook picks winners
  • #93 Build resolve step: types → specific recipes
  • #99 Add recipe_bookmarks table
  • #100 Add bookmark buttons to recipe cards and detail page
  • #101 Feed bookmarks into ranking engine
  • #105 Improve meal type coverage from 86% to 95%

Carry-over

  • Vercel deploy is live at www.getdinly.com but Supabase redirect URLs need to be updated in the dashboard (Site URL → www.getdinly.com, add www.getdinly.com/** to redirect allowlist)
  • Supabase email templates need to be pasted into the dashboard manually (6 HTML files in web/supabase/email-templates/)
  • Supabase SMTP needs to be configured (Brevo SMTP credentials in Auth → SMTP Settings)
  • HTTP 431 cookie issue in Chrome — clearing cookies fixes it, but may recur. Consider implementing cookie chunking cleanup.

Risks

  • The new planning flow (Draft → Vote → Curate → Resolve → Days) is built but not yet tested end-to-end with real data. The cycle detail page links to “Curate plan” after voting closes, but the entire flow from type candidates through resolution hasn’t been walked through in the browser.
  • The voting form now handles mixed candidates (types + recipes) but vote saving only stores recipe-based votes. Type candidate votes use the request signal path. This could be confusing if the ranking engine doesn’t weight them correctly.
  • The requireHousehold() call in the layout means every page load includes an auth + membership query. If this becomes a performance issue, consider caching the membership in a cookie.

Flags and watch-outs

  • is_cook replaced is_primary_cook globally. Any new code should use is_cook on family_members and isPrimaryCook() helper.
  • isAdmin() checks against DINLY_ADMIN_EMAILS env var — currently only [email protected].
  • Admin pages use createAdminClient() (service role) to bypass RLS for cross-household queries.
  • The sidebar is now an async server component that calls requireHousehold() — this is intentional for role-based nav.
  • Bookmark filter in recipe search uses userBookmarks prop — this fetches all bookmarks for the current user on page load.

Next session

  • Test the full new planning flow in the browser: create a cycle with type + recipe candidates → vote → curate → resolve → assign days → shopping list
  • Wire Brevo email notifications into the app actions (notifyVotingOpen when cook opens voting, notifyPlanFinalized when plan is finalized)
  • Configure Supabase SMTP + email templates in the dashboard
  • Update Supabase redirect URL allowlist for www.getdinly.com
  • Consider: Stripe trial plan integration (Paul mentioned this on day 1, still unscoped)
  • Consider: Paprika import support (spec mentions it as second priority after Mela)

Why customer tools are organized wrong

This article reveals a fundamental flaw in how customer support tools are designed—organizing by interaction type instead of by customer—and explains why this fragmentation wastes time and obscures the full picture you need to help users effectively.

Infrastructure shapes thought

The tools you build determine what kinds of thinking become possible. On infrastructure, friction, and building deliberately for thought rather than just throughput.

Server-side dashboard architecture: Why moving data fetching off the browser changes everything

How choosing server-side rendering solved security, CORS, and credential management problems I didn't know I had.

The work of being available now

A book on AI, judgment, and staying human at work.

The practice of work in progress

Practical essays on how work actually gets done.

The last mile is all the miles

Building the product is the fun part. Deploying it, configuring auth, pasting email templates into dashboards, rotating leaked API keys — that's where the work actually lives.

The day we shipped two products and the agents got bored

112 issues across 12 projects. Two new products went from nothing to code-complete MVP in single sessions. And the most interesting signal wasn't the speed — it was the scout that came back empty-handed.

The org chart your agents need

The AI community is reinventing organizational design from scratch — badly. Agencies figured this out decades ago. Competencies, not clients. Briefs, not prompts. Lateral communication, not hub-and-spoke. The answers are already there.