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.
