- TypeScript 70.9%
- Kotlin 16%
- Swift 10.9%
- HTML 0.8%
- JavaScript 0.6%
- Other 0.7%
|
Some checks are pending
Android / Android Validation (push) Has started running
Release Artifacts / Build and push Docker release images (push) Blocked by required conditions
Release Artifacts / Validate release candidate (push) Has started running
Release Artifacts / Deploy to staging (push) Blocked by required conditions
Security, Type Check & Runtime / Dependency Audit (push) Waiting to run
Security, Type Check & Runtime / Type Check (push) Waiting to run
Security, Type Check & Runtime / Backend Runtime Smoke (push) Waiting to run
Security, Type Check & Runtime / Migration Guardrails (push) Waiting to run
Code Scanning / Gitleaks secret scan (push) Successful in 7s
Code Scanning / Semgrep OSS source scan (push) Successful in 36s
Reviewed-on: #332 |
||
|---|---|---|
| .github | ||
| apps | ||
| docs | ||
| packages/shared-types | ||
| scripts | ||
| tests | ||
| tools | ||
| .dockerignore | ||
| .gitignore | ||
| .gitleaks.toml | ||
| .node-version | ||
| .npmrc | ||
| .nvmrc | ||
| .osv-scanner.toml | ||
| AGENTS.md | ||
| backup.sh | ||
| bun.lock | ||
| CHANGELOG.md | ||
| CLAUDE.md | ||
| cliff.toml | ||
| deploy.sh | ||
| DESIGN.json | ||
| DESIGN.md | ||
| docker-compose.prod.yml | ||
| docker-compose.staging.yml | ||
| docker-compose.test.yml | ||
| docker-compose.yml | ||
| Dockerfile.backend | ||
| Dockerfile.frontend | ||
| LICENSE | ||
| memory-security.md | ||
| nginx.conf | ||
| nginx.staging.conf | ||
| package.json | ||
| playwright.config.ts | ||
| pnpm-lock.yaml | ||
| pnpm-workspace.yaml | ||
| PRODUCT.md | ||
| README.md | ||
| renovate.json | ||
| rollback.sh | ||
| tsconfig.json | ||
Shiftd — Constraint-Aware Staff Scheduling
A powerful staff scheduling platform that prioritizes coverage visibility, administrative control, and auditability. Built for departments managing complex shift patterns, staffing constraints, and role-based access.
Project Goals
- Clear visibility into staffing coverage across day, week, month, and coverage views
- Administrative scheduling workflows that surface constraints rather than hiding them
- Reduce manual reconciliation of over/understaffed shifts
- Support complex lifecycle workflows: self-signup windows, admin adjustment phases
- Role-based access control across staff, schedulers, admins, and department admins
- Audit trail for every scheduling decision
Tech Stack
| Layer | Stack |
|---|---|
| Frontend | React 19 · TypeScript · Vite · Tailwind CSS · shadcn/ui · React Query |
| Backend | Express · TypeScript · Drizzle ORM · pg-boss (job scheduling) |
| Database | PostgreSQL 16 |
| Mobile | Native iOS (SwiftUI, published) · native Android (Kotlin + Jetpack Compose, planned/in scaffold) |
| Resend · React Email templates | |
| Push | Provider-aware push delivery for native mobile clients, including direct APNs for iOS (gated behind PUSH_ENABLED) |
| Testing | Vitest (unit/integration) · Playwright (E2E) |
| Monorepo | pnpm workspaces |
Architecture
The system follows a strict UI-as-projection model: the frontend renders engine state but never decides what's allowed. All mutations flow through a backend assignment engine that validates permissions, lifecycle state, and constraints before execution. Every mutation is auditable.
Key architectural layers:
- Repository — Drizzle ORM data access with optimistic locking (version columns)
- Assignment Engine — constraint validation, conflict detection, lifecycle enforcement
- Controllers — HTTP boundary, actor-based authorization via
requireRole()middleware - Frontend — React Query for server state, context providers for UI state only
Shared runtime + domain types live in packages/shared-types/. Node-targeted runtime consumers must resolve the package's built exports, not raw src/*.ts files.
See docs/ARCHITECTURE.md for the full architectural doctrine.
Mobile Strategy
- iOS:
apps/iosis the published native SwiftUI app. - Android:
apps/androidis the native Kotlin + Jetpack Compose forward path and is currently planned/in scaffold. - Retired: the React Native / Expo app formerly under
apps/mobilehas been removed and is no longer a supported app target.
Key Features
Schedule Lifecycle
Eight-phase extended lifecycle: draft → open_self_signup → admin_adjustment → open_supplemental → open_call → locked → balanced → published. Transitions are engine-validated with rollback support.
Signup Windows
Offset-based window definitions with eligibility rules. Windows are computed at release time into absolute-timestamped instances. Supports manual and scheduled release (via pg-boss).
Assignment Engine & Conflict Detection
Four mutation types (assign, unassign, reassign, move) with conflict detection across: shift overlap, max hours, max consecutive days, capacity, and role mismatch. Each conflict carries error or warning severity.
Changeset / Staging
Bulk edits staged in-memory, previewed before commit. Commit re-validates against fresh DB state. Pure staging engine (structuredClone isolation).
Staff Recurring Patterns
Per-staff patterns (recurring schedules, optional days, fixed schedules) resolved against schedule date ranges. Bulk-apply to draft schedules with conflict reporting.
Broadcasts & Comms
Manager-initiated broadcast messaging with audience targeting (department, role, schedule, individual staff). Read receipts, async email delivery via pg-boss.
Email Notifications
Provider-agnostic notification system with Resend + React Email. Triggers: schedule release, window open, schedule published. Staff opt-in/out per notification type.
Push Notifications (Mobile)
Provider-aware push delivery gated behind PUSH_ENABLED. The native iOS app registers APNs device tokens through the shared backend contract, and native Android will use the same mobile push-token boundary when rebuilt. Legacy Expo token support may remain server-side only until existing token rows and transport code are retired. Triggers: schedule released, signup window opened, broadcast received, schedule change. Per-staff push preference toggles live in the shared notification-preferences system. No-op in development — logs what would be sent for wiring verification.
Requests & Approvals
Time-off requests, shift trades (two-phase: counterpart → admin), and shift giveaways with claim workflow. Admin approval queue, staff My Requests page (web + mobile), assignment engine side effects on approval.
Multi-Admin Awareness
Advisory presence system via SSE — see who else is editing a schedule in real-time. Non-blocking, informational only.
Definition Snapshot Model
Schedule creation freezes all definitions (roles, tiers, scenarios, shift configs) into an immutable snapshot. Runtime reads from snapshot, not live definitions.
Staff Tier Constraints
Define shift count limits per staff tier — weekly and per-period windows with soft/hard enforcement. Pre-screens available shifts to prevent over-assignment.
Coverage Analytics
Visual coverage heatmaps, fill-rate tracking, and unfilled shift reporting. Identify understaffed periods at a glance.
Local Development
Prerequisites
- Node.js 24 LTS (the supported runtime line for this monorepo)
- pnpm 10+
- Docker (for PostgreSQL)
Runtime Contract
packages/shared-typesmust export built artifacts fromdist/, never rawsrc/*.tsruntime entrypoints.apps/backendexecutes as direct Node ESM, so relative runtime imports must use explicit.jssuffixes.pnpm check:runtime-contractsenforces those rules, andpnpm smoke:backend-runtimebootsapps/backend/dist/server.jsand waits for/api/ready.
Run
docker compose up -d postgres
cd apps/backend && pnpm db:push && pnpm db:seed
cd apps/backend && pnpm dev # :3002
cd apps/frontend && pnpm dev # :5173 (proxies /api → :3002)
Testing
pnpm test:backend:db # full backend Vitest suite against shiftd_test
cd apps/backend && pnpm tsc --noEmit # backend TypeScript
cd apps/frontend && pnpm check-types && pnpm lint # TypeScript + ESLint
pnpm smoke:backend-runtime # direct Node 24 backend boot smoke (build + Postgres required)
pnpm test:e2e # Playwright (requires running frontend + backend)
pnpm test:e2e:ui # Playwright visual debugger
Environment Variables
| Variable | Required | Purpose |
|---|---|---|
DATABASE_URL |
Yes | PostgreSQL connection string |
JWT_SECRET |
Yes | Auth token signing (min 32 chars in prod) |
JWT_REFRESH_SECRET |
Yes (prod) | Refresh token signing |
RESEND_API_KEY |
No | Email delivery (logs to console without it) |
NOTIFICATION_FROM_EMAIL |
No | Sender address for notifications |
APP_BASE_URL |
No | Base URL for email links |
APP_BASE_HOST / VITE_APP_BASE_HOST |
No | Overrides the default base host (getshiftd.com) for generated tenant URLs |
PUSH_ENABLED |
No | Enable mobile push notifications (true/false) |
EXPO_ACCESS_TOKEN |
No | Legacy Expo Push API access token, only while server-side Expo token support remains enabled |
APNS_TEAM_ID / APNS_KEY_ID / APNS_BUNDLE_ID / APNS_PRIVATE_KEY |
No | Direct APNs transport for native iOS push delivery |
Authorization Model
Role hierarchy: staff < scheduler < admin < department_admin. Permissions augment role defaults (never remove). All API write operations require role-based authorization via requireRole() middleware.
| Role | Capabilities |
|---|---|
| staff | View schedules, self-signup during open windows, submit requests |
| scheduler | Assign/reassign staff, manage changesets, create broadcasts, apply patterns |
| admin | Override constraints, manage definitions, delete schedules, manage users |
| department_admin | Full department access including user management and bulk operations |
Project Structure
apps/frontend/ # React SPA (web)
apps/backend/ # Express API server
apps/ios/ # Published native SwiftUI iOS app
apps/android/ # Native Kotlin + Jetpack Compose Android scaffold
packages/shared-types/ # Built shared runtime + domain types
docs/ # Architecture doctrine, API spec, security audit
tests/e2e/ # Playwright test suites
Documentation
| Topic | File |
|---|---|
| Architecture doctrine | docs/ARCHITECTURE.md |
| Implementation rules | apps/backend/CLAUDE.md · apps/frontend/CLAUDE.md · apps/ios/README.md · apps/android/CLAUDE.md |
| API spec | docs/openapi.yaml |
| Security guidelines | docs/Security/SECURITY.md |
| Backend systems & patterns | docs/agent/backend-systems.md |
| Frontend pages & layouts | docs/agent/frontend-pages.md |
| Imported schedule onboarding | docs/imported-schedule-onboarding.md |
| Operational UX map | docs/operational-ux-phase.md |
| Priority 2 pilot readiness | docs/runbooks/pilot-priority-2.md |
Key Design Principles
No Automation Magic — The platform surfaces constraints and conflicts for human review. Scheduling decisions remain visible and auditable, not hidden behind opaque algorithms.
Engine-First Validation — All business rules live in the backend assignment engine, not the frontend. The UI can never bypass constraints.
Immutable History — Schedule definitions are frozen at creation time. Changes don't retroactively affect past schedules.
Multi-Tenancy Native — Two-level hierarchy (facilities → departments). Every entity is scoped to its department; users span across departments via platform roles.
Developer-Friendly — Clear separation of concerns, strong TypeScript types, comprehensive test coverage, and detailed architectural documentation.
Contributing
- Read
docs/ARCHITECTURE.mdfor design constraints and non-negotiable invariants - Check app-level
CLAUDE.mdfiles for gotchas and conventions - All mutations require end-to-end tests via Playwright
- 80%+ test coverage required for new code
License
Proprietary. See LICENSE file for details. For licensing inquiries: info@getshiftd.com