The civfix consumer plane in one repo: the @civfix/shared contract, the @civfix/ui component set,
the public web app and the community mobile app. It is a pnpm workspace + Turbo monorepo.
The three former repos (civfix-shared, civfix-web, civfix-mobile) were merged into this one and
are retired. The other civfix repos (civfix-backend, civfix-admin, civfix-infra, the gov plane)
stay separate and consume @civfix/shared from the private registry.
| Path | Package | What it is |
|---|---|---|
packages/shared |
@civfix/shared |
The contract: Zod schemas + inferred types, the vendor-neutral interface seams + in-memory fakes, design tokens, the typed API client. Framework-free; only runtime dep is zod. Still published to the private registry for the out-of-repo consumers. README |
packages/ui |
@civfix/ui (private) |
The shared React Native UI (theme, primitives, shells, nav store, feature bodies, the Map seam) rendered on web via react-native-web. Never published. README |
apps/community-web |
community-web |
Next.js 15 static-export SPA (React 19 + RN-web + shadcn) plus its Cloudflare Pages config and Functions. README |
apps/community-mobile |
community-mobile |
React Native + Expo SDK 54 user app; custom dev client only, never Expo Go. README |
docs/superpowers |
— | Older design/plan documents, kept for history. |
pnpm 9.12.0 (pinned via packageManager), Node 22 (.nvmrc), Turbo 2. .npmrc sets
node-linker=hoisted (Metro and the config plugins need a flat node_modules),
prefer-workspace-packages / link-workspace-packages, and scopes @civfix to
https://repo.civfix.org/.
Root scripts are turbo-delegated across every workspace package:
pnpm install
pnpm build # @civfix/shared -> dist (tsup), @civfix/ui -> dist-types, web -> out/
pnpm typecheck
pnpm lint
pnpm test
pnpm i18n:check # @civfix/ui locale-key check
pnpm clean
pnpm doctor # expo-doctor on the mobile appPer package:
pnpm --filter @civfix/shared dev # tsup --watch on the contract
pnpm --filter community-web dev # next dev, http://localhost:3000
pnpm --filter community-mobile start # expo start --dev-client (Metro)Recommended for local testing:
NEXT_PUBLIC_API_URL=https://api.civfix.dev pnpm --filter community-web devpnpm installpnpm buildonce (orpnpm --filter @civfix/shared devto keep the contract'sdistwatched).- Web:
pnpm --filter community-web dev. - Mobile: build the custom dev client first
(
pnpm --filter community-mobile exec expo prebuild, thenpnpm --filter community-mobile exec expo run:ios/run:android), thenpnpm --filter community-mobile start.
Editing packages/shared or packages/ui reaches both apps directly — there is no link: override
to add and no version bump to adopt inside this repo.
The umbrella's local e2e environment (../dev/run.sh in the civfix checkout) still drives the
backend, Postgres/Redis, the media worker and the seeded data for end-to-end runs.
- Both apps depend on
@civfix/sharedand@civfix/uiasworkspace:*. @civfix/sharedis built: consumers resolve itsexportsfromdist/(gitignored), so it must be built before anything typechecks against it.turbo's^builddependency handles that forbuild/typecheck/lint/test/dev; the mobile app also has apostinstallthat runspnpm --filter @civfix/shared buildso EAS builders get adisttoo.@civfix/uiships untranspiled.tsxsource through its exports map (Metro and Next transpile it; the.web.tsx/.native.tsxseams must stay unbundled) and emits onlydist-types/fortsc. Turbo runs that build before any typecheck/lint/test, so there is no manual rebuild step.
main is staging. Production is a v* release. There is no dev branch — every feature branch PRs
into main, and a merge is assumed to be production-ready, but it lands on staging first and is promoted
to production only when a release is cut (issue
civfix/issue-tracker#108). PRs into main run
.github/workflows/ci.yml (three jobs: packages, web, mobile).
| Event | What happens |
|---|---|
push to main |
deploy-web.yml builds the web static export against api.civfix.dev and publishes it to the staging branch of the Cloudflare Pages project civfix-web (https://civfix.dev). publish-shared.yml publishes @civfix/shared if its version is ahead of the registry. |
published v* release |
deploy-web.yml rebuilds the same commit against api.civfix.org with Turnstile on, and publishes it to the Pages production branch (https://civfix.org). |
A static export inlines every NEXT_PUBLIC_* at build time, so production is a rebuild of the release
commit rather than a byte-copy of the staging artifact — unlike the backend, whose Docker images really
are promoted as-is. The workflow resolves those values once and then asserts they match the target, so a
build cannot ship the staging API URL to civfix.org or the production one to the public preview.
The mobile app deploys through .github/workflows/deploy-mobile.yml on the same lane: a push to
main that touches the app or the packages builds the testflight profile (staging API) on a
GitHub-hosted Mac and uploads it to App Store Connect, where TestFlight hands it to the internal
testers; a manual run with profile=production uploads a build that bakes no API URL at all and
picks one at runtime from its iOS install source (api.civfix.dev while it is handed out through
TestFlight, api.civfix.org once it is downloaded from the App Store), and attaching that build to
a version and submitting it for review stays a human step in App Store Connect. The runner executes
the same scripts/store-build.sh a developer runs locally (eas build --local, then a direct
fastlane pilot upload to App Store Connect — no EAS Submit queue), so CI and laptop builds share
one EAS signing-credential store and one build-number counter. See that app's README for the
prerequisites (an EXPO_TOKEN secret, the App Store Connect API key secrets, EAS credentials, the
remote build number).
Android still ships only by hand.
@civfix/ui and both apps are private and are never published. @civfix/shared still is, because
civfix-backend (services/api + services/media-worker), civfix-admin and the gov plane install
it from repo.civfix.org.
Record a changeset while developing (pnpm changeset), apply it before merging
(pnpm changeset version), and merging to main publishes it. Full runbook, including how each
external consumer adopts the new version: RELEASING.md.
civfix-app is free software, licensed under the
GNU Affero General Public License, version 3 only. The apps built
from this repository are additionally conveyed under the
App Store and Play distribution exception, because the
store binaries bundle packages/shared and packages/ui. Every file is
covered by the declaration in REUSE.toml; there are no per-file
license headers. Contributions are accepted under the
Contributor License Agreement — see CONTRIBUTING.md.
civfix is a project of Reach Out Los Angeles Inc.; the civfix name and logos are its
trademarks and are not covered by the license.