Multi-user verification¶
Rerun this after you change auth, membership, account balance or cabal funding, governance, the member board, or Home P&L. It pairs the automated backend suite with a two-account pass on the gold simulator. The style follows the demo checklist.
Never simctl erase. Never hardcode another laptop's simulator UDID.
Automated coverage (run first)¶
just test backend
just test mobile
just build mobile
The backend suite covers the same rules as the manual pass:
- Each member's P&L comes only from their own funding. When only B funds and the pot gains, A's slice, People row, net worth and leaderboard row stay at
+0.00with a nil percent. B's rows carry the whole gain. - A's People board shows B's P&L only from the cabal they share. B's own row adds up all of B's cabals.
- While B's funding is still pending, none of B's USDC is credited to A. After confirmation the credit goes to B.
- A non-member gets nothing from any cabal-scoped read or write, even with enough account balance to fund. A refused vote records no ballot, and a refused fund leaves no pending funding.
- A member outside a named voter set cannot vote or propose, and a non-admin cannot list join requests.
In the rewrite, each rule is an acceptance test for its flow (Flows).
Prerequisites (manual pass)¶
dotenvx run -f .env.local -- just run backend # backend on 127.0.0.1:8080
curl -s http://127.0.0.1:8080/health
just build mobile
Simulator (gold, SimSlim):
- Find your own gold UDID with
./scripts/gold-sim-udid.sh. It printsSIMSLIM_UDIDor exits 1. UDIDs are per machine, so export yours in your shell rc or in plain.env. - Slim the simulator with SimSlim without erasing it:
bash scripts/prepare-simulator.sh "$(./scripts/gold-sim-udid.sh)". SetSIMSLIM_BIN=<path>/simslimif the binary is not onPATHor in.tools/bin. The script runssimslim on --no-reboot, thenverifyanddoctor, withscripts/simslim-profile.json. On iOS 18.0 the overrides last only for the current boot, so run it again after each boot. If your checkout does not have the script yet, runsimslim on <udid> --no-reboot --profile <profile>.json, thensimslim verify <udid> --profile <profile>.jsonandsimslim doctor <udid>(see the README SimSlim section). - Agents drive taps with
.cursor/skills/ios-simslim-fast-qa/SKILL.md(MobAI claim → bridge → DSL) or XcodeBuildMCP with--simulator-id "$(./scripts/gold-sim-udid.sh)". Do not drive taps with AppleScript, CGEvent or coordinates.
Accounts:
| Role | Login | Notes |
|---|---|---|
| User A (creator) | test-8081@privy.io, email OTP 465354 |
M1 email OTP path, the same account as the M5 demo script |
| User B (joiner) | A second Privy login: the Privy dashboard test phone number, or a teammate's real email/SMS | Must be a different Privy user, which gives a different users.privy_user_id and member wallet, so a separate account balance |
| User C (outsider, optional) | Any third Privy login | Only needed for step 11 |
To switch accounts on one simulator, use Settings → Sign out, then sign in again. just stop mobile also uninstalls com.monaco.app to clear the Privy session. For side-by-side sessions, clone the slimmed gold simulator once (see the skill) and sign in as B on the clone. MobAI Free drives one device at a time.
Checklist¶
- Sign in as A. Use the email OTP. The session gate loads Home. Check the network log for
POST /v1/auth/session→ 200 with A'suserId. - Create a cabal as A. Go to Cabals → Create cabal and keep the defaults (open join, all members vote, majority). Copy the cabal id from the cabal screen.
- Fund A. Go to Home → Add money and send USDC on Solana to A's deposit address. The Account balance card updates. Open the cabal → Fund this cabal, then enter an amount up to the available balance. Wait for the transfer to confirm: the pending "funding a cabal" line clears. A's "You" slice shows the funded amount with
+0.00. - Switch to B. Go to Settings → Sign out, then sign in as B. Auth isolation check: before any pull-to-refresh, Home, Cabals and Profile must not show A's net worth, account balance, cabals or name. B starts with no cabals, a
0.00net worth and B's own (empty) account balance. The app loads Home and the account balance with B's token and gets B's data only. - Join by id as B. Go to Cabals → Join cabal, paste the id, and join. The cabal opens, and the member board lists A and B. B shows
+0.00and no percent. B can open the cabal treasury. - Check isolation with only A funded. On B's device, B's "You" slice is
0.00and B's Home net worth is0.00. B's row on the Home leaderboard shows+0.00with no percent. A's row shows A's own P&L. - Fund B. Go to Add money and send USDC to B's own deposit address (it differs from A's). After B's balance updates, open the cabal → Fund this cabal. Wait until the funding clears. The member board shows both members with their own dollar P&L, as two rows with distinct users.
- Propose as A. Sign out, then sign in as A. In the cabal, go to Propose → search
AAPL→ get a quote → propose. - Vote as B. Sign out, then sign in as B. The proposal appears under Home → Needs your vote and in the cabal's Proposals list. Vote yes and confirm the toast. The proposal detail lists B's ballot, the Vote button disappears, and the proposal leaves Needs your vote. A second vote does not add a ballot.
- Refresh boards. After the proposal passes and executes, pull to refresh on both accounts. The cabal pot shows the AAPL row. Each account's "You" slice matches its own share. Home "Your cabals", net worth and the Leaderboard (ALL) update. A's and B's dollar P&L split by share, and neither account's row shows the other's funding.
- Outsider (optional, C). Signed in as C with some account balance, the cabal is not in C's cabals and A and B do not appear on C's Home leaderboard. Pasting the cabal id into Join cabal offers to join. Without joining, C must not see the pot, the member board or the proposals, and cannot fund the cabal.
- Leave with withdraw (optional). As B, go to Leave cabal → Withdraw and leave. A partial Withdraw without leaving keeps B in the cabal. B's stake returns to B's account balance only. A's slice and account balance do not change.
Expected isolation¶
| Screen or action | Who | Expect |
|---|---|---|
| Account balance | the caller | The caller's own account balance and pending funding, never another user's |
| Home | any member | The cabal list shows every cabal, joined or not, per viewer. People come only from the viewer's cabals, one row per user with their own dollar P&L |
| Home net worth, Your cabals, Needs your vote, Leaderboard (ALL) | any user | Net worth, cabals and missed proposals belong to the viewer. The leaderboard covers the members of the viewer's cabals |
| Cabal screen: pot, member board, treasury | members only | The member board lists every member. "You" is the caller's slice |
| Proposals list | members only | Open and past proposals with their proposer |
| Fund this cabal, asset search, quotes | members only | Any joined member, not only the creator. Funding is capped at the caller's available balance |
| Propose | eligible proposers only | The proposal is created |
| Vote | eligible voters only | The ballot shows on the proposal detail |
Done when¶
Every checklist step passes on the gold simulator with two distinct Privy accounts, and just test backend is green. Record failures with a screenshot, the MobAI ui_tree, and the request/response of the failing call.