# Agent Contract β€” TimeSafari Logging (Unified, MANDATORY) **Author**: Matthew Raymer **Date**: 2025-08-19 **Status**: 🎯 **ACTIVE** - Mandatory logging standards ## Overview This document defines unified logging standards for the TimeSafari project, ensuring consistent, rest-parameter logging style using the project logger. No `console.*` methods are allowed in production code. ## Scope and Goals **Scope**: Applies to all diffs and generated code in this workspace unless explicitly exempted below. **Goal**: One consistent, rest-parameter logging style using the project logger; no `console.*` in production code. ## Non‑Negotiables (DO THIS) - You **MUST** use the project logger; **DO NOT** use any `console.*` methods. - Import exactly as: - `import { logger } from '@/utils/logger'` - If `@` alias is unavailable, compute the correct relative path (do not fail). - Call signatures use **rest parameters**: `logger.info(message, ...args)` - Prefer primitives/IDs and small objects in `...args`; **never build a throwaway object** just to "wrap context". - Production defaults: Web = `warn+`, Electron = `error`, Dev/Capacitor = `info+` (override via `VITE_LOG_LEVEL`). - **Database persistence**: `info|warn|error` are persisted; `debug` is not. Use `logger.toDb(msg, level?)` for DB-only. ## Available Logger API (Authoritative) - `logger.debug(message, ...args)` β€” verbose internals, timings, input/output shapes - `logger.log(message, ...args)` β€” synonym of `info` for general info - `logger.info(message, ...args)` β€” lifecycle, state changes, success paths - `logger.warn(message, ...args)` β€” recoverable issues, retries, degraded mode - `logger.error(message, ...args)` β€” failures, thrown exceptions, aborts - `logger.toDb(message, level?)` β€” DB-only entry (default level = `info`) - `logger.toConsoleAndDb(message, isError)` β€” console + DB (use sparingly) - `logger.withContext(componentName)` β€” returns a scoped logger ## Level Guidelines (Use These Heuristics) ### DEBUG Use for method entry/exit, computed values, filters, loops, retries, and external call payload sizes. ```typescript logger.debug('[HomeView] reloadFeedOnChange() called'); logger.debug('[HomeView] Current filter settings', settings.filterFeedByVisible, settings.filterFeedByNearby, settings.searchBoxes?.length ?? 0); logger.debug('[FeedFilters] Toggling nearby filter', this.isNearby, this.settingChanged, this.activeDid); ``` **Avoid**: Vague messages (`'Processing data'`). ### INFO Use for user-visible lifecycle and completed operations. ```typescript logger.info('[StartView] Component mounted', process.env.VITE_PLATFORM); logger.info('[StartView] User selected new seed generation'); logger.info('[SearchAreaView] Search box stored', searchBox.name, searchBox.bbox); logger.info('[ContactQRScanShowView] Contact registration OK', contact.did); ``` **Avoid**: Diagnostic details that belong in `debug`. ### WARN Use for recoverable issues, fallbacks, unexpected-but-handled conditions. ```typescript logger.warn('[ContactQRScanShowView] Invalid scan result – no value', resultType); logger.warn('[ContactQRScanShowView] Invalid QR format – no JWT in URL'); logger.warn('[ContactQRScanShowView] JWT missing "own" field'); ``` **Avoid**: Hard failures (those are `error`). ### ERROR Use for unrecoverable failures, data integrity issues, and thrown exceptions. ```typescript logger.error('[HomeView Settings] initializeIdentity() failed', err); logger.error('[StartView] Failed to load initialization data', error); logger.error('[ContactQRScanShowView] Error processing contact QR', error, rawValue); ``` **Avoid**: Expected user cancels (use `info`/`debug`). ## Context Hygiene (Consistent, Minimal, Helpful) - **Component context**: Prefer scoped logger. ```typescript const log = logger.withContext('UserService'); log.info('User created', userId); log.error('Failed to create user', error); ``` If not using `withContext`, prefix message with `[ComponentName]`. - **Emojis**: Optional and minimal for visual scanning. Recommended set: - Start/finish: πŸš€ / βœ… - Retry/loop: πŸ”„ - External call: πŸ“‘ - Data/metrics: πŸ“Š - Inspection: πŸ” - **Sensitive data**: Never log secrets (tokens, keys, passwords) or payloads >10KB. Prefer IDs over objects; redact/hash when needed. ## Migration β€” Auto‑Rewrites (Apply Every Time) - Exact transforms: - `console.debug(...)` β†’ `logger.debug(...)` - `console.log(...)` β†’ `logger.log(...)` (or `logger.info(...)` when clearly stateful) - `console.info(...)` β†’ `logger.info(...)` - `console.warn(...)` β†’ `logger.warn(...)` - `console.error(...)` β†’ `logger.error(...)` - Multi-arg handling: - First arg becomes `message` (stringify safely if non-string). - Remaining args map 1:1 to `...args`: `console.info(msg, a, b)` β†’ `logger.info(String(msg), a, b)` - Sole `Error`: - `console.error(err)` β†’ `logger.error(err.message, err)` - **Object-wrapping cleanup**: Replace `{{ userId, meta }}` wrappers with separate args: `logger.info('User signed in', userId, meta)` ## DB Logging Rules - `debug` **never** persists automatically. - `info|warn|error` persist automatically. - For DB-only events (no console), call `logger.toDb('Message', 'info'|'warn'|'error')`. ## Exceptions (Tightly Scoped) Allowed paths (still prefer logger): - `**/*.test.*`, `**/*.spec.*` - `scripts/dev/**`, `scripts/migrate/**` To intentionally keep `console.*`, add a pragma on the previous line: ```typescript // cursor:allow-console reason="short justification" console.log('temporary output'); ``` Without the pragma, rewrite to `logger.*`. ## CI & Diff Enforcement - Do not introduce `console.*` anywhere outside allowed, pragma'd spots. - If an import is missing, insert it and resolve alias/relative path correctly. - Enforce rest-parameter call shape in reviews; replace object-wrapped context. - Ensure environment log level rules remain intact (`VITE_LOG_LEVEL` respected). ## Quick Before/After ### **Before** ```typescript console.log('User signed in', user.id, meta); console.error('Failed to update profile', err); console.info('Filter toggled', this.hasVisibleDid); ``` ### **After** ```typescript import { logger } from '@/utils/logger'; logger.info('User signed in', user.id, meta); logger.error('Failed to update profile', err); logger.debug('[FeedFilters] Filter toggled', this.hasVisibleDid); ``` ## Checklist (for every PR) - [ ] No `console.*` (or properly pragma'd in the allowed locations) - [ ] Correct import path for `logger` - [ ] Rest-parameter call shape (`message, ...args`) - [ ] Right level chosen (debug/info/warn/error) - [ ] No secrets / oversized payloads / throwaway context objects - [ ] Component context provided (scoped logger or `[Component]` prefix) --- **Status**: Active and enforced **Priority**: Critical **Estimated Effort**: Ongoing reference **Dependencies**: TimeSafari logger utility **Stakeholders**: Development team, Code review team