syncc — Universal Agent Brief
Tool-agnostic project guidelines for any coding agent (Codex, Copilot, Cursor, Claude, etc.). For Claude-specific instructions, see
CLAUDE.md. For app-specific details, seeapps/<app>/CONTEXT.md.
Project
syncc (always lowercase, two C's) — digitalization of public transportation for Bolivian municipalities. B2G SaaS targeting municipalities, transport syndicates, and government regulators.
Monorepo Structure
apps/
├── api-server/ NestJS 11 + Prisma 6 + PostgreSQL/PostGIS + Redis
├── web-admin/ Angular 20 admin dashboard (Tailwind, Leaflet)
├── mobile-driver/ Ionic 8 / Angular 20 driver app (Capacitor)
├── passengers-app/ Ionic 8 / Angular 20 passenger app (Capacitor)
├── syncc-landing/ Angular 20 public landing page (Tailwind, dark mode)
├── analytics/ Angular 20 internal analytics dashboard (tsparticles login)
└── qa-portal/ Angular 20 internal QA portal — test case management
Each app is self-contained with its own package.json, build config, and Dockerfile. No shared libs/ directory. Each app has a CONTEXT.md with routes, services, and app-specific instructions.
Internal Tools (analytics, qa-portal)
analytics and qa-portal are internal-only apps — never referenced from web-admin or the mobile apps:
- analytics: monitors platform-wide events, device telemetry, and silo health. Authenticated via the same JWT/roles system. Uses ngx-particles (tsparticles) on the login page.
- qa-portal: manages test cases, manual execution logs, and QA coverage. Roles: QA, ADMIN, SUPER_ADMIN, SUPER_ADMIN_LOCAL. QA users are created exclusively inside the portal (never from web-admin). Test case codes follow the format TC-{APP}-{NNN} (e.g. TC-DR-001).
Coding Conventions
Angular (all frontend apps)
- Angular 20, standalone components only — no
@NgModuleanywhere - Signals:
signal(),computed(),effect()— noBehaviorSubjectfor local state - Data fetching:
resource()API — no manual subscribe/unsubscribe - Route params:
input.required<string>()— noActivatedRoute.snapshot - Templates:
@if,@for,@switch— never*ngIf,*ngFor,ngSwitch - DI:
inject()function — no constructor parameter injection - File naming:
kebab-case.component.ts,kebab-case.service.ts, selector prefixapp-
NestJS (api-server)
- Prisma 6 for all DB access — no raw SQL except
$queryRawfor PostGIS - TypeScript only — no
.jsfiles insrc/ - Feature modules follow:
<feature>.module.ts,<feature>.controller.ts,<feature>.service.ts,dto/
Styling
- Tailwind CSS per-app (
tailwind.config.jsin each app) — no Sass/SCSS, no inlinestyle="" - Font: Inter via Google Fonts
<link>tag — never via npm
Branding (Canonical Colors)
Primary: #32737E (DEFAULT) | #E8F2F3 (light) | #245960 (dark)
Surface: #FFFFFF | Secondary: #000000
Semantic: emerald-500 (success), orange-400 (warning), red-600 (error), blue-500 (info)
Spacing: rounded-2xl cards, rounded-xl buttons/inputs, p-5/p-6 card padding, space-y-6 sections.
Architecture Rules
GIS
Line.polyline(String) = Google Encoded Polyline — visual geometry onlyCheckpoint.location= PostGISgeography(Point,4326)— proximity viaST_DWithinRoute.pointsdoes NOT exist — geometry lives onLine.polylineonly- OSRM called from the browser/device — never proxied through the backend
- Every polyline on a map MUST follow real road geometry — no straight lines through buildings
Auth
- JWT strategy returns
{ userId, email, role, tenantId }— driver services look up byuserId - OTP dev bypass (
OTP_MASTER_KEY=000000) fires before driver status guard ConfigModuleloads.env.${NODE_ENV}first, then.envas fallback
Data Integrity
Trip.actualCheckpoints(Json) is a historical log — never remove or reset- QR / check-in / payment UI is hidden (not deleted) — restore instructions in mobile-driver and passengers-app
CONTEXT.md
E2E Testing Strategy
Tool: Playwright
Core Principle: Tests MUST use the actual codebase algorithms and functions. The goal is to validate real implementation, not simplified mocks.
What to Test
- Line creation flow: polyline drawing → OSRM road snapping → encoding/decoding
- Route creation: checkpoint placement → snapping to polyline → distance calculations
- Driver GPS flow: location updates → proximity checks → checkpoint clearance
- Passenger flow: line search → route display → ETA calculations
- Real-time monitoring: GPS updates → map rendering → polyline decoding
Critical Rules
- Use actual algorithms — import and call
decodePolyline(),encodePolyline(),snapToPolyline(), distance calculation functions, ETA logic from the codebase - Use actual APIs — call OSRM (
router.project-osrm.org), backend endpoints (/lines,/routes,/location/update) - NO simplified mocks — do not draw straight lines manually, do not hardcode distances, do not bypass real logic
- If a test fails, fix the code — never adapt the test to make it pass unless the test itself is wrong
- Validate end-to-end — tests should exercise the full flow from user action → backend → database → response
Definition of Done
A feature/flow is NOT complete until its E2E test passes.
- After coding a new flow → create E2E test → run test → feature is done when test passes
- When modifying an algorithm, business logic, or flow → update corresponding E2E tests → verify tests still pass
- No feature ships without E2E test coverage
This ensures quality and prevents regressions. Tests are not optional—they are part of the implementation.
Example Anti-Patterns (DO NOT DO THIS)
// ❌ WRONG: Drawing straight lines instead of using OSRM
const straightLine = [[lat1, lng1], [lat2, lng2]];
// ✅ CORRECT: Use actual OSRM routing + polyline encoding
const route = await fetch(`router.project-osrm.org/route/v1/driving/${lng1},${lat1};${lng2},${lat2}?geometries=polyline`);
const polyline = encodePolyline(route.geometry.coordinates);
// ❌ WRONG: Hardcoding distance calculation
const distance = 1000; // meters
// ✅ CORRECT: Use actual distance calculation from codebase
const distance = calculateDistance(checkpoint1.location, checkpoint2.location);
// ❌ WRONG: Adapting test expectations to match wrong output
expect(eta).toBe(5); // changed from 10 to make test pass
// ✅ CORRECT: If ETA is wrong, fix the ETA calculation logic, not the test
Test Organization
apps/web-admin/e2e/tests/— admin flows (line/route creation)apps/mobile-driver/e2e/tests/— driver flows (GPS, trip management)apps/passengers-app/e2e/tests/— passenger flows (search, booking)- Shared helpers in
e2e/helpers/— but helpers MUST import actual codebase functions
Seeding Strategy
- Use
apps/api-server/prisma/seed-e2e.tsfor consistent test data - Seed real coordinates, real polylines (not straight lines)
- Seed complete entity graphs (Tenant → Line → Route → Checkpoints)
Docker & Build
- Two-stage Dockerfile:
node:22-alpinebuild →nginx:stable-alpineserve npm ci --legacy-peer-depsrequired (Leaflet/Angular peer dep conflicts)- Build output:
dist/<app>/browser/(Angular 17+ esbuild nested subfolder) - nginx: SPA fallback (
try_files), gzip,no-cacheonindex.html, 1y immutable on assets
docker compose --env-file apps/api-server/.env.development up --build
Security
- Never commit
.envfiles — only.env.examplein version control - Never log JWTs, OTP codes, or passwords
- Avoid
anytype without explicit justification - Never disable CSP or CORS globally
- All DB queries through Prisma — no string interpolation in
$queryRaw
Knowledge Base & Wiki
Location: GitHub Wiki on the syncc repository
The Wiki contains the project's Knowledge Base, including all algorithms, calculations, technical documentation, architectural decisions, and conventions.
When to Consult the Wiki
If you lack context or have doubts about an algorithm, calculation, or architectural pattern — consult the Wiki first.
You don't need to check the Wiki before every change, but use it when: - You need to understand how an existing algorithm works - You're unsure about a calculation formula or business logic - You need context on an architectural decision - You're implementing something similar to existing functionality
The Wiki is the single source of truth for domain knowledge that isn't obvious from the code itself.
Critical Rule: Keep Wiki in Sync
When you create or modify an algorithm, you MUST update the Wiki immediately.
Examples of algorithm changes that require Wiki updates: - Polyline encoding/decoding logic - Distance calculation formulas - ETA calculation algorithms - Proximity detection logic - Route optimization algorithms - Checkpoint snapping algorithms - GPS accuracy filtering - Fare calculation formulas
How to Update
- When implementing/modifying an algorithm, document it in the Wiki
- Include: purpose, inputs, outputs, formula/logic, edge cases, examples
- Keep Wiki page structure organized by domain (GIS, Routing, Pricing, etc.)
- Cross-reference Wiki pages from code comments where applicable
The Wiki is the single source of truth for algorithm documentation. Keeping it current ensures knowledge continuity and helps onboard new developers.
Quick Reference
| Need | Where to look |
|---|---|
| Backend schema, auth, modules | apps/api-server/CONTEXT.md |
| Admin routes, components | apps/web-admin/CONTEXT.md |
| Driver routes, GPS flow | apps/mobile-driver/CONTEXT.md |
| Passenger routes, services | apps/passengers-app/CONTEXT.md |
| Landing dark mode, assets | apps/syncc-landing/CONTEXT.md |
| Analytics routes, silos, events | apps/analytics/CONTEXT.md |
| QA portal routes, test case codes, execution API | apps/qa-portal/CONTEXT.md |
Last updated: 2026-07-07.