Address the review findings that blocked collaboration and hardened tenancy:
- Ownership validation and the view/budget/real-estate policies scope by the
active space instead of user_id, so a member can open detail pages and create
or edit data in a shared space (previously 422/403).
- Authorize purely on live space membership, dropping the current_space_id
shortcut, and reset a removed member's pointer to their personal space so no
stale-pointer write window remains.
- Apply automation rules per space, so a multi-space owner's personal-space
rules never categorise a business-space transaction.
- Flush the offline cursor before the rows on space switch to avoid a stale
re-sync. Adds member-access tests.
Add the collaboration layer for Business spaces: owners invite people by email
(seat-capped per subscription), invitees accept via a tokened link to join as
members, and owners can remove members while members can leave. Paid-feature
gates (subscription middleware, open-banking) now resolve against the active
space's owner, so a member of a Business space gets its features without a plan
of their own. Members management and invitations live on the spaces settings
page.
Flip every user-facing read (dashboard, cashflow, transactions, budgets,
accounts, settings, analytics, sync, shared props) from user-scoped to
active-space-scoped via explicit $space->… relations, and re-key the category
tree, spending, and budget-assignment services by space_id. Authorization moves
from creator-ownership to space membership.
Add the visible spaces layer behind a Pennant 'spaces' flag: a sidebar space
switcher, a settings page to create/rename/delete/switch spaces, per-space
offline-cache flushing, and the currentSpace/spaces shared props. Data creation
is unchanged — the BelongsToSpace trait already stamps space_id from the active
space. Isolation tests prove no cross-space leakage.