High-level guide for coding agents working in this repository.
Multi-tenant LLM observability platform. The repo is a pnpm workspace orchestrated with Turbo.
At a glance: apps/* own transport (middleware, authn, mounting); @repo/operations owns the public API's operation contracts (validation, public schemas, routing to use-cases) shared by HTTP/MCP/SDK/CLI and in-process agent tools; packages/domain/* own business rules and ports; packages/platform/* implement infrastructure adapters; @repo/utils holds cross-cutting pure helpers. Telemetry and control data flow through Postgres, ClickHouse, Redis, and object storage, with organization-scoped access everywhere at the boundary.
- Organization-scoped Redis or cache keys must start with the organization prefix:
org:${organizationId}:.... Put the org id first so tenancy is obvious and keyspaces stay consistently partitioned. - Code comments are rare, and one line when they exist. Default to no comment — naming and structure should carry the meaning; a comment explaining what code does is a signal to rewrite the code, not to annotate it. Write a comment only for something the code cannot show: a trap (code that looks wrong, removable, or simplifiable but breaks something — ordering requirements, magic values, workarounds for framework/library quirks), a non-obvious external constraint, or contract docs where the file's existing convention documents every sibling (e.g. JSDoc on every method of a port). Never narrate the change you just made, restate what the next line does, paraphrase a well-named identifier, add section-header comments, or explain design rationale in the code — rationale belongs in the commit or PR description. When editing existing code, don't add comments describing your edit, and feel free to delete comments your change made stale or redundant. A comment describes the code as it stands, not the development process that produced it: never anchor one to transient context like a rollout phase, ticket id, or "new"/"now" framing (e.g.
// Phase 2 (LAT-749): ...) — that reads as stale the moment the phase lands; state what the code does and put the ticket in the commit/PR. The only comments that may name a ticket are actionableTODO/FIXMEmarkers for work not yet done. - Never invoke
tscdirectly. Typechecking goes throughtsgovia the packagetypecheckscript — usepnpm --filter <pkg> typecheckfor one package orpnpm typecheckfor the whole workspace.tscwould diverge from CI. - ClickHouse migrations must be created with
pnpm --filter @platform/db-clickhouse ch:create <migration_name>. Do not create ClickHouse migration files manually. - PR base branch follows the branch's origin. Branches forked from
development(v2 work, default) PR intodevelopment; branches forked fromlatitude-v1(v1 maintenance) PR intolatitude-v1. Never start a branch frommainwithout confirmation — if the working branch is based onmain, or the user asks to branch frommain, confirm with the user first. Detect an existing branch's base by testing ancestry in order: ifgit merge-base --is-ancestor origin/latitude-v1 HEADis true →latitude-v1; else ifgit merge-base --is-ancestor origin/development HEADis true →development; else the branch is likely based onmain(or something unusual) — stop and confirm with the user before opening a PR. When the user asks to start a new branch, take the base from their wording (mentions of v1 →latitude-v1; otherwise default todevelopment). - Production deploys use the single-branch release-tag flow:
developmentis trunk and deploys to staging by default; production is triggered only by pushing avX.Y.Ztag that points at the latestorigin/developmentcommit. Before tagging a production release, updateCHANGELOG.mdwith a human-readable diff of the code being pushed to production since the previous production deploy, focusing on the major aspects rather than every commit. Usescripts/release.sh [version]to fetch and tag the latestorigin/developmentcommit; without a version it bumps the latestvX.Y.Ztag to the next patch version, with--minor/--majoravailable for larger bumps. Do not promote by mergingdevelopmentintomain. - Keep the product cleanly OSS and self-hostable. Shipped runtime dependencies must be permissively licensed (MIT/Apache-2.0/BSD/ISC) — no AGPL/SSPL/source-available code in the application bundle; audit a new dependency's license (and its transitive additions) before adding it. The self-host object store is SeaweedFS (Apache-2.0) — never reintroduce MinIO or any AGPL store as a bundled default. Keep every infra dependency isolatable and bring-your-own-able (dedicated schema/db/namespace/bucket; Redis keys namespaced under
latitude:) so a self-hoster can swap any bundle for a managed instance. Rationale + full audit:dev-docs/licensing.md; self-host architecture:dev-docs/self-hosting.md. - Private partner endpoints live under
/v1/private/*and are plain Hono routes. They serve vetted, staff-registered partners, are authenticated by per-partner HMAC request signing (never a bearer token), and must stay off every generated surface — use a plainapp.post(...), neverapp.openapi/createRoute, neverdefineOperation. Every pre-scope refusal returns an identical401 {"error":"unauthorized"}so the surface can't be used to enumerate partner ids; only a scope failure gets a distinct 403. Details:dev-docs/partners.md. - Two image registries, distinct jobs. Public self-host images go to Docker Hub
latitudedata/<service>— env-neutral, multi-arch, for the six build targets (api,ingest,workers,workflows,web,migrations) — tagged to the release flow::X.Y.Z+:lateston a release (gitvX.Y.Zwith thevstripped) are the stable tags self-hosters pin,:developmentis the trunk edge tag. GHCR (latitude-<env>-<service>, sha-tagged) stays Latitude's private own-deploy registry and is never the self-host source.
- Skim the skill glossary below and open the skill that matches your task.
- Read that skill's
SKILL.mdin full before editing code in that area.
Detailed policies, command examples, and code samples live under .agents/skills/<skill-name>/SKILL.md. Load narrow skills instead of memorizing the entire monorepo at once.
Index coverage: The glossary lists every skill in .agents/skills/ (one row per */SKILL.md, 28 total), ordered alphabetically by folder name. When you add or remove a skill folder, update this table in the same change.
| Skill | Path | Use when |
|---|---|---|
| Agentation watch mode | .agents/skills/agentation-watch-mode/SKILL.md | Agentation annotation watch loops, continuous feedback handling, or when the user says watch mode and wants annotations acknowledged, fixed, and resolved as they arrive |
| Analyze problem | .agents/skills/analyze-problem/SKILL.md | Investigating a bug, task, or reported issue to explain behavior, root cause, proposed fix, and verification steps before implementation |
| API endpoints (HTTP, MCP, SDK, CLI) | .agents/skills/api-endpoints/SKILL.md | Adding or changing API operations in @repo/operations, defineOperation, openapi.json / mcp.json regen, OperationModule manifests, group/sdkMethod/rateLimitTier, defineToolset agent toolsets, writing field descriptions that propagate to the TS + Python SDKs, MCP tool, and latitude CLI consumers |
| Architecture and boundaries | .agents/skills/architecture-boundaries/SKILL.md | Layering, web vs public API, app layout (clients, routes, logging), ports/adapters, web-standard APIs in domain/shared/utils, multi-tenancy, DDD layout, anti-patterns, machine-facing MCP/API product surfaces |
| Background jobs and events | .agents/skills/async-jobs-and-events/SKILL.md | Queues/workers, domain events, side effects outside HTTP handlers, task payload design, debounce/dedupe, delayed job semantics, domain event naming, publisher–consumer decoupling |
| Authentication | .agents/skills/authentication/SKILL.md | Better Auth, sessions, web session helpers, org context on session, @domain/auth flows |
| Backoffice | .agents/skills/backoffice/SKILL.md | Staff-only /backoffice features, createAdminServerFn factory, admin guards + RLS-bypass path, @domain/admin feature-folder layout |
| Better Auth best practices | .agents/skills/better-auth-best-practices/SKILL.md | Better Auth server/client setup, DB adapters, sessions, plugins, env (auth.ts); email/password, OAuth; better-auth.com API reference |
| CI watchdog | .agents/skills/ci-watchdog/SKILL.md | Watching GitHub PR checks, monitoring CI status, diagnosing failures from logs, and looping on fixes until checks pass |
| Code style and TypeScript | .agents/skills/code-style/SKILL.md | Biome, imports, strict TS, naming, Zod-first shared contracts, literal-union enums, named constants, generated files |
| Create PR | .agents/skills/create-pr/SKILL.md | Creating PRs, writing PR descriptions, summarizing changes, and preparing a reviewable pull request |
| ClickHouse | .agents/skills/database-clickhouse/SKILL.md | Parameterized CH queries, Goose migrations, append-only migration rules |
| Postgres and SqlClient | .agents/skills/database-postgres/SKILL.md | Drizzle schema, RLS, SqlClient, migrations (Drizzle Kit), no-FK rules, repository mappers |
| Documentation and specs | .agents/skills/docs/SKILL.md | dev-docs/*.md (domain), docs/ (ADRs, Mintlify), specs/*.md, durable documentation sync, spec structure, promoting stable knowledge into dev-docs/ |
| Effect and errors | .agents/skills/effect-and-errors/SKILL.md | Effect composition, Data.TaggedError, HttpError, boundary error handling |
| Environment configuration | .agents/skills/env-configuration/SKILL.md | LAT_* / VITE_LAT_*, .env.example, parseEnv / parseEnvOptional |
| Fix Datadog issues | .agents/skills/fix-datadog-issues/SKILL.md | Finding, triaging, and fixing production errors from Datadog Error Tracking (plugin:datadog:mcp); picking which issue to work (occurrence/trend/recency, v2-only, prod-only), root-causing in code, reproducing with tests, commenting on the issue, and opening a PR to development |
| GitHub issues | .agents/skills/gh-issue/SKILL.md | Creating clear, actionable GitHub issues for bugs, features, and improvements, optimized for LLM/actionability |
| Humanizer | .agents/skills/humanizer/SKILL.md | Editing or reviewing prose (docs, PR and commit copy) to remove AI-writing tells such as em dashes, rule of three, promotional language, and filler, so it reads naturally |
| Managing maintenance windows | .agents/skills/managing-maintenance-windows/SKILL.md | Enabling, disabling, verifying, or preparing production maintenance mode, which redirects console.latitude.so to the Better Stack status page with the Pulumi enableWebMaintenanceRedirect switch |
| Mintlify docs preview | .agents/skills/mintlify-preview/SKILL.md | Running the public Mintlify docs site (docs/) locally for live preview (mint dev); Node <25 (nvm v22) requirement, keeping the CLI current, non-default port, page-path mapping |
| Notifications | .agents/skills/notifications/SKILL.md | Adding a notification kind, group, or channel; in-app + email delivery; NOTIFICATION_KIND_META / NOTIFICATION_GROUPS; per-user prefs (users.notification_preferences); project-level gates (projects.settings.notifications); idempotency + cascade-on-ProjectDeleted |
| Production release | .agents/skills/production-release/SKILL.md | Preparing a production release, updating CHANGELOG.md from the production diff, and pushing vX.Y.Z release tags with scripts/release.sh |
| Review PR comments | .agents/skills/review-pr-comments/SKILL.md | Loading issue-level and inline PR feedback with GitHub CLI/API, deduping comments, replying in the right thread, and resolving addressed review threads |
| Temporal workflows | .agents/skills/temporal-developer/SKILL.md | Editing workflows or activities in apps/workflows, reordering/inserting activities in a running workflow, patched() / deprecatePatch() / Worker Versioning, debugging non-determinism errors, terminating stuck workflows, replay/history semantics |
| Testing | .agents/skills/testing/SKILL.md | Vitest layers, PGlite/chdb testkit, /testing package exports, avoiding vi.mock for repositories |
| Toolchain and commands | .agents/skills/toolchain-commands/SKILL.md | Node/pnpm/Turbo/Vitest/Biome, scripts, filters, CI, .env.* setup, Docker Compose, dev servers, Mailpit |
| Web frontend | .agents/skills/web-frontend/SKILL.md | apps/web UI, TanStack Start, collections, @repo/ui, layout, -components/, legacy UI reference, useMountEffect policy, useForm + createFormSubmitHandler + fieldErrorsAsStrings for Zod field errors on forms |
The update script runs scripts/cloud-install.sh and scripts/cloud-start.sh on every session start — dependencies are installed, Docker infra is started, and all databases are migrated and seeded automatically.
For services, ports, health checks, Docker Compose, dev servers, and Mailpit auth flow, see toolchain-commands skill. Seeded users include owner@acme.com, admin@acme.com, etc., all in the "Acme Inc." organization.
Cloud-only gotchas:
- mise activation: Use
eval "$(mise env)"(notmise activate bash) before running scripts that callnode/pnpmin child shells.mise activateonly hooks the current shell's prompt; childbashinvocations won't inherit the PATH. pnpm buildmust complete beforepnpm db:up— migration scripts depend on compiled platform packages.- The
pnpm installoutput may warn about unapproved build scripts (@swc/core,sharp, etc.). These are non-blocking — pre-built binaries are used. - The Docker daemon in cloud VMs needs the
fuse-overlayfsstorage driver andiptables-legacy; on Docker 29+ you must also setfeatures.containerd-snapshotter=falsein/etc/docker/daemon.jsonorfuse-overlayfsis ignored. This daemon config plusdockergroup membership for the run user are baked into the VM snapshot (not the update script);cloud-start.shonly starts the already-configured daemon.