Skip to main content

What we delivered

Recent updates and new features in plain English.

June 2026

  • Fix

    Dedup check counts action-bearing widgets once (task_d12d6e67)

    Fixes `pnpm run check-manifest` in `platform/packages/widgets`, which falsely reported 6 core widgets as duplicate registrations.

  • Fix

    Create InspectionTrackerWidget source (#6545)

    Shipped Create InspectionTrackerWidget source (#6545).

  • Fix

    Plot History tab no longer shows Add Tenancy CTA (#6519)

    Fixes the Plot History tab in TerraLedger dashboard which incorrectly showed an "Add Tenancy" CTA button on an empty read-only audit view.

  • New

    Birds launch polish B1+B2+B3 + ADR-0056 Phase 2 treasurer read-gate GREEN

    Shipped Birds launch polish B1+B2+B3 + ADR-0056 Phase 2 treasurer read-gate GREEN.

  • Fix

    Go-live UX audit — 7 P0/P1 widget fixes across TerraLedger, allotments.info, RootLytics (#6470)

    Seven production UX bugs fixed across the three launch-critical recipes. **RootLytics widgets 401 fixed** — intelligence widget proxy was missing a service token; all SaaS metrics, KPI dashboard, MRR, and anomaly widgets now load. **EntityTable empty states** show humanised entity labels ("No Tenancys yet") not raw namespaced types ("No terraledger.tenancys yet"). **Financial chart 400** — ChartWidget falls back to `count` when `aggregateField` is absent for sum/avg/min/max aggregations. **Anomaly detector blank period** — Zod defaults now applied so "No anomalies detected in the last 24h." renders correctly. **Stripe setup form in demo** — widget shows connected state in demo sessions instead of the setup form. **Leaflet map partial render** — two deferred `invalidateSize()` calls after dashboard widget layout settles. Fixes #6471 #6472 #6473 #6474 #6475 #6476

  • New

    Capability spec layer — the platform's intended user actions (#6459)

    Adds the **"should" layer** to the second brain: `platform/scripts/index-capabilities.mjs` → `.codebase-index/capabilities.json` (gitignored artifact, chained into `index-codebase`). **What it does:** extracts intended capabilities from recipe `valuePropositions.ahaMoments[]` + `uxStandards.crystallizationMoments[]`, binds each to its widget (manifest) + spec (test-catalog) + source file, and computes a deterministic **code-vs-intent status**: `wired` / `drift` (claims-implemented but widget missing) / `mock-suspect` (widget exists but **0 data-fetching** in source) / `unwired` / `no-widget`. **Why:** turns *"what should the platform do, and does it?"* from a multi-million-token LLM audit into a `jq` query — cutting token cost and accelerating launch decisions. Also the consumer for the planned RootCheck journeys-gate. **Proof (first run, 151 capabilities, milliseconds):** deterministically reproduced the DoD audit's mock findings — box-office, accessibility-seating, visitor-directory — **and surfaced 2 new launch-spine P0 mock-suspects** the LLM audit missed: `allotments-info-vanity-site` site-online → `layers/site-directory` (0 data-fetching) `terraledger` estate-relationship-map → `RootPlanRelationshipGraphWidget` (0 data-fetching) Query examples: ``` jq '.capabilities[] | select(.status=="mock-suspect")' capabilities.json jq '.capabilities[] | select(.launchSpine and .status!="wired")' capabilities.json ``` Note: `mock-suspect` is a heuristic (0 data-fetching) — pre-filters to a tiny set for review, not a hard verdict (some widgets receive data via props). Fixes #6459 🤖 Generated with [Claude Code](https://claude.com/claude-code)

  • Improvement

    Release-20260620 → preprod

    Sprint bundle for **Release-20260620 → preprod** promotion. Closes the allotments.info → TerraLedger DoD money path (5 real billing bugs fixed, £99 credit surfaced), wires ADR-0056 shadow-first read-gate across Collections + TerraLedger GET routes (committee roles cannot see applicant PII), lands the waitlist quad-lens (Phases 1 + 2a) and the check-position velocity lens, makes IG-01 silent allocation create a tenancy end-to-end, and delivers 14 critical DoD test fixes plus 3 new TerraLedger journey specs.

  • Fix

    Entity-quad layout for multi-position view

    Shipped Entity-quad layout for multi-position view.

  • Improvement

    Promote preprod to main

    Promotes the current `preprod` release candidate into `main`.

  • Fix

    Remove stale dashboard P0 gate + patch shell-quote critical vuln

    Shipped Remove stale dashboard P0 gate + patch shell-quote critical vuln.

  • Fix

    Eliminate 47 phantom widgets + populate plots/tenants tabs — TerraLedger July 11 demo

    Shipped Eliminate 47 phantom widgets + populate plots/tenants tabs — TerraLedger July 11 demo.

  • Fix

    IG-01 offer-accepted webhook end-to-end (5 bug cascade)

    Shipped IG-01 offer-accepted webhook end-to-end (5 bug cascade).

  • New

    Populate check-position velocity fields from offer throughput

    Shipped Populate check-position velocity fields from offer throughput.

  • New

    Check-position velocity fields (Little's Law) + bug fix

    Shipped Check-position velocity fields (Little's Law) + bug fix.

  • New

    ADR-0056 waitlist quad lens — terraledger.applicant (Phase 1)

    Begins the ADR-0056 **quad refactor of the allotment waitlist**: exposes waitlist applicants as a TerraLedger-namespaced lens (`terraledger.applicant`) over QueueSpark's `queuespark.entry` source, so council staff read applicants through the same recipe-namespaced quad model as plots/tenants — instead of a `queuespark`-namespaced island. This is **Phase 1 (data-model foundation)**; Phases 2–3 are sequenced below and need the running stack.

  • New

    ADR-0056 quad lens Phase 2a — gate + projection on /applicants

    Phase 2a of the ADR-0056 waitlist quad lens: adds the **read gate** and the **canonical `terraledger.applicant` projection** to the existing `GET /api/terraledger/waiting-list/applicants` route, so council staff read applicants through the recipe-namespaced quad model with role-scoping. Phase 1 (`terraledger.applicant` entity type + `projectQueueEntryToApplicant`) merged via #6375.

  • Fix

    Fix topbar overflow clipping user avatar dropdown + z-index

    Shipped Fix topbar overflow clipping user avatar dropdown + z-index.

  • Fix

    Repair £99 upgrade-credit money path + /sites reads (5 bugs)

    Fixes 5 real bugs that blocked the allotments.info → TerraLedger money path and the cross-recipe "unified queue" / "living directory" goals — none of which worked end-to-end before. All verified on the live OrbStack stack against real Stripe TEST mode.

  • Fix

    Restore Stripe webhook delivery via /api/stripe/webhook proxy

    Shipped Restore Stripe webhook delivery via /api/stripe/webhook proxy.

  • Fix

    Workspace loading loop + TerraLedger recipe widget cleanup

    Shipped Workspace loading loop + TerraLedger recipe widget cleanup.

  • Fix

    Send x-service-token on Intelligence promotion calls

    Fixes a service-to-service auth gap caught by **live-stack testing** of the council-discount money path (follow-up to #6318).

  • Fix

    'system' service-account UUID error + billing→intelligence URL

    Fixes two defects that blocked the **platform Stripe key path** and the **council-discount money path**, found by running the full live OrbStack stack to validate the £0-invoice flow (follow-up to #6318 / #6321).

  • Fix

    Free-pin TL-subscriber UUID query + store-stripe-key script auth

    Resolves the two follow-ups from the council-discount live run (#6323), plus a real money-path bug found while verifying them.

  • New

    Council-account-scoped auto-applied discounts

    Adds an account-scope primitive to the existing promotion system so a council/society discount **auto-applies silently at checkout** — no email, no one-time code to type — and covers every allotments.info pin AND every recipe that the council account subscribes to.

  • New

    Viewport border overlay when sandbox mode is active

    Shipped Viewport border overlay when sandbox mode is active.

  • New

    ADR-0056 P8 — per-entity-type permissions + advanced-mode editor

    ADR-0056 **P8 — per-entity-type permissions ("advanced mode")**. An account admin can grant a member explicit CRUD per Collections entity type (e.g. a treasurer gets full control of plots, or read-only), and the data layer enforces it. Includes the editor UX and the admin read-only roles view. Builds on the merged ADR-0056 stack (P1–P7, now in `preprod`). Base is `preprod`.

  • Fix

    Use ESM crypto imports instead of dynamic require (unblocks platform key storage)

    Shipped Use ESM crypto imports instead of dynamic require (unblocks platform key storage).

  • Fix

    Align subscription SQL to live schema (recipe add + subscription-status)

    Fixes two pre-existing 500s caused by billing-service schema drift against the live `billing_subscriptions` relation, unblocking the customer "add / activate a recipe" flow and the per-recipe subscription-status surface.

  • Fix

    T5 money path — #6112 signup→Stripe TEST subscription→entitlement (+ #6257 byok schema)

    Shipped T5 money path — #6112 signup→Stripe TEST subscription→entitlement (+ #6257 byok schema).

  • New

    Wire Collections imports into runtime surfaces (#4162)

    Wires imported (snake_case) Collections rows into the TerraLedger, QueueSpark, and allotments.info runtime surfaces so a council can import its workbook and immediately see/use the data — closing the runtime-projection gap (#4162).

  • Improvement

    Release-20260616.2 → preprod (T2 #6162 committee spine + import)

    Promote Release-20260616.2 to preprod after T2 (#6162) Pass. **T2 delivered:** #4162 (import→runtime projection), #6139 (CRUD boundaries + dual-role), #6135 (clerk daily loop). **Proof (local/OrbStack, authoritative):** USP 18 passed/0 failed (3.6m); Rung 2 api-integration 90/90; Rung 3 critical-pages 177/0-failed. Report: platform/docs/tranches/T2-completion-report.md. **Held out of scope:** #6227, #6228. CI infra-red (Actions budget) → admin merge. 🤖 Generated with [Claude Code](https://claude.com/claude-code)

  • Fix

    Support nested data.<key> sort fields in find() (#6228)

    Fixes a 502 when listing entities sorted by a nested JSONB `data.<key>` field (e.g. TerraLedger invoices' `data.created_at`).

  • Fix

    Declare skipAuth on FastifyContextConfig (#6232)

    Fixes 2 pre-existing TypeScript errors in `terraledger/src/routes/public.ts` (`skipAuth` not in `FastifyContextConfig`).

  • Fix

    Widen offer claim-link proxy timeout 6s→15s + R2 claim-token regression gate (#6100)

    Widens the public allotment-offer "View claim" action proxy timeout (6s→15s) so the synchronous `accept` path no longer surfaces a spurious 503 to applicants, and adds a real-browser USP regression gate proving the launch-critical claim-token link end to end (R2).

  • Fix

    Homepage map UX — UK zoom, wait-time pin colours, taller map (#6077)

    Aligns the allotments.info homepage map with the launch UX: UK-wide default zoom, a taller map, and confirms the wait-time pin colours + footer "Guides" reorganisation are in place. UI-only — no DB, API, or recipe JSON changes.

  • Improvement

    Release-20260616.3 → preprod (M9 T4 complete)

    Promotes Release-20260616.3 to preprod. Milestone 9 **T4 (#6164) is complete** — RootPlan handoff/rendering + launch polish, both batches merged and proven on the live OrbStack stack + :3100 production build.

  • Fix

    Resolve recipes from Collections platformRecipe (#6213)

    Billing service resolves recipe JSON (pricing, plans, trial config, invoice floor) from **Collections `platformRecipe` (ADR-0055)** with a filesystem seed fallback — matching the PlotsparkOS app + RootVibe. Fixes #6213.

  • Fix

    Log swallowed errors on idempotent migration catches (RootCheck #6222)

    Shipped Log swallowed errors on idempotent migration catches (RootCheck #6222).

  • Fix

    Align demo contract whitelist with quad-entity seeder

    Aligns the TerraLedger demo UI contract generator with the quad-entity demo seeder (`site`, `plot`, `tenant`, `tenancy`) so contract tests assert the correct `minRows` and entity-detail child tables.

  • Fix

    Green the api-integration suite (90/90, 1475 tests) + close header-spoof auth bypass

    Greens the **entire api-integration suite** (90 files / 1475 tests, 0 failed) on the authoritative OrbStack stack, fixing a batch of **pre-existing** failures surfaced by the Milestone 9 go-live gate (#6178 family) — including a real header-injection auth bypass.

  • Fix

    Activate subscriptions for dynamic-pricing recipes (no plan_key)

    Fixes the platform Stripe money path so a paying customer on a **dynamic-pricing recipe** (TerraLedger et al.) actually gets their recipe granted after checkout — previously they were charged but landed with no recipe attached.

  • Fix

    Wire workspace sharing UI to real permissions API

    Wires the workspace Share dialog (`DashboardSharing.tsx`) to the existing, already-tested `GET/PUT /api/workspaces/[id]/permissions` route — replacing the hardcoded `alice@example.com`/`bob@example.com` mock data that meant sharing never actually persisted.

  • Fix

    Resilient recipe/domain resolution + wire dead controls + prove every control

    Fixes the dashboard (the platform's main surface) failing to load its data, converges recipe/domain resolution on one resilient pattern, wires two dead controls, and backs every dashboard control with a green E2E suite. Fixes #6189 Relates to #6160 (T0 — launch environment and test integrity)

  • Fix

    Guard unpriced premium widgets + contain transient render errors

    Stops `/why-plotspark/checkout/[recipeId]` from intermittently rendering the full-page "Something went wrong" error boundary, by guarding an unguarded `widget.pricing.monthly` access and containing transient render-throws. Found during T0 Rung 3 validation.

  • Fix

    Wire SheetDescription on PinnedSidebarPanel help Sheet

    Wires `SheetDescription` (auto-`aria-describedby`) onto the `PinnedSidebarPanel` help Sheet so Radix no longer warns about a missing description on the dialog content.

  • Fix

    Resolve 10 check-types errors in ParentPortal + useColumnMapping

    Fixes all 10 `pnpm check-types` errors in `platform/apps/PlotsparkOS` (3 distinct root causes across 2 files).

  • Fix

    Stop aha-moment widget 500 + prove dogfooding table fix (#6088)

    Closes the remaining RootLytics widget 500 (`aha-moment-intelligence` querying a non-existent `accounts` relation) and adds durable proof that the previously-landed dogfooding-table fix (migration `0139` + startup DDL, already on Release-20260615) stops the `recipe-performance` 500.

  • Fix

    Add debug/trace to rootplan-terraledger-handoff logger mock

    Shipped Add debug/trace to rootplan-terraledger-handoff logger mock.

  • Improvement

    Release-20260615 → preprod — claim/approval/provisioning/RootLytics

    Promotes T1 (Milestone 9 tranche #6161 — claim, approval, provisioning, RootLytics control plane) from Release-20260615 to preprod. The delta is **exactly** the verified T1 work — nothing else.

  • Fix

    Send siteName in claim send-code body

    Sends `siteName` in the claim send-code request body so the RootLytics operator claim queue shows the human-readable site name instead of the opaque slug.

  • New

    Extend #4095 gate with signed-webhook + RootLytics signal (#5552)

    Extends the offer-lifecycle real-browser gate (#4095 / #4043) to exercise the two production paths that were previously unexercised: (1) signed-webhook issuance on offer-accept via HMAC-SHA256, and (2) RootLytics offer signal hand-off after tenancy creation.

  • New

    Add column-mapping step to ImportWizardWidget (#5523)

    Adds a schema-driven column-mapping step to ImportWizardWidget, inserted between the "Upload & Analyse" and "Review Changes" steps. Users can map CSV headers to entity schema fields, promote unmatched columns to new custom fields, and the confirmed mapping is persisted as a reusable `vaultImportMapping` template in Collections.

  • Fix

    Seed widget.title into config.title via buildSyntheticComposition (#5771)

    Closes the remaining gap from PR #5772: `CompositionLoader.buildSyntheticComposition` was dropping the top-level `widget.title` from recipe JSON, so `config.title` was never seeded into compositions. Widgets fell back to the auto-generated entityType label ('Terraledger.site Canvas') instead of showing the recipe-configured friendly title ('Sites', 'Plots'). Fixes #5771

  • New

    Entitlement-driven widget picker + cross-recipe remix (#5565)

    Replaces the hardcoded 10-widget `SERVICE_WIDGETS` list in `WidgetPicker` with a live `grantedWidgets` prop derived from the account's active recipe collections. Adds "Add Widget" access on cross-recipe workspaces. Fixes `forkWorkspace` to carry `isMultiRecipe`/`recipeIds` for cross-recipe workspace forks. Fixes #5565

  • New

    Import pipeline — prepare, import, discount scripts

    Shipped Import pipeline — prepare, import, discount scripts.

  • Improvement

    Preprod → main (2026-06-14)

    Shipped Preprod → main (2026-06-14).

  • New

    Remove early-access gate, keep early-access banner

    Shipped Remove early-access gate, keep early-access banner.

  • Fix

    UK map zoom, wait-time pin colours, taller map, Guides to footer

    Fixes four homepage UX issues on allotments.info/en: the map defaulting to a Europe-wide view, all pins rendering identically in grey despite wait-time data being available, the map being too small, and the 'Gardening Hub' nav item confusing users who came to find a plot. Fixes #6077

  • New

    Council logo upload surfaced + threaded into allotments.info emails

    Shipped Council logo upload surfaced + threaded into allotments.info emails.

  • Fix

    Repair broken 'View claim' email link (#3075)

    Shipped Repair broken 'View claim' email link (#3075).

  • New

    E2E validation dashboard + goal reporter

    Shipped E2E validation dashboard + goal reporter.

  • Fix

    Show all widgets by default + cleaner card design

    Shipped Show all widgets by default + cleaner card design.

  • Fix

    Open help in-panel Sheet instead of navigating away

    Shipped Open help in-panel Sheet instead of navigating away.

  • Fix

    Remove orchestration/workflow-marketplace from recipe

    Shipped Remove orchestration/workflow-marketplace from recipe.

  • Fix

    Missing intelligence DB tables + widget loading performance

    Shipped Missing intelligence DB tables + widget loading performance.

  • Fix

    Remove fabricated install counts from Popular Widgets fallback

    Shipped Remove fabricated install counts from Popular Widgets fallback.

  • Fix

    Wire notifications bell and search input

    Shipped Wire notifications bell and search input.

  • Fix

    Toggle uses middleware header not Core service; clean rootlytics settings tab

    Shipped Toggle uses middleware header not Core service; clean rootlytics settings tab.

  • Fix

    Email safeguard, council branding, tenant limits, TerraLedger route fixes

    Shipped Email safeguard, council branding, tenant limits, TerraLedger route fixes.

  • Fix

    Repair 3 bugs blocking Excel import from allotments files

    Shipped Repair 3 bugs blocking Excel import from allotments files.

  • Fix

    Plot id fallback, tenant pagination, IG-03 route, E2E spec repairs

    Shipped Plot id fallback, tenant pagination, IG-03 route, E2E spec repairs.

  • Fix

    Public-render auth (vanity-content + creator shop) + Birds journey specs proving them

    Fixes **two production bugs** in anonymous public vanity rendering (found by running the Birds journey specs against the live stack with no skips), plus the journey specs + addon-mount model that prove them. Fixes #6109

  • Fix

    Repair 6 syntax errors that zeroed the entire DoD test suite

    Repairs 6 syntax errors that made `playwright.dod.config.ts` collect **0 tests in 0 files** — silently taking the entire DoD recipe go-live gate (612 tests / 107 files) offline on Release-20260614.

  • New

    Position timeline per-site cards + movement data (#5929)

    Shipped Position timeline per-site cards + movement data (#5929).

  • New

    Rename p0-p1-issue-loop → ralph-loop + milestone scoping + workflow pipeline

    Renames the autonomous issue-resolution skill from `p0-p1-issue-loop` to `ralph-loop` and adds two new capabilities: milestone-scoped runs and a Workflow-delegated per-issue implementation pipeline.

  • Fix

    Remove malformed double-dotted keys breaking Next.js build

    Shipped Remove malformed double-dotted keys breaking Next.js build.

  • New

    Anonymous /register and /check-position on council vanity domains

    Adds anonymous `/register` (waitlist join form) and `/check-position` pages to council vanity domains (`birds.allotments.info`, `birdscommunityallotments.org.uk`) without requiring authentication — enabling the existing notice-board QR code at `birdscommunityallotments.org.uk/register` to work on launch day (2026-07-11).

  • New

    Seed CMS pages + fix CNAME target + wire admin editor

    Shipped Seed CMS pages + fix CNAME target + wire admin editor.

  • Fix

    30s request timeout on all service proxies + DLQ creation order

    Shipped 30s request timeout on all service proxies + DLQ creation order.

  • Fix

    Fix layout sizing issues on allotments.info directory page

    Shipped Fix layout sizing issues on allotments.info directory page.

  • Fix

    Pest Risk + Frost Risk cron jobs crash — column "status" does not exist on plotspark_gardens

    Adds the missing `status` column to `plotspark_gardens` so the Pest Risk and Frost Risk cron jobs no longer crash with `column "status" does not exist`.

  • Fix

    Auth.users Supabase schema reference fails on direct-PostgreSQL

    Fixes 4 SQL queries in the Billing service that referenced `auth.users` (a Supabase-specific schema). On the direct-PostgreSQL Railway database the `auth` schema does not exist, causing `relation "auth.users" does not exist` errors whenever a Stripe webhook or renewal/winback sweep runs.

  • New

    Key fob / gate access register

    Adds a key fob / gate access register to TerraLedger: issue physical keys to tenants, track returns, report lost keys, and surface outstanding keys for the termination checklist. Closes the competitor-parity gap with Rialtas and Scribe for day-one allotment-society support. Closes #5894

  • Fix

    UsageAggregationWorker passes string plan IDs to UUID column

    Fixes a PostgreSQL type error in `UsageAggregationWorker.snapshotRootPlanPlantingsForAllAccounts()` where the query filtered `s.plan_id IN ('pro', 'family-and-friends')` but `plan_id` is a UUID FK column. PostgreSQL rejects the literal strings with `"invalid input syntax for type uuid: 'pro'"`, crashing the usage aggregation job for all RootPlan accounts on every scheduled run. Closes #5960

  • Fix

    Column e.context does not exist in plotspark_workflow_executions

    Fixes a PostgreSQL runtime error in the rules-policy-engine workflow service where queries referenced `e.context` and `e.status` on `plotspark_workflow_executions`, but the canonical SQL migration (`003_plotspark_workflow_executions.sql`) created the table with `execution_context` and `execution_status` column names — causing `resumeStuckExecutions` to fail on every startup. Closes #5957

  • New

    Wire Communications service into tenant portal recipe

    Wires the Communications service into the TerraLedger tenant portal recipe by adding notification preference management for tenants, a mobile inspection form widget, TerraLedger email templates in the Communications service, and a QueueSpark site-stats endpoint surfaced via the allotments-info API proxy. Closes #6008

  • Fix

    Replace slug IDs and literal 'system' with valid UUIDs in plotspark_rules

    Replaces human-readable slug IDs (`marketplace-contact-unlock-default`, `full-tl-free-pin-renewal-default`) and the literal string `'system'` with valid deterministic UUIDs in the Rules Policy Engine seed files, fixing `invalid input syntax for type uuid` errors on every service startup.

  • New

    Mobile inspection form UX

    Adds `MobileInspectionFormWidget` — a field-grade plot inspection form for council officers doing plot inspections on a mobile device. It provides ≥44px touch targets throughout, a prominent tap-to-capture camera tile, inline GPS capture, an 8-item compliance checklist, a notes field, a maintenance-required flag, and an embedded offline/sync indicator. Submissions are stored as `plotInspection` entities in Collections via `POST /api/terraledger/inspections`, with an offline fallback using IndexedDB (`inspection-sync.ts`). Closes #5194

  • New

    Per-site wait-time aggregate for map labels

    Adds a per-site wait-time aggregate endpoint to QueueSpark and wires it into the national allotments.info map so each pin displays a real wait-time label (e.g. `<14mo`) derived from actual queue throughput (Little's Law) rather than postcode-prefix heatmap averages.

  • New

    Council-configurable priority rules browser workflow

    Adds a full CRUD workflow for councils to configure allotment waitlist priority rules via the browser, wiring `PriorityRulesManagerWidget` → Next.js API proxy → `rules-policy-engine` REST routes → `QueueSparkService` scoring integration.

  • Fix

    Add x-service-token to Communications service calls

    Shipped Add x-service-token to Communications service calls.

  • Fix

    Add DATABASE_PUBLIC_URL fallback for Railway private DNS failures

    Shipped Add DATABASE_PUBLIC_URL fallback for Railway private DNS failures.

  • Fix

    DATABASE_PUBLIC_URL fallback for Railway private DNS failures

    Shipped DATABASE_PUBLIC_URL fallback for Railway private DNS failures.

  • Fix

    Prefer DATABASE_PUBLIC_URL over internal Railway DNS

    Shipped Prefer DATABASE_PUBLIC_URL over internal Railway DNS.

  • Fix

    Add x-service-token to all Communications service callers

    Shipped Add x-service-token to all Communications service callers.

  • Fix

    Waitlist cart 'Continue to apply' navigates to 404 — missing locale prefix

    Fixes the waitlist cart "Continue to apply" button on `/en/directory` which was navigating to `/join-waitlist` (no locale prefix) causing a 404. Users could add sites to the cart but could not proceed to the application form.

  • Fix

    Replace ParentPortal mock data + harden Stripe invoice.payment_succeeded MRR sync

    Shipped Replace ParentPortal mock data + harden Stripe invoice.payment_succeeded MRR sync.

  • Fix

    Add env-var fallback for Stripe webhook secret

    Shipped Add env-var fallback for Stripe webhook secret.

  • Improvement

    Remove env-var fallback for Stripe webhook secret

    Shipped Remove env-var fallback for Stripe webhook secret.

  • Fix

    Replace ALLOTMENTS_ADMIN_TOKEN env var with JWT role auth

    Replaces the per-deployment `ALLOTMENTS_ADMIN_TOKEN` environment variable with standard Core JWT role authentication (`x-verified-role: owner|admin`, injected by Next.js middleware) across all 8 allotments-info admin routes and the Layers service.

  • Fix

    Remove duplicate MobileInspectionFormWidget exports — unblocks build

    Shipped Remove duplicate MobileInspectionFormWidget exports — unblocks build.

  • Fix

    Expose startup error stack in Railway logs

    Shipped Expose startup error stack in Railway logs.

  • Fix

    Remove duplicate GET /sites/:siteId/stats route (crash-loop fix)

    Removes the duplicate `GET /api/queuespark/sites/:siteId/stats` route that was added by PR #6025, which caused QueueSpark to crash-loop on preprod with `FST_ERR_DUPLICATED_ROUTE`.

  • Improvement

    Railway DNS resilience, QueueSpark priority config, allotments-info JWT auth, vanity site flows

    Promotes **preprod → main** for the Release-20260613 sprint (89 commits, 164 files). Key themes: Railway private DNS resilience across all services, allotments-info JWT-based admin auth replacing per-deployment env token, QueueSpark council-configurable waitlist priority rules, vanity site `/register` + `/check-position` pages, and a raft of P0 bug fixes for billing, magic-link auth, and widget exports.

  • Fix

    Catch stale Server Action IDs and auto-reload after deploy

    Catches stale Server Action errors (`Failed to find Server Action`) in both error boundaries and triggers a guarded page reload, eliminating user-visible crash screens after Railway redeploys.

  • Fix

    Guard DELETE handler against missing accountId (#5979)

    Adds test coverage confirming the existing `getWorkspaceActor → 401` guard in the workspace DELETE route correctly prevents `deleteDashboard` from being called when `x-verified-account-id` / `x-verified-user-id` headers are absent — the root cause of the burst of "Missing account ID" errors in Railway production on 2026-06-12 (#5979).

  • Fix

    Add migration 021 — primary_color column on plotspark_accounts

    Shipped Add migration 021 — primary_color column on plotspark_accounts.

  • Fix

    Resolve both broken audit paths (#5981)

    Fixes both broken audit-logging code paths in the Core Service that were silently dropping all audit events on every significant action. Fixes #5981.

  • New

    Auto-sync recipe JSON to Collections on Railway deploy

    Adds a Railway `preDeployCommand` and a `sync:recipes:deploy` npm alias so that every Railway deploy automatically reseeds all platform recipe JSON into Collections. Recipe changes merged to the Release branch now propagate to every existing account without requiring a manual CLI run.

  • New

    Wire Orchestration into waitlist offer lifecycle

    Defines the `allotment-offer-lifecycle` Orchestration workflow — a declarative spec that registers the 21-day offer response cycle and wires both allotments.info waitlist recipes to reference it as a registered automation.

  • New

    Complete use-case relationship graph in schema (#5740)

    Adds 7 new entity types to the TerraLedger schema with FK relationships, completing the use-case relationship graph so master-detail navigation can auto-discover all plot-centric entities.

  • New

    Unified Connected-Allotments demo — terraledger + rootplan + rootlytics

    Adds a unified Connected-Allotments demo that provisions all three symbiotic recipes (TerraLedger, RootPlan, RootLytics) for a single demo account with linked data (plot↔garden↔rent↔metrics), so a visitor can experience the full IG-01–IG-06 cross-recipe story in a single session.

  • Fix

    Resolve three map screen overlap issues

    Shipped Resolve three map screen overlap issues.

  • New

    Schema-driven contract test pack — 6-layer coverage for 13 recipes + Collections seeder expansion

    Introduces a **6-layer schema-driven contract test pack** covering 13 recipes, expands demo seeders to cover 4 new recipe families, and registers 2 missing entity types in `ALL_SCHEMAS`.

  • New

    Expand L3 contract runner to all 30 recipes

    Shipped Expand L3 contract runner to all 30 recipes.

  • New

    Waitlist transfer audit log + public vanity site API (Sprint 1 Fix 1a+1b)

    Closes the two remaining DoD gaps for the Birds 2026-07-11 launch: adds a waitlist ownership transfer audit log (Fix 1a) and an unauthenticated public site API for vanity sites (Fix 1b).

  • Fix

    Regenerate manifest + disable RootLytics AI Insights tab

    Shipped Regenerate manifest + disable RootLytics AI Insights tab.

  • New

    Schema display contract + vanity site as entity-spine consumer (#5778)

    Shipped Schema display contract + vanity site as entity-spine consumer (#5778).

  • Fix

    Un-escape unicode in recipe JSON (\u2014 → —, \u00a3 → £)

    Shipped Un-escape unicode in recipe JSON (\u2014 → —, \u00a3 → £).

  • Fix

    Gate Garden3DView behind WebGL + lazy-load Three.js (#5671)

    Prevents the '3D mode crash → Widget Error tile' in the cockpit by gating Garden3DView behind a WebGL availability check and lazy-loading Three.js so it never blocks the 2D path.

  • New

    Record-ID column picker for idempotent re-imports (#5661)

    Shipped Record-ID column picker for idempotent re-imports (#5661).

  • New

    Show per-row validation errors in import preview panel (#5905)

    Shipped Show per-row validation errors in import preview panel (#5905).

  • New

    Archetype wireframes, contract test, and mapping doc (#5564 + #5568)

    Shipped Archetype wireframes, contract test, and mapping doc (#5564 + #5568).

  • New

    RecipeManager uses Collections for recipe storage (ADR-0055 #5684)

    Shipped RecipeManager uses Collections for recipe storage (ADR-0055 #5684).

  • Fix

    Set managedByUser on forkWorkspace and cross-recipe creation (#5796)

    Shipped Set managedByUser on forkWorkspace and cross-recipe creation (#5796).

  • New

    Replace ManualOfferWidget with declarative schema action (#5617)

    Shipped Replace ManualOfferWidget with declarative schema action (#5617).

  • New

    Platform-reviewer role gate on recipe review endpoint (#5625)

    Adds a role gate to \`PATCH /community/submissions/:id/review\`: only callers with role \`admin | owner | platform_admin | platform-admin\` may review submissions. Non-reviewers receive 403 \`REVIEWER_ROLE_REQUIRED\` regardless of whether the submission is their own or another developer's. Fixes #5625.

  • Improvement

    Pricing & metering model — free-to-build, pay-for-scale

    Shipped Pricing & metering model — free-to-build, pay-for-scale.

  • Fix

    Remove dead templates/DemoBanner stub (closes #5655)

    Shipped Remove dead templates/DemoBanner stub (closes #5655).

  • New

    Load per-council custom application questions in waitlist forms (#5922)

    Implements the missing per-council custom application questions in both the national and local allotments waitlist forms. Previously, `AllotmentsInfoLocalWaitlistApplicationFormWidget` and `AllotmentsInfoNationalWaitlistApplicationFormWidget` used hardcoded fields only — councils' `QueueFormBuilderWidget` configuration was never loaded or shown to applicants.

  • New

    Column mapper MVP (#5613)

    Shipped Column mapper MVP (#5613).

  • New

    Add GoCardless DD setup banner to tenant-portal tab

    Adds the `allotments-info/gocardless-setup-banner` widget as the first entry in the `tenant-portal` dashboard tab of `terraledger-recipe.json`, prompting tenants to set up Direct Debit on first visit. Fixes #5896

  • New

    Half-plot data model — allowHalfPlots site flag + co-tenancy guard

    Adds the half-plot data model to TerraLedger: a `'half'` PlotType, an `allowHalfPlots` site-level flag, and co-tenancy enforcement so a half-plot can hold up to 2 concurrent active tenancies when the site opts in. Fixes #5893

  • New

    Make map-to-application flow frictionless (#5928)

    Makes the map-to-application flow frictionless by surfacing the existing cart infrastructure on the public directory and adding progressive disclosure on the join-waitlist form.

  • Fix

    Wire default tab master-detail + tenant-portal selection binding

    Wires the TerraLedger default dashboard tab to master-detail layout with `terraledger.site` as the master entity, and adds `boundTo` selection bindings on 4 tenant-portal widgets. Also adds a `sync:recipe:terraledger` script alias and 3 new Vitest tests proving widgets with the same `widgetId` as the master still resolve as `boundChildren` when `config.boundTo` is set. Fixes #5936

  • New

    Fix public testimonials display + remove illustrative seeds (#5932)

    Shipped Fix public testimonials display + remove illustrative seeds (#5932).

  • New

    Wire position timeline widget on check-position page (#5929)

    Replaces the flat metadata list on the `/waitlist/my-position` check-position page with per-site timeline cards, each showing position headline, percentile copy, and `PositionTimelineWidget` loaded via the CDN loader. Fixes #5929

  • Fix

    Enforce allocationSafe compliance gate server-side on QueueSpark form-config

    Adds server-side enforcement of the statutory council compliance gate on QueueSpark's `GET /sites/:siteId/form-config` endpoint. Statutory UK councils (Allotments Acts 1908/1950) must not receive scoring or barrier questions — only fields explicitly marked `allocationSafe: true` are served to them. Fixes #5926

  • New

    Wire harvest → ESG aggregation + leaderboards (#5946)

    Shipped Wire harvest → ESG aggregation + leaderboards (#5946).

  • New

    National map discovery — wait labels, filter sidebar, layers panel (#5930)

    Improves allotments.info national map discovery: adds wait-time labels on pins at zoom ≥11, replaces 3 floating status pills with a slide-in filter sidebar (including wait-time range slider and founding-only toggle), and moves heatmap/boundaries toggles into a discoverable Layers panel.

  • New

    Applicant milestones and 90-day still-looking confirmation (#5931)

    Adds the 90-day "still looking" confirmation flow for allotments.info waitlist applicants, along with marketplace notification opt-in widget placement. Closes #5931.

  • New

    Map filter sidebar, layers panel + founding-member marker

    Shipped Map filter sidebar, layers panel + founding-member marker.

  • New

    Align to entity-trio + RootVibe model (#5734)

    Completes RootSchool's alignment to the entity-trio / RootVibe model by: 1. Adding 8 new schema definitions for sub-recipe entity types 2. Fixing the demo seeder to use prefixed `rootschool.*` entity types throughout 3. Fixing 2 camelCase entity type names in sub-recipes Fixes #5734

  • Fix

    Resolve strict-mode TS errors across 10 services and 3 packages

    Shipped Resolve strict-mode TS errors across 10 services and 3 packages.

  • Fix

    Resolve 31 TS strict-mode errors

    Shipped Resolve 31 TS strict-mode errors.

  • Fix

    Resolve 16 TS strict-mode errors (Anthropic SDK types + CJS/import.meta)

    Shipped Resolve 16 TS strict-mode errors (Anthropic SDK types + CJS/import.meta).

  • Fix

    Include API Key Manager URL in OAuth credential fetch error log

    Shipped Include API Key Manager URL in OAuth credential fetch error log.

  • New

    Per-job last-run tracking in VeraScheduler + /health exposure

    Shipped Per-job last-run tracking in VeraScheduler + /health exposure.

  • Improvement

    Preprod → main — master-detail entity framework + self-serve workspace (DoD §4 complete)

    Promotes **preprod → main** (production): the Release-20260608 sprint — schema-driven master-detail + self-serve builders, the Collections schema infrastructure, RootVibe platform designer foundations, a production-ready demo system, bulk import/export, entity action system, and 4 new ADRs. 80+ commits across 431 files spanning 10 services, the widgets package, 16 recipes, and 149 docs.

  • New

    Entity-spine Site Workspace — one render path + generic config-driven capabilities

    Unifies the demo and logged-in dashboard render paths onto one RootVibe/Collections recipe-JSON entity-spine, deletes 9 legacy composition overrides, and adds generic config-driven capabilities to the shared Collections spine widgets (chromeless, single-site auto-open, master-detail record surface, schema-gated views, per-column filters, record-scoped charts).

  • Fix

    Register report widgets + shared/relationship-graph (broken aha moments)

    Fixes two of the 13 marketed TerraLedger aha moments that **fail to load at runtime** because their widget IDs are referenced by the recipe but registered in no widget map. Surfaced by the competitive gap audit.

  • New

    EntityAction pillar — collections/entity-action (Track A, Part 5)

    Adds the **fourth quad pillar** — `collections/entity-action` (Track A, Part 5). A recipe-composable, tab-placeable widget that renders the declared schema `actions[]` for the **selected** record and invokes the bound workflow/rule/service — schema-driven, zero per-recipe action code. This is the highest-leverage unlock for Track C (#5796): the action-heavy tabs (financial, enforcement, waitlist, reports) can become quad master-detail without bespoke action widgets.

  • New

    EntityDetail pillar — collections/entity-detail (Track A, Part 4)

    Adds the **record-workspace quad pillar** — `collections/entity-detail` (Track A, Part 4). A tab-placeable widget that renders the full detail for the **selected** record (fields, schema-derived related children, SCD-2 history, inline edit).

  • New

    Tenants tab is a quad master-detail (Track C, #5796)

    Bakes the **quad master-detail `tenants` tab** into the committed recipe JSON. It previously existed only in local Collections (the deployability gap flagged in the reflection) — this makes it the version-controlled, deployable source.

  • New

    Wire birds-dogfood IG spec into release-gate Playwright project

    Shipped Wire birds-dogfood IG spec into release-gate Playwright project.

  • Fix

    Align harvest entityType refs to prefixed rootplan.harvest-log

    Shipped Align harvest entityType refs to prefixed rootplan.harvest-log.

  • Fix

    Prefix unprefixed entityType refs + allowlist system types in validator

    Shipped Prefix unprefixed entityType refs + allowlist system types in validator.

  • Fix

    Seed recipe schema on provision so master-detail drill-down resolves

    Shipped Seed recipe schema on provision so master-detail drill-down resolves.

  • New

    Public read-only Collections proxy for council vanity domains (#5817)

    Shipped Public read-only Collections proxy for council vanity domains (#5817).

  • New

    Media FieldType + img rendering in EntityDetail (MV slice, #5810)

    Shipped Media FieldType + img rendering in EntityDetail (MV slice, #5810).

  • New

    Wire overdue detection into arrears escalation sweep (#5812)

    Shipped Wire overdue detection into arrears escalation sweep (#5812).

  • Fix

    60 failing tests → 0 (launch readiness)

    $(cat <<'EOF'

  • Fix

    Correct port 3018 leak in service-to-service URLs

    Shipped Correct port 3018 leak in service-to-service URLs.

  • New

    Formalise beforeAction/afterAction hook contract (#5813)

    Shipped Formalise beforeAction/afterAction hook contract (#5813).

  • New

    Wire schema→tab generator into CompositionLoader (C2, #5815)

    Shipped Wire schema→tab generator into CompositionLoader (C2, #5815).

  • New

    Cardinality-driven FK navigation via entityGraphResolver (#5814)

    Shipped Cardinality-driven FK navigation via entityGraphResolver (#5814).

  • Fix

    Wire recipe-render-smoke spec to demo-sweep project + add test:release gate

    Shipped Wire recipe-render-smoke spec to demo-sweep project + add test:release gate.

  • Fix

    Wire face-blurring into asset upload pipeline (#5831)

    Shipped Wire face-blurring into asset upload pipeline (#5831).

  • Fix

    Cap connection pool at 3/service to prevent Railway exhaustion (#5808)

    Shipped Cap connection pool at 3/service to prevent Railway exhaustion (#5808).

  • Fix

    Prefix remaining 6 entityType errors — epr/subscribe/storefront (#5823 #5824 #5825)

    Shipped Prefix remaining 6 entityType errors — epr/subscribe/storefront (#5823 #5824 #5825).

  • Fix

    Resolve TS2786/TS2352 JSX type mismatch — cms.app + mobile-bridge (#5510)

    Shipped Resolve TS2786/TS2352 JSX type mismatch — cms.app + mobile-bridge (#5510).

  • New

    Per-account VAT on tenant invoices (#5805)

    Shipped Per-account VAT on tenant invoices (#5805).

  • Fix

    Timeout + null-guard production bug bundle (#5786 #5789 #5790)

    Shipped Timeout + null-guard production bug bundle (#5786 #5789 #5790).

  • Fix

    Resolve 13 TS strict-mode errors blocking Railway preprod build

    Shipped Resolve 13 TS strict-mode errors blocking Railway preprod build.

  • Fix

    Thread DATABASE_PUBLIC_URL to all 39 services

    Shipped Thread DATABASE_PUBLIC_URL to all 39 services.

  • Fix

    Align seeder entityTypes to recipe — fix empty demo tabs (#5666)

    Shipped Align seeder entityTypes to recipe — fix empty demo tabs (#5666).

  • Fix

    Sync pnpm-lock.yaml after @types/react 18→19 bump

    Shipped Sync pnpm-lock.yaml after @types/react 18→19 bump.

  • New

    Migrate financial/waitlist/committee/reports/operations to quad (#5561)

    Replaces ~100 bespoke widgets across 5 TerraLedger tabs with quad composition. Infrastructure was already built — recipe JSON wiring only. Net: -702 lines. | Tab | Before | After | Kept bespoke | |---|---|---|---| | financial | 37→7 | 6 quad + 1 | agresso-export (external integration) | | waitlist | 25→6 | 4 quad + 2 | queue-form-builder, risk-controls-editor | | committee | 11→4 | 2 quad + 2 | election-list, voting-dashboard (ballot UIs) | | reports | 13→7 | 7 quad + 0 | fully quad | | operations | 6→3 | 1 quad + 2 | staff-roster, shift-planner (calendar UIs) | Closes #5561

  • New

    Current-user sentinel + tenant-portal quad + config widget extraction

    Shipped Current-user sentinel + tenant-portal quad + config widget extraction.

  • New

    Schema-driven tabs via Collections/RootVibe + delete 8 superseded widgets

    Shipped Schema-driven tabs via Collections/RootVibe + delete 8 superseded widgets.

  • Fix

    Fix broken chart widget IDs + remove 3 config widgets from ops tabs

    Fixes 7 `WidgetUnavailable` renders in TerraLedger financial/waitlist/reports tabs, and removes 3 remaining config/authoring widgets from operational recipe tabs.

  • New

    C1 master-detail tab contract — declarative spec + shell + validator gate (#5560)

    Establishes the repeatable master-detail tab contract: types, shared shell, renderer branch, conformance validator, and first conformant recipe (TerraLedger).

  • New

    Part 2 — cardinality-driven FK navigation (#5863)

    Wires `entityGraphResolver.preferredViewForRelation()` into the FK click flow. Navigation is now schema-driven rather than hardcoded to always open entity-detail. **M-to-1 FK clicks** (child → parent): open entity-detail as before (e.g. plot.siteId → site record) **1-to-M FK clicks** (parent → children): call `onNavigateToFilteredTable` to re-root the EntityTable to the related entity set filtered by FK (e.g. site → plots filtered by siteId) **M-to-M FK clicks**: same as 1-to-M — table view for the related set **Graceful degradation**: if `onNavigateToFilteredTable` is not provided, falls back to entity-detail with a warn log

  • New

    Seed allotments.info family as platformRecipe entities in Collections (#5865 P0)

    Shipped Seed allotments.info family as platformRecipe entities in Collections (#5865 P0).

  • New

    Entity-trio alignment — schemas + seeder + prefixed entityTypes (#5866)

    Shipped Entity-trio alignment — schemas + seeder + prefixed entityTypes (#5866).

  • New

    Canvas-master tabs — GIS canvas selection drives bound child widgets (#5562)

    Shipped Canvas-master tabs — GIS canvas selection drives bound child widgets (#5562).

  • New

    Register system schemas for remaining recipes (#5572)

    Wires 37 additional data-bearing recipes into \`RECIPE_SYSTEM_SCHEMAS\` so the schema-driven master-detail engine can render entity-tables and selection-driven drill-down for those recipes without any bespoke widget code. Fixes #5572

  • New

    Schema-driven master-detail as default nav — add explore tabs (#5738)

    Adopts \`collections/master-detail\` as the default navigation surface for data-bearing recipes, eliminating the need to hand-wire per-relationship \`selected:\` filters. Fixes #5738

  • New

    Add live recipe integrity validation

    Shipped Add live recipe integrity validation.

  • New

    CreateRecipe writes platformRecipe entity — RootVibe recipe renders (#5682)

    Shipped CreateRecipe writes platformRecipe entity — RootVibe recipe renders (#5682).

  • New

    Reposition as the platform vibe builder (#5721)

    Shipped Reposition as the platform vibe builder (#5721).

  • New

    Entity-map above entity-table on site/plot screens (#5723)

    Shipped Entity-map above entity-table on site/plot screens (#5723).

  • Fix

    Seeder protects RootVibe-edited entities from reseed clobber (#5727)

    Shipped Seeder protects RootVibe-edited entities from reseed clobber (#5727).

  • Fix

    Seed rootlytics drill-down records (#5710)

    Shipped Seed rootlytics drill-down records (#5710).

  • New

    User-collapsible widgets (#5724)

    Shipped User-collapsible widgets (#5724).

  • New

    Align to the entity-trio + RootVibe model (#5734)

    Shipped Align to the entity-trio + RootVibe model (#5734).

  • New

    Wire tenants→plots reverse drill-down (#5736)

    Shipped Wire tenants→plots reverse drill-down (#5736).

  • New

    Schema-driven master-detail Explore tab (#5738)

    Demonstrates schema-driven relationship navigation: an Explore tab with one `collections/master-detail` widget (`{ entityType: terraledger.site }`) — the engine derives the whole drill graph (parents + children, both directions, recursive) from the schema. No `selected:` filters. Platform-wide adoption + retiring the hand-wired drills is #5738. Self-review: PASS ✅ — recipe config only (existing tested widget), valid JSON, reconciled JSON→DB. Refs #5738. 🤖 Generated with [Claude Code](https://claude.com/claude-code)

  • New

    Complete the use-case relationship graph in the schema (#5740)

    Shipped Complete the use-case relationship graph in the schema (#5740).

  • New

    Schema-driven master-detail Explore tab (#5744)

    RootSchool parity with terraledger #5739. The data model was already loaded + validated; added the Explore tab (collections/master-detail over rootschool.school) so the rich school graph drills automatically. Reconciled JSON→DB. Self-review: PASS ✅. Refs #5744.

  • New

    Email-first returnable demo accounts (#5668)

    Shipped Email-first returnable demo accounts (#5668).

  • Fix

    Entity-table filters as JSON array — fixes empty dashboards (#5754)

    **P0 root cause** of the empty demo dashboards. `useCollections` sent `filters` as an object; the collections endpoint requires a JSON array (rejects objects with 400 since #4043). Every entity-table/map read 400'd → empty widgets, despite data being seeded (330 rows DB-confirmed). Fix: object→array conversion + drop empties. Proven via browser response capture (400 'filters must be a JSON array'). Affects all recipes; needs a bundle+app rebuild to ship. Refs #5754.

  • Fix

    Local hosts load widgets locally, not from prod CDN (#5767)

    Root cause of the empty demos. The loader loaded widget JS from the prod CDN even locally (and disabled the local fallback in dev) — so local widget changes were untestable, which hid the #5754 fix. Now local hosts prefer the freshly-built local bundle; prod unchanged. Proven: terraledger renders the seeded site once local bundles are used. Refs #5767, #5754.

  • New

    Real OSM site boundary + plots inside (#5769)

    Real allotment boundary (Evington Hill Top Allotment, Leicester — OSM polygon) wired into the terraledger demo, plot grid fitted inside. The site canvas renders the actual physical outline. Refs #5769.

  • Fix

    Recipe title (Sites/Plots) not entityType label (#5771)

    Widgets now show the recipe-configured title ('Sites') instead of the entityType design-mode label ('Terraledger.site Canvas'). Renderer merges widget.title into config; EntityTable prefers it, falling back to the entityType label only when no title is set (RootVibe design-mode default). Refs #5771.

  • New

    Config-driven entity-map MI status overlay (#5773)

    Shipped Config-driven entity-map MI status overlay (#5773).

  • New

    Entity-map polish — titles, plots-in-boundary, schema display contract

    Shipped Entity-map polish — titles, plots-in-boundary, schema display contract.

  • Fix

    Seed claimant TerraLedger site from directory on claim approval

    When an allotments.info pin claim is approved, copies the public directory listing into the claimant's new TerraLedger account so their map opens pre-populated with the real boundary they just claimed — instead of starting empty.

  • Fix

    Resolve accountId from verified JWT header in billing & agent-actions routes (#5783)

    Fixes a verified multi-tenant IDOR flaw: authenticated `billing/*` and `agent-actions/*` API routes resolved the tenant `accountId` from client-controlled input (query string / request body) instead of the trusted `x-verified-account-id` header that `middleware.ts` injects from the JWT, letting any logged-in user read/write another tenant's financial data by changing `?accountId=`. Fixes #5783

  • New

    Site-wide TPO search (#5687)

    Shipped Site-wide TPO search (#5687).

  • New

    Recipe entityType validator (#5695)

    Shipped Recipe entityType validator (#5695).

  • New

    5 missing terraledger schemas — compliance/enforcement/casework/journal/concession (#5701)

    Shipped 5 missing terraledger schemas — compliance/enforcement/casework/journal/concession (#5701).

  • Fix

    Seed rootplan harvest-log + task so all tabs render (#5666)

    Shipped Seed rootplan harvest-log + task so all tabs render (#5666).

  • Fix

    Seed terraledger compliance + enforcement so those tabs render (#5666)

    Shipped Seed terraledger compliance + enforcement so those tabs render (#5666).

  • New

    Schema FK/PK relationship validator (#5695)

    Shipped Schema FK/PK relationship validator (#5695).

  • Fix

    Membership-schemas.test.ts fails-to-run + stale ids (#5712)

    Shipped Membership-schemas.test.ts fails-to-run + stale ids (#5712).

  • New

    TableBuilder — complete the self-serve table/map/chart trio (#5607)

    Completes the **self-serve table/map/chart trio**: adds a schema-aware **TableBuilder** so `collections/entity-table` is now addable *and configurable* in-app, like map (#5593) and chart (#5587). A `TableWidgetSlot` shows the builder when unconfigured / on Configure, and the real (self-fetching) entity-table once configured.

  • Fix

    Narrow accounts[0] in DpaAcceptanceStep (TS2532) — clears last app type error

    Shipped Narrow accounts[0] in DpaAcceptanceStep (TS2532) — clears last app type error.

  • Fix

    Restore bulk import/export — wrong table + missing multipart (#5611)

    Restores **bulk import/export** (and custom-field mapping), which was doubly broken — both pre-existing breaks left behind by the Collections consolidation, found via a live round-trip prompted by "does import/export still work + can a client map to custom fields?".

  • New

    Entity action descriptor + resolver (action layer child 1, #5618 / epic #5617)

    Shipped Entity action descriptor + resolver (action layer child 1, #5618 / epic #5617).

  • New

    Render + invoke record actions in the detail panel (action layer child 2, #5620 / epic #5617)

    Shipped Render + invoke record actions in the detail panel (action layer child 2, #5620 / epic #5617).

  • New

    RootVibe recipe submission review — approve/reject + scope + state machine (#5622)

    Shipped RootVibe recipe submission review — approve/reject + scope + state machine (#5622).

  • New

    Self-serve action binding — Actions tab + schema actions endpoint (action layer child 4, #5626 / epic #5617)

    Shipped Self-serve action binding — Actions tab + schema actions endpoint (action layer child 4, #5626 / epic #5617).

  • New

    Table over a service endpoint — datasource binding (action layer child 3, #5628 / epic #5617)

    Shipped Table over a service endpoint — datasource binding (action layer child 3, #5628 / epic #5617).

  • Fix

    Seed garden/bed/planting into Collections, not the rootplan service (#5602 keystone)

    Shipped Seed garden/bed/planting into Collections, not the rootplan service (#5602 keystone).

  • New

    Mark terraledger/rootplan/rootlytics as system recipes (flags.isSystem) (#5631)

    Shipped Mark terraledger/rootplan/rootlytics as system recipes (flags.isSystem) (#5631).

  • New

    Reviewer queue dogfood — action method + path-templating + marketplaceSubmission schema (#5634)

    Shipped Reviewer queue dogfood — action method + path-templating + marketplaceSubmission schema (#5634).

  • New

    Resolver reads schema Collections-first (entitySchema) — keystone of #5636

    Shipped Resolver reads schema Collections-first (entitySchema) — keystone of #5636.

  • New

    Seed system schemas as entitySchema Collections entities — child 2 of #5636

    Shipped Seed system schemas as entitySchema Collections entities — child 2 of #5636.

  • New

    Schema writes dual-write entitySchema entities — child 3 of #5636

    Shipped Schema writes dual-write entitySchema entities — child 3 of #5636.

  • New

    Platform-owned system schemas + account→platform resolver fallback — child 4a of #5636

    Shipped Platform-owned system schemas + account→platform resolver fallback — child 4a of #5636.

  • New

    Fork-on-edit for schemas — child 4b of #5636

    Shipped Fork-on-edit for schemas — child 4b of #5636.

  • Fix

    GET /schemas/:entityType resolves account→platform (builder sees forks)

    Shipped GET /schemas/:entityType resolves account→platform (builder sees forks).

  • Fix

    AI chat assistant hidden by default (collapsed launcher) — was occluding the cockpit

    Shipped AI chat assistant hidden by default (collapsed launcher) — was occluding the cockpit.

  • Fix

    Batch endpoint does a real bulk INSERT — demo/import seeded only ~1 of N records

    Shipped Batch endpoint does a real bulk INSERT — demo/import seeded only ~1 of N records.

  • Fix

    Prefix 2 stray entityTypes (empty tables for all users) (#5662)

    Shipped Prefix 2 stray entityTypes (empty tables for all users) (#5662).

  • Fix

    Onboarding creates rootplan.garden (entityType alignment, #5666)

    Onboarding 'Create Your First Garden' used unprefixed `garden`; aligned to `rootplan.garden` (the type the data + dashboard widgets use). Same class as #5663. Part of #5666. JSON valid; takes effect on rootplan platformRecipe re-seed. 🤖 Generated with [Claude Code](https://claude.com/claude-code)

  • New

    Rootplan seed uses the full sample (12 beds + 36 plantings) — fixes the thin demo (#5666)

    Shipped Rootplan seed uses the full sample (12 beds + 36 plantings) — fixes the thin demo (#5666).

  • New

    Live countdown timer in the demo banner

    Shipped Live countdown timer in the demo banner.

  • Fix

    GardenOverview reads/creates gardens via Collections (#5672, #5662)

    Shipped GardenOverview reads/creates gardens via Collections (#5672, #5662).

  • New

    Bulk-insert + clear/update/append mode + danger-zone clear (#5666 #5675 #5676)

    Shipped Bulk-insert + clear/update/append mode + danger-zone clear (#5666 #5675 #5676).

  • New

    EntityImportWidget — unified import dialog (sample + mode picker) (#5675 #5678)

    Shipped EntityImportWidget — unified import dialog (sample + mode picker) (#5675 #5678).

  • Fix

    Hide Sign Up Free CTA inside an active demo session

    Small UX fix (David): the sign-up/waitlist CTA is noise mid-demo — the visitor is already trying the product. Now only shown on read-only/shared demo views (genuine prospect surfaces), not the active session banner. Type-clean; the existing DemoBanner test (a different templates/ component) still passes 4/4. 🤖 Generated with [Claude Code](https://claude.com/claude-code)

  • Fix

    Compact cockpit — full-width entity tables, kill the 92-row scroll (#5662)

    The terraledger default cockpit was a **92-row grid with huge gaps** (today-summary at y=42, AI panels at y=84) → endless scroll, and entity-tables were half-width. Now: hero map (full) + **3 full-width entity tables** (plots/tenancies/tenants) + 2 charts. **15→6 widgets, 27 rows.** Dropped the 4 ai-core panels (hidden in demo per David) + 2 bespoke service widgets. Applied to the recipe JSON **and** the DB platformRecipe — this is a RootVibe config operation. Users still customise (resize/add charts) via the existing WorkspaceSwitcher/DashboardEditor. 🤖 Generated with [Claude Code](https://claude.com/claude-code)

  • Fix

    Double-seed terraledger unprefixed so the cockpit renders (#5662)

    Root cause: cockpit widgets query UNPREFIXED entity types (entityType=site) while the seeder wrote PREFIXED terraledger.site → empty cockpit. Now seeds both. Verified: `entityType=site` query returns 2 sites (was 0). Band-aid until #5685 unifies the convention (the real fix). Known follow-up: minor over-count where the old seeder already wrote some unprefixed (plot/site) — needs a dedupe. 🤖 Generated with [Claude Code](https://claude.com/claude-code)

  • Fix

    DB recipe dashboard wins + single-seed — terraledger demo renders (#5685 #5662)

    **The keystone fixes that made the demo render** (committed but not yet PR'd): 1. `mergeFreshRecipeStructure` now lets the DB recipe dashboard win over the stale manifest — RootVibe config takes effect platform-wide (was silently discarded). 2. Removed the double-seed band-aid (was timing out the seed → empty cockpit). Single-seed. Verified: fresh terraledger demo seeds 64 plots prefixed, FK-linked, cockpit renders. 🤖 Generated with [Claude Code](https://claude.com/claude-code)

  • New

    Schema-driven engine + MasterDetailTab — real widgets, no mock (#5560)

    Builds the **real** schema-driven master-detail engine (#5560): a pure function of the live schema that renders the **real** `collections/entity-table` / `entity-map` widgets — no mocks, no per-recipe code.

  • New

    Slice 1 — buildEntityGraph + recursive drill (#5569)

    Slice 1 of the entity-graph browser (#5569): evolves the one-level master-detail resolver into a navigable **entity graph** with bidirectional traversal and **recursive re-root** — still a pure function of the live schema, zero per-entity/per-recipe code.

  • Fix

    Correct demo bootstrap so live preview renders real data

    Fixes the `/dev/templates/master-detail` bootstrap so the real engine renders live demo data. Found by testing against the running stack.

  • New

    Connect 4 DoD recipes to the engine + register RootLytics schemas (#5572)

    Connects all four DoD recipes (TerraLedger, RootPlan, allotments.info, RootLytics) to the schema-driven master-detail engine, and registers RootLytics' schemas (the one DoD recipe that had none).

  • New

    Seed entity schemas on recipe activation (real accounts get live data)

    Wires the existing schema seeder into recipe activation so REAL accounts get their entity schemas — the data the schema-driven master-detail engine reads. Previously only the demo flow seeded schemas, so real accounts had none and the engine had nothing to render.

  • New

    Master-detail is a widget — collections/master-detail (#5560)

    Makes master-detail a **widget** (`collections/master-detail`), per the widget-driven workspace model. Connecting a recipe is now config — drop the widget on a tab with `{ entityType }`; the existing CDN loader + DashboardWidgetRenderer + selection context render it. No app-layer component, no route.

  • New

    Inline master-detail cockpit — reuse #5008, drop the duplicate (#5560)

    Reconciles the master-detail widget with the #5008 foundation instead of duplicating it. #5008's `EntityDetailPanel` already derives a record's related children from the schema (`GET /entities/{id}/related`) with recursive drill + breadcrumb — so the widget now reuses it.

  • Improvement

    Release-20260607 → preprod (schema-driven master-detail)

    Shipped Release-20260607 → preprod (schema-driven master-detail).

  • New

    Wire garden tab as master-detail — garden→bed→planting (#5562)

    Wires RootPlan's **garden** tab as a master-detail surface — a `garden → bed → planting` selection cascade — so the primary garden-management tab actually drives the workspace, using the exact pattern the recipe already ships on its `records` tab.

  • Fix

    Make /related schema-driven, not hardcoded (#5579)

    Makes the master-detail cockpit's child panels **schema-driven**: `GET /api/collections/entities/:id/related` now derives relationships from the entity's stored schema instead of a hardcoded inverse-pattern table.

  • Fix

    Add 'use client' to MasterDetailWidget — unblocks preprod build

    Adds the missing `'use client'` directive to `MasterDetailWidget.tsx`, which was breaking the preprod Next.js build.

  • New

    Wire records tab to the generic master-detail engine (#5563)

    Wires RootLytics' `records` tab to the **generic** `collections/master-detail` engine widget — making `billing.subscription` a navigable master that drills to its payments / transactions / reconciliation ("the records behind the numbers"), schema-driven. This is the one launch recipe that uses the generic engine (hybrid-C: it has no curated cascade).

  • New

    Self-serve Chart Builder — add & configure collections/chart on any page (#5586)

    Adds a self-serve **Chart Builder** so a client can add and configure an MI chart (`collections/chart`) on **any** workspace page — schema-driven, no code. Completes the third member of the #5008 EntityTable/EntityMap/Chart spine.

  • New

    Existing-account schema backfill (#5584 layer 1)

    Adds an idempotent **schema backfill** so EXISTING accounts get their system entity-schemas — Layer 1 of DoD #7 (the master-detail engine resolves nothing for accounts that predate activation-time seeding).

  • New

    Composition reconcile-on-drift, areaId-keyed (#5584 layer 2)

    Adds **reconcile-on-drift** so existing saved dashboards additively pick up recipe widgets added after the workspace was first seeded — completing DoD #7 (the last master-detail DoD item). Keyed on `areaId`, gated by `managedByUser`, additive-only.

  • Improvement

    Release-20260607.2 → preprod (master-detail DoD complete + self-serve workspace)

    Shipped Release-20260607.2 → preprod (master-detail DoD complete + self-serve workspace).

  • New

    Self-serve entity-map — completes the table/map/chart trio (#5592)

    Makes `collections/entity-map` self-serve — completing the **table / map / chart trio**: a client can now add a map of any geometry-bearing entity on any page and configure it in-app, no code.

  • New

    Upsell UI, locked states, and data-driven pricing calculator

    Shipped Upsell UI, locked states, and data-driven pricing calculator.

  • Fix

    Remove favourite star toggle from workspace tabs

    Shipped Remove favourite star toggle from workspace tabs.

  • Fix

    Sidebar starts collapsed by default

    Sidebar nav rail now starts in icon-only (collapsed) mode on every page load instead of fully expanded.

  • Fix

    Invite member crashes with UNAVAILABLE error

    Shipped Invite member crashes with UNAVAILABLE error.

  • Fix

    Add New Tenant form — accept string address and surface real API errors (#4978)

    Fixes Add New Tenant form which failed silently on every submission due to a schema mismatch and masked error messages.

  • Fix

    Add Vary header to prevent RSC cache poisoning + fix build

    Shipped Add Vary header to prevent RSC cache poisoning + fix build.

  • New

    Move Getting Started into user avatar menu; repurpose ? as help launcher

    Shipped Move Getting Started into user avatar menu; repurpose ? as help launcher.

  • Fix

    Collapse isDefault=false numbered-suffix workspace duplicates

    Shipped Collapse isDefault=false numbered-suffix workspace duplicates.

  • Fix

    Remove unwired NaturalLanguageSearch from header

    Shipped Remove unwired NaturalLanguageSearch from header.

  • Fix

    DPA guard on bulk import, migration commit, and waitlist PII routes (#5439)

    Closes the GDPR Art. 28 bypass: adds `assertDpaForCouncilAccount` to the three routes that could ingest council-controlled PII without a signed DPA.

  • Fix

    Remove duplicate AI lightbulb button from workspace header

    Shipped Remove duplicate AI lightbulb button from workspace header.

  • New

    Add non-skippable DPA acceptance step to onboarding (#5440)

    Adds a non-skippable `dpa-acceptance` onboarding step to TerraLedger (between `compliance-profile` and `entity-creation`) so council admins can record their countersigned GDPR Art. 28 DPA before reaching first-site/tenant creation — eliminating the `assertDpaForCouncilAccount` 403 that would otherwise surface mid-flow.

  • Fix

    Replace AJV union-type arrays with oneOf for strict mode compatibility

    Replaces two `type: ['string', 'object']` union arrays in Fastify route schemas with `oneOf: [{type:'string'},{type:'object'}]` so AJV v8 strict mode no longer emits `strictTypes` errors at service startup.

  • Fix

    Add DPA guard to bulk import, migration, and waitlist PII routes (#5439)

    Adds the GDPR Art. 28 DPA guard (`assertDpaForCouncilAccount`) to 4 PII-ingesting POST routes in TerraLedger that were missing it — a council could bypass the requirement entirely by using the import wizard or migration flow instead of the Add Tenant form. Fixes #5439

  • Fix

    Add DeployVersionGuard to prevent Server Action hash mismatch 500s after deploy

    Adds a client-side deploy version guard that polls `/api/version` every 60s and triggers a hard reload when the server's build ID differs from the one baked into the current client bundle. This prevents the "Failed to find Server Action" 500 errors users experience when a Railway deploy happens while they have a stale JS bundle open in the browser.

  • Fix

    Persist service registry defaults to DB on startup, fix empty-vs-error logging

    Makes the orchestration service registry durable across restarts by persisting default service definitions to the DB on first boot, and fixes misleading error logging that conflated an empty table with a DB failure. Fixes #5395

  • Fix

    No loading animation on revisit + unsafe anchor + AGM DOD

    Shipped No loading animation on revisit + unsafe anchor + AGM DOD.

  • Fix

    Resolve TypeScript compile errors (#5455)

    Fixes TypeScript compile errors in \`packages/cms.app\` and \`packages/knowledge-base\` that were blocking \`tsc --noEmit\` clean runs platform-wide. The fix adds missing \`types\` arrays to each package's \`tsconfig.json\`.

  • Fix

    Resolve TypeScript compile errors (#5457)

    Fixes TypeScript compile errors in \`packages/data-marketplace-sdk\` that were blocking \`tsc --noEmit\` clean runs platform-wide. The test file referenced method names that no longer existed on the SDK client; this aligns the tests with the current SDK API surface.

  • Fix

    Resolve TypeScript compile errors (#5458)

    Fixes TypeScript compile error in `packages/mobile-bridge` (TS2786) that was blocking `tsc --noEmit` clean runs platform-wide. The `WebView` component from `react-native-webview` was not satisfying `@types/react` 18's stricter JSX element constraint.

  • Fix

    Resolve TypeScript compile errors (#5459)

    Fixes TypeScript compile errors in `packages/database` that were blocking `tsc --noEmit` clean runs platform-wide. Also includes two companion fixes surfaced by the same TS pass: `WaitlistCartDrawer` (`<a>` → `<Link>`), and `DashboardEditor` module-level initial-load guard to skip the loading animation on revisit.

  • Fix

    Resolve TypeScript compile errors (#5460)

    Fixes TypeScript compile errors in `packages/widgets` that were blocking `tsc --noEmit` clean runs platform-wide.

  • Fix

    Resolve TypeScript compile errors (#5456)

    Fixes TypeScript compile errors in \`packages/core\` that were blocking \`tsc --noEmit\` clean runs platform-wide.

  • Fix

    Guard pg-boss constructor crash + stale Server Action reload on deploy

    Shipped Guard pg-boss constructor crash + stale Server Action reload on deploy.

  • Fix

    Fire post-tenancy side effects from manual /convert route (IG-02 gap)

    Shipped Fire post-tenancy side effects from manual /convert route (IG-02 gap).

  • Fix

    Fix Birds dogfood seed for current API contracts + seed Birds live

    Shipped Fix Birds dogfood seed for current API contracts + seed Birds live.

  • New

    DPA recording widget — onboarding step + compliance tab (#5440)

    Shipped DPA recording widget — onboarding step + compliance tab (#5440).

  • Fix

    Resolve TypeScript errors — mcp-server Record|undefined (#5478)

    Fixes TypeScript compile errors in mcp-server (issue #5478).

  • Fix

    Resolve TypeScript errors — layers wrong arg count + string|undefined (#5477)

    Fixes TypeScript compile errors in layers (issue #5477).

  • Fix

    Resolve TypeScript errors — collections metricsClient not in config (#5474)

    Fixes TypeScript compile errors in collections (issue #5474).

  • Fix

    Resolve TypeScript errors — terrain-service GeoJSON namespace (#5483)

    Fixes TypeScript compile errors in terrain-service (issue #5483).

  • Fix

    Resolve TypeScript errors — queuespark LatLng.success + duplicate keys (#5480)

    Fixes TypeScript compile errors in queuespark (issue #5480).

  • Fix

    Resolve TypeScript errors — exactOptionalPropertyTypes DB pool config (#5470)

    Fixes TypeScript compile errors in mental-health-orchestrator, sustainability-orchestrator (issue #5470).

  • Fix

    Resolve TypeScript errors — core isCouncil missing + unintentional comparison (#5476)

    Fixes TypeScript compile errors in core (issue #5476).

  • Fix

    Resolve TypeScript errors — billing possibly undefined services (#5473)

    Fixes TypeScript compile errors in billing (issue #5473).

  • Fix

    Resolve TypeScript errors — cms hapi types (#5471)

    Fixes TypeScript compile errors in cms (issue #5471).

  • Fix

    Resolve TypeScript errors — ai-core AgentMessageBus missing methods (#5472)

    Fixes TypeScript compile errors in ai-core (issue #5472).

  • Fix

    Resolve TypeScript errors — TS2769 Fastify no-overload route handlers (#5469)

    Fixes TypeScript compile errors in blockchain, marketplace, scheduling, studio, cdn (issue #5469).

  • Fix

    Resolve TypeScript errors — weather GET_FORECAST missing from WeatherOperation (#5484)

    Fixes TypeScript compile errors in weather (issue #5484).

  • Fix

    Resolve TypeScript errors — rules-policy-engine id missing from RuleCondition/RuleAction (#5481)

    Fixes TypeScript compile errors in rules-policy-engine (issue #5481).

  • Fix

    Resolve TypeScript errors — Fastify FastifyInstance import path mismatch (#5467)

    Fixes TypeScript compile errors in api-key-manager, backup, governance, rootrewards, terraledger, tools, weather (issue #5467).

  • Fix

    Resolve TypeScript errors — plotspark-intelligence Error/undefined + IntelligenceQuery (#5479)

    Fixes TypeScript compile errors in plotspark-intelligence (issue #5479).

  • Fix

    Resolve TypeScript errors — communications untyped request.query/payload (#5475)

    Fixes TypeScript compile errors in communications (issue #5475).

  • Fix

    Resolve TypeScript errors — rootshift FastifyRequest generic schema (#5482)

    Fixes TypeScript compile errors in rootshift (issue #5482).

  • New

    Seed system schema into tenant accounts + persist import column-mapping as schema amendments (#5507)

    Seeds the platform's system entity schemas into each tenant's per-account `plotspark_entity_schemas` store, and persists customer import column-mapping decisions back onto that account schema as custom fields — closing the "start from the system schema, amend it for your use-case" gap behind the import process.

  • Fix

    KeepAlive + runtime ECONNRESET failover so services stop crash-looping on Railway

    Makes `DatabaseService` survive Railway private-network blips so the platform stops crash-looping platform-wide on `read ECONNRESET`.

  • Fix

    Single BYOK-aware Stripe provider for all payment widgets

    Collapses the two divergent client-side Stripe initialisation paths into a single BYOK-aware provider hook, fixing silent payment failures for BYOK councils (e.g. Birds).

  • Fix

    Render widgets in production (CSP unsafe-eval) + resolve build-breaking merge conflict

    Fixes two launch blockers found while verifying widget rendering in OrbStack: (1) widgets fail to render in production because CSP omits `'unsafe-eval'`, and (2) an unresolved git merge conflict in `api/version/route.ts` breaks the production build.

  • New

    Wire role-based workspace templates (#5198)

    Makes TerraLedger's three role-based workspace templates (Secretary / Site Manager / Committee) actually installable in one click — they were declared in the recipe but unwired, and every referenced widget ID was fabricated.

  • Fix

    Officer-created tenancy triggers IG-02 RootPlan garden (#5446)

    Auto-creates a RootPlan garden (IG-02) when a council officer creates a tenancy directly — previously only the offer-accept paths did this, so manually-added tenants never got a garden.

  • Fix

    Place renewal widgets on dashboard (#4705 root cause)

    Places `renewal-block-config` and `renewal-overrides` on the TerraLedger compliance dashboard tab — they were defined in the recipe but never placed anywhere, which is the root cause of the yearly-renewal E2E's 12 hard layout-skips (#4705).

  • New

    Schedule daily renewal cycle cron (#5196)

    Schedules the TerraLedger tenancy renewal cycle to run automatically every day, instead of only when an officer manually hits the endpoint. Closes the automation gap vs Colony's annual cycle (#5196).

  • Fix

    Repoint dashboard to real widget id + ratchet test (#5540)

    Fixes the last residual widget-render error on the RootLytics dashboard: the recipe referenced a widget id (`rootlytics/connect-data-source`) that exists in no manifest, so the slot rendered "Couldn't load this widget".

  • Fix

    Repoint launch-critical dashboards to real widget ids (#5548)

    Fixes the widget-render placeholders on the three June 11 Connected-Allotments demo dashboards (terraledger, allotments-info, allotments-info-local-waitlist) — the launch-critical slice of #5548.

  • Fix

    Repoint remaining dashboard widget ids to real manifest ids (#5548)

    Clears the bulk of the remaining #5548 dashboard widget-id gaps via mechanical/domain renames — 7 recipes fully fixed, 3 partially. Follows the launch-critical slice (#5554).

  • Fix

    Repoint final 8 dashboard widget ids — closes #5548

    Resolves the last 8 #5548 dashboard widget-id gaps. After triage, **all 8 repoint to real manifest widgets** — no recipe dashboard renders a "Couldn't load this widget" placeholder anymore.

  • New

    Live occupancy + waitlist on allotments tab (#5450)

    Makes the RootLytics **allotments** tab show live occupancy and waiting-list numbers sourced from TerraLedger plots in Collections — instead of "Failed to load" / empty stubs.

  • New

    Add meal pre-ordering widget

    Shipped Add meal pre-ordering widget.

  • Fix

    Resolve 26 TypeScript errors blocking Docker app build

    Fixes 26 TypeScript errors discovered during the build verification of PR #5339 (Release-20260605 → preprod). All errors were introduced by recent feature branches and blocked the `@plotspark/ui` Docker build step. Closes no issues — this is a build-gate fix for PR #5339.

  • Fix

    Add ARG declarations so CDN widget deploy runs on Railway

    Fixes the CDN widget deploy step silently skipped on every Railway deploy since it was added. **Root cause:** Docker `RUN` commands only see variables declared with `ARG`. The Dockerfile checked `$AWS_S3_BUCKET_NAME` and `$AWS_ENDPOINT_URL` without declaring them — always empty, always skipped. Secondary bug: `BUNDLE_COUNT` was set in one `RUN` layer and referenced in the next (vars don't survive across layers). **Fix:** Add 4 `ARG` declarations + merge bundle/manifest/deploy into one `RUN` command. **What you need to add in Railway** on the PlotSparkOS service → Variables: `AWS_S3_BUCKET_NAME=cdn-bucket-qbjvemiyugpomn` `AWS_ENDPOINT_URL=https://t3.storageapi.dev` (AWS_ACCESS_KEY_ID / AWS_SECRET_ACCESS_KEY already in shared vars — no change needed.)

  • Fix

    Remove Growth nav button from header

    Shipped Remove Growth nav button from header.

  • Fix

    Stop rootlytics duplicate workspace creation on every page load

    Shipped Stop rootlytics duplicate workspace creation on every page load.

  • Fix

    Hide widget boundaries during load, fill workspace with particles

    Shipped Hide widget boundaries during load, fill workspace with particles.

  • Fix

    Remove stale SandboxEnableCTA from AccountModeToggle

    Shipped Remove stale SandboxEnableCTA from AccountModeToggle.

  • Fix

    Replace 14 `any` types in CoreService.ts with strict types (#5032)

    Replaces all 14 \`any\` type usages in \`CoreService.ts\` Tier 1 auth functions with proper TypeScript strict-mode types.

  • New

    Create platform operator recipe — 7 tabs, 30 widgets (#5061)

    Creates \`allotments-info-admin-recipe.json\` — the operator counterpart to the allotments.info customer recipe, after its admin/records/operations tabs were stripped in PR #5149 (GDPR persona split). Widget blocks recovered from PR #5149's diff rather than authored from scratch.

  • Fix

    Convert audience arrays to scalar strings per #5060 contract

    Fixes a security bypass introduced in PR #5173: all 30 operator widgets in \`allotments-info-admin-recipe.json\` were tagged \`audience: ["admin"]\` (array), but the #5060 server-side enforcement uses scalar string comparison. Arrays silently bypass the gate. **Root cause**: \`DashboardBuilderManager.validateWidgetReference\` checks \`AUDIENCE_MIN[audience]\` where audience is the raw field value. \`AUDIENCE_MIN[["admin"]]\` → \`undefined\` → \`requiredLevel = 1\` (viewer, permissive). The PII operator tabs (waitlist entries, payments) were NOT actually restricted. **Fix**: Convert all 30 \`audience: ["admin"]\` → \`audience: "admin"\` (scalar string). Update test assertion from \`.toEqual(['admin'])\` to \`.toBe('admin')\`.

  • New

    Add audience scalar strings to allotments-info + terraledger widgets (#5062)

    Adds \`audience\` scalar string field to all 265 widgets in the two main customer-facing recipes, completing the audience enforcement layer started by #5060 (server-side gate) and #5149/#5173 (operator persona split).

  • New

    Align audience filter to server ROLE_LEVEL ladder (#5063)

    Fixes a client/server drift in the WidgetPicker audience filter: the existing binary \`ADMIN_ROLES\` check allowed \`admin\` role users to see \`owner\`-audience widgets that the PUT /api/intelligence/dashboards/:id gate (#5060) would 403. Now both sides use the same numeric ladder.

  • Fix

    Allowlist ORDER BY sort field — SQL injection prevention (#5186)

    Fixes a SQL injection vector in \`CollectionsService.find()\`: client-supplied \`sort.field\` and \`sort.direction\` were interpolated raw into the \`ORDER BY\` clause. An authenticated user could craft \`?sort=[{"field":"name;DROP TABLE x","direction":"asc"}]\` to inject arbitrary SQL into the query identifier position. SQL identifiers cannot be parameterised in PostgreSQL — allowlisting is the correct and only effective mitigation.

  • Fix

    Replace console.* with Fastify structured logger (#5034)

    Replaces all 132 \`console.log/error/warn\` calls in the Communications service with Fastify's pino structured logger, eliminating unstructured stdout noise from a Tier 2 production service.

  • New

    Embedded child entity-tables in EntityDetailPanel (#5191)

    Adds `embeddedChildTables` config to `EntityTable` / `EntityDetailPanel`, enabling inline child entity-tables inside the detail panel — the Notion "database within a page" pattern.

  • New

    TerraLedger Marina + Allotments Admin tabs (#5181, #5190)

    Wires entity-table spine into TerraLedger Marina (all tabs) and fixes Allotments.info Admin (canonical widget IDs + detailPanelWidgets).

  • New

    QueueSpark default tab entity-table (#5183)

    Adds a `queuespark.queue` entity-table to the QueueSpark default tab.

  • New

    RootScrum agile + shapeup tabs (#5180)

    Adds entity-table companions to RootScrum agile and shapeup tabs.

  • New

    TerraLedger compliance + enforcement + reports tabs (#5178)

    Adds entity-table spine to TerraLedger compliance, enforcement, and reports tabs.

  • New

    TerraLedger financial + waitlist tabs (#5185)

    Adds entity-table companions to TerraLedger financial and waitlist tabs.

  • New

    Convert 29 analytics tabs to collections/chart (#5184)

    Converts analytics tabs in 28 recipes from intelligence/* wrapper widgets to collections/chart — completing the P2 analytics tab conversion. (club-management is excluded — it has no analytics tab in its dashboard.)

  • Fix

    Remove workspace switcher pill from top bar

    Removes the green dot + workspace name pill from the app's top bar — it was redundant with the left nav rail.

  • Fix

    Regenerate manifest + CDN deploy step after widget bundle build

    Fixes the CDN staleness that caused TerraLedger widget loading failures ('Couldn't load this widget' on Plots tab).

  • Fix

    Remove dead crypto import + fix stale SHA256 test (#5253 #5254)

    Closes P0 security audit findings. Both core fixes were already shipped. This PR: removes dead SHA256 `crypto` import from SignatureEnvelopeService and fixes a stale test that used SHA256 mock hashes instead of bcrypt. 44/44 tests pass. Closes #5253 Closes #5254

  • Fix

    Correct Communications endpoint calls — email notifications (#5256)

    Fixes broken email notifications in RootSign. Two service classes were calling `POST /api/communications/email` (non-existent) instead of the real endpoint. Fixed to use `/api/communications/send` + `/api/communications/send-bulk` with `channel: 'email'` shape. 234 tests pass. Closes #5256 Closes #5255

  • Fix

    Use existing AWS_* vars for CDN deploy — no new config needed

    Corrects #5341. The Railway Bucket is already connected and automatically injects `AWS_ENDPOINT_URL`, `AWS_S3_BUCKET_NAME`, `AWS_ACCESS_KEY_ID`, `AWS_SECRET_ACCESS_KEY`. No separate `CDN_AWS_*` setup needed. Removes 18 lines of unnecessary `ARG CDN_AWS_*` + credential remapping. Replaces with a clean 4-line conditional that uses the already-configured `AWS_*` vars. The manifest regeneration step (from #5341) is kept — that part was correct. Self-review: PASS ✅ — Dockerfile only. Fixes #5341.

  • New

    Wire registered webhooks to delivery + observability endpoint (#5247)

    Wires RootSign's registered webhook system to actual delivery. Previously, `POST /api/documents/webhooks` stored registrations in Collections but `broadcastWebhookEvent` never read them — events only went to hardcoded TerraLedger/QueueSpark targets.

  • Fix

    Surface AI errors + conditional workflow proof (#5258 #5257)

    AI errors silently swallowed (empty return) → now throw DocumentServiceError 503 so routes return structured error to widgets. +19 tests. Closes #5258 Closes #5257

  • New

    SMS reminders wired + audit trail download widget (#5246 #5245)

    Wire SMS (signerPhone field, was checking signerEmail). Add AuditTrailDownloadWidget (routes already existed). 45 tests pass. Closes #5246 Closes #5245

  • New

    Workspace intro onboarding step + audience tag 9 new widgets (#5197)

    Adds workspace-intro step to TerraLedger onboarding (step 8 of 9) with preset Secretary/SiteManager/Committee templates. Also tags 9 entity-spine widgets added after #5062 that were missing audience. 3 audience tests pass. Closes #5197

  • New

    AI-written narrative in weekly MRR digest (#5308 #5309)

    Wires AI-Core narrative generation into the existing weekly digest job. Digest send path was already implemented (CommunicationsIntegration, Monday 08:00 UTC cron). This PR adds non-fatal AI narrative — 2-3 sentence summary of MRR metrics from AICoreIntegration. Falls back gracefully when AI unavailable. All existing Intelligence tests pass. Closes #5308 Closes #5309

  • New

    Bulk send UI widget — CSV upload dispatches envelopes (#5252)

    BulkSendWidget: CSV upload → parse envelope IDs → POST bulk-dispatch → success/failure summary. Auth via useStudioAuth, audience:admin, wired into recipe. Closes #5252

  • New

    Premium workspace assembly animation (#5349)

    Replaces `SnowflakeLoader` and plain `DashboardSkeleton` with a premium canvas particle-vortex loading experience that makes the workspace feel like it's assembling for the user, not waiting for data.

  • New

    Zoom map to selected council/town when filter changes

    Shipped Zoom map to selected council/town when filter changes.

  • New

    Wire LocationRecommendationsWidget to proximity API (#5213)

    Replaces 'coming soon' stub with real implementation. Calls existing QueueSpark sites-by-proximity endpoint (Layers geocoding already wired). 17 tests. Closes #5213

  • Fix

    Three production crashes from latest Railway deploy

    Shipped Three production crashes from latest Railway deploy.

May 2026

  • Fix

    Delete dead booking manager files (#4557)

    Deletes 5 booking manager files (+ their tests) verified to have zero live callers outside of test files. Routes only import `BookingService`; `BookingJobs.ts` references `AppointmentManager` only in a comment (not an import).

  • Fix

    Delete 5 dead manager files — ~5000 LOC dead code removal (#4556)

    Deletes 5 dead marketplace manager files that have zero route references and zero widget references. All marketplace logic lives in MarketplaceService.ts inline SQL.

  • New

    Auto-zero allotments.info pin for full-TerraLedger councils (#4748)

    Auto-zeros the allotments.info pin checkout price (to £0/yr) when the purchasing council already has an active TerraLedger subscription. Matches the existing renewal behaviour in `applyFreePinRenewalDiscount`.

  • Fix

    Add in-process nightly snapshot scheduler — wire Stripe meter events (#4747)

    Adds `snapshotScheduler.ts` — an in-process setInterval-based daily scheduler that fires `snapshotTerraLedgerPlotsForAllAccounts()` and `snapshotRootPlanPlantingsForAllAccounts()` at 01:00/01:30 UTC respectively.

  • New

    Install Matt Pocock skills — diagnose, zoom-out, handoff

    Shipped Install Matt Pocock skills — diagnose, zoom-out, handoff.

  • New

    Replace hardcoded Pricing.tsx cards with PricingInformation component

    Shipped Replace hardcoded Pricing.tsx cards with PricingInformation component.

  • New

    Wire useAhaMomentTracker and useFirstValueMoment to RecipeDashboardPageClient

    Shipped Wire useAhaMomentTracker and useFirstValueMoment to RecipeDashboardPageClient.

  • New

    Fold rent-collection, compliance, storage & comms into…

    … TerraLedger base price Councils on the per-plot usage model already pay £1.7k–£3k+/yr; fragmenting that with £5/mo rent-collection and £49/yr compliance addons adds checkout friction on features that cost PlotSpark nothing to serve (Stripe + comms are BYOK/pass-through). Rent collection and GDPR compliance are table stakes for a public-sector data processor and should never be a separate line item. Folds rent-collection, document-storage, communications and compliance into includedFeatures across all four paid plans (allotment-community, marina-paid, farm-paid, beach-hut-paid). rootplan-lite stays as the only addon — it's a genuinely separate product (tenants get Lite; upsell to RootPlan Pro). Margin gate passes (targetMargin 0.7, unchanged). No feature lock: the capabilities move to includedFeatures so entitlement resolution still grants them via BillingService.planGrantsFeature(). Refs #4646

  • Fix

    Add billing/investor-statement to terraledger financial tab

    Shipped Add billing/investor-statement to terraledger financial tab.

  • Fix

    Trim MVP default workspaces to first-value widget set (#4565)

    Shipped Trim MVP default workspaces to first-value widget set (#4565).

  • Fix

    RootLytics dashboard parity — dev route accessible in Docker/CI (#4667)

    Fixes the dev recipe dashboard route guard so it works in local Docker, CI, and staging — enabling RootLytics DoD E2E tests to assert canonical widget layout reliably.

  • Fix

    Uncomment ScheduledReportsWidget POST/PATCH assertions

    Shipped Uncomment ScheduledReportsWidget POST/PATCH assertions.

  • New

    Mount NotificationsBell in DashboardHeader

    Shipped Mount NotificationsBell in DashboardHeader.

  • Fix

    Delete misplaced widget files from apps/ directory

    Shipped Delete misplaced widget files from apps/ directory.

  • Fix

    Wire allotmentsInfoId to checkout so £99 pin credit is applied

    Wires the council's vanity slug through the pricing page → checkout so the billing service can apply the £99 pin fee as a Stripe customer balance credit on TerraLedger subscription creation.

  • Fix

    Plots route accepts service-path auth + activate passes x-account-id

    Two related fixes that unblock TerraLedger workspace provisioning via the service path (allotments.info activate route + service-to-service calls): 1. **plots.ts** — replace the local Bearer-only `getUserFromRequest` with the shared `getSharedUserFromRequest` from `../lib/auth.js`. Site routes were already updated in #4163; plot routes were left behind. Service-path callers (activate provisioning, bulk seeding) got 401 on every plot create. 2. **activate/route.ts** — extract `provisionAccountId` from the approved claim and pass it as `x-account-id` in the TerraLedger POST `/sites` headers. Add a guard that skips provisioning (with a warning) if the claim has no per-council accountId. 3. **package.json** — add `node-cron` + `@types/node-cron` as explicit deps (VeraScheduler was importing them transitively).

  • New

    Wire PopularWidgetsDropdown and MessageState to dashboard components

    Shipped Wire PopularWidgetsDropdown and MessageState to dashboard components.

  • Fix

    Use bracket notation for process.env to fix TS4111

    Shipped Use bracket notation for process.env to fix TS4111.

  • Fix

    Remove all references to legacy plotspark_terraledger_plots table

    Removes all 10 remaining references to the legacy `plotspark_terraledger_plots` table, replacing each with the correct `plotspark_collections` query. Fixes #4686.

  • Improvement

    Add media to abavus-one-click-migration P0 aha moment

    Shipped Add media to abavus-one-click-migration P0 aha moment.

  • Improvement

    Fix schema gate, name competitors, expand onboarding to 5 steps

    Shipped Fix schema gate, name competitors, expand onboarding to 5 steps.

  • Fix

    Add painPoints and ahaMoments to 9 early-access addon recipes

    Shipped Add painPoints and ahaMoments to 9 early-access addon recipes.

  • Improvement

    Add painPoints + ahaMoments to 9 addon recipes; fix allotments-info and terraledger gaps

    Shipped Add painPoints + ahaMoments to 9 addon recipes; fix allotments-info and terraledger gaps.

  • Fix

    Implement 3 missing step-control routes

    Shipped Implement 3 missing step-control routes.

  • Fix

    Unskip real-agent orchestration tests with flexible assertions

    Shipped Unskip real-agent orchestration tests with flexible assertions.

  • Fix

    Exclude recipes/base/ from recipe completeness gate

    Shipped Exclude recipes/base/ from recipe completeness gate.

  • Fix

    Remove all references to legacy plotspark_terraledger_sites table

    Removes all 9 remaining references to the legacy \`plotspark_terraledger_sites\` table across 4 files, replacing each with \`plotspark_collections\` queries. Fixes the split-brain where \`PUT /sites/:id/configuration\` wrote to the legacy table while PlotManager read from collections. Fixes #4710.

  • Fix

    Standardise account_id to UUID across 14 tables

    Fixes the `account_id` type inconsistency (#4731) — 14 tables had `TEXT` or `VARCHAR(255)` instead of `UUID`, which blocks RLS policies, causes implicit cross-type joins, and makes multi-tenant analytics unreliable.

  • Fix

    Point Dockerfile at dist/index.js to match tsup esm output

    Fixes the mcp-server Dockerfile so `docker compose build mcp-server` succeeds — it referenced `dist/index.mjs` but the build emits `dist/index.js`.

  • Fix

    Remove duplicate hasFeature shadowing #2717 entitlements + refresh stale billing tests

    Removes a duplicate `BillingService.hasFeature` method that was silently shadowing the canonical #2717 entitlements implementation, and refreshes the 5 stale billing test suites it surfaced. Resolves #4737.

  • Fix

    Graceful degradation when service down + weather location CTA (#4509)

    Fixes RootPlan dashboard widget UX when the rootplan service is unavailable, and adds a missing CTA to the weather widget's location-not-set state. Fixes #4509

  • Improvement

    Add mcp-server service to docker-compose.yml (#4505)

    Shipped Add mcp-server service to docker-compose.yml (#4505).

  • Fix

    Remove stub redirect, link directly to settings/changelog (#4508)

    Shipped Remove stub redirect, link directly to settings/changelog (#4508).

  • Fix

    Remove orphaned chrome components; add data-import to nav (#4507)

    Shipped Remove orphaned chrome components; add data-import to nav (#4507).

  • Fix

    Resolve 33 pre-existing TypeScript errors

    Shipped Resolve 33 pre-existing TypeScript errors.

  • New

    Capture UTM + referrer attribution on waitlist signup

    Captures UTM + referrer attribution on the early-access **waitlist** signup flow, so we can trace where waitlist signups actually came from.

  • New

    Live SubscriptionRenewalBanner + type cleanup + island audit

    Shipped Live SubscriptionRenewalBanner + type cleanup + island audit.

  • Fix

    Wire recipe tab bar into live workspace (#4559)

    Wires the already-built \`RecipeTabBar\` component into the live authenticated workspace so logged-in users see the same multi-tab recipe layout as the demo.

  • Fix

    Call initializeManagers() so routes don't 500

    Shipped Call initializeManagers() so routes don't 500.

  • Fix

    Migrate all settings tabs to SettingsTemplate (#4560)

    Migrates all settings tabs to use `SettingsTemplate` from `@plotspark/ui`, replacing 6 inconsistent visual framings with a single consistent chrome.

  • Improvement

    Add painPoints, ahaMoments, uxStandards (#4561)

    Verifies that rootlytics-recipe.json already contains all required marketing content: `painPoints.primary`, `secondary[]`, `research` — all present with specific SaaS founder voice 12 `ahaMoments` including 6 P0 with `media` screenshot paths and `linkedTestOrSpec` `uxStandards.crystallizationMoments` — 4 moments defined (first_data_source_connected, first_dashboard_viewed, first_anomaly_detected, first_report_created) `valuePropositions.cantLiveWithout` and `beforeAfter` present

  • New

    Mount NaturalLanguageSearch in dashboard header

    Mounts the `NaturalLanguageSearch` component in `DashboardHeader` — visible in the centre of the header on md+ screens, passing `currentRecipeId` and an `onNaturalLanguageQuery` callback prop (defaults to no-op when not provided by callers).

  • New

    Mount FloatingHelpButton in authenticated app shell

    Mounts `FloatingHelpButton` once in `AuthAppShell` outside the scrolling content area, so it floats persistently over all authenticated routes (bottom-left, complementing the AI Assistant at bottom-right).

  • New

    Use SnowflakeLoader for recipe activation

    Wires the existing `SnowflakeLoader` component into `DashboardEditor` so recipe activation shows a branded animated loading overlay with real progress (0 → 33 → 66 → done) instead of no visual feedback.

  • New

    Mount DemoBadge alongside FloatingCtaButton

    Mounts `DemoBadge` ("Explore Templates" CTA) in the public marketing layout alongside where `FloatingCtaButton` appears, positioned at `bottom-24 right-6` so it stacks above without overlap.

  • New

    Add agent-in-progress label locking (#4586)

    Adds GitHub label-based mutex locking to the p0-p1-issue-loop skill so that concurrent agent runs (manual `/p0-p1-issue-loop` + scheduled routine) cannot claim the same P0/P1 issue simultaneously.

  • New

    Mount AdminGrowthLink in authenticated shell navigation

    Mounts `AdminGrowthLink` in the `AppShell` top bar alongside the notification/theme/avatar controls, so admin and owner users see a "Growth" nav link — gated by `usePermissions().isAdminOrOwner`, returning null for non-admins.

  • New

    Add billing/vat-number-input widget to TerraLedger and rootplan settings tabs

    Adds the `billing/vat-number-input` widget (built as part of #4404 but never wired into any recipe) to the `settings` dashboard tab of `terraledger-recipe.json` and `rootplan-recipe.json`, making the UK VAT registration number input visible and editable for customers on those recipes.

  • New

    Shared product-agent approval runtime + Vera Rent Agent tooling

    Adds the shared product-agent approval-gate runtime and Vera — the first customer-facing AI agent for TerraLedger — automating rent arrears reminders, payment reconciliation, and formal letter drafting with a human-in-the-loop approval queue for consequential actions.

  • New

    Wire ProtectFallback to permission-gated UI (#4535)

    Wires the existing `ProtectFallback` tooltip component to three silent-disable/hide patterns in `WorkspaceSwitcher`, so users who lack the `canManage` permission see a "You don't have permission" tooltip instead of a greyed-out or invisible control.

  • New

    Wire useSubscriptionStatus to billing UI components (#4536)

    Wires the existing `useSubscriptionStatus` hook into `SubscriptionRenewalBannerContainer` and `SubscriptionLapseBanner`, eliminating duplicated inline fetch logic in both components.

  • New

    Wire 6 billing managers + yearComparison route (#4541)

    Wires 6 previously unconstructed billing managers (Subscription, Invoice, Payment, Pricing, RevenueAnalytics, Accounting) into the billing service at startup, and registers the orphaned `yearComparisonRoutes`.

  • New

    Stripe portal — Update Payment Method in renewal banner (#4580)

    Adds the missing 'Update payment method' button to `SubscriptionRenewalBanner`. The portal session route, app proxy, and Settings → Billing entry point already existed — this is the final UI entry point from the renewal banner.

  • New

    Failed payment dunning flow — tiered banner + recovery gate (#4581)

    Wires the existing dunning state machine to the frontend: **Backend**: adds `paymentFailedAt` to `RecipeSubscriptionSummary` (sourced from `plotspark_billing_dunning.created_at` via LEFT JOIN LATERAL — only set when status is not `recovered` or `exhausted`) **Hook**: adds `gracePeriodEndsAt` (earliest lapsed `paymentFailedAt` + 7 days) and `subscriptionLapsed` (any status=`canceled`) to `useSubscriptionStatus` **Banner**: tiers `SubscriptionLapseBanner` amber (D1–3) → red (D4–6) → hidden (D7+, gate takes over); grace constant extracted as `GRACE_PERIOD_DAYS = 7` (was hardcoded 14) **Gate**: new `SubscriptionRecoveryGate` in `AuthAppShell` — hard-blocks access after grace expires with Update Payment Method / sign-out **Recovery**: `StripeWebhookService.handleInvoicePaymentSucceeded` now sets dunning `status='recovered'` when `invoice.payment_succeeded` fires (previously unhandled)

  • Fix

    Resolve critical handlebars CVE blocking Security Audit gate

    Adds a `pnpm.overrides` entry forcing `handlebars >= 4.7.9`, eliminating the one critical vulnerability that was blocking the Security Audit CI gate on every push to `Release-*` / `preprod` / `main`.

  • Fix

    Wire three island-code components into live paths (#4530, #4531, #4532)

    Shipped Wire three island-code components into live paths (#4530, #4531, #4532).

  • Fix

    Send x-service-token on auto-allocation calls — IG-01 (#4600)

    Fixes IG-01 (Silent Allocation): TerraLedger's auto-allocation fire-and-forget block was POSTing to QueueSpark without an \`x-service-token\` header (silent 401s), and QueueSpark's process endpoint didn't resolve accountId from \`x-account-id\` for service callers.

  • Fix

    Allow pre-auth allotments.info pin checkout via service token

    Fixes the £99 allotments.info pin checkout, which 401'd before a council ever reached the Stripe payment screen. The claim funnel is **pre-auth** — there is no logged-in council user when the £99 directory pin is purchased (the council `accountId` is resolved later, at the activate step, from the approved claim record). The funnel calls billing `POST /api/checkout/session` with an `x-service-token` (signed with `SERVICE_JWT_SECRET`) and **no** `Authorization: Bearer` header. Two compounding bugs blocked this: 1. **App proxy** (`allotments-info/subscription/checkout/route.ts`) sent the service JWT on **both** `Authorization: Bearer` *and* `x-service-token`. Billing's auth middleware validates a `Bearer` token as a **user** token against `JWT_SECRET`, so the service JWT (signed with `SERVICE_JWT_SECRET`) failed validation and 401'd before the service-token path was reached. 2. **Billing handler** (`routes/checkout.ts` `/session`) hard-required a `Bearer` token and 401'd any caller without one — even a valid service caller already authenticated by the shared middleware.

  • Fix

    IG-03 Layers push + IG-04 site_id mapping (#4602 #4606)

    Fixes two Impossible Goals blockers (IG-03 and IG-04) in a single branch: **IG-04 (#4606):** `QueueSparkService.getQueue()` was discarding `site_id/council_id/society_id` from DB rows, so `queue.siteId` was always undefined and `forwardNationalEntry` never fired. National allotments.info applicants never appeared in council TerraLedger views. **IG-03 (#4602):** `PlotManager.syncSiteAvailability` was updating Collections but never pushing the fresh `availablePlots` count to the Layers service. The allotments.info directory chip never updated after plot status changes.

  • Fix

    Re-point billing & communications deep-imports to public entry (#4635)

    Fixes a runtime crash in the `billing` and `communications` containers caused by importing manager interfaces from the package-internal source subpath `@plotspark/shared-types/src/managers`, which is not declared in the package `exports` map.

  • Fix

    Align metricType filter to 'mrr' — MRR no longer always zero (IG-05) (#4603)

    Fixes IG-05 (Revenue Picture): `SaaSMetricsManager` was querying with `metricType='revenue'` but TerraLedger writes `metricType='mrr'` to Collections, so MRR was always zero. Aligned the filter and extended the type union additively so `'revenue'` rows (from Stripe) still resolve correctly.

  • Fix

    Add POST /notifications/push endpoint — frost alerts now deliverable (IG-06) (#4607)

    Fixes IG-06 (Daily Assistant): `CronJobManager.runFrostRiskAlerts()` was calling `POST /api/communications/notifications/push` but the endpoint didn't exist. Added the route to the communications service so frost alerts (and pest alerts) can be delivered.

  • Fix

    Skip dashboard redirect for verify-email page to break infinite loop (#4594)

    Fixes infinite redirect loop: middleware redirected unverified users to `/verify-email`, while the `(auth-shell)` layout redirected any authenticated user back to `/dashboard`. Added a narrow exemption in the layout — when the current path is `/verify-email`, the dashboard redirect is skipped.

  • Fix

    Send /invite/[code] link for team invites — accept route no longer 404s (#4595)

    Fixes team invite flow: `CoreService.inviteTeamMember` was calling `sendMagicLink` which sent a Supabase magic-link URL. Changed to generate a UUID invite code, persist it with a 7-day expiry, and send `/invite/{code}`. Added `GET /invitations/:code` (unauthenticated) and `POST /invitations/:code/accept` (authenticated) routes to Core service.

  • Fix

    Require SERVICE_JWT_SECRET at startup — grantRecipeAccess no longer fails silently (#4599)

    Fixes silent post-payment failure: `SERVICE_JWT_SECRET` was in the billing service's `recommended` env list with `exitOnMissing: false`, so a missing secret only produced a warning at startup. When Stripe webhook fired `grantRecipeAccess`, the token mint failed and the error was silently swallowed — paying customers never received recipe access. Promoted to `required` so the service exits at deploy time if missing.

  • Fix

    TenantDashboardWidget + TenantPaymentPortalWidget self-resolve tenantId (#4601)

    Both tenant widgets required a `tenantId` prop that was always undefined (CDN widget loader has no mechanism to inject session-derived props). Added a `useTenantId` hook that resolves `tenantId` from the authenticated user's email via `/api/core/auth/me` → `/api/terraledger/tenants`. Non-tenant users (council officers) get a graceful empty state.

  • Fix

    Tier sync on plan upgrade, portal accountId, portal config, PlanOfferCard mount

    Shipped Tier sync on plan upgrade, portal accountId, portal config, PlanOfferCard mount.

  • Fix

    Implement visitor check-in with Collections persistence

    Shipped Implement visitor check-in with Collections persistence.

  • Fix

    44px close button and footer fallback for mobile basket drawer

    Shipped 44px close button and footer fallback for mobile basket drawer.

  • Fix

    Onboarding entity-creation step — garden body schema, field mapping, tab unlock

    Shipped Onboarding entity-creation step — garden body schema, field mapping, tab unlock.

  • Fix

    Add /api/intelligence/metrics/{mrr,churn-rate} routes

    Shipped Add /api/intelligence/metrics/{mrr,churn-rate} routes.

  • Fix

    Stripe connect settings page calls wrong proxy — fix…

    … to plotspark-intelligence (#4605) Status check: /api/api-key-manager/stripe/status → /api/plotspark-intelligence/stripe/status Connect: /api/api-key-manager/stripe/connect → /api/plotspark-intelligence/stripe/connect Connect body: { secretKey } → { stripeSecretKey } (matches intelligence service schema) Disconnect: /api/api-key-manager/stripe/disconnect → /api/plotspark-intelligence/stripe/disconnect Webhook secret route (/api/api-key-manager/keys) left unchanged — that one is correct Add Vitest tests covering all four fetch paths (7 tests, all passing)

  • Fix

    Add purgeAccount() GDPR cascade + AccountDeletionJob sc…

    …heduler (#4592) Migration 0132: adds `pending_deletion` to the account_status CHECK constraint AccountManager.purgeAccount(): cascades audit log → billing (non-fatal) → collections → api_keys → supabase auth users (non-fatal per user) → users → accounts AccountDeletionJob: runs every 6 hours, picks up pending_deletion accounts past their deletionScheduledFor date, limits to 50 per run Wired into core service startup via setInterval after app.listen() 15 Jest tests covering happy path, non-fatal billing/supabase failures, job scheduling (due vs. not-yet-due accounts, DB failure resilience)

  • Fix

    Route parent invites through Communic…

    …ations send-bulk (#4608) Replace non-existent /api/orchestration/email/bulk and /api/orchestration/sms/bulk with the correct /api/communications/send-bulk endpoint After Collections bulk import, fetch imported parent entities and map to Communications BulkRecipient shape (address + per-recipient variables) qr-letter channel maps to channel:'email' (Communications only supports email|sms|push|whatsapp) Map response { data: { succeeded, failed, results } } to existing result display Update WidgetDefinition: serviceId orchestration→communications, permissions orchestration:send→communications:send Fix pre-existing test failures: remove stale beforeEach mock queue that was causing mock-call ordering issues; fix aria-label on send button so /send invites/i regex matches correctly All 18 tests now pass

  • Fix

    Enforce one recipe-default workspace per recipeId (#…

    …4504) The legacy name-based dedupe collapsed duplicates only when names matched exactly. Historical activation bugs produced 7+ isDefault=true workspaces per recipe with subtly different names ("Alerts & Monitoring", "Alerts & Monitoring 1", "Alerts & Monitoring (copy)", etc.) that the truncated switcher UI rendered as 7 identical "Default" rows. Adds a stricter first pass to dedupeGeneratedRecipeDashboards that collapses by recipeId alone for isDefault!==false candidates — the PRD-002 invariant from workspaces-and-recipes.md and PLOTSPARKOS_MVP_WORKSPACE_GAP_PRD.md: > At most one isDefault=true workspace per (accountId, recipeId). User-renamed (isDefault=false) workspaces are preserved. The legacy same-name pass remains as a safety net for older data. Refs #4504

  • Fix

    Serve widget bundles from local OrbStack nginx (#4515)

    Shipped Serve widget bundles from local OrbStack nginx (#4515).

  • Fix

    Grid fills full width + TerraLedger tenant 502 (#4512 #4513)

    Shipped Grid fills full width + TerraLedger tenant 502 (#4512 #4513).

  • Fix

    Fix IG-01, IG-02, IG-05 — all Connected Allotments IGs pass

    Shipped Fix IG-01, IG-02, IG-05 — all Connected Allotments IGs pass.

  • New

    VAT invoice PDF + account VAT fields (#4404)

    Wires UK VAT compliance into the invoice PDF pipeline: adds `vatNumber`/`countryCode` to accounts, renders A4-compliant VAT invoices (per GOV.UK Notice 700/21) with conditional reverse-charge notices, and exposes a `GET /invoices/:invoiceId/pdf` download route. Default is no-op — activates only when `BILLING_VAT_ENABLED=true` AND `BILLING_VAT_NUMBER` are both set.

  • New

    Route contract signing through RootSign envelopes (#4485)

    Wires TerraLedger tenancy contract creation through RootSign (Document service SignatureEnvelopeService). When a contract is created with parties that have email addresses, a signing envelope is automatically created and the tenant receives an email link to sign. When all parties complete signing, the Document service fires a callback to TerraLedger which marks the contract signed and stores the audit-trail PDF URL.

  • Fix

    Wrap ?? + && in parens — fixes PlotSparkOS build failure

    Fixes PlotSparkOS production build failure caused by mixing `??` and `&&` operators without explicit parentheses in `recipe-resolver.ts`. ES2020 forbids this combination (SyntaxError). Webpack/swc rejects it at build time.

  • Fix

    Wire vanity site to shared booking contract + fix E2E contract test (#4361)

    Wires the RootSchool vanity site into the shared booking contract by adding a `book-parent-evening` page using `booking-services-vanity/public-booking-page` + `booking-services-vanity/staff-selector` (no bespoke widgets — pure platform composition), and fixes a pre-existing drift in the allotments-info vanity config that was causing 1 of 9 contract tests to fail.

  • New

    Add plotspark improve — self-improving release loop

    Implements the self-improving wrapper for PlotSparkOS, inspired by Tom Blomfield's YC talk on recursive self-improving AI loops.

  • Fix

    Stop raw JS TypeError messages leaking into DOM

    Shipped Stop raw JS TypeError messages leaking into DOM.

  • Fix

    Resolve missing widget IDs in saas-founder-bundle + skip unconfigured homeschool demo

    Shipped Resolve missing widget IDs in saas-founder-bundle + skip unconfigured homeschool demo.

  • New

    Wire customer-voted feedback into self-improving loop

    Closes the customer-feedback loop end-to-end (issue #4497): the highest-voted, admin-triaged customer requests now drive `plotspark improve --loop backlog`, and shipping a feedback item notifies the original submitter automatically.

  • Fix

    Address code-review blockers on #4501 promotion

    Shipped Address code-review blockers on #4501 promotion.

  • New

    Subscription renewal pre-notice cadence + dashboard banner (closes #4405)

    Adds 30/14/7-day pre-renewal email cadence + post-renewal confirmation email + a customer-facing dashboard banner with a one-click cancel-before-renewal CTA, closing the gap where Stripe auto-renewals fired with zero advance warning.

  • New

    Refund credit notes + admin UI + audit (closes #4406)

    Adds the legal evidence paper trail that the existing Stripe Refunds wiring was missing — every refund now generates a Stripe Credit Note, a UK HMRC-compliant credit-note PDF, an append-only audit row, and a customer email with the credit note attached. Adds an admin refund page at `/admin/billing/refunds`.

  • New

    Winback cron sweep over cancelled subscriptions (#4416)

    Adds a daily 10:00 UTC cron sweep that re-engages subscribers who cancelled 30, 60, or 90 days ago by sending targeted winback emails via the Communications service. Idempotency is enforced via `winback_sent_log` entities in `plotspark_collections` (max 3 attempts per subscription). A manual admin trigger endpoint is also added for backfill and testing.

  • Fix

    Wire retention eligibility check into CancelSubscriptionDialog (#4415)

    Wires `POST /api/billing/retention/check-eligibility` into `CancelSubscriptionDialog` so the backend's cooldown and eligibility rules are actually enforced in the UI. If a user is ineligible for a retention offer (e.g. they recently received one), the save-offer step is skipped entirely.

  • New

    Sandbox account routing — Stripe test keys for sandbox, live for real, all via API Key Manager (#4423)

    Routes non-BYOK Stripe client selection through API Key Manager: sandbox accounts use the platform `sk_test_` key; real accounts use the platform `sk_live_` key; BYOK tenants use their own key. Eliminates the single boot-time Stripe client built from a raw env var. Closes #4423

  • Fix

    Gate welcome email garden link on RootPlan garden creation success

    Gates the `gardenPlanUrl` deep link in the tenant welcome email on whether RootPlan's garden auto-creation actually succeeded, so tenants never receive a broken link to a garden that doesn't exist. Closes #4427

  • Fix

    Structured audit + clerk alert on TerraLedger conversion failure

    On TerraLedger conversion failure after an applicant accepts an allotment offer, writes a `conversion_failure` audit record to Collections and fires an async clerk alert via Communications — both fire-and-forget so the applicant's success response is never blocked. Closes #4426

  • New

    Branding upload widget — logo & cover for vanity sites (#4434)

    Shipped Branding upload widget — logo & cover for vanity sites (#4434).

  • New

    Admin API for BYOD domain registration with Cloudflare Custom Hostname (#4437)

    Adds `POST/GET/DELETE /api/admin/byod-domains` — a platform admin endpoint that registers BYOD (Bring Your Own Domain) custom hostnames via Cloudflare for SaaS and persists the corresponding `platformDomain` entity to Collections, enabling runtime domain routing without redeployment.

  • New

    Wire BrandingUploadWidget into council admin settings (#4434)

    Shipped Wire BrandingUploadWidget into council admin settings (#4434).

  • New

    Seed directory_profiles in Birds script + PATCH proxy for branding uploads

    Shipped Seed directory_profiles in Birds script + PATCH proxy for branding uploads.

  • Fix

    Remove fabricated data — null out all non-verifiable fields

    Shipped Remove fabricated data — null out all non-verifiable fields.

  • Fix

    Populate verified data from birdscommunityallotments.org.uk

    Shipped Populate verified data from birdscommunityallotments.org.uk.

  • Fix

    Remove real PII from git, add anonymised Birds fixture data

    Shipped Remove real PII from git, add anonymised Birds fixture data.

  • New

    Break-glass admin access — PR 1/5: DB table, ImpersonationManager, Core routes

    Shipped Break-glass admin access — PR 1/5: DB table, ImpersonationManager, Core routes.

  • New

    Break-glass access-requests proxy routes — PR 2/5

    Shipped Break-glass access-requests proxy routes — PR 2/5.

  • New

    Break-glass email templates — PR 3/5

    Shipped Break-glass email templates — PR 3/5.

  • New

    Break-glass widgets — AdminAccessRequest + AdminAccessInbox (#4446 PR4)

    Shipped Break-glass widgets — AdminAccessRequest + AdminAccessInbox (#4446 PR4).

  • New

    ImpersonationBanner break-glass variant — red banner + accessRequestId (#4446 PR5)

    Shipped ImpersonationBanner break-glass variant — red banner + accessRequestId (#4446 PR5).

  • Fix

    Correct break-glass notification payload shape — Communications service (#4446)

    Shipped Correct break-glass notification payload shape — Communications service (#4446).

  • Fix

    Inline readDryRunComms — fix plotsparkos webpack build

    Shipped Inline readDryRunComms — fix plotsparkos webpack build.

  • Fix

    Correct 4 birds dogfood IG test failures

    Shipped Correct 4 birds dogfood IG test failures.

  • New

    AI Widget Builder + Creator Recipe Builder + Community Family Routing

    Shipped AI Widget Builder + Creator Recipe Builder + Community Family Routing.

  • Fix

    Skeleton stuck indefinitely when Studio fetch hangs

    Shipped Skeleton stuck indefinitely when Studio fetch hangs.

  • Fix

    Header logo, search, bell, sidebar aria-label (#4463)

    Adds the missing PlotSpark logo, search input, and notification bell to the dashboard header; fixes sidebar `<nav>` aria-label from "Main navigation" / "Section navigation" to "Recipe navigation"; adds `aria-label` support on nav items for the Add recipe button and Settings link.

  • Fix

    Coming-soon recipes always render as locked (#4462)

    Fixes FirstRecipeSelection showing coming-soon recipes as activatable in the full catalog — now they always render as locked (greyed-out with Join Waitlist CTA), regardless of grant status.

  • Fix

    Raise muted-foreground, domain-primary, green CTAs to WCAG AA (#4458)

    Fixes three systemic WCAG AA color contrast failures: muted text tokens, domain CTA backgrounds, and green action buttons across the allotments.info surface.

  • Fix

    Clear corrupted auth cookies; prevent infinite redirect (#4460)

    Fixes an infinite redirect loop where a corrupted/invalid auth token on the sign-in page caused middleware to redirect to `/en/dashboard`, which then redirected back to `/en/sign-in`.

  • Fix

    Align edit-mode E2E tests with actual button labels (#4461)

    The edit mode toggle button and Ctrl+E shortcut are fully implemented. E2E tests were failing because they queried by wrong accessible name (/^edit$/i, /^preview$/i). This fixes the test selectors to match the actual aria-labels and data-testid.

  • Fix

    Atomic SCD version allocation — ON CONFLICT retry loop (closes #4338)

    Shipped Atomic SCD version allocation — ON CONFLICT retry loop (closes #4338).

  • Fix

    Env auto-load + smoke redirect + logo nav cluster

    Fixes 3 related navigation/env issues that together caused smoke and critical-pages test failures.

  • Fix

    Clear auth state for unauthenticated tests; fix skeleton testid

    Shipped Clear auth state for unauthenticated tests; fix skeleton testid.

  • Fix

    SCD Type 2 plantings + bed polygons + conflict detection (closes #4397)

    Promote RootPlan from Cartesian-rectangle storage to true SCD Type 2 + polygon storage so the runtime can model succession planting (same square, different time ranges) and reject overlapping plantings with 409 PLANTING_CONFLICT.

  • New

    Wire UK VAT invoices behind BILLING_VAT_ENABLED flag (closes #4404)

    Wires the existing-but-dead `calculateVat()` VAT module into the two invoice creation paths (`BillingService.createInvoice` and `InvoiceManager.create`) behind the existing `BILLING_VAT_ENABLED` + `BILLING_VAT_NUMBER` flags, plus adds a GOV.UK Notice 700/21-compliant render helper for the invoice PDF data shape. Closes #4404.

  • New

    Tenant-facing signed contract PDF surfaces (closes #4409)

    Cherry-picks the 4 tenant-facing files from orphan branch `codex/issue-4267-contract-signing-pdf` and adapts them to PR #4301's canonical Document service architecture, so tenants can see their signed contract PDF on the tenant dashboard.

  • New

    Pin-a-tab star icon in RecipeTabBar (#4398)

    Wires the pin-a-tab UX end-to-end — adds a star icon to every tab in `RecipeTabBar` (core tabs + items in the `Advanced ▾` dropdown). Clicking the star round-trips through the existing `usePinnedTabs` hook (#4396) so the pinned tab appears in `PinnedSidebarPanel`'s Favourites section.

  • New

    Pinned workspaces + favourites sidebar slot — Sprint 3 PR B

    Fills the recipe-dashboard sidebar slot freed by Sprint 3 PR A with a new `PinnedSidebarPanel` — pinned workspaces, pinned recipe tabs (favourites), and quick action links — and ships the persistence + hook plumbing required for user-pinned tabs.

  • New

    RecipeTabBar + NEXT_PUBLIC_DOD_SIDEBAR_IA flag — Sprint 3 PR A

    Introduces a horizontal `RecipeTabBar` at the top of main content for the 5 DOD recipes, gated behind a new `NEXT_PUBLIC_DOD_SIDEBAR_IA` feature flag. Replaces the current per-recipe left-sidebar nav with the OSS-dashboard-standard pattern of "single sidebar + recipe-internal tabs at top of main" (Plane, Linear, Notion, etc.).

  • Fix

    Seeded sample garden + daily-summary 200 on empty (#4378)

    Fixes the two related rootplan defects from the 2026-05-27 DOD UX review: a 404 on `/api/rootplan/gardens/:gardenId/daily-summary` for fresh demo accounts, and an always-blank garden canvas because demo provision never seeded a starter garden.

  • Fix

    Wire end-to-end — link hook + CTA + auto-config + cleanup (P0-P3 follow-ups)

    Wires the Stripe-style data-isolation sandbox end-to-end after an audit of PRs #4392 (backend), #4391 (frontend), #4390 (ADR + CTA) found one disconnect that prevented the chain from lighting up at all, plus four cleanup items.

  • New

    Backend foundation — paired account + JWT + middleware (#4387)

    Sprint A of true Stripe-style data-isolation sandbox: adds the paired sandbox account, the JWT `activeAccountId` claim, the `/api/core/sandbox` routes, and the middleware switch that routes a sandbox-mode JWT to a separate Collections tenant while auto-enabling all 4 capture flags.

  • New

    Frontend toggle + UX — Sprint B (#4388)

    Sprint B of the Stripe-style data-isolation sandbox: adds the user-facing Production/Sandbox toggle, the upgraded banner copy, and the provision/reset controls — the visible surface that makes the Sprint A backend usable.

  • New

    ADR + SandboxEnableCTA + billing decision (#4389)

    Sprint C of the sandbox / Stripe-Test-Mode-style data isolation feature: lands the architectural decision record, the empty-state CTA that lets users enable a sandbox, and the billing policy decision (with a regression test). Fixes #4389.

  • Fix

    Separate the two concepts in copy + lock with tests + CLAUDE.md docs

    Phase 0 of the sandbox roadmap: removes the long-standing conflation between **demo** (anonymous, pre-seeded, 2-hour TTL) and **sandbox** (currently a comms interceptor for authed customers) by fixing the UI copy, adding regression tests, and documenting the two concepts as a first-class section in CLAUDE.md. This unblocks the upcoming Sprints A/B/C that will turn the "sandbox" feature into a true Stripe Test Mode-style data-isolation environment (tracked separately as P0 issues).

  • Fix

    Fan stripeMetrics into per-KPI business_metric entities (#4376)

    Repairs the single biggest credibility leak from the 2026-05-27 DOD UX review: the rootlytics demo dashboard rendered \"+£0\", \"0.0%\", and \"No data available\" for almost every KPI tile despite the headline MRR/ARR cards being populated. A SaaS-metrics buyer would leave in <30 seconds. Closes #4376.

  • Fix

    Table-first landing + friendly error contract (#4377)

    Two focused changes to close the council-officer trust gaps in the TerraLedger authed dashboard. 1. **Table-first landing** — \`terraledger/tenant-directory\` is now the first widget on the default dashboard (y:0 w:12 h:4). Officers land on the operational records view (tenant names, plot assignments, contact info, search) — the same view Scribe/Rialtas open with. \`claimed-sites\` swapped into the y:9 slot. 2. **Friendly error contract** — \`WidgetUnavailable\` no longer shows \"Widget unavailable\" + bare widgetId. New \`humaniseWidgetErrorMessage()\` helper sanitises raw JSON envelopes so widgets can never leak \`{\"error\":{\"message\":\"Authentication required\",\"code\":\"UNAUTHORIZED\"}}\` to the user (the exact pattern the DOD review found in \`council-02/04-offer-sent.png\`). Closes #4377.

  • Fix

    Polish — kill B2B hero, friendly waitlist error, no machine IDs (#4379)

    Three small surface fixes that close the trust gaps on the public allotments.info funnel (per DOD UX review 2026-05-27). None require a service rebuild — all gated by existing patterns (feature flag + friendly fallback + label fallback). Closes #4379.

  • Fix

    Collapse AI Insights panel by default (#4375)

    Flips \`DemoAIPanels\` \`useState(true)\` → \`useState(false)\` so the AI Insights panel no longer pops open on first paint of every demo recipe. Users still summon it via the existing floating \"AI Insights\" pill. Closes #4375.

  • Fix

    Widget-state contract + screenshot helper waits for content (#4373)

    Introduces a formal **widget-state contract** so the DOD journey screenshot helper stops capturing pre-paint frames. Every widget wrapper now exposes \`data-widget-state\` ∈ {\`loading\` | \`ready\` | \`error\` | \`empty\`}, and the helper waits for any \`loading\` element to detach (and for \`networkidle\`) before \`page.screenshot\`. Closes #4373.

  • Fix

    Hide duplicate body h1 collision on every authed recipe (#4374)

    Hides the duplicate body \`<h1>\` in \`RecipeDashboardPageClient\` (legacy fallback path) by changing it to \`sr-only\`. The visible recipe title is owned by \`RecipeDashboardShell\`'s top bar — the body h1 was rendering the same string and visibly colliding on every authenticated recipe dashboard. Closes #4374

  • Improvement

    Promote Release-20260525 to preprod — DOD UX overhaul + true Stripe-style sandbox

    Shipped Promote Release-20260525 to preprod — DOD UX overhaul + true Stripe-style sandbox.

  • New

    Add public resource vanity contract

    Fixes #4362 by making PlotDeck a shared public resource-planning contract for recipe vanity pages, with Birds/local DOD coverage for TerraLedger allotment tap booking.

  • Fix

    Restore silent allocation conversion

    Fixes #4354 by restoring IG-01 silent allocation conversion: accepted QueueSpark allotment offers now call TerraLedger with a plotId, TerraLedger writes the resulting tenancy to Collections first, and canonical journey specs can run without the global auth setup project.

  • Fix

    5 production bugs blocking offer-accept lifecycle (IG-01)

    Fixes five real production bugs that together block the entire allotment offer-accept journey (IG-01 "Silent Allocation"). These bugs prevent every existing offer-accept Playwright spec from passing — surfaced while attempting to drive the journey through the real local stack.

  • Fix

    Restore DOD waitlist routes

    Shipped Restore DOD waitlist routes.

  • New

    Codebase indexer — 36k tests, 15k symbols, 2.9k routes indexed

    Shipped Codebase indexer — 36k tests, 15k symbols, 2.9k routes indexed.

  • New

    Shared VanityUrlSettingsWidget + /api/platform/vanity-slug + Birds E2E tests

    Shipped Shared VanityUrlSettingsWidget + /api/platform/vanity-slug + Birds E2E tests.

  • Fix

    In-memory FK mappings + parallel row processing — Birds import 5min→30s

    Shipped In-memory FK mappings + parallel row processing — Birds import 5min→30s.

  • Fix

    Repair demo seeder Docker path + birds-dogfood spec response normalization

    Shipped Repair demo seeder Docker path + birds-dogfood spec response normalization.

  • Fix

    Allow VIGA_PROVIDER=mock in local Docker dev environments

    Shipped Allow VIGA_PROVIDER=mock in local Docker dev environments.

  • Fix

    Forward contact fields on offer acceptance

    Shipped Forward contact fields on offer acceptance.

  • Fix

    Mark entry offered on send, revert on decline/expiry

    Shipped Mark entry offered on send, revert on decline/expiry.

  • New

    Add public pricing page and nav link for RootSupport

    Shipped Add public pricing page and nav link for RootSupport.

  • New

    Add Stripe SKU fields to 3 addons + Buy CTA in widget sidebar (#4256)

    Shipped Add Stripe SKU fields to 3 addons + Buy CTA in widget sidebar (#4256).

  • Fix

    Remove VIGA mock fallback — fail at startup if VIGA_PROVIDER unset

    Shipped Remove VIGA mock fallback — fail at startup if VIGA_PROVIDER unset.

  • Fix

    Register health checks before routes in 5 services (#4253)

    Registers health check endpoints BEFORE route registration in 5 services where the order was reversed: billing, business, cdn, orchestration, studio.

  • Fix

    Replace booking/testimonial placeholder copy with analytics-specific content (#4252)

    Adds a regression-guard Vitest test to enforce analytics-specific copy in `rootlytics-recipe.json`, closing issue #4252. The `painPoints` and `valuePropositions` sections were previously fixed; this PR adds a 6-assertion test that prevents reintroduction of booking/testimonial placeholder tokens. Closes #4252

  • Fix

    Implement unified-profile aggregation — replace {_stub:true}

    Shipped Implement unified-profile aggregation — replace {_stub:true}.

  • Fix

    Emit plot lifecycle usage events to billing meter (#4250)

    Fixes #4250 — `PlotManager.createPlot()` now emits `plot.created` usage events to the billing meter, `archivePlot()` emits `plot.deleted`, and the nightly `terraledger.plots.snapshot` cron is registered so Stripe meter `meter_terraledger_plots` receives actual events.

  • Fix

    Apply allotments pin coupon at checkout

    Shipped Apply allotments pin coupon at checkout.

  • Fix

    Send renewal reminder emails

    Shipped Send renewal reminder emails.

  • Fix

    Guard queue-dependent sends

    Fixes #4261

  • Fix

    Gate PII report exports

    Shipped Gate PII report exports.

  • Fix

    Replace governance notification fire-and-forget with DLQ (#4263)

    Replaces 3 fire-and-forget `.catch(console.error)` governance notification calls in `TerraLedgerService` with a `sendNotificationWithDlq` helper that writes failed notifications to Collections on Communications failure. Fixes #4263 Refs #4242

  • Fix

    Eager DB init + fail-fast on connection failure at startup (#4247)

    Replaces the lazy nullable DB init pattern (`let db: DatabaseService | null = null`) in the Core Service with eager non-null `const` construction, and makes `start()` verify DB connectivity before binding the HTTP port.

  • Fix

    Remove silent DB fallback — fail loud on init failure (#4246)

    Removes the silent `{} as DatabaseService` fallback in the business service startup path and replaces it with a fail-loud approach that logs a FATAL error and calls `process.exit(1)`.

  • Fix

    Extend payment_intent.payment_failed handler to trigger dunning workflow (#4248)

    Extends the `handlePaymentIntentFailed` webhook handler so that `payment_intent.payment_failed` events trigger the dunning workflow (mark subscription `past_due`, upsert dunning record, send notification email at attempts 1/3/5) — not just update the transaction row. Fixes #4248

  • Fix

    Wire DocumentService.generatePDF() in contract signing widget submit (#4267)

    Wires `DocumentService.generatePDF()` into the TerraLedger contract signing flow so a PDF artefact is produced and stored when a contract is signed. Also fixes a response-key bug in ContractSigningWidget where the widget was reading `data.contract` instead of `data.data` from the API response.

  • Fix

    End-to-end multi-tenant scoping audit on analytics routes (#4266)

    Security fix: analytics routes in `platform/services/plotspark-intelligence/` accepted `accountId` from client input (query string, body, URL params) instead of the JWT, creating an IDOR vulnerability where any authenticated user could query any tenant's analytics data.

  • Fix

    Add DLQ for gamification activity on harvest log (#4264)

    Adds a DLQ (dead-letter queue) pattern to `HarvestLogManager` so that when the gamification service is unreachable, failed activity events are persisted to Collections rather than silently dropped. Harvest log creation always succeeds regardless of gamification service state.

  • Fix

    Validate downstream URLs at startup + /api/ai-core/query alias (#4262)

    Two fixes bundled (as specified in issue #4262): 1. Add startup health check for `COLLECTIONS_SERVICE_URL` and `RULES_ENGINE_SERVICE_URL` — logs WARN at startup if unreachable (soft-fail, no crash-loop) 2. Add `/api/ai-core` prefix alias for all AI routes so `PlantManager`, `MealPlanningManager`, `AIRecognitionSuggestionsManager`, and `ai-layout-fastify` calls to `/api/ai-core/query` no longer 404

  • New

    Add dashboard summary aggregator

    Shipped Add dashboard summary aggregator.

  • Fix

    Attach TerraLedger recipe on claim approval

    Shipped Attach TerraLedger recipe on claim approval.

  • Fix

    Wire Layers spatial changes

    Shipped Wire Layers spatial changes.

  • Fix

    Persist last-minute settings

    Shipped Persist last-minute settings.

  • Fix

    Fail startup when system tasks do not register

    Shipped Fail startup when system tasks do not register.

  • Fix

    Queue pest alerts when communications is down

    Shipped Queue pest alerts when communications is down.

  • Fix

    Rethrow markitdown json parse errors

    Fixes #4274

  • New

    Add user task reminders

    Fixes #4275

  • Fix

    Await route registration before listen

    Target branch: `Release-20260524` Release-gate DoD: focused Jest regression added for the changed production startup ordering path.

  • New

    GET /gardens/:id/daily-summary endpoint (IG-06 P0)

    Shipped GET /gardens/:id/daily-summary endpoint (IG-06 P0).

  • New

    Frost-risk-alerts cron job (IG-06 P0)

    Shipped Frost-risk-alerts cron job (IG-06 P0).

  • New

    Wire daily-summary hero widget into RootPlan dashboard

    Shipped Wire daily-summary hero widget into RootPlan dashboard.

  • New

    DailySummaryWidget — IG-06 hero widget

    Shipped DailySummaryWidget — IG-06 hero widget.

  • Fix

    Allow non-UUID accountIds in contract validation

    Relaxes `accountId` Zod validation in TerraLedger's contract validation from strict UUID to non-empty string, so demo-session account IDs (`demo-session-<uuid>`) issued by `/api/demo/provision` are accepted.

  • New

    DailySummaryManager aggregator (IG-06 P0)

    Shipped DailySummaryManager aggregator (IG-06 P0).

  • Fix

    Resolve remaining journey test failures

    Shipped Resolve remaining journey test failures.

  • Fix

    Mark national entry as offered/waiting across offer lifecycle

    Shipped Mark national entry as offered/waiting across offer lifecycle.

  • Fix

    Standardise contact field passing on offer acceptance

    Shipped Standardise contact field passing on offer acceptance.

  • Fix

    Simulate Birds workbook import cleanly

    Fixes the TerraLedger import profile and preview normalisation so the refreshed `BirdsAllotments.xlsx` workbook can run through simulate/dry-run import without missing primary-key failures.

  • Fix

    Remove spurious escaped-bracket locale dir causing Next.js page path mismatch

    Shipped Remove spurious escaped-bracket locale dir causing Next.js page path mismatch.

  • Fix

    Tenant welcome email — switch from non-existent /notifications to /send with correct template variables

    Fixes a silent bug where every new allotment tenant received no welcome email after accepting their plot offer. The code was calling \`POST /api/communications/notifications\` which does not exist in the communications service, so the call silently 404'd and the welcome email was never sent.

  • New

    AI-03 + AI-02 + RL-01 — plot availability, national waitlist bridge, revenue events

    Wires up three Connected Allotments gaps (AI-03, AI-02, RL-01) and delivers the full Shared Auth — Who/What/How universal permission architecture (P0 #4163), plus three security fixes identified in code review.

  • New

    Full TerraLedger Community councils → free Allotments.info pin renewal

    Shipped Full TerraLedger Community councils → free Allotments.info pin renewal.

  • New

    Wire play mode, flyover cinema, and 2D spacing rings

    Shipped Wire play mode, flyover cinema, and 2D spacing rings.

  • Fix

    Correct RootPlan mode descriptions — walkthrough is not first-person

    Shipped Correct RootPlan mode descriptions — walkthrough is not first-person.

  • New

    ContractTemplateManager + CRUD routes + ContractTemplateBuilderWidget (#4166c)

    Adds the contract (tenancy agreement) template entity to TerraLedger — the final part of the #4166 billing/tenancy-setup cluster. A council customises a template once, then TerraLedger merges plot/tenant/billing data into the active template to generate agreements (for signature via RootSign).

  • New

    Bind ChargeCatalogueManager routes + ChargeCatalogueWidget (#4166b)

    Shipped Bind ChargeCatalogueManager routes + ChargeCatalogueWidget (#4166b).

  • New

    Bind ClientBillingConfig route + BillingCycleSetupWidget (#4166a)

    Shipped Bind ClientBillingConfig route + BillingCycleSetupWidget (#4166a).

  • New

    Add example dataset download CTA (#4167)

    Shipped Add example dataset download CTA (#4167).

  • Fix

    Add snake_case fallbacks in entity managers for Vault-imported data

    Shipped Add snake_case fallbacks in entity managers for Vault-imported data.

  • New

    Provision per-council account on claim approval

    Shipped Provision per-council account on claim approval.

  • New

    Add /tenant-accounts and /recipes/attach route bindings

    Shipped Add /tenant-accounts and /recipes/attach route bindings.

  • Fix

    Replace fake-email fallbacks with hard errors (#4168)

    Shipped Replace fake-email fallbacks with hard errors (#4168).

  • Fix

    Unblock import validation gates

    Unblocks the import validation gates after the Release-20260520 import-profile merge by tightening TypeScript types in the Vault and Collections import paths.

  • Fix

    Restore widget-manifest.json deleted in PR #4155

    Shipped Restore widget-manifest.json deleted in PR #4155.

  • Fix

    Restore pnpm-lock.yaml deleted in PR #4155 — preprod builds broken

    Shipped Restore pnpm-lock.yaml deleted in PR #4155 — preprod builds broken.

  • New

    Resolve imports through account mappings

    Routes TerraLedger/allotment imports through account-scoped `schema.mapping` profiles stored in Collections, so council-specific Excel shapes map into canonical collection entities without hardcoded dogfood profiles.

  • New

    Charge catalogue with 8 calculation methods + Type 2 SCD pricing

    Shipped Charge catalogue with 8 calculation methods + Type 2 SCD pricing.

  • New

    ImportProfileRegistry — shared import engine with TerraLedger + QueueSpark profiles

    Shipped ImportProfileRegistry — shared import engine with TerraLedger + QueueSpark profiles.

  • New

    Add configurable import profile choices

    Shipped Add configurable import profile choices.

  • Fix

    Enforce sandbox import simulation

    Enforces TerraLedger/Vault sandbox import semantics so `simulate` validates real workbook data without persisting preview payloads, Collections rows, audit rows, or email side effects.

  • New

    Seeker → allotment full flow wiring (#4135)

    Shipped Seeker → allotment full flow wiring (#4135).

  • New

    Migrate allotments.info pin credit from Stripe coupon to customer balance

    Fixes a quiet revenue bug in the allotments.info pin → TerraLedger upgrade credit flow. The existing `duration: 'once'` coupon was consumed by the **first invoice only**, silently discarding unused credit. A 50-plot council with a £99 pin credit and £10/month TerraLedger usage lost £89 — 9 months of free service they were sold. Replaces Stripe coupons with Stripe customer balance transactions that auto-deplete across subsequent invoices until the credit is exhausted.

  • New

    Wire Metric 6 snowflake-metric calculation (#4135)

    Replaces the hard-coded `null` for `rootplan_allotment_seeker_waitlist_rate` in `plotspark-intelligence`'s rootlytics route with a real cohort + funnel SQL calculation. IMPOSSIBLE_GOALS Metric 6 — the snowflake metric — is now measurable instead of permanently null.

  • Fix

    ROICalculatorWidget inverted error guard — calculator now renders

    Shipped ROICalculatorWidget inverted error guard — calculator now renders.

  • New

    Wire TerraLedger plot metering — nightly snapshot + meter seed (#4109)

    Wires the missing per-plot metering for the TerraLedger Community plan — nightly snapshot worker plus Stripe meter seed — so councils are billed against the configured `usageRates.plots.bands` ladder instead of stalling at the £10/mo floor.

  • New

    Competitive-displacement claim-evidence gate + 4 wired (#4063)

    Adds Step 3 of #3956 — a sibling validator to the aha-schema gate that audits `competitiveAdvantage` claims and asserts each one can be backed by a test path, a docs citation, or a `TODO:#<issue>` placeholder. Wires 4 example recipes as proof and includes 7 vitest tests covering all rules.

  • Fix

    Bespoke P0 ahas for 7 launch-ready recipes, defer 8 scaffolds (#4055)

    Replaces the auto-promoted placeholder P0 ahas (from #4054) with bespoke trigger+moment content for 7 launch-ready recipes, and marks 8 addon/scaffold/internal-page recipes as `status: planned + deferred: true` so they no longer require a `linkedTestOrSpec`.

  • Fix

    Prepend MRR-on-default-dashboard P0 aha for reachability (#4077)

    Prepends a new P0 aha to rootlytics-recipe.json that ties directly to the gotchaMoment ('MRR ... all on one dashboard') and points at `intelligence/mrr-dashboard` — already on the default dashboard tab — so the reachability roll-up in release gate #3901 hits 4/4 instead of 3/4.

  • Fix

    Reword automatic cross-recipe context as on-demand

    Rewords PlotSupport copy so the cross-recipe customer context claim accurately describes the mechanism (on-demand Collections query when an agent opens a ticket) rather than implying an event-driven push that does not exist.

  • Fix

    Make marketing shell social icons dts-safe

    Shipped Make marketing shell social icons dts-safe.

  • Fix

    Enforce Communications allow-list in failed-job tile gate

    Closes the only allow-list-enforcement gap found in the post-merge audit. Failed-job tile gate now declares COMMUNICATIONS_ALLOW_LIST + assertRecipientInAllowList() and routes the synthetic payload recipient through the guard. Matches the sibling-gate pattern (#3934 claim queue, #3935 cert queue, all TerraLedger gates, etc.). Vitest 24/24.

  • Fix

    Demand-MI gate strict-null guard

    Closes the 1 new TS strict-null error surfaced by the PR #3922 code review. demand-mi-tile-proof-gate.ts:1002 needed an explicit guard before destructuring stats.topDemandAreas[0]. Runtime-safe, vitest still 20/20. Carries on the test-only nature of the gate file — no production code path changed.

  • Fix

    Widget install URL encoding, installed widget shape, booking customer ID schema, booking jobs startup

    Shipped Widget install URL encoding, installed widget shape, booking customer ID schema, booking jobs startup.

  • Fix

    Remove deleted service refs from start/build/test scripts

    Shipped Remove deleted service refs from start/build/test scripts.

  • Fix

    Wire columnMapping, auth header, and Excel format for CSV/Excel import

    Shipped Wire columnMapping, auth header, and Excel format for CSV/Excel import.

  • New

    Load add-on widget catalog from recipe addon JSON files

    Shipped Load add-on widget catalog from recipe addon JSON files.

  • New

    Add widget listing, categories, install and installed endpoints

    Shipped Add widget listing, categories, install and installed endpoints.

  • New

    Wire WidgetMarketplace into DashboardHeader

    Shipped Wire WidgetMarketplace into DashboardHeader.

  • New

    GCSE planner exam board cost reference and practical warnings

    Shipped GCSE planner exam board cost reference and practical warnings.

  • New

    Agent identity, capabilities discovery, and reference agent

    Shipped Agent identity, capabilities discovery, and reference agent.

  • Fix

    Correct RootScout widget endpoint paths in USP parity audit

    Shipped Correct RootScout widget endpoint paths in USP parity audit.

  • Fix

    Correct rootscrum approval route in USP API parity audit

    Shipped Correct rootscrum approval route in USP API parity audit.

  • New

    #3837 extend add-on catalog with content/marketing, analytics/gamification, project-management

    Shipped #3837 extend add-on catalog with content/marketing, analytics/gamification, project-management.

  • New

    #3835 high-MRR launch proof gate

    Shipped #3835 high-MRR launch proof gate.

  • New

    #3833 high-MRR launch proof gate

    Shipped #3833 high-MRR launch proof gate.

  • New

    High-MRR USP release gate — lead to outreach outcome proof contract

    Shipped High-MRR USP release gate — lead to outreach outcome proof contract.

  • New

    Widget Add-On Catalog — AI & Intelligence browse UI (#3827)

    Shipped Widget Add-On Catalog — AI & Intelligence browse UI (#3827).

  • New

    High-MRR USP release gate — entitlement activation proof contract

    Shipped High-MRR USP release gate — entitlement activation proof contract.

  • New

    High-MRR USP release gate — RTK session to field operation proof contract

    Shipped High-MRR USP release gate — RTK session to field operation proof contract.

  • New

    High-MRR USP release gate — waitlist-offer lifecycle proof contract

    Shipped High-MRR USP release gate — waitlist-offer lifecycle proof contract.

  • New

    Generic core/ProcessDiagramWidget with steps[] config (#3810)

    Shipped Generic core/ProcessDiagramWidget with steps[] config (#3810).

  • Fix

    Resolve 37 pre-existing TypeScript strict-mode errors (#3816)

    Shipped Resolve 37 pre-existing TypeScript strict-mode errors (#3816).

  • New

    Promote allotments-info, rootplan, rootlytics to implemented (#3805)

    Shipped Promote allotments-info, rootplan, rootlytics to implemented (#3805).

  • New

    Stripe-to-MRR USP launch proof gate (#3806)

    Shipped Stripe-to-MRR USP launch proof gate (#3806).

  • New

    Paid-season-plan USP launch proof gate (#3807)

    Shipped Paid-season-plan USP launch proof gate (#3807).

  • New

    Allocation process diagram widget for admin waitlist view (#3803)

    Shipped Allocation process diagram widget for admin waitlist view (#3803).

  • New

    Comms bounce/failure events trigger staff tasks (#3799)

    Shipped Comms bounce/failure events trigger staff tasks (#3799).

  • New

    Staff hold/skip/override routes with mandatory audit reasons (#3798)

    Shipped Staff hold/skip/override routes with mandatory audit reasons (#3798).

  • New

    Resource state machine, readiness gate, and offer lock (#3797)

    Shipped Resource state machine, readiness gate, and offer lock (#3797).

  • New

    DB foundation for policy-driven waitlist workflow (#3796)

    Lays the database and Collections schema foundation for the generic policy-driven waitlist offer workflow — the prerequisite that #3797 and #3798 depend on. Fixes #3796 Refs #3795

  • Fix

    Resolve extends inheritance in manifest generator

    Shipped Resolve extends inheritance in manifest generator.

  • New

    Wire AI coaching + smart recommendation widgets into top-tier recipes

    Shipped Wire AI coaching + smart recommendation widgets into top-tier recipes.

  • New

    Wire 6 remaining gamification widgets into rootplan recipe

    Shipped Wire 6 remaining gamification widgets into rootplan recipe.

  • New

    Wire backlog-items + time-tracking widgets into rootscrum recipe

    Shipped Wire backlog-items + time-tracking widgets into rootscrum recipe.

  • New

    Multi-window offer reminders, re-engagement template, live missions widget

    Shipped Multi-window offer reminders, re-engagement template, live missions widget.

  • New

    Anonymous public booking + branding config + customer timezone

    Adds anonymous public booking creation (no login required), per-workspace branding config, and customer timezone support to the public booking page. Fixes #3479

  • New

    Wire calendar sync into appointment lifecycle + availability

    Wires the existing `schedulingService.syncToExternalCalendar()` into appointment create/cancel/reschedule and makes AvailabilityManager respect external calendar blocks. Fixes #3480

  • New

    Deposit/full payment toggle + tiered refund policy + slot release

    Wires deposit-vs-full payment toggle, tiered cancellation refund policy, and slot release on checkout expiry into the booking payment flow. Fixes #3481

  • New

    Implement no-show fee charge + daily sweep cron

    Replaces the \`handleNoShow()\` console.log stub with a real implementation that charges no-show fees and adds a daily sweep cron for stale confirmed appointments. Fixes #3485

  • New

    Wire RoutingManager auto-assignment into AppointmentManager

    Connects the already-implemented \`RoutingManager\` to \`AppointmentManager.create()\` so bookings with no explicit staff member auto-assign via round-robin / least-busy / skill-weighted routing. Fixes #3484

  • New

    Wire rootplan plantings metering cron + snapshot job

    Wires the already-implemented `UsageTrackingService.snapshotRootPlanPlantingsForAllAccounts()` into the billing cron scheduler so RootPlan planting usage is tracked for metered billing. Fixes #3773

  • Fix

    Skip auth tests when E2E credentials not configured

    Shipped Skip auth tests when E2E credentials not configured.

  • Fix

    Revoke agent API keys via direct DB update

    Shipped Revoke agent API keys via direct DB update.

  • New

    Complete RootLytics recipe marketing content

    Shipped Complete recipe marketing content.

  • New

    Rebuild /verticals/restaurant as multi-recipe landing page

    Shipped Rebuild /verticals/restaurant as multi-recipe landing page.

  • New

    Auto-generate 58 proxy route files from service-registry.json

    Shipped Auto-generate 58 proxy route files from service-registry.json.

  • New

    AgentRateLimitWidget — per-agent rate limit dashboard

    Shipped Add AgentRateLimitWidget — per-agent rate limit dashboard.

  • Fix

    Widget UX states — AvailabilityCalendarWidget and AudienceCalloutWidget

    Shipped Add loading/error/empty UX states to AvailabilityCalendarWidget and AudienceCalloutWidget.

  • New

    MostLikedGardensWidget — monthly community garden rankings (#3220)

    Shipped MostLikedGardensWidget — monthly community garden rankings (#3220).

  • New

    Photo journal widgets + recipe wiring (#3742)

    Shipped Photo journal widgets + recipe wiring (#3742).

  • New

    PhotoVisibilityManager + public photo filter (#3740 #3741)

    Shipped PhotoVisibilityManager + public photo filter (#3740 #3741).

  • New

    P2 quick wins — marketing content + remove as-any (#3732 #3733 #3734)

    Shipped P2 quick wins — marketing content + remove as-any (#3732 #3733 #3734).

  • New

    Photo upload pipeline — schema, PhotoUploadManager, upload/delete routes (#3739)

    Shipped Photo upload pipeline — schema, PhotoUploadManager, upload/delete routes (#3739).

  • New

    Real upvote aggregation and monthly garden rankings (#3220)

    Shipped Real upvote aggregation and monthly garden rankings (#3220).

  • New

    Public garden timeline slider for vanity pages (#3218)

    Shipped Public garden timeline slider for vanity pages (#3218).

  • New

    Alias system, booking registry gaps, NotificationCenter + 59 smoke tests (#3691/#3692/#3695/#3697/#3701/#3703/#2372)

    Implements a widget alias system absorbing 22 missing recipe widget refs, fixes 3 booking registry gaps, adds the canonical `NotificationCenterWidget`, and delivers 59 widget smoke tests — resolving issues #3691, #3692, #3695, #3697, #3701, #3703, and #2372.

  • New

    Translate 9 settings pages to 7 languages (#1703)

    Wires all 9 previously un-translated settings pages with `next-intl` `useTranslations`, adding 224 translation keys across 9 namespaces in 7 locale files (en, fr, de, pl, es, it, pt).

  • New

    LA council CNIS register portal (#2861)

    Introduces the `homeschool-la` widget namespace with 3 widgets and a recipe for council officers managing the statutory Children Not in School (CNIS) register under the Children's Wellbeing and Schools Bill 2024–25.

  • New

    Recipe inheritance via `extends` — base templates for SaaS, land, school, booking (#2458)

    Implements recipe inheritance via an `extends` field so child recipes deep-merge from 4 new base templates (SaaS, land management, school, booking), reducing per-recipe size by ~36% and cutting new recipe scaffold time from 3 days to under 2 hours.

  • New

    Embed read-only garden canvas for allotments.info (#3219)

    Adds a read-only embeddable garden canvas at `/embed/rootplan/[gardenId]` for use on allotments.info featured garden pages, and adds an `embedMode` flag to `GardenShowcaseWidget` for chromeless inline embeds.

  • New

    Template versioning with update notifications

    Implements workspace template versioning so users see an update badge when an official template has a newer version and can merge new widgets with a single click.

  • New

    Formalise composite service boundary, closes G-007 (ADR-0045)

    Formalises the RootSchool composite service boundary (ADR-0045), closing audit gap G-007 from PR #3364 by creating a typed ownership manifest and a machine-readable `GET /api/rootschool/ownership` endpoint.

  • Fix

    Verify garden public status before serving public spatial layers (#3210)

    Shipped Verify garden public status before serving public spatial layers (#3210).

  • New

    Whole-garden tracking — permanent plants, fruit trees, containers (#3212)

    Shipped Whole-garden tracking — permanent plants, fruit trees, containers (#3212).

  • New

    Garden observations & journal CRUD (notes, photos, health, pest, harvest) (#3214)

    Shipped Garden observations & journal CRUD (notes, photos, health, pest, harvest) (#3214).

  • New

    Support polygon boundary at garden creation — GeoJSON, derived dimensions (#3208)

    Shipped Support polygon boundary at garden creation — GeoJSON, derived dimensions (#3208).

  • Fix

    Correct source value + require explicit consent for plot-history publishing (#3211)

    Shipped Correct source value + require explicit consent for plot-history publishing (#3211).

  • New

    PlotSpark Sites — site builder CRUD + public renderer (#1608)

    Shipped PlotSpark Sites — site builder CRUD + public renderer (#1608).

  • New

    Agent identity & scoped auth (#2833)

    Shipped Agent identity & scoped auth (#2833).

  • New

    Add subject grant model and audit events for permissions

    Extends the workspace permission grant model to be forward-compatible with group/role subjects, and adds structured audit logging for all permission lifecycle events. Fixes #3661

  • Fix

    RootCheck local gate, GitHub CI advisory, explicit loop flow

    Updates the \`p0-p1-issue-loop\` skill to clarify the exact end-to-end loop and make RootCheck (local, \`localhost:4242\`) the primary code-review gate — GitHub Actions CI is explicitly advisory since credits are unavailable. Refs #3668

  • New

    Add p0-p1-issue-loop autonomous issue resolution skill

    Adds the \`p0-p1-issue-loop\` Claude Code skill — a fully autonomous gated control loop for resolving P0/P1 GitHub issues into the active release branch without human intervention on each PR.

  • New

    Land P0/P1 launch agent batch

    Lands the integrated P0/P1 agent batch across TerraLedger, Allotments.info, RootPlan, RootLytics, AI billing, recipe manifest/build gates, and high-MRR recipe workflow gates.

  • Fix

    Fallback when Railway private DB DNS fails

    Shipped Fallback when Railway private DB DNS fails.

  • New

    Auto-seed platform agent profiles on startup

    Shipped Auto-seed platform agent profiles on startup.

  • New

    Recipe completeness, widget skeleton loaders, and API key tests

    Resolves four self-contained go-live items from the RootLytics epic (#3127): fixes a truncated aha moment media description in the recipe JSON, replaces spinner loading states with proper skeleton loaders in the two API key widgets, and adds an integration test suite + E2E smoke test for the RootLytics dashboard. Fixes #3127

  • Fix

    Wire CommunityForumWidget to real API, remove hardcoded mock posts

    Extends the issue #3527 mock-surface hardening branch beyond the original social widget fix. The branch keeps the live API wiring for `CommunityForumWidget`, updates the recipe mock-surface inventory to match the merged RootFarm gates, and expands the Playwright release gate so stale inventory claims or unlabelled RootFarm mock widgets cannot slip back into release surfaces.

  • Fix

    Verify P0 widget gate — 0 failures, add audit reports

    Closes the widget wiring gap audit for high-MRR recipes. All 39 P0 widgets across 10 recipes pass the gate (0 failures). Adds the missing workflow audit reports and refreshes the widget reference audit.

  • Fix

    Duplicate recipe tabs cleaned up

    Recipe dashboards now collapse repeated generated tabs so RootSchool, RootPlan, and other workspaces show one tab per recipe workspace instead of long duplicate lists.

  • Fix

    Light and dark mode made consistent

    The theme toggle is visible again and dashboard surfaces now use matching light or dark tokens instead of mixing a dark widget canvas with a pale page background.

  • Improvement

    Recipe and widget audit added

    Added a script and report that list every widget referenced by recipe files, flag missing bundles or service routes, and classify event notifications as dashboard events delivered through webhooks.

  • Improvement

    Allotments workflow test plan documented

    Audited Playwright coverage for every recipe and defined the critical allotments.info to QueueSpark to TerraLedger tests needed for waitlists, vanity pages, imports, offers, and webhooks.

  • Fix

    Duplicate recipe subscriptions blocked

    Billing now returns one row per recipe, the My Recipes widget defensively deduplicates dirty data, and Studio recipe attachment now reuses an existing active recipe instead of inserting another duplicate.

  • Fix

    Database cleanup for duplicate subscriptions

    Added migrations to deactivate duplicate Studio recipe attachments, cancel duplicate active billing subscriptions, and add unique indexes so the same active recipe cannot be subscribed twice.

  • Improvement

    Current release notes refreshed

    The changelog now reflects the May 11 development build instead of leading with older February billing work.

February 2026

  • New

    Stripe checkout wired for first revenue

    Checkout now resolves correct prices from Stripe via recipe-specific lookup keys. No more hardcoded fallback amounts — if a price isn't configured, checkout fails safely instead of charging the wrong amount.

  • New

    30-day free trials for B2B recipes

    TerraLedger, RootSchool, and PlotSpark ERP now offer a 30-day free trial. Your card is collected at checkout but not charged until the trial ends. After 30 days, Stripe automatically starts your subscription.

  • New

    Trial-ending email 3 days before you're charged

    You'll now receive an email 3 days before your free trial ends, telling you exactly what you'll be charged and how to cancel or manage your subscription.

  • Improvement

    Stripe price sync tool for recipe pricing

    New script reads all recipe pricing tiers and creates matching Stripe Products and Prices. Idempotent, supports dry-run mode, and uses lookup keys so checkout always charges the right amount.

  • Improvement

    Pricing increased to reflect value delivered

    Prices raised 50–100% across all recipes following our Chapter 9 pricing review. Low prices signalled instability to B2B buyers — the new prices position PlotSpark as a serious, sustainable platform.

  • Improvement

    B2B free tiers replaced with 30-day trials

    10 B2B recipes (TerraLedger variants, RootSchool, PlotSpark ERP) no longer have permanent free tiers. Councils and schools now get 30 days of full access, then convert to the Starter plan. Consumer recipes like RootBooker and RootScrum keep their free tiers.

  • Improvement

    6-month grandfathering policy — no permanent grandfather pricing

    When prices increase, existing customers keep their current rate for up to 6 months. After that, everyone moves to the new pricing. This keeps revenue sustainable and pricing fair.

  • New

    Invoice history and team management in Settings

    New pages at /settings/invoices and /settings/team. View and download past invoices, and manage team members with role-based access.

  • New

    Webhook delivery logs for developers

    The developer webhooks page now shows delivery history, retry counts, and response times. You can also send test events to debug your integration.

  • Fix

    Billing test suite fully green — 174 tests, 0 failures

    Fixed all pre-existing test failures in the billing service. Resolved ESM import issues with nanoid v5, TypeScript errors in SAP accounting integration, and a syntax error in widget definitions.

  • New

    Careers and Data Marketplace onboarding enabled

    The Careers and Data Marketplace recipes now have complete onboarding flows guiding new users through initial setup.

  • Improvement

    49 new tests across billing, RootTrade, and grandfathering

    Added 34 tests for the grandfathering policy, 15 tests for RootTrade backtesting strategies, plus 20 tests for trial checkout and email templates.

  • New

    110 new widgets fill every gap across the platform

    Added 110 missing widget components across 17 services. Every recipe now has all the widgets it references — no more placeholder gaps or blank panels.

  • New

    RootDate: AI-powered dating recipe launched

    New dating recipe with AI-powered compatibility matching, double opt-in consent, profiles, a discovery feed, and full platform integration including notifications, gamification, and analytics.

  • New

    RootShift: shift scheduling recipe launched

    Shift scheduling is now live — manage rotas, swap shifts, and track availability. Includes API endpoints, aha moments, and full recipe integration.

  • New

    ERP recipe with Odoo comparison

    New ERP recipe for small businesses with a marketing page, domain configuration, E2E tests, and a detailed buildability assessment comparing PlotSpark to Odoo.

  • New

    25 new interactive map widgets

    Added 25 layered map widgets across 5 recipes — interactive site maps, zone overlays, and spatial data views powered by the Layers service.

  • Improvement

    Featured recipes now in a carousel with category filters

    The homepage recipe showcase is now a swipeable carousel instead of a wall of cards. Filter by category to find the recipe for your industry, or swipe through all 10. Shows 3 at a time on desktop, 2 on tablet, 1 on mobile.

  • Improvement

    Recipe count shown for social proof

    The 'View All' button on the homepage now shows the total number of recipes available, so you can see the breadth of the platform at a glance.

  • Improvement

    Schedule a Call moved next to 'Ready to get started?'

    The booking form now appears right below the 'How it works' section so you can schedule a call without scrolling to the bottom of the page.

  • Fix

    Less whitespace on marketing pages

    Reduced the large gap at the top of marketing pages. The hero section is now more compact and banners no longer flash a spinner before disappearing.

  • Fix

    5,765 missing translations added across 7 languages

    Filled in 5,765 missing translation keys across English, German, French, Polish, Spanish, Welsh, and Italian. Fixed broken JSON in German, French, and Polish locale files that was causing crashes.

  • Fix

    All tests passing — zero failures

    Resolved all remaining test failures across dashboard, auth, internationalisation, widget, and service tests. The full test suite now passes cleanly.

  • Fix

    Type errors fixed across all services

    Resolved TypeScript errors in terraledger, social-service, billing, plotspark-intelligence, and other services. Clean compilation across the entire platform.

  • Fix

    Booking calendar fixed on iPad

    Fixed the calendar layout that was broken on iPad and improved the booking page layout for tablet-sized screens.

  • Fix

    Widget styles now render correctly everywhere

    Fixed an issue where some widget styles weren't loading. Tailwind now scans widget source files so all styles are included in the build.

  • Fix

    Removed placeholder G2 badge

    The 'Coming soon on G2' rating badge has been removed from the homepage until real reviews are collected.

  • Fix

    Redundant floating call-to-action removed

    Disabled the floating 'Schedule a Call' button that was duplicating the inline booking form further up the page.

  • New

    Automated refunds when you cancel

    Cancel a subscription and any eligible refund is now processed automatically through the rules engine. No manual intervention needed.

  • New

    Garden snapshots kept for 6 months

    RootPlan now saves snapshots of your garden layout every time you change it, and keeps them for 6 months. You'll get a heads-up before old snapshots expire.

  • New

    Full German and EU legal compliance

    Impressum, accessibility statement, DPA, cookie policy, privacy policy, and terms & conditions are now available in German. All legal pages include company registration details and comply with EU regulations including the European Accessibility Act.

  • Improvement

    14-day no-quibble refund guarantee

    Our terms now include a 14-day cooling-off period with a simple withdrawal form, plus the German VSBG dispute resolution declaration.

  • New

    Aha moment screenshots on marketing pages

    Recipe marketing pages now show screenshots and videos next to each aha moment, so you can see exactly what the product looks like before signing up.

  • Fix

    Stability and crash fixes

    Fixed a crash that could happen when environment variables were missing, resolved a layout issue with the skip-to-content link, and upgraded next-intl to v4 for better internationalisation support.

  • Improvement

    Faster, cleaner builds

    Eliminated Turbopack warnings, fixed 2,188 widget type errors, and resolved build issues across shared packages. The platform now builds cleanly with zero TypeScript errors.

  • New

    Plot transfers with configurable pricing

    Councils can now set transfer fees when tenants swap or hand over plots. Pricing is fully configurable per site.

  • Improvement

    Marketing content across all recipes

    Every recipe now has clear descriptions of the problems it solves, the value it delivers, and how it compares to alternatives. Helps you pick the right recipe for your needs.

  • New

    7-language support across all domains

    All 9 domains now support English, German, French, Polish, Spanish, Welsh, and Italian. Your language preference is remembered between visits.

  • Improvement

    Better SEO with hreflang tags

    All pages now include hreflang alternate links and a multilingual sitemap, so search engines show the right language version to the right audience.

  • Improvement

    47 TerraLedger widgets polished to production standard

    Every TerraLedger widget now has skeleton loading, error recovery, empty states with clear next steps, toast notifications, and full keyboard accessibility.

  • New

    Self-service vanity domain setup

    You can now set up your own branded domain (e.g., yourcouncil.allotments.info) from the Settings tab without contacting support.

  • Improvement

    Improved rent calculator

    The allotment rent calculator now includes UK regional comparisons, concession rate support, rod/perch units, and auto-calculates based on your site details.

  • New

    Per-recipe video embeds

    Each recipe can now have its own explainer video on its marketing page.

  • New

    Personalised email sender identity

    Emails now come from your account's name and address, not a generic PlotSpark mailbox.

  • New

    6 gotcha moment widgets for the marketing funnel

    Interactive calculators on marketing pages now link directly to the matching recipe, converting interest into signups.

  • New

    RootSchool, RootFaith, RootBooker, RootVibe, and RootSign complete

    Five more recipes are now production-ready with full dashboards, onboarding flows, E2E tests, and widget test suites.

  • New

    TerraLedger dashboard with 26 widgets across 4 tabs

    Council officers now have a full daily workflow dashboard: plots and tenants, finances, compliance, and communications all in one view.

  • Improvement

    RootPlan and RootLytics dashboards redesigned

    RootPlan now has 2 tabs with 5 tested widgets. RootLytics has SaaS metrics tabs with full widget and E2E tests.

  • Improvement

    15 widgets now have empty states with clear next steps

    When a widget has no data yet, it now tells you exactly what to do first instead of showing a blank screen.

  • New

    Guided onboarding for new users

    First-time users now see a step-by-step onboarding wizard that creates their first site, garden, or queue as they go. No more staring at an empty dashboard.

  • Improvement

    Full user funnel tracking

    We now track the complete journey from landing page through to paid conversion, helping us find and fix drop-off points.

  • Improvement

    Platform recipe config (for recipe builders)

    Recipes can now declare optional platform config (AI Team, retention tools, Smart Inbox) so one implementation serves many recipes with recipe-specific context. No change to end-user UI.

  • New

    Billing wired end-to-end with Stripe

    Subscription management, usage dashboards, and recipe checkout now work end-to-end. You can subscribe, see your usage, and upgrade plans from inside the app.

  • New

    Usage limits and upgrade prompts

    Free-tier users now see their usage against limits (e.g., collections, map layers) with a clear prompt to upgrade when they need more.

  • Improvement

    Health monitoring and status dashboard

    A production status dashboard with automated alerting so we know about issues before you do.

  • New

    2-year billing option and improved waitlist experience

    You can now choose a 2-year billing period with locked pricing. Waitlist signups get clearer confirmation emails, and we've expanded E2E tests for critical user journeys (tenant-to-plot, recipe subscription, checkout).

January 2026

  • Fix

    Security hardening: httpOnly cookies

    Authentication tokens moved from localStorage to httpOnly cookies, eliminating a class of XSS vulnerabilities. Your sessions are now more secure.

  • Improvement

    Structured logging across the platform

    Replaced all console.log/error/warn calls with structured pino logging. Better diagnostics when things go wrong, and no accidental data leaks in browser consoles.

  • Fix

    33 widget TODOs resolved

    Action handlers and API integrations that were stubbed out are now fully wired up across 33 widgets.

  • New

    Salon waitlist auto-fill on cancellation

    When a salon booking is cancelled, the next person on the waitlist is automatically offered the slot.

  • New

    Feedback & Roadmap SaaS recipe

    New recipe for collecting user feedback with voting, a public roadmap, and changelog. Dogfooding it ourselves.

  • New

    Bring Your Own Stripe Key (BYOK)

    You can now connect your own Stripe account for direct payouts instead of using PlotSpark's shared payment processing.

  • New

    Role-based access control

    Admins, officers, and members now see different features based on their role. Permissions are enforced across all recipes.

  • Improvement

    Accessibility improvements

    ARIA labels, keyboard navigation, and focus indicators added across all core widgets. Working towards WCAG AA compliance.

  • Improvement

    CDN widget delivery architecture

    Widgets are now pre-built and served from the CDN instead of being bundled with the main app. Build times dropped from 14+ minutes to under 4 minutes.

  • New

    TerraLedger launch preparation

    Platform-wide RBAC, comprehensive launch checklist, and all TerraLedger domains enabled for production deployment.

  • New

    Onboarding flows for 8 Root* recipes

    RootPlan, RootLytics, RootScout, RootScan, RootMarks, RootSchool, RootFaith, and RootBooker now guide you through setup when you first arrive.

  • New

    What we delivered: release notes are here

    You can now see what we ship on a dedicated changelog page and in email updates.

  • New

    You can set a daily limit on bookings

    Stops your calendar from being overbooked when you have a cap.

  • Improvement

    Build reliability and memory fixes

    Pre-compiled recipe manifest cuts memory usage by 99.1%. Removed ignoreBuildErrors — all TypeScript errors must be resolved before deploy.

  • Improvement

    Free tier adjusted to 10 plots

    The free plan for TerraLedger now includes up to 10 plots (previously 50), better reflecting the value of the platform.

  • New

    Core auth flows wired up

    Password reset, profile updates, and billing management now work end-to-end from the UI.

  • New

    All 40 recipes finalised for MVP

    Every recipe has complete pain points, value propositions, competitive advantages, bundle descriptions, dashboard layouts, and onboarding flows.