Skip to content

Assumptions

Every rule on this site is a bet about the product it will run in. This page lists those bets in one place. It answers what the decisions assume and what would make us reopen them. When a bet turns out false, start here, then revisit every topic that leans on it.

ID We assume Revisit when
A1 A long-lived server. The container profile runs one deployable as two or more replicas behind a load balancer, on Node by default, with Bun supported. Rolling deploys mix two releases. Replica clocks agree to well under a minute, which the job lease and the outbox claim depend on. Only the app’s clock is read, never the database’s. A project runs a single replica, or only the Worker profile (A2), or replica clock skew approaches a minute.
A2 Or a Cloudflare Worker, deployed by alchemy. The layer graph is built once per isolate in the init closure. There is no process, no SIGTERM and no closing of the graph’s scope. Recurring jobs are Cron Triggers registered in the same init closure. alchemy changes its init or Config binding semantics, or the Worker profile is dropped. Every Worker citation is checked against the pinned alchemy version.
A3 One SQL database per deployable: Postgres, SQLite or D1. D1 has no interactive transactions and does not classify constraint errors. A second database per deployable, or a non-SQL primary store.
A4 Multi-org SaaS with roles. Tenants are orgs, users are members with a role, and data is isolated per org. A single-owner product, such as one personal workspace per user. Most of authorization and orgs would then be more than the product needs.
A5 Humans authenticate through a reachable external identity provider. Self-hosting with no identity provider, or building our own auth.
A6 One client: our own web app. No external API consumer and no machine client. The first API consumer that is not our web client. Every “second audience” trigger fires together.
A7 One developer, working with agents. No second human reviewer, and zero required approvals. A second human joins: previews, required approvals and several review-only rules change.
A8 One deployable, a modular monolith. apps/server, with features as folders. A second process kind or a second team.

Impact: MEDIUM a silent bet is the one nobody revisits

  • Each decided note names the bets it depends on by ID, in an **Assumes:** line. “none” means the decision holds under every bet here.
  • A bet moves from Active to Invalidated (with the date and the evidence) or Retired. An invalidated row stays, so the reason survives.
  • A new implicit bet found in a note gets a row here, in the same change that finds it.

❌ Incorrect — the bet lives only in prose, so nobody knows which rules to revisit:

**Status:** decided
We use Postgres advisory locks for the job lease.

✅ Correct — the bet is named, so invalidating A3 finds every note that leans on it:

**Status:** decided
**Assumes:** A1, A3 ([[assumptions]])

Source: notes/00-assumptions/assumptions.md