* feat(autopilot): default subscriber template (MUL-2533) — server Add per-autopilot member subscriber template that fans out to every issue the autopilot spawns. New autopilot_subscriber table; extend issue_subscriber.reason with 'autopilot' so the dispatch-time fanout is distinguishable from manual subscriptions. API: POST/PATCH /api/autopilots accept a `subscribers` array (member user_type only for the first version); PATCH semantics are full-replace. GET returns subscribers on the detail endpoint; the list endpoint omits them to avoid an N+1. Dispatch: dispatchCreateIssue lists the template inside the same tx as the issue insert and writes the rows with reason='autopilot' before EventIssueCreated fires, so notification listeners see the full subscriber set on the first event. Co-authored-by: multica-agent <github@multica.ai> * feat(autopilot): default subscriber template (MUL-2533) — frontend New SubscriberMultiSelect picker (members-only search + chips) wired into the create / edit AutopilotDialog. The detail page renders the saved template as read-only chips; edits flow through the dialog. TS types expose the new `subscribers` field on Autopilot, plus an AutopilotSubscriberInput shape for the create/update wire payloads. Co-authored-by: multica-agent <github@multica.ai> * fix(autopilot): notify template subscribers on issue creation (MUL-2533) The autopilot create-issue path fans out template subscribers into issue_subscriber inside the same tx as the issue insert, but the issue:created notification listener only matches handler.IssueResponse payloads and only direct-notifies the assignee + @mentions. The autopilot publishes a map[string]any payload, so the listener falls through and the template subscribers never receive an inbox item for the creation event — breaking OQ3 ("reason='autopilot' subscribers receive all subscription events, consistent with reason='manual'"). Fix it where the divergence lives: in dispatchCreateIssue, right after EventIssueCreated fires, write an inbox_item (type='issue_subscribed', severity='info') for each member subscriber and publish EventInboxNew so the recipient's inbox WS feed updates in real time. The write is after the tx commit so an inbox hiccup can't roll back the issue; failures are logged, not propagated. The manual path is unchanged — manual subscribers don't exist at creation time, so there is nothing to notify there. Adds a new InboxItemType 'issue_subscribed' (en/zh labels) and two covering tests in autopilot_subscriber_test.go: one asserts the inbox row lands for a template subscriber on dispatch, the other asserts the no-subscriber autopilot stays silent. Co-authored-by: multica-agent <github@multica.ai> * fix(autopilot): align subscriber PR with current main Co-authored-by: multica-agent <github@multica.ai> * fix autopilot subscriber template transaction Co-authored-by: multica-agent <github@multica.ai> --------- Co-authored-by: Lambda <lambda@multica.ai> Co-authored-by: multica-agent <github@multica.ai>
Multica Mobile (iOS)
Expo + React Native iOS client for Multica. Independent from web/desktop — shares only types from @multica/core/. See CLAUDE.md for the locked tech-stack baseline and import rules.
Just want to use it on your phone? (no development)
Multica isn't on the App Store yet — until that changes, anyone who wants it on their iPhone builds from source. One command:
pnpm ios:mobile:device:prod:release
This connects to the same backend as multica.ai, so your existing account just works.
Prerequisites: Mac with Xcode, a free Apple ID added under Xcode → Settings → Accounts, iPhone connected via USB with Developer Mode enabled. Walk through Expo's Set up your environment (pick Development build → iOS Device) if any of that is missing.
Xcode signs the build with the "Personal Team" your Apple ID automatically owns — created silently the first time you signed into Xcode, no setup needed. The first build downloads CocoaPods + compiles React Native from source — expect 10–20 minutes. Subsequent builds reuse Xcode's cache.
If Xcode rejects signing with "No matching provisioning profiles found" — rare, happens if someone has claimed the default bundle id ai.multica.mobile on Apple's developer portal. Pick any reverse-domain you own and re-run:
export EXPO_BUNDLE_IDENTIFIER_PROD=com.yourname.multica
pnpm ios:mobile:device:prod:release
7-day signing limit: a free Apple ID signs builds for 7 days. After that, plug back into the Mac and re-run the command to re-sign. An Apple Developer Program account ($99/yr) extends this to 1 year.
Everything below is for app developers — you can ignore the rest if you only wanted a personal install.
Scripts
| Command | What it does | Backend |
|---|---|---|
pnpm dev:mobile |
Metro only (reuse existing install) | local (.env.development.local) |
pnpm dev:mobile:staging |
Metro only (reuse existing install) | staging (.env.staging) |
pnpm dev:mobile:prod |
Metro only (reuse existing install) | production (.env.production) |
pnpm ios:mobile |
Full rebuild + install on iOS Simulator, Debug | local |
pnpm ios:mobile:staging |
Full rebuild + install on iOS Simulator, Debug | staging |
pnpm ios:mobile:prod |
Full rebuild + install on iOS Simulator, Debug | production |
pnpm ios:mobile:device |
Full rebuild + install on USB iPhone, Debug | local |
pnpm ios:mobile:device:staging |
Full rebuild + install on USB iPhone, Debug | staging |
pnpm ios:mobile:device:staging:release |
Full rebuild + install on USB iPhone, Release (standalone) | staging |
pnpm ios:mobile:device:prod |
Full rebuild + install on USB iPhone, Debug | production |
pnpm ios:mobile:device:prod:release |
Full rebuild + install on USB iPhone, Release (standalone) | production |
dev:* runs Metro only — assumes the matching variant is already installed. ios:mobile* does a full native rebuild + install.
Bundle id and display name switch on APP_ENV (see app.config.ts), so Dev / Staging / Production variants can coexist on the same device or simulator.
First-time setup
.env.staging is committed (public staging URL). .env.development.local is gitignored — copy the template once:
cp apps/mobile/.env.example apps/mobile/.env.development.local
# then edit EXPO_PUBLIC_API_URL inside it to your Mac's LAN IP, e.g. http://192.168.1.42:8080
If your Apple ID isn't on the Multica Apple Developer team yet, also uncomment and set EXPO_BUNDLE_IDENTIFIER_DEV to a reverse-domain you own (e.g. com.yourname.multica.dev). This only overrides the dev variant — staging / production bundle ids are intentionally not overridable so variants can coexist.
Build it onto your iPhone
Two paths, depending on what you want to do:
Day-to-day development (Mac in front of you)
pnpm ios:mobile:device:staging
Produces a Debug build with expo-dev-launcher embedded. Every launch the app probes Metro on your Mac and pulls fresh JS — perfect for hot-reload, painful when the Mac is asleep or you're on a different WiFi.
Standalone / "just use it" (walk away from the Mac)
pnpm ios:mobile:device:staging:release
Produces a Release build. No expo-dev-launcher, no Metro probe, no "Downloading…" screen. Splash → app, exactly like an App Store install. Trade-off: every JS change requires re-running this command.
Both paths share the same prerequisites: Mac with Xcode, free Apple ID added under Xcode → Settings → Accounts, iPhone connected via USB with Developer Mode enabled. Follow Expo's Set up your environment — pick Development build → iOS Device — if any of that is missing.
First build of either variant downloads CocoaPods + compiles React Native from source — expect 10-20 minutes. Subsequent builds reuse Xcode's DerivedData cache.
Try it in the iOS Simulator (no iPhone needed)
pnpm ios:mobile:staging
Boots the simulator, builds, installs the dev-client. Faster to iterate than a device build because no signing / provisioning step. Same dev:mobile:staging Metro flow afterward.
7-day signing limit (device only)
A free Apple ID signs builds for 7 days only, Debug and Release both. After that the app refuses to launch on the iPhone. Plug back into the Mac and re-run the corresponding ios:mobile:device* script to re-sign. Simulator builds are unaffected. The only workaround for the device limit is an Apple Developer Program account ($99/yr), which extends to 1 year.
Pointing at a different backend
Edit EXPO_PUBLIC_API_URL in .env.staging, .env.production, or .env.development.local (whichever variant you're running). Then:
- For an installed Debug build: restart Metro (
pnpm dev:mobile:staging) so the next JS bundle picks up the new value. - For an installed Release build: re-run the
ios:mobile:device:staging:releasecommand — the value is baked into the embedded bundle at build time.
For local backend testing, use your Mac's LAN IP (ipconfig getifaddr en0), not localhost.