export interface ChangelogEntry {
  date: string; // ISO yyyy-mm-dd
  tags: readonly ChangelogTag[];
  title: string;
  body: string;
}

export type ChangelogTag = 'feat' | 'fix' | 'security' | 'docs' | 'infra' | 'chore';

export const CHANGELOG_ENTRIES: readonly ChangelogEntry[] = [
  {
    date: '2026-07-21',
    tags: ['docs', 'infra'],
    title: 'Doltgres-first docs + setup/connect split + Auth/S3 handoff accuracy',
    body: 'Public docs, MCP briven_ask guides, handoff, dashboard connect copy, and CLI help now match production: control + project SQL on Doltgres (not dual-engine forever); files on MinIO S3 (s3.briven.tech); `briven setup` = new project only, `briven connect` = existing (no setup --project); three key families (pk_briven_auth_, brk_, storage brvn, MCP pk_briven_mcp_). Handoff gate remains open for Auth + project S3.',
  },
  {
    date: '2026-07-18',
    tags: ['feat', 'security', 'docs'],
    title: 'S6 auth reliability bar — metrics, tests, admin snapshot',
    body: 'Closes the code/ops half of the Clerk-gap reliability sprint: process counters + Prometheus briven_auth_* series (rate-limit denials, memory fallback, mailer failures, auth 5xx); GET /v1/admin/auth-reliability; admin Health panel section; unit tests for memory rate limits and per-project isolation; scripts/s6-auth-verify.sh for read-only platform probes. Human browser AUTH-GO-LIVE + second-project check still required before “friends can rely on auth.” Auto-deploy remains OFF.',
  },
  {
    date: '2026-07-18',
    tags: ['docs', 'security'],
    title: 'beta next steps: auth checklist track + S6 reliability notes',
    body: 'BETA-V1-NEXT-STEPS.md tracks auth go-live, second-project isolation, and dogfood. docs/S6-RELIABILITY.md documents Redis rate-limit fail-open behavior and customer status blurbs. Status pages explain “if sign-in feels broken.” Platform probe: production /ready redis ok. Human browser checklist rows 1–4+7 still required before “friends can rely on auth.”',
  },
  {
    date: '2026-07-18',
    tags: ['feat', 'docs'],
    title: 'briven setup — Convex-style one-command connect + project wiring',
    body: 'New CLI command `briven setup` signs in via browser OAuth, creates a new cloud project or attaches an existing one (`--name` / `--project`), scaffolds the folder, mints a CLI key, and writes briven.json. Bare `briven` with no linked project starts setup. Docs (connect, quickstart, cli, templates), dashboard empty-state, and project connect panel all promote this path. Templates remain optional starters only — not the product model. Manual `login` + `link` still works for CI/key paste.',
  },
  {
    date: '2026-05-12',
    tags: ['feat', 'docs'],
    title: 'AI.md operator setup guide + per-feature model overrides',
    body: 'docs/AI.md (tracked in repo alongside docs/MIGRATION.md) documents which ollama model to run, why qwen2.5-coder:32b is the default, how to differentiate models per feature, hardware sizing reference, what NOT to pick. four new env vars — BRIVEN_OLLAMA_MODEL_SCHEMA / _FUNCTION / _EXPLAIN / _DOCS — each falls back to BRIVEN_OLLAMA_MODEL when unset so single-model operators don\'t need to set them. logs now record the actual model used per request (not the global default).',
  },
  {
    date: '2026-05-12',
    tags: ['fix'],
    title: 'mobile responsive pass — dashboard nav, project tabs, marketing footer',
    body: 'project tabs row (14 secondary tabs inside a project — overview / functions / logs / etc.) now horizontally scrolls on mobile instead of overflowing; sidebar hides below md and a horizontal nav strip appears at the top of the dashboard so projects / teams / billing / settings stay one tap away on phones. admin stats grid + project overview cards + invoke-panel grid all gain responsive col counts. subprocessors table wraps in overflow-x. marketing footer rewritten as a proper five-column nav (brand + product + docs + legal) on top of a thin bottom strip with copyright + flndrn limited + heart-in-flanders. fixed dashboard body padding so phones get the full viewport width.',
  },
  {
    date: '2026-05-12',
    tags: ['feat'],
    title: 'briven ai cli — schema / function / explain from the terminal',
    body: '`briven ai schema "..."`, `briven ai function "..."`, `briven ai explain --file <path>` proxy to the same three api endpoints the dashboard hits. takes a prompt as a positional arg, writes to stdout (or `--out path`). `briven ai function --with-schema` pulls the project\'s current schema snapshot in as model context. exit code 2 means the api host hasn\'t wired BRIVEN_OLLAMA_URL — a clear "AI assistant offline" message instead of an opaque http error. completes the AI surface: api + dashboard + cli all wire to the same self-hosted Qwen 2.5-coder backend.',
  },
  {
    date: '2026-05-12',
    tags: ['feat', 'docs'],
    title: 'docs search + corpus index (AI assistant pre-staged)',
    body: 'new endpoint at /api/search?q=… returns the top N docs pages by word-overlap against a hand-curated corpus (27 entries covering every published page). /search renders the same results as a browseable page with a search box. when the ollama backend lands the AI docs assistant wraps this same ranking — the search picks the top 3 pages, the assistant forwards them to the model as system-prompt context.',
  },
  {
    date: '2026-05-12',
    tags: ['docs'],
    title: 'migration · mongodb → briven',
    body: 'new page at /migration/mongodb. covers the decision matrix for keeping embedded docs as jsonb vs flattening into separate tables, ObjectId → text + ulid for new ids, the mongoexport → transform-script → COPY data move, find/aggregate → ctx.db chain rewrites, and the 2+ week parallel-run window mongo → relational migrations need to catch shape mismatches.',
  },
  {
    date: '2026-05-12',
    tags: ['feat'],
    title: 'ai explain code — third feature in the AI trifecta',
    body: 'paste any briven schema or function snippet, get a plain-english walkthrough in briven idioms (what the wrapper means, which db calls happen, where reactivity hooks in, sharp edges). same self-hosted Qwen 2.5-coder backend + same not-logged privacy posture as schema/function gen. dashboard surface at /dashboard/projects/:id/ai-explain. third tab in the AI section.',
  },
  {
    date: '2026-05-12',
    tags: ['docs'],
    title: 'per-source migration sub-pages — drizzle + prisma',
    body: 'two new pages on docs.briven.tech: /migration/drizzle and /migration/prisma. each covers schema-column mapping, indexes, data export (pg_dump + pg_restore), the functions-port rewrite (drizzle\'s db.select chains and prisma\'s findMany calls both become ctx.db chains), auth hand-off to the nextauth guide, and reactivity as a new capability. the /migration overview separates them out of the lumped "raw postgres / drizzle / prisma" entry now that each has its own page.',
  },
  {
    date: '2026-05-12',
    tags: ['feat'],
    title: 'ai function generator — companion to ai schema',
    body: 'natural-language → draft briven/functions/<name>.ts. /v1/projects/:id/ai/generate-function takes { prompt, schemaContext? } and forwards to ollama with a system prompt baking in the @briven/cli/server DSL (query / mutation / action wrappers, ctx.db chains, brivenError shape, ulid prefixing). dashboard surface at /dashboard/projects/:id/ai-function ssrs the current schema snapshot into a compact "table { col: type PK NOT NULL -> fk }" summary the model reads as context. the same not_configured 503 + privacy stance as the schema generator: prompts and responses are not logged.',
  },
  {
    date: '2026-05-12',
    tags: ['feat'],
    title: 'tier-aware realtime subscription cap + project cap-warning banner',
    body: 'TIERS.concurrentSubscriptions (free=100 / pro=1000 / team=10000) is now enforced by the realtime service, replacing the platform-wide env ceiling. realtime resolves the cap from a new internal endpoint /v1/internal/projects/:id/limits on first subscribe per project (5-min cache, shared-secret gated). project overview page grows a yellow/red banner at 75%/90% of the cap so the user can upgrade before subscribes start getting rejected; banner sources from a new /v1/projects/:id/realtime-stats endpoint scoped to the caller\'s own project. admin · realtime page auto-refreshes every 10s via meta refresh so an operator can keep it open while watching a noisy project.',
  },
  {
    date: '2026-05-12',
    tags: ['feat'],
    title: 'admin · usage — retry skipped polar pushes',
    body: 'when polar push rows land in `skipped` (missing meter id, bad customer mapping, durable 4xx), the operator had to UPDATE usage_events directly in psql to re-enable them. now /dashboard/admin/usage grows a "retry → pending" button (window: 1/7/30/90 days) that flips skipped rows back to pending in one click. window is bounded server-side so a runaway click can\'t re-push years of stale data. action is audit-logged with the row count.',
  },
  {
    date: '2026-05-12',
    tags: ['feat'],
    title: 'admin · realtime — live subscription snapshot',
    body: 'new admin page at /dashboard/admin/realtime shows per-project subscription counts + per-channel refcounts + the current cap values, with severity coloring at 60% and 90% of the per-project cap so an operator can spot a noisy project before it gets clamped. backed by a shared-secret-gated /v1/realtime/stats on the realtime service (no PII leaked — only project ids and channel names). 503 with a clear "not configured" state when the realtime URL or shared secret is unset.',
  },
  {
    date: '2026-05-12',
    tags: ['security'],
    title: 'subscription caps on realtime — per-ws + per-project',
    body: 'realtime now refuses subscribe frames past BRIVEN_REALTIME_MAX_SUBS_PER_WS (default 200) and BRIVEN_REALTIME_MAX_SUBS_PER_PROJECT (default 10,000 — the year-one platform ceiling). counter briven_realtime_subscribe_rejected_total{reason="ws_limit"|"project_limit"} lights up on /metrics so an operator can spot a runaway client. error frames carry the offending subscriptionId so the client can correlate. closes the "single bad ws can exhaust 10k concurrent subs" surface noted in BUILD_PLAN §7.4.',
  },
  {
    date: '2026-05-12',
    tags: ['feat'],
    title: 'connection-seconds in usage + tier caps',
    body: 'TIERS now ships connectionSecondsPerMonth (1M / 10M / 100M for free / pro / team) and concurrentSubscriptions (100 / 1000 / 10000). hourly aggregator scrapes briven_realtime_connection_seconds_total from the realtime /metrics endpoint, diffs per project, writes connection_seconds rows to usage_events (the polar push worker already drains them). dashboard usage card now shows realtime usage alongside invocations + storage + compute, formatted as s/m/h/d so a Team plan reads as "115d / 1158d" instead of an unreadable 100 million seconds.',
  },
  {
    date: '2026-05-12',
    tags: ['feat'],
    title: 'polar meter push: real fetch + org→customer resolution',
    body: 'the metering worker no longer logs intent — it actually posts to polar. customer ids resolve through project → org → subscriptions.polar_customer_id (5-min in-process cache, invalidated on the polar webhook so a fresh checkout takes effect on the next tick). 5xx + network errors leave the row pending for the next minute; 4xx marks the row skipped so it doesn\'t loop forever. invocations + storage_bytes flow today; connection_seconds queued for the realtime /metrics scraper.',
  },
  {
    date: '2026-05-12',
    tags: ['feat'],
    title: 'storage caps on every tier, surfaced through usage',
    body: 'TIERS now ships storageBytes alongside projectsPerOrg / functionsPerProject / invokesPerMonth: 1 GiB on free, 10 GiB on pro, 100 GiB on team. dashboard usage card shows "used / cap" — pg_total_relation_size sampled live against the project schema, excluding the platform\'s _briven_* bookkeeping.',
  },
  {
    date: '2026-05-12',
    tags: ['security'],
    title: 'project-suspension gate mounted globally',
    body: 'blockIfProjectSuspended is now mounted once at /v1/projects/:id/* instead of sprinkled per-router. read methods (GET/HEAD/OPTIONS) skip the check so dashboards stay readable while a project is being investigated; every mutation route (env writes, member moves, studio writes, db shell, deploys, invokes) inherits the gate without per-route wiring. closes the drift risk where a new route lands without picking up the suspension check.',
  },
  {
    date: '2026-05-12',
    tags: ['feat', 'security'],
    title: 'account deletion (gdpr article 17) — 30-day soft-delete cascade',
    body: "settings · danger zone now ships a real account-deletion flow. typed-email confirmation gates the click; the api revokes every session, every api key on sole-ownership projects, and every pending invitation the user sent. sole-owner orgs (personal + any team where the user is the only owner) and the projects under them soft-delete. multi-owner team orgs survive — the user is just removed from membership. PII clears in the same transaction (legal name, address, VAT, company, display name, image); id + email + createdAt stay so audit-log FKs survive. confirmation email lands before the cascade runs. a daily 03:30 UTC worker hard-deletes rows past the 30-day grace window; FK CASCADE handles the rest. polar subscriptions are NOT auto-cancelled — manage via the polar portal during the grace window.",
  },
  {
    date: '2026-05-12',
    tags: ['infra'],
    title: 'alertmanager + discord bridges in the observability stack',
    body: 'prometheus alerts now route through alertmanager → benjojo/alertmanager-discord bridges → two discord channels. severity=critical|warning lands in #briven-alerts (page-worthy); info lands in #briven-deploys. operator pastes DISCORD_WEBHOOK_ALERTS + DISCORD_WEBHOOK_DEPLOYS into the dokploy env; the bridges hold them inside the docker network so urls never leave the host. group_wait 30s, group_interval 5m, repeat_interval 4h, plus an inhibit rule so a critical for (service, alertname) suppresses redundant warnings.',
  },
  {
    date: '2026-05-12',
    tags: ['docs'],
    title: 'http api reference at /docs/api',
    body: 'every public endpoint grouped by area (invoke, realtime, projects, deployments, studio, logs + stats, usage, api keys, project members, orgs, billing) with method, path, and a one-line summary. linked from the docs sidebar.',
  },
  {
    date: '2026-05-12',
    tags: ['feat'],
    title: 'studio polish: ALTER COLUMN, TRUNCATE, schema overview, project move, invocation sparkline',
    body: 'ALTER COLUMN endpoint toggles nullability and changes/drops default expressions with a 2-mode PATCH (rename OR alter). TRUNCATE TABLE button (RESTART IDENTITY, opt-in CASCADE) for wiping rows without dropping the schema. New /studio/schema page renders every table in one canvas with FK arrows + inbound references. Project settings grows "move to another team" — re-parents a project across orgs after verifying target-org membership. Project overview gains a 24-hour invocation sparkline (server-rendered SVG, no client JS) with red overlay for errors.',
  },
  {
    date: '2026-05-12',
    tags: ['feat'],
    title: 'function logs + per-function stats in the dashboard',
    body: "new /logs tab on every project surfaces function_logs (already populated by the runtime log-fanout worker) with chip filters per function and ok/err status, cursor-based pagination, and a JSON export that walks the cursor for up to 1000 rows honoring the active filter. Per-function stats badge on the functions tab shows last-24h count + error rate + p50/p99 duration via a single percentile_cont query. Project overview surfaces last 5 errors in a red-tinted card with a deep-link to the filtered logs view.",
  },
  {
    date: '2026-05-12',
    tags: ['feat'],
    title: 'dashboard-managed schemas — convex-style manual database creation',
    body: "studio gains the full DDL surface: + new table with typed columns (text/integer/bigint/boolean/timestamptz/jsonb/uuid/numeric), primary keys, foreign keys with ON DELETE behavior, defaults; add/drop column on a live table; create/drop indexes (multi-column + unique). Schema panel renders relationships as “→ users.id”. FK values in the data view become “↗” links to the referenced row. Copy-as-schema.ts generates the equivalent briven/schema.ts so anything you click can graduate to git. SQL editor at /studio/sql runs arbitrary statements scoped via SET LOCAL ROLE to the project owner role, 5s statement_timeout, every query audit-logged. New connect tab shows project endpoints + a button to issue a 15-minute shell DSN for psql / pgcli / external tools. CLI path unchanged; both write to the same schema.",
  },
  {
    date: '2026-05-12',
    tags: ['feat'],
    title: 'team org admin — invitations, members, role change, delete',
    body: 'team owners can now invite collaborators by email (mirrors project_invitations: 7-day hashed-token email link, accept page at /dashboard/org-invitations/accept), list current members, change roles inline (with last-owner protection), remove non-owners, and soft-delete a team org (refused while live projects still exist). Tier-aware: free tier = 1 personal org, no team creation; pro/team = unlimited. Dashboard surfaces pending team invites in the projects banner alongside project invites.',
  },
  {
    date: '2026-05-11',
    tags: ['feat'],
    title: 'AI schema generator — Phase 3 differentiator',
    body: 'POST /v1/projects/:id/ai/generate-schema accepts a natural-language prompt and returns a draft briven schema.ts via a self-hosted Qwen 2.5-coder 32B running on briven infrastructure (no third-party AI provider). Dashboard surface at /dashboard/projects/:id/ai-schema with a copy button. Gated on BRIVEN_OLLAMA_URL — service shows a friendly "AI assistant offline" message until configured. Prompts and responses are not logged.',
  },
  {
    date: '2026-05-11',
    tags: ['feat'],
    title: 'github + google OAuth credentials live on briven.tech',
    body: 'the OAuth providers (Google + GitHub) now have their credentials configured in the production env. signin page shows both buttons; existing magic-link flow continues to work in parallel.',
  },
  {
    date: '2026-05-11',
    tags: ['feat'],
    title: 'polar billing wired end-to-end',
    body: 'production polar configured: access token, webhook secret, and product IDs for briven Pro ($21.99/mo) and briven Team ($99.99/mo). Existing webhook receiver at /v1/billing/webhook now authoritative for tier sync — subscription.* events flip projects.tier in the same transaction. checkout flow + customer portal both live.',
  },
  {
    date: '2026-05-11',
    tags: ['docs'],
    title: 'migration guide: nextauth / auth.js → briven',
    body: 'sixth per-source migration page. schema maps 1:1 (both target Better Auth\'s shape); the work is the api shape (getServerSession → brivenServer.session(), useSession imports). covers account preservation strategies, provider port, callback hooks, and the cutover checklist.',
  },
  {
    date: '2026-05-11',
    tags: ['feat'],
    title: 'usage metering: hourly aggregation cron + usage_events table',
    body: 'phase 3 GA-blocker progress. startUsageAggregator() runs ~5min after every wall-clock hour and writes one usage_events row per (project, hour, metric) for invocations + storage_bytes. Idempotent via the unique index, so a catch-up re-run after a restart is safe. Polar metering push is a separate worker landing once meter IDs are configured.',
  },
  {
    date: '2026-05-11',
    tags: ['feat'],
    title: 'realtime connection-seconds metric per project',
    body: 'apps/realtime tracks per-project cumulative subscription-seconds (closed subs + live deltas) via briven_realtime_connection_seconds_total{project} on /metrics. Drives the eventual Polar metering push for connection-minutes billing.',
  },
  {
    date: '2026-05-11',
    tags: ['feat'],
    title: 'project auto-suspension on abuse-report resolution',
    body: 'projects.suspended_at column + blockIfProjectSuspended() middleware on invoke + deploy routes (403 project_suspended). The /v1/admin/abuse-reports/:id PATCH endpoint now accepts an optional projectId; resolving with resolution=suspended or banned flips the project in one step. Manual /v1/admin/projects/{suspend,unsuspend} endpoints for the non-abuse paths. Admin triage modal in the dashboard surfaces the new projectId input only when the resolution implies suspension.',
  },
  {
    date: '2026-05-11',
    tags: ['feat'],
    title: 'github OAuth alongside google + magic link',
    body: 'sign-in page now wires three auth methods: magic-link via email, Google OAuth, and GitHub OAuth. Each button is gated by NEXT_PUBLIC_BRIVEN_HAS_*_OAUTH so the UI hides providers whose creds aren\'t configured.',
  },
  {
    date: '2026-05-11',
    tags: ['fix'],
    title: '/info reports a real sha on dokploy auto-deploys',
    body: 'health.ts now resolves the commit sha from .git/HEAD when BRIVEN_BUILD_SHA isn\'t passed at image build time (which is exactly the case for dokploy auto-deploys). loose refs + packed-refs both supported. Paired with treating the literal "dev" env value as "fall back to git" sentinel. Promoted to @briven/shared so apps/realtime + apps/runtime use the same chain.',
  },
  {
    date: '2026-05-11',
    tags: ['feat'],
    title: 'deploy_history table + /dashboard/admin/deploys',
    body: 'every api boot now writes one row into deploy_history (service, buildSha, buildAt, env, bootedAt). new admin page renders a timeline with "live" badge on the most recent row so operators can correlate "the bug appeared at 14:32" with "deploy abc1234 went live at 14:30" without ssh-ing to the box. /v1/admin/deploys?service=api&limit=N exposes the raw stream.',
  },
  {
    date: '2026-05-11',
    tags: ['docs'],
    title: 'migration guide: hasura → briven',
    body: 'fifth per-source migration page. postgres half ports for free via pg_dump; the work is the permissions port — every (role, table, action) triple from hasura metadata becomes a guard in function code. covers actions, event triggers, scheduled triggers, remote schemas, auth (preserve-ids vs preserve-jwts) and subscriptions vs briven\'s reactive useQuery.',
  },
  {
    date: '2026-05-11',
    tags: ['feat'],
    title: 'deploy_history table + /dashboard/admin/deploys',
    body: 'every api boot now writes one row into deploy_history (service, buildSha, buildAt, env, bootedAt). new admin page renders a timeline with "live" badge on the most recent row so operators can correlate "the bug appeared at 14:32" with "deploy abc1234 went live at 14:30" without ssh-ing to the box. /v1/admin/deploys?service=api&limit=N exposes the raw stream.',
  },
  {
    date: '2026-05-11',
    tags: ['fix'],
    title: '/info reports a real sha on dokploy auto-deploys',
    body: 'health.ts now resolves the commit sha from .git/HEAD when BRIVEN_BUILD_SHA isn\'t passed at image build time (which is exactly the case for dokploy auto-deploys). loose refs + packed-refs both supported. paired with treating the literal "dev" string as a "fall back to git" sentinel since that\'s the dockerfile ARG default.',
  },
  {
    date: '2026-05-11',
    tags: ['infra'],
    title: 'observability stack live + postgres-exporter sidecar',
    body: 'grafana / loki / prometheus / promtail running on briven.tech kvm4 as compose project briven-obs; postgres-exporter ships pg_stat_* metrics from briven-postgres. all five prometheus jobs (api / runtime / realtime / postgres / prometheus) report up; four starter dashboards (api requests, runtime invocations, realtime subs, postgres health) populated.',
  },
  {
    date: '2026-05-11',
    tags: ['feat'],
    title: 'mittera email suppression layer',
    body: 'new email_suppressions table + service. mittera webhook handler dispatches permanent bounces / complaints / mittera-suppressed events into the suppression list; outbound send short-circuits on suppressed recipients before posting to mittera. admin ui at /dashboard/admin/email-suppressions with manual add / remove.',
  },
  {
    date: '2026-05-11',
    tags: ['feat'],
    title: 'mittera outbound + webhook live on briven.tech',
    body: 'POST https://api.mittera.eu/api/v1/emails with Bearer auth verified end-to-end (smoke + magic-link both 200). inbound webhooks at https://api.briven.tech/mittera-webhook verify X-mittera-Signature: v1=<hex> + X-mittera-Timestamp: <ms> with a 5-minute replay window, dispatch per spec §6, audit-log every event.',
  },
  {
    date: '2026-05-11',
    tags: ['feat'],
    title: 'nightly backup cron on kvm4',
    body: 'systemd timer fires /usr/local/bin/briven-backup.sh daily at 02:17 UTC. pg_dump --format=custom against briven-postgres for both briven_control + briven_data, 30-day local retention, off-site upload gated on /etc/briven/backup.env (BRIVEN_BACKUP_S3_*). off-site disabled until B2/R2 creds land.',
  },
  {
    date: '2026-05-11',
    tags: ['feat'],
    title: 'briven init --template={blank,todo-app,chat}',
    body: 'cli init now scaffolds from one of three inline templates. blank = minimal notes; todo-app = 4 mutations + 1 reactive query; chat = two-table per-room reactive. templates are embedded so init works on a fresh machine with no network.',
  },
  {
    date: '2026-05-11',
    tags: ['feat'],
    title: 'briven doctor + GET /info build identity',
    body: 'doctor now pings /info (new endpoint) and reports build sha + build timestamp + uptime alongside the existing health / ready / auth checks. Dockerfile passes BRIVEN_BUILD_SHA + BRIVEN_BUILD_AT through as ARGs for compose to inject.',
  },
  {
    date: '2026-05-11',
    tags: ['feat'],
    title: 'admin: email events + suppressions dashboards',
    body: 'two new pages under /dashboard/admin — email-events (last 200 webhook deliveries with severity-tinted chips) and email-suppressions (recipients we won\'t send to, with manual add / remove). both gated on is_admin.',
  },
  {
    date: '2026-05-11',
    tags: ['infra'],
    title: 'git actions ci',
    body: 'CI workflows run pnpm -r lint + typecheck + test on every push to main. real eslint config replaces 13 lint stubs; 15 packages green workspace-wide.',
  },
  {
    date: '2026-05-11',
    tags: ['chore'],
    title: 'briven.cloud → briven.tech sweep (78 files)',
    body: 'every public-facing reference to the placeholder briven.cloud domain replaced with briven.tech. scripts/swap-domain.sh ships as the rename helper for future cutovers.',
  },
  {
    date: '2026-05-10',
    tags: ['feat'],
    title: '@briven/svelte + @briven/vue clients',
    body: 'svelte stores (query / mutation, reference-counted by svelte\'s store contract) and vue 3 composables (useQuery / useMutation, onScopeDispose-cleaned). same shape as the react hooks; same setBrivenClient bootstrap.',
  },
  {
    date: '2026-05-10',
    tags: ['feat'],
    title: 'briven export / briven import',
    body: 'briven export writes a project\'s schema + functions to a json archive; briven import reads the archive back into a target project as a deployment. data movement (pg_dump streaming) follows in a later slice.',
  },
  {
    date: '2026-05-10',
    tags: ['feat'],
    title: 'briven projects (list + set-default)',
    body: 'list projects authenticated on this machine; set the local default that other commands fall back to when there\'s no briven.json. zero server round-trip — works against the per-project api keys the cli already stores.',
  },
  {
    date: '2026-05-10',
    tags: ['feat'],
    title: 'studio (read mode)',
    body: 'embedded data browser at /dashboard/projects/:id/studio. table list with approx row counts and storage size; per-table view with column metadata and paginated rows. admin-tier gated; identifier escaping prevents cross-schema reads.',
  },
  {
    date: '2026-05-10',
    tags: ['infra'],
    title: 'observability stack + metrics endpoints',
    body: 'grafana / loki / prometheus / promtail compose ships under infra/observability with four starter dashboards (api requests, runtime invocations, realtime subs, postgres health). /metrics now live on api + realtime; postgres-exporter sidecar template ready for the data plane.',
  },
  {
    date: '2026-05-10',
    tags: ['feat'],
    title: 'restore drill (monthly cron)',
    body: 'pulls the latest pg_dump off off-site storage, sha256-verifies, restores into a throwaway db, sanity-counts core tables, drops the db. systemd timer on the 1st at 04:30 UTC. non-zero exit fires the discord webhook.',
  },
  {
    date: '2026-05-10',
    tags: ['feat'],
    title: 'public status page',
    body: 'docs.briven.tech/status — live probes against api / runtime / realtime, red/green per service, latency + http status, no cache. dns cutover to status.briven.tech is a future ops move.',
  },
  {
    date: '2026-05-10',
    tags: ['feat'],
    title: 'open-signups env flag',
    body: 'BRIVEN_OPEN_SIGNUPS toggles invite-only beta vs. public signups across all three Better Auth paths (email+password, github oauth, magic link). cutover is now config-only, no code change.',
  },
  {
    date: '2026-05-10',
    tags: ['docs'],
    title: 'sla matrix on /dashboard/billing',
    body: 'uptime, response target, support response, rollback window — per tier. free best-effort; pro 99.5% / p99 < 500ms invoke; team 99.9% / p99 < 200ms invoke + 100ms RT fan-out.',
  },
  {
    date: '2026-05-10',
    tags: ['chore'],
    title: 'metrics module promoted to @briven/shared',
    body: 'createMetricsRegistry({help}) factory in @briven/shared/observability replaces three duplicated hand-rolled prometheus exposition modules in api / runtime / realtime. each app instantiates its own scoped registry; ~400 LOC of duplication retired.',
  },
  {
    date: '2026-05-10',
    tags: ['feat', 'docs'],
    title: 'public migration guide + public changelog',
    body: 'docs.briven.tech/migration documents the five principles + ten-step playbook that every briven migration follows, with per-source teasers for convex / supabase / raw-postgres / firebase. this changelog ships alongside.',
  },
  {
    date: '2026-05-10',
    tags: ['feat'],
    title: 'phase 3 abuse-report pipeline',
    body: 'public POST /v1/abuse-reports (anonymous, rate-limited 5/min/IP via cf-connecting-ip) + admin GET/PATCH /v1/admin/abuse-reports for triage. severity (spam / phishing / malware / csam / tos / other) and resolution (no_action / warned / suspended / banned) frozen.',
  },
  {
    date: '2026-05-10',
    tags: ['feat'],
    title: 'phase 3 beta-invite dashboard surface',
    body: 'recipients can now see and accept pending invitations from /dashboard/invitations without going through the email link. the email-link flow stays intact for not-yet-signed-in recipients. accept-by-id replaces the token-based path inside the dashboard so the listing API never has to expose one-time tokens.',
  },
  {
    date: '2026-05-10',
    tags: ['feat'],
    title: 'phase 3 usage-metering (invocations slice)',
    body: 'GET /v1/projects/:id/usage returns invocation count + total duration for the current calendar month UTC, with optional ?from=&until= for custom windows. backed by aggregation over function_logs (no new event table needed for this slice).',
  },
  {
    date: '2026-05-10',
    tags: ['feat'],
    title: 'tier-aware rate limits at the gateway',
    body: 'rateLimit middleware now accepts a dynamic limit fn so caps can vary per project tier. RATE_LIMITS_BY_TIER table: invoke = 60 / 600 / 6000 per minute (free / pro / team), deploy = 5 / 30 / 100. wired onto the invoke and deploy routes; rollout to remaining mutations is the next pull.',
  },
  {
    date: '2026-05-10',
    tags: ['feat', 'fix'],
    title: 'briven invoke command',
    body: 'briven invoke <function> [--body <json>] [--body-file <path>] [--raw] is in the CLI. closes the dogfood smoke-test gap that previously required curl. --raw mode prints the unwrapped function value, jq-pipeable.',
  },
  {
    date: '2026-05-09',
    tags: ['feat'],
    title: 'shared structured logger with redaction',
    body: '@briven/shared/observability exports createLogger(). every emitted line runs through the redaction pass — emails and IPv4 addresses can\'t leak into Loki even if a caller forgets to scrub at the call site. apps/api, apps/runtime, apps/realtime all delegate.',
  },
  {
    date: '2026-05-09',
    tags: ['security'],
    title: 'constant-time shared-secret comparison',
    body: 'the runtime shared secret is now compared with crypto.timingSafeEqual across apps/api/src/routes/internal.ts, apps/runtime/src/index.ts, and apps/realtime/src/index.ts. a remote attacker measuring response latency can no longer recover the secret byte-by-byte.',
  },
  {
    date: '2026-05-09',
    tags: ['security'],
    title: 'rate-limit pinned to cf-connecting-ip outside dev',
    body: 'rate-limit middleware now returns 403 origin_direct_rejected when BRIVEN_ENV !== development and cf-connecting-ip is missing. prevents bypass via direct origin hits (cloudflare-only ingress in production).',
  },
  {
    date: '2026-05-09',
    tags: ['security'],
    title: 'BRIVEN_ENCRYPTION_KEY required at boot in non-dev',
    body: 'previously failed-closed at request time when missing — a deploy could boot, only blowing up when the first customer read an encrypted env var. now fails loud at startup.',
  },
  {
    date: '2026-05-09',
    tags: ['feat'],
    title: 'realtime LISTEN/NOTIFY pipeline complete',
    body: 'apps/realtime now closes the loop: WS subscribe → LISTEN on per-table channels → NOTIFY → re-invoke → push fresh data. internal invoke route added on apps/api so realtime can use the runtime shared secret. fireChannel snapshot iteration prevents touchedTables drift from skipping subscribers mid-fan-out.',
  },
  {
    date: '2026-04-27',
    tags: ['security'],
    title: 'owner-tier scaffolding pinned (PR #20)',
    body: 'reservation for future destructive routes (project delete, member removal). enforced via the project-auth chain.',
  },
  {
    date: '2026-04-27',
    tags: ['security'],
    title: 'per-key role scoping for API keys (PR #19)',
    body: 'human users can issue keys at their own role or lower (viewer / developer / admin). owner-tier is reserved for human owners and never assignable to a key.',
  },
  {
    date: '2026-04-26',
    tags: ['security'],
    title: 'HTTPS-origin invariant fail-fast outside dev (PR #18)',
    body: 'BRIVEN_API_ORIGIN and BRIVEN_WEB_ORIGIN must start with https:// when BRIVEN_ENV !== development. catches misconfigured prod deploys at boot.',
  },
  {
    date: '2026-04-26',
    tags: ['fix'],
    title: 'realtime + runtime smoke tests (PR #17)',
    body: 'bun-test smoke files so CI exits 0 with empty suites. closed the noise from CI runs that warned "no tests found".',
  },
  {
    date: '2026-04-25',
    tags: ['security'],
    title: 'org-vs-project authz reconciliation (PR #16)',
    body: 'effective-role gates resolve project-level role from org membership when the project doesn\'t carry an explicit member row. fixes a class of bugs where org owners couldn\'t access their own projects.',
  },
  {
    date: '2026-04-25',
    tags: ['security'],
    title: 'cross-site CSRF closed on /v1/* state-changing routes (PR #15)',
    body: 'sameSite=strict on the session cookie + Origin-check middleware. unsafe-method requests with a session cookie now require an allow-listed Origin.',
  },
  {
    date: '2026-04-25',
    tags: ['security'],
    title: 'schemaSnapshot validator + bound migration insert (PR #14)',
    body: 'closed a high-severity SQL-injection vector — the schema snapshot is now strictly validated before any DDL is interpolated.',
  },
  {
    date: '2026-04-25',
    tags: ['security'],
    title: 'security hardening phase-0 (PR #13)',
    body: 'better-auth secret rotation, audit-log IP pepper, IP redaction in logs, realtime WebSocket upgrade gate.',
  },
  {
    date: '2026-04-24',
    tags: ['feat'],
    title: '@briven/cli ships as a self-contained tarball (PR #10)',
    body: 'tsup bundles the cli with @briven/schema, @briven/shared, @briven/config inlined. consumers install via pnpm add -D @briven/cli without the workspace-ref problem the prior file: install hit. exposes @briven/cli/schema and @briven/cli/server sub-imports.',
  },
  {
    date: '2026-04-24',
    tags: ['feat'],
    title: 'briven link writes projectId into briven.json (PR #11)',
    body: 'briven link --project <p_…> records the project id locally so subsequent commands (deploy, invoke, env, db) infer it without --project flags.',
  },
  {
    date: '2026-04-24',
    tags: ['infra'],
    title: 'wildcard TLS via Cloudflare DNS-01 (PR #9)',
    body: 'traefik issues *.apps.briven.tech certificates via the cloudflare DNS challenge. each customer project gets its own routable subdomain at deploy time.',
  },
  {
    date: '2026-04-23',
    tags: ['fix'],
    title: 'auto-create personal org on signup + lazy backfill (PR #12)',
    body: 'every user has exactly one personal org from the moment they sign in. /v1/me lazily backfills for older users who pre-date the change.',
  },
];
