Build Cozi Gold

YESreplaces $3.25/mosaves $39/yrback to the verdict

0%0 of 20 items done

Saved on this device only. Tick prerequisites first, then work the phases in order · do not start one until the checks above it pass.

A family organiser where the technical work is unglamorous and the failure modes are social: a shared calendar that handles recurrence and time zones correctly, lists that two people can edit at once without losing an item, and a meal plan that produces the shopping list.

estimated effort multi-daythe files for this build are in the project pack

RuntimeTypeScript with Fastify and PostgreSQLCalendarEvents stored as UTC instants or floating local dates, explicitlyListsItem-level operations, never whole-list writesAuthHousehold accounts with per-person logins and a simple role splitNotificationsReminders through a queue with per-person quiet hours

Before step 1

Everything below is assumed from the first step. Tick each one when you actually have it, not when you plan to.

  1. installfree

    Why Runs the server.

    Get it nodejs.org, or nvm install 22. open ↗

    Verify node --version prints v22 or higher

  2. installfree locally

    Why Shared household data with concurrent editors needs real transactions.

    Get it Docker or your package manager. open ↗

    Verify psql connects and creates a table

  3. installfree

    Why Writing recurrence expansion yourself means reimplementing a specification whose edge cases are exactly the bugs you are trying to avoid.

    Get it A maintained library for recurrence rules, plus your platform's zone data kept current.

    Verify A weekly rule expands correctly across a daylight transition in a test

  4. free tiers cover a household

    Why Phase 5 needs delivery somewhere real, and reminder timing bugs only appear against an actual channel.

    Get it Email through any transactional provider is sufficient, or push if you are building a mobile client later.

    Verify A test reminder arrives at the right time

  5. have readyfree, and it will produce feedback

    Why Concurrent list editing, notification fatigue and role restrictions are all social failures that a single tester cannot observe.

    Get it Your own household, using it for real for two weeks including a supermarket trip with two people on the same list.

    Verify Three people have used it for two weeks and complained about something

Data model

Create these before the first phase that stores anything. Changing a table later is the expensive kind of change.

- households(id, name, timezone, created_at)
- people(id, household_id, name, colour, role, login_email) · role is adult or child
- events(id, household_id, title, kind, starts_at_utc, ends_at_utc, local_date, all_day, rrule, rrule_zone, created_by)
- event_attendees(event_id, person_id)
- event_exceptions(event_id, occurrence_date, cancelled, override_json) · because one instance always changes
- lists(id, household_id, kind, title) · kind is shopping, todo or packing
- list_items(id, list_id, text, checked, checked_by, ordinal, updated_at, deleted_at)
- meals(household_id, date, slot, recipe_id, note) · slot is breakfast, lunch or dinner
- recipes(id, household_id, title, ingredients_json, servings)
- An all-day event stores a local date and no instant, because a birthday does not move when someone travels.

Environment variables

These go in a .env file the app reads at startup. The pack's .env.example is this table as a file · copy it, never commit the filled-in version.

VariableNeededExampleWhere the value comes from
DATABASE_URLsecretrequiredpostgres://localhost:5432/householdA family's calendar and whereabouts. Treat it accordingly.
SESSION_SECRETsecretrequireda random 32-byte hex stringSigns login sessions. Rotating it logs the household out, which is the intended emergency action.
DEFAULT_TIMEZONErequiredEurope/LondonThe household's zone. Individuals can differ, which is what phase 2 handles.
QUIET_HOURS_DEFAULTrequired21:00-07:00When reminders defer. The default that stops the tool being muted in week one.
FEED_BASE_URLrequiredhttps://home.example.com/feedWhere calendar feeds are served. The token in the path is the credential.
SMTP_URLsecretoptionalsmtp://user:pass@host:587Phase 5 only. Absent means reminders are visible in the application but not delivered.

The build, in order

  1. A household and the people in it

    Several people share one household with appropriate access.

    1. Attribution retrofitted later is a migration nobody enjoys.

      Files src/auth.ts

    2. Adult and child. More roles is a family tool nobody configures.

      Files src/roles.tssrc/roles.test.ts

    3. A permanent invite link is a permanent way into your family's calendar.

      Files src/invite.ts

    done when · tick each as it passes
    watch out
    • A single shared login for the household means you can never tell who added an event or checked an item, and there is no way to give a child limited access, which is the whole reason a family tool needs accounts at all. Per-person logins from the start; retrofitting attribution is painful.
  2. Time that behaves correctly

    Birthdays do not move and recurring events survive daylight saving.

    1. Instant or floating date. Conflating them is the recurring bug in every calendar.

      Files src/events.ts

    2. Materialising occurrences means editing the rule requires rewriting them all.

      Files src/recurrence.tssrc/recurrence.test.ts

    3. One instance always changes. Without exceptions the answer is deleting the series.

      Files src/exceptions.ts

    done when · tick each as it passes
    watch out
    • Storing an all-day event as midnight UTC makes a birthday show on the wrong day for anyone west of the meridian, which in a family with a member abroad is an immediate and embarrassing bug. Store all-day events as a local date with no instant at all.
  3. Lists two people can edit at once

    Two people in a supermarket do not lose each other's items.

    1. Add, check, reorder, delete. Never a whole-list write.

      Files src/lists.ts

    2. So reconciliation deduplicates rather than duplicating.

      Files src/offline.ts

    3. A reconnecting client with a stale copy otherwise resurrects deleted items.

      Files src/lists.ts

    done when · tick each as it passes
    watch out
    • Saving the whole list on every change means the second person's save silently discards the first person's additions, and in a supermarket with two people on the same list this happens within minutes. Operate on items, never on the list as a document.
  4. Meals that produce the shopping list

    Planning the week's dinners fills the shopping list without retyping.

    1. So regeneration touches only what it created.

      Files src/generate.ts

    2. Quantities added, units normalised where possible, left separate where not.

      Files src/ingredients.ts

    3. Only its own, and only if nobody checked them off already.

      Files src/generate.ts

    done when · tick each as it passes
    watch out
    • Regenerating the shopping list after a plan change wipes the items someone added by hand, which is the household's actual shopping list and the thing they will be most annoyed to lose. Mark generated items with their source and only ever replace those.
  5. Reminders that do not annoy the household

    People are reminded usefully and can control when.

    1. From the expanded recurrence, not from the series row.

      Files src/reminders.ts

    2. A dropped reminder is a missed appointment. Deferral is the correct behaviour.

      Files src/quiet.ts

    3. Notifying everyone is how a household mutes the application.

      Files src/notify.ts

    done when · tick each as it passes
    watch out
    • Sending a reminder for every event to everyone in the household means the notifications are muted within a week and the tool has quietly stopped working. Default to notifying only attendees, respect quiet hours per person, and make a child's notifications adult-configurable.
what this build does not replace
after v1, if you want it

Need the files? The project pack on the verdict page hands your agent the whole brief · more home.