Guidance for Claude Code working in this repository. Super Productivity is a todo and time-tracking app on Angular + Electron + Capacitor.
- Styling changes →
docs/styling-guide.md - User-facing functionality changes →
docs/documentation-guide.md - Sync, op-log, vector clocks →
docs/sync-and-op-log/ - Effects/reducers/bulk-dispatch touching synced state →
docs/sync-and-op-log/contributor-sync-model.md - E2E tests →
e2e/CLAUDE.md - Load-bearing decisions →
ARCHITECTURE-DECISIONS.md
ALWAYS run npm run checkFile <filepath> on every .ts or .scss file you modify before reporting work as done.
npm run checkFile <filepath> # prettier + lint a single file
npm run prettier # multi-file format
npm run lint # multi-file lint
npm test # all unit tests (Jasmine/Karma, .spec.ts co-located)
npm run test:file <filepath> # single spec
npm run e2e # all E2E (Playwright, slow)
npm run e2e:file <path> -- --retries=0 # single E2E (~20s/test); add --grep "name" for one test
npm start # Electron dev
ng serve # web dev (or npm run startFrontend)
npm run dist # production build (all platforms available locally)For SuperSync E2E (docker-compose) and the full E2E reference, see e2e/CLAUDE.md.
- Translations: UI strings go through
T/TranslateService. Edit onlyen.json; never other locales. - Privacy: no analytics or tracking — user data stays local unless explicitly synced.
- Electron: check
IS_ELECTRONbefore using Electron-specific APIs. - Templates: plain HTML, minimal CSS/classes, Angular Material sparingly. See
docs/styling-guide.md. - Strict TypeScript: no
any(useunknownif truly unknown). - State: never mutate NgRx state — return new objects in reducers. Prefer Signals to Observables.
- Tests: add unit tests for new services and state logic.
Touched on most state-related PRs. Read the linked source/doc for full reasoning before editing. Rules 1–3 and 6 are one invariant — one user intent = one op; replayed/remote ops must not re-trigger effects — fully explained in docs/sync-and-op-log/contributor-sync-model.md.
- Effects inject
LOCAL_ACTIONS, neverActions(ALL_ACTIONSonly for the op-log capture effect; remote archive side effects →ArchiveOperationHandler, notALL_ACTIONS). Lint-enforced (no-actions-in-effects). → contributor-sync-model.md,src/app/util/local-actions.token.ts. - Prefer action-based effects; a selector-based effect needs
skipDuringSyncWindow(). Lint-enforced (require-hydration-guard). → contributor-sync-model.md. - Multi-entity change = meta-reducer, not an effect fan-out (one reducer pass = one op). → contributor-sync-model.md,
src/app/root-store/meta/task-shared-meta-reducers/. - Logical clock: route "what day is this?" through
DateService(getLogicalTodayDate,isToday,todayStr). Pure reducers/selectors takestartOfNextDayDiffMsas an arg and callisTodayWithOffsetfor replay determinism. The rawDateService.startOfNextDayDiffisprivate; usegetStartOfNextDayDiffMs()at service boundaries. TODAY_TAG('TODAY') is virtual — never add totask.tagIds; membership comes fromtask.dueWithTimeortask.dueDay.TODAY_TAG.taskIdsonly stores ordering. →ARCHITECTURE-DECISIONS.mdDecision #2.- Bulk dispatch loop:
await new Promise(r => setTimeout(r, 0))after the loop (else 50+ rapid dispatches lose state). → contributor-sync-model.md,OperationApplierService.applyOperations(). SYNC_IMPORT/BACKUP_IMPORTreplace state and intentionally drop concurrent ops (CONCURRENT or LESS_THAN by vector clock) — by design, not a bug. →SyncImportFilterService.- Vector clocks:
MAX_VECTOR_CLOCK_SIZE = 20. Server prunes after conflict detection, before storage. →docs/sync-and-op-log/vector-clocks.md. - Logging:
Log.log({ id: task.id }), neverLog.log(task)orLog.log(title)— log history is exportable, never log user content.
Angular format type(scope): description. Types: feat, fix, docs, style, refactor, perf, test, build, ci, chore. Examples: feat(tasks): add recurring task support, fix(sync): handle network timeout. Never fix(test): or fix(e2e): — test changes use test:.
| Avoid | Do instead |
|---|---|
any type |
proper types, unknown if truly unknown |
| Direct DOM access | Angular bindings, viewChild() |
| Side effects in constructors | async pipe or toSignal |
| Subscribing without cleanup | takeUntilDestroyed() or async pipe |
NgModules for new code |
standalone components |
| Re-declaring Material theme styles | existing theme variables |