Multica Docs

Conventions

Single source of truth for code naming and the i18n translation glossary.

This page is the single source of truth for code naming and the i18n translation glossary. Anything that used to live in packages/views/locales/glossary.md or in scattered comments now lives here.

If you write Multica code or change a translation, this is the page to reference.


1. Code naming

Routes

Pre-workspace routes (the routes that exist before the user is in a workspace) MUST use either a single word or the /{noun}/{verb} pattern.

  • /login, /inbox, /workspaces/new
  • /new-workspace, /create-team, /accept-invite

Hyphenated word groups at the root collide with user-chosen workspace slugs and force endless reserved-slug audits. Reserving the noun (workspaces) automatically protects the entire /workspaces/* subtree.

Workspace-scoped routes

Always live under /{slug}/{section}/{slug}/issues, /{slug}/agents, /{slug}/settings. Never duplicate workspace routing logic; use useNavigation().push() from shared code, never framework-specific link APIs.

Packages and modules

The monorepo enforces strict package boundaries:

PackageMay depend onMust NOT depend on
packages/corenothing app-specificreact-dom, localStorage, process.env, next/*, UI libraries
packages/uinothing@multica/core, business logic
packages/viewscore/, ui/next/*, react-router-dom, stores
apps/web/platform/next/*other apps
apps/desktop/.../platform/react-router-dom, electronother apps

If logic appears in both apps, it MUST be extracted to a shared package. There are no exceptions for "small" duplication.

Files and components

  • Files: kebab-case.tsx / kebab-case.ts (e.g. agent-row-actions.tsx)
  • Components: PascalCase (e.g. AgentRowActions)
  • Hooks: useCamelCase (e.g. useWorkspaceId)
  • Tests: colocated as <file>.test.ts(x)
  • Stores (Zustand): <feature>-store.ts, exported as use<Feature>Store

Database (Go + sqlc)

  • Tables: snake_case singular (user, workspace, agent_runtime)
  • Columns: snake_case (workspace_id, created_at, last_seen_at)
  • Foreign keys: <table>_id
  • Booleans: is_<state> or <state>_at (timestamp form preferred for state changes)
  • Migration files: NNN_descriptive_name.up.sql + .down.sql — always provide both directions

Go

  • Standard gofmt + go vet. No exceptions.
  • Handler files mirror domain: agent.go, auth.go, runtime.go
  • Tests: <file>_test.go colocated
  • For UUID parsing in handlers, follow the rule in the root CLAUDE.mdparseUUIDOrBadRequest for boundary input, parseUUID (panicking) for trusted round-trips, never util.ParseUUID directly without checking the error.

TypeScript

  • API responses on the wire are snake_case; the api client converts to camelCase at the boundary. Inside TS code, always camelCase.
  • Types: PascalCase (Issue, AgentRuntime); never IPrefix, never _t suffix.
  • Enums: prefer string literal unions; reserve enum for runtime-iterable cases.
  • TanStack Query keys: factory functions in <feature>/queries.ts, e.g. issueKeys.detail(id).

Issue keys

Every issue has a human-readable key like MUL-123: workspace issue_prefix (uppercase letters and digits, typically 3 chars, max 10) + sequence number. Workspace admins can change the prefix in Settings → General; changing it renumbers every existing issue, so external references that embed the old prefix (PR titles, branch names, links in docs and chat) stop resolving.

Comments in code

English only. The repo enforces this for both Go and TypeScript. If you find a Chinese comment in code, it's a bug — replace it.

Commit messages

Conventional format: feat(scope), fix(scope), refactor(scope), docs, test(scope), chore(scope). Atomic commits grouped by intent.


2. i18n translation glossary

This is the mandatory glossary for every translation PR. It used to live at packages/views/locales/glossary.md; that file is now a stub pointing here.

Don't translate — brands and acronyms

CategoryTerms
BrandsMultica, GitHub, Slack, Google, Anthropic, OpenAI, Claude, Codex, Cursor, Linear, Jira
AcronymsAPI, CLI, URL, SDK, OAuth, JWT, SSO, WebSocket, HTTP, JSON, YAML, SQL

Plurals and counts

i18next uses _one / _other.

// en/issues.json
{
  "issue_count_one": "{{count}} issue",
  "issue_count_other": "{{count}} issues"
}

Interpolation

Use {{var}}.

// en
{ "welcome_message": "Welcome back, {{name}}!" }

Translation key naming

Three-level nesting: feature.component.action.

{
  "feature_or_component": {
    "subcomponent_or_section": {
      "action_or_label": "..."
    }
  }
}

Examples:

  • issues.toolbar.batch_update_success
  • issues.detail.comment_form.placeholder
  • inbox.empty.title
  • settings.preferences.language.title

Web-only / desktop-only copy

  • Shared copy: top level of the namespace JSON
  • Web-only: web section
  • Desktop-only: desktop section

See auth.json for the canonical example (the web section contains prefer_desktop / desktop_handoff.*).


Updating this page

If you change a rule here, also:

  1. Apply it in the relevant locale JSONs / CLAUDE.md / docs page
  2. Note the change in the PR description so reviewers know to look for downstream sweep

This page is the contract; nothing else overrides it.