Skip to content

Design Decisions (ADRs)

Architecture Decision Records live in adrs/ and record why each significant decision was made (constitution §5.3). All nineteen are Accepted. Summaries below; follow the link for full context and alternatives.

ADRDecisionConstitution refs
001API versioning via URL prefix /v1§7.3, §15
002Error envelope = RFC 9457 problem+json§7.3, §7.5
003Contract tooling = @hono/zod-openapi (contract-first)§7.3, §7.9
004CLI auth = OAuth PKCE§7.12, §7.15
005CLI = TypeScript, distributed via npm§7.15, §8.1
006Real-time transport = polling for MVP; DO WebSocket the planned upgrade§7.14.6, §8.4
007Supply-chain tooling = OSS stack (SBOM, osv-scanner, gitleaks)§9.6, §11.9
008i18n = Paraglide/Inlang + native Intl§7.16, §8.1
009PWA tooling = post-build workbox-build + prerendered app shell§7.14.2, §8.1
010Auth UI = Clerk prebuilt components§7.16.1, §7.12
011Event push = signal-only WebSocket over a per-user inbox Durable Object§7.14.6, §8.1, §8.4
012Docs toolchain = Astro Starlight (build-time tooling; no §8.1 amendment)§8.1, §8.2, §9.6, §14
013Account deletion = hard delete; D1-first → Clerk, idempotent convergence (app-orchestrated, no webhook)§9.7, §7.12, §7.10
014CLI token = JWT access tokens + short TTL + refresh-revoke; broaden acceptsToken (supplements 004)§7.12, §7.15, §9.5
015Scheduled evaluation = Cloudflare Cron Triggers (~1-min reminder scan); §8.1 amendment v0.19.0§8.1, §7.8, §8.4
016Web Push transport = VAPID + aes128gcm on WebCrypto (no Node web-push); §8.1 amendment v0.19.0§8.1, §9.2, §9.3, §9.5
017Outbound email = Cloudflare Email Routing send_email to a verified destination (free tier; no secret/paid plan); §8.1 amendment v0.20.0§8.1, §7.1, §9.1, §9.3
018App monitoring = Sentry (Issues/Traces/Logs/Metrics) behind a swappable facade; first third-party sub-processor (PII-scrubbed); §8.1 amendment v0.21.0§8.1, §9.1, §9.3, §10.2
  • Real-time transport (006 → 011). Polling was the correct MVP transport; ADR-011 realises ADR-006’s named upgrade as signal-only WebSocket push over the SyncInbox Durable Object, demoting polling to a fallback. ADR-006 remains Accepted, not superseded.
  • Contract-first (003). The OpenAPI document is generated from Zod schemas and is the single source of truth shared by all clients — including the API Reference on this site.
  • Stack governance & the §8.1 amendment ladder (011, 015–019). Each ADR that adds a production platform capability also amends §8.1: Durable Objects (011), Cron Triggers + Web Push (015/016, v0.19.0), outbound email (017, v0.20.0), Sentry monitoring (018, v0.21.0), and R2 object storage (019, v0.22.0). Build-time-only tooling like the docs site (012) reuses the approved stack and does not amend §8.1.
  • Reuse needs no ADR (spec 038). The 30-day auto-deletion of completed Tasks ships with no ADR and no §8.1 amendment (constitution stays v0.22.0): it reuses the existing Cron Trigger (015), D1 soft-delete/tombstones, the Clerk auth boundary, and the SyncInbox fan-out (011) — no new dependency, secret, external service, or authz actor. A feature is only an ADR/amendment when it introduces a new production capability; composing approved ones is ordinary spec work.
  • Object storage (019). Task file attachments store bytes in a private Cloudflare R2 bucket reached only through the Worker download endpoint (no public bucket, no presigned URLs); metadata lives in D1. Free-tier scoped (the 5 MB cap + no thumbnails are budget levers); no npm dependency (EXIF stripping is hand-rolled), no new secret. Uploads are validated server-side and image EXIF is stripped before storage so shared images can’t leak the uploader’s location/device between task members.
  • Observability (018). Sentry is the project’s first third-party data sub-processor. The decision is wired behind a vendor-neutral facade (src/lib/obs/report.ts) so the provider is swappable from two adapter files, and the privacy posture is load-bearing: only scrubbed error/trace context and route-shape metadata leave the app — never task/list content, cookies, auth headers, or user. See Observability.