| name | coding-style |
| description | Design decisions, implicit rules, and anti-patterns for the Moca project. Covers coding conventions, project structure rationale, and testing guidelines. |
Coding Style — Design Decisions & Implicit Rules
This document captures decisions that cannot be inferred from code or config files alone. For formatting rules, see .prettierrc and eslint.config.mjs. For project structure, explore the packages/ directory.
Implicit Rules
Language Policy
All code, comments, commit messages, and documentation MUST be written in English — even when the user communicates in Japanese.
- WHY (agent/backend): ESLint
no-restricted-syntax detects Japanese characters in string literals. Backend code is never user-facing; Japanese would block future internationalization.
- WHY (frontend): User-visible strings go through
react-i18next (t() function). The i18next/no-literal-string ESLint rule catches hardcoded JSX text. Raw Japanese in components bypasses the translation pipeline.
Do NOT Run npm run dev Unless Told To
Developers typically already have npm run dev running with hot-reload. Starting a second instance causes port conflicts. Only run it when the user explicitly asks.
Comments Explain WHY, Not WHAT
The codebase uses // WHY: prefixed comments for non-obvious decisions (see useMessageEventsSubscription.ts, chatStore.ts). Write comments only when the reason behind the code is not self-evident. Never comment what the code literally does.
Design Decisions
ID Generation: nanoid vs uuid
| Context | Library | Reason |
|---|
| New IDs (sessions, triggers, frontend) | nanoid / customAlphabet | Shorter, URL-safe, no hyphens |
Cognito sub (external) | UUID | Comes from AWS, cannot change format |
agents-service.ts | uuid (v4) | Historical — predates nanoid adoption. Do not migrate without reason. |
sessionId specifically uses customAlphabet('ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789', 33) because AgentCore requires [a-zA-Z0-9][a-zA-Z0-9-_]*. Hyphens and underscores are excluded to avoid edge-case issues with the first-character constraint.
CONVERSATION_WINDOW_SIZE Must Be Even
The SlidingWindowConversationManager truncates from the oldest messages. An odd window size can break the required user→assistant→user→assistant alternation pattern, causing Bedrock API errors. The Zod schema enforces val % 2 === 0.
Agent Package: ESM with .js Extensions
packages/agent runs as ESM. TypeScript compiles .ts → .js, but Node.js ESM resolution requires explicit .js extensions in import paths. ESLint import/extensions rule enforces this. Other packages (backend, frontend) do not have this constraint.
Zustand: One Store Per Domain
The frontend has ~12 Zustand stores in stores/ (auth, agent, session, chat, ui, etc.). Each store owns one domain.
- WHY: Zustand re-renders subscribers when any state in the store changes. Mixing domains (e.g., auth + chat) would cause unnecessary re-renders across unrelated components.
- Pattern: Use selectors —
useAgentStore((state) => state.agents) — to subscribe to only the needed slice.
Frontend i18n: No Hardcoded Strings in JSX
eslint-plugin-i18next with no-literal-string (jsx-text-only mode) catches raw text in JSX and key attributes (title, aria-label, alt, placeholder). All user-visible text must use t('key'). Agent names/descriptions use translateIfKey() utility for dynamic content that may or may not have translation keys.
Testing Guidelines
Test Behavior, Not Implementation
Tests should verify observable outcomes, not internal method calls. Use the AAA pattern (Arrange-Act-Assert). See existing tests in __tests__/ directories for conventions.
Coverage Priority
Focus testing effort in this order:
- High: Business logic, data transformations, error handling
- Medium: Utilities, validators
- Low: Simple getters, configuration wiring
Test Files Location
- Unit tests: co-located in
__tests__/ or *.test.ts next to source
- Integration tests:
tests/ directory with *.integration.test.ts suffix
Anti-patterns
- Do NOT duplicate ESLint/Prettier rules in code reviews — they are enforced automatically via
eslint.config.mjs and .prettierrc
- Do NOT use
any — use unknown or proper types. ESLint warns on @typescript-eslint/no-explicit-any
- Do NOT create new uuid usage — use
nanoid for new IDs. Existing uuid usage in agents-service.ts is grandfathered
- Do NOT hardcode Japanese in agent/backend — ESLint detects it. Use English only
- Do NOT add
npm run dev to automated scripts — it is a long-running process meant for interactive use