Smart Approve

Master MVP Spec — Reconciliation Evidence Pack

12 October 2026

No spec document lives in the repo; reconciled against the recorded MVP decisions and preferences (project context). Live code audited 2026-10-12. Stripe account audited the same date.

1. Delivered and verified in code

Delivered

Multi-tenant row-level security

Every customer entity carries a tenant_id with org-scoped read/write rules and an admin fallback. Evidence: base44/entities/Property.jsonc, Tenancy.jsonc, RentInvoice.jsonc (rls blocks).

Full data model

All planned entities present (Property, Tenant, Tenancy, Contract, Reference, Guarantor, RentInvoice, Payment, MaintenanceIssue, Jurisdiction) plus Branch, Team, Staff, Role, AuditEvent, CalendarEvent, Notification, ComplianceCertificate, ContractTemplate, PropertyExpense, HubspotSync, GuarantorAgreement.

Jurisdiction-aware core

Jurisdiction entity with UK/Europe/USA phases, linked from Organisation, Property, Tenancy and Contract.

Enterprise Settings control plane

Settings sections: OrganisationSettings, UsersRoles, RolesManager, BranchesManager, PermissionsMatrix, PlanBilling (src/pages/Settings.jsx).

RBAC server-side gating

checkPermission enforced across privileged functions: saveOrganisation, removeOrganisationMember, assignOrganisationRole, enforceEntitlement, logInvoiceToSheets, syncSubscriptionToHubspot (base44/shared/permissions.ts).

Entitlement enforcement

enforceEntitlement + confirmSubscription apply plan limits and activation server-side.

Stripe checkout replay guard

confirmSubscription records consumed_checkout_sessions on the Organisation entity, preventing replay of a paid session.

Contract signature centralised

signContract verifies auth + tenant email, then flips status to fully_signed server-side; no client-gated status change (base44/functions/signContract/entry.ts).

Gmail header sanitisation

sanitizeHeader strips CRLF and Unicode line separators; sendRentReminders routes through sendGmail (base44/shared/gmailSend.ts).

Sheets formula-injection guard

logInvoiceToSheets prefixes = + - @ with an apostrophe under USER_ENTERED (FORMULA_PREFIX).

Safe invoice numbers

generateMonthlyInvoices builds INV-YYYYMM-NNNN from fixed templates; no user-supplied invoice_number.

Finance page property-only

src/pages/Finance.jsx contains no MRR, subscription or HubSpot deal references.

HubSpot revenue sync

syncSubscriptionToHubspot + workflows/SubscriptionToHubspotDeal.jsonc.

Help and Settings relocated to header

Removed from the sidebar and added as header icon buttons in src/components/app/AppShell.jsx.

2. Resolved this session — GBP pricing mismatch

Delivered

Root cause

The marketing Pricing page displayed GBP 10 / 35 / 250 / 350 per month, but Stripe held GBP prices at 29 / 79 / 199 / 399, and the in-app PlanBilling panel showed a third set (10 / 29 / 249 / 499). createSubscriptionCheckout resolves the price by the active lookup key sa_<plan>_monthly, so the charged amount followed Stripe, not the display.

Fix applied

Created new active GBP recurring prices at the displayed amounts (GBP 10 / 35 / 250 / 350) with the canonical lookup keys, moved each product default_price to the new price, and archived the previous 29 / 79 / 199 / 399 prices. Aligned src/components/settings/PlanBilling.jsx PLANS to GBP 10 / 35 / 250 / 350. Checkout now charges the displayed GBP amounts.

Evidence

src/components/marketing/PlanComparisonTable.jsx (display), base44/functions/createSubscriptionCheckout/entry.ts (lookup-key resolution), src/components/settings/PlanBilling.jsx (PLANS aligned), Stripe price records sa_solo_monthly / sa_professional_monthly / sa_agency_monthly / sa_business_monthly (GBP, active).

3. Gaps still open

Open gap

Branch / team settings inheritance

Branch and Team entities have no inheritance field or logic; settings do not cascade from Organisation to Branch to Team.

Security Centre and LegalAcceptanceRecord

No entities, files or UI for a Security Centre or legal acceptance records exist in the codebase.

Solo advanced-features entitlement

Spec wants Solo to include AI assistants, integrations and the full finance/compliance suite; entitlement gating for Solo was not audited and should be verified so Solo is not wrongly restricted.

4. Implemented but not yet test-run

Not test-run

Subscription payment to HubSpot deal sync

syncSubscriptionToHubspot + workflows/SubscriptionToHubspotDeal.jsonc present; end-to-end flow not test-run.

Rent invoice to Google Sheets logging

logInvoiceToSheets present with the formula-injection guard; not test-run against a live sheet.

Security scan re-run

Remediations (replay guard, signContract, header sanitisation, RBAC gating) landed; the security scan has not been re-run to confirm clean.