This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
Sangeetha Grantha is a digital compendium of Carnatic classical music compositions (Krithis). It’s a multi-module monorepo with:
This is a Carnatic Music Krithi Analyser project using: Kotlin backend (Exposed ORM), React/TypeScript frontend, Python data pipeline scripts, PostgreSQL database. Always verify changes compile/build across all three layers before committing.
make dev # Full stack via Docker Compose (DB + Backend + Frontend + Extraction)
make dev-down # Stop dev stack
make db # Start database only
make db-reset # Drop → create → Flyway migrate (schema V__ + reference data R__)
make seed-dev # Dev-only sample content (reference data now ships via R__ repeatables)
make migrate # Run pending migrations (Flyway)
make migrate-status # Show Flyway migration status
make bootstrap-admin # Provision the admin user (argon2id); needs ADMIN_EMAIL / ADMIN_PASSWORD
make test # Backend unit tests
make test-integration # Backend integration tests (Testcontainers)
make test-frontend # Frontend tests
make steel-thread # End-to-end steel thread test
make check-docs # Validate documentation links
make agent-evals # Deterministic agent-config evals (CLAUDE.md, skills, hooks)
make clean # Remove all containers and volumes
# Backend build (produces fat JAR)
./gradlew :modules:backend:api:build
# Backend tests
./gradlew :modules:backend:api:test
# Frontend (from modules/frontend/sangita-admin-web)
bun install
bun run dev # development server on port 5001
bun run build # production build
./gradlew :modules:backend:api:run # Run backend
./gradlew :modules:backend:api:runDev # Run backend in dev mode
Run the matching check before reporting a task complete, and paste the command output. Do not skip, delete, or weaken a failing test to make a task pass. If a test fails, fix the code, not the test — unless the Plan says the test itself is wrong. Deliberate test edits (new assertions, fixture changes that are themselves tests) require SANGITA_ALLOW_TEST_EDITS=1; 0/false do not opt out. Fixtures and src/test/setup.ts are not blocked.
make testmake test-integrationmake test-frontendverify-import skill (junction tables, not just FKs)make check-docsCLAUDE.md, .claude/**, REVIEW.md, evals/): make agent-evalsflowchart TB
subgraph repo["modules"]
direction TB
subgraph shared["shared"]
direction TB
DOM["domain — @Serializable DTOs"]
PRES["presentation — Rasika Compose UI"]
MDATA["mobile-data — V2 client, storage, fixtures"]
end
subgraph mobile["mobile"]
direction TB
AND[androidApp]
IOS[iosApp]
end
subgraph backend["backend"]
direction TB
API["api — Ktor AppKt"]
DAL["dal — Exposed"]
TS[test-support]
end
WEB["frontend/sangita-admin-web"]
end
Backend (Kotlin + Ktor)
DatabaseFactory.dbQuery { } for all database operationsAUDIT_LOG tablerequireRole (ADR-004 v1.3) — roles are
derived from stored role_assignments, never from the requestFrontend (React + TypeScript)
react-router-dom for navigationDatabase
make migrate / make db-reset) per ADR-013; the custom Python db-migrate and the test-side Kotlin MigrationRunner are retireddatabase/migrations/ (VNN__description.sql)make migrate or make db-reset; never Liquibase, never ad-hoc SQL executors, never custom migration runners. Rationale for the 2026-06 switch: the previous custom tooling had forked into two diverging implementations (Python db-migrate for dev/prod, Kotlin MigrationRunner for tests) with incompatible tracking tables and no checksum validation on the test path; Flyway gives one standards-based engine for Kotlin (JVM API in Testcontainers), Python (CLI), Make, and CI, plus repeatable migrations for reference seed data. See application_documentation/02-architecture/decisions/ADR-013-db-migration-with-flyway.md.gradle/libs.versions.toml, no hardcoded versions in build.gradle.ktsAUDIT_LOG tableRef: application_documentation/... linecurrent-versions.md:
application_documentation/00-meta/current-versions.mdapplication_documentation/02-architecture/tech-stack.mdapplication_documentation/00-onboarding/getting-started.mdmake migrate / make db-reset). Never Liquibase, never a custom runner, never edit a committed V__ file — add a new versioned migration.krithi_ragas), not only FK columns on the main entity.cursor/ git branch prefix unless the user asks.VITE_API_BASE_URL and the frontend proxy, plus committed *.env.example / .env.*.example templates. Never Read or commit .env / config/local.env.Carnatic correctness (lakshana) — musical forms (KRITHI/VARNAM/SWARAJATHI section requirements), Ragamalika, notation-vs-lyrics, and raga/tala/terminology rules — is documented in Domain Model §6. Treat it as a correctness contract for data entry, extraction, validation, and any generated SQL/seed data.
This file stays canonical for cross-cutting rules; layer-specific conventions live in project skills so they load only when relevant. Don’t duplicate content between the two — link.
.claude/skills/)Load the matching skill before working in a layer instead of re-deriving its conventions:
kmp-compose-mobile — modules/shared/ (KMP targets, expect/actual, Compose rules)ktor-exposed-backend — modules/backend/ (layering, dbQuery, DTO, audit, auth non-negotiables)postgres-flyway-db — database/migrations/ (V__/R__ naming, PG18 conventions, seeding checks)react-vite-frontend — modules/frontend/sangita-admin-web/ (Bun-only tooling, strict TS, test commands)python-extraction-worker — tools/krithi-extract-enrich-worker/ (Pydantic paradigm, module map, uv)monorepo-orchestration — mise/Makefile/Compose workflows and layer ownershipverify-import — post-import data verification checklistsangita-restart-on-kotlin-change — bounce Compose after Kotlin or extraction-worker Python edits (make dev-down then make dev)Cursor loads the same skills via symlinks under .cursor/skills/. Canonical files stay in .claude/skills/. Do not copy the bodies into .cursor/ or .cursorrules.
.claude/commands/)/dev-start, /db-reset, /test-all, /steel-thread, /new-migration, /spec-from-track, /plan-from-spec, /commit (follows this repo’s commit conventions), /Sangeetha-Krithi-Analyser (krithi section analysis). Cursor mirrors these files in .cursor/commands/.
.claude/agents/)Delegated to on demand (not loaded every turn): kotlin-backend-engineer, postgres-engineer, python-engineer, and carnatic-musicologist (reviews krithi data for musicological correctness — useful for bulk-import batches). They defer to this file and the domain model for shared rules and add domain judgment on top.
.claude/launch.json)Named launch configs exist for frontend (port 5001), backend (8080), and full-stack (make dev). Start dev servers through the browser-preview tooling with these names — never as raw background Bash — then verify changes in the preview (console/network/page checks) rather than asking the user to check manually.
.cursor/).cursorrules and .cursor/rules/project.mdc are pointers at this file. Git naming is .cursor/rules/git-conventions.mdc. Layer skills and slash commands are symlinks to .claude/skills/ and .claude/commands/ so they cannot drift. Edit-time hooks are wired in .cursor/hooks.json and run the same .claude/hooks/*.py scripts as Claude Code. Extra Cursor-only skills: agentic-prompt-optimizer, sangeetha-krithi-analyser.
Work is tracked in conductor/tracks/TRACK-<ID>-<slug>.md files. Check conductor/tracks.md for active tracks before starting work. New work uses Intent → Spec → Plan on the track (see conductor-track-manager). Do not implement until Plan Status is Accepted. PR review follows REVIEW.md.
For current toolchain and library versions, see Current Versions.
application_documentation/00-onboarding/getting-started.mdapplication_documentation/02-architecture/application_documentation/04-database/schema.mdopenapi/sangita-grantha.openapi.yamlapplication_documentation/02-architecture/decisions/ADR-013-db-migration-with-flyway.mdapplication_documentation/07-quality/integration-tests-approach.mdarchive/tools/db-migrate/ (Python, superseded by ADR-013), archive/tools/sangita-cli/ (Rust)For CORS/auth issues, check VITE_API_BASE_URL and the frontend proxy first, not TOML. Use committed *.env.example / .env.*.example templates for variable names. Never Read gitignored .env files.
## Data & Migrations Always verify seed data populates junction tables (e.g., krithi_ragas), not just foreign key columns on the main entity. After any seed/migration, confirm data appears correctly through the full stack (DB → API → UI).
Follow commit-policy strictly. Cursor agents also load .cursor/rules/git-conventions.mdc.
track-<nnn>-<kebab-slug> when a conductor track exists; otherwise <type>/<kebab-slug> (fix, feat, docs, chore, ci). Do not use a cursor/ prefix unless the user asks.TRACK-ID in the title when a track exists; exactly one Ref: application_documentation/... line; never commit until asked.When debugging Docker/infrastructure issues: check Dockerfile base image versions match project requirements, verify volume mount paths for the current DB version, and ensure Gradle caches are cleared before assuming code changes aren’t taking effect.