The Open Context Layer for Data and AI , OpenMetadata is the open platform for building trusted data context and business semantics for humans, AI...
# AGENTS.md
Always-loaded guidance for every session, whatever the harness β **the only instruction file:** every `CLAUDE.md` and
`.github/copilot-instructions.md` is a symlink to the `AGENTS.md` beside it
(ADR:2026-10-09-agents-md-is-the-one-instruction-file), so edit this file, never a copy. **Language- and path-specific
rules live in `.claude/rules/*.md` (Claude Code auto-loads them on matching files; other harnesses read the matching
one before editing); procedures live in skills (loaded on invoke).** This file is the map β see the pointer index at
the bottom. Read [ARCHITECTURE.md](ARCHITECTURE.md) for the **system map** (modules, the request/ingestion/search
paths, the invariants that hold); read [DEVELOPER.md](DEVELOPER.md) for **how to build, test, and add an entity or
connector** (deep dives + end-to-end checklists). Consult [docs/index.md](docs/index.md) β the **knowledge index** β
to **find existing design, plan, and reference docs** for whatever area you're working on.
## About OpenMetadata
OpenMetadata is a unified metadata platform for data discovery, observability, and governance β a
multi-module project with a Java backend, a React/TypeScript frontend, a Python ingestion framework,
and Docker infrastructure.
## Stack at a glance
- **Backend**: Java 21 + Dropwizard, multi-module Maven.
- **Frontend**: React + TypeScript, built with **Vite** (dev server on :3000); component library
`openmetadata-ui-core-components` β the **UntitledUI + Tailwind v4** (`tw:` prefix, react-aria-components)
go-forward design system; legacy stack is Ant Design + Less (deprecated). Machine-readable design-system
specs: `openmetadata-ui/src/main/resources/ui/specs/` (read `specs/README.md` before UI work).
- **Ingestion**: Python (`>=3.10`, no pinned ceiling; **CI runs 3.10**) + Pydantic 2.x, 75+ connectors.
- **Database**: MySQL (default) or PostgreSQL. **Search**: Elasticsearch 7.17+ or OpenSearch 2.6+.
- **Infrastructure**: Apache Airflow for ingestion orchestration.
## Environment setup (every session)
- **Python venv is REQUIRED before any Python work, `make generate`, or `make install_dev*`:**
```bash
source env/bin/activate # first time: python3.11 -m venv env
python --version # expect 3.10.x/3.11.x
```
In a Claude Code **worktree** the venv is NOT copied β create one
(`python3.11 -m venv env && source env/bin/activate && cd ingestion && make install_dev`) or
symlink the main repo's (`ln -s /path/to/main-repo/env env`).
- **One-call setup (macOS + Linux)**: `make dev_setup` (or `./scripts/dev_setup.sh`) does everything
below β toolchain, venv, generation, UI deps, pre-commit β and is idempotent. `make dev_check`
diagnoses an existing checkout without changing it. See the `dev-setup` skill.
- **First-time bootstrap** (from the **repo root** β `make generate` is a root-only target; it does
not exist under `ingestion/`):
```bash
make prerequisites
source env/bin/activate && cd ingestion && make install_dev_env && cd ..
make generate # regenerate models after any schema change
make yarn_install_cache
make install_test precommit_install # activate the commit-time format/license gate (pre-commit)
```
The last line installs the `pre-commit` hooks (`.pre-commit-config.yaml`): on `git commit` they run
Java format (spotless), Python format (ruff), UI format (prettier), design-token, and Apache-2.0
license checks on your changed files, matching CI. Do not skip them with `--no-verify`.
- **Java**: Java 21; use `mvn`. **Frontend**: use `yarn` (never `npm`); frontend root is `openmetadata-ui/src/main/resources/ui/`.
- **Docker dev services**: `docker compose -f docker/development/docker-compose.yml up -d`.
## Repository layout
Maven modules (reactor order is computed from the graph, not this list):
- `openmetadata-spec/` β JSON Schemas + generated POJOs; the schema-first source of truth
- `openmetadata-sdk/` β Java client SDK
- `common/` β shared utilities (`CommonUtil`, etc.)
- `openmetadata-shaded-deps/` β ES/OS clients relocated behind `es.*`/`os.*` (do not edit β see rules)
- `openmetadata-service/` β core Java backend, REST APIs, repositories, migrations runner
- `openmetadata-k8s-operator/` β Kubernetes operator
- `openmetadata-integration-tests/` β backend API integration tests (`*IT.java`)
- `openmetadata-mcp/` β MCP server
- `openmetadata-ui-core-components/` β canonical React component library
- `openmetadata-ui/src/main/resources/ui/` β React frontend application
- `openmetadata-dist/` β packaging/distribution
- `openmetadata-clients/` β client artifacts
Other key trees: `ingestion/` (Python framework + connectors), `bootstrap/sql/` (DB migrations), `conf/` (configuration), `docker/` (local + prod deployment).
## Hard cross-cutting constraints (apply to every session, all languages)
**Secrets & security.** Never commit secrets β use environment variables or a secrets manager.
Auth is JWT with OAuth2/SAML; RBAC lives in Java entities; config in `conf/openmetadata.yaml`.
**Do not modify `.github/workflows/**` on your own** β CI workflows are a supply-chain surface; a
`PreToolUse` hook blocks edits there unless the user explicitly authorizes them (by setting
`CLAUDE_ALLOW_WORKFLOW_EDITS=1`). Ask first.
**All caches MUST be bounded.** Never use a bare `dict` / `HashMap` / `Map` as a cache without an
explicit size cap β they grow with input and OOM on large catalogs/ingestions (only exception: the
user explicitly asks for unbounded). Pick a sane default (100β1000 entries); if unsure, ask.
Python: `collections.OrderedDict` + `popitem(last=False)`, `@functools.lru_cache(maxsize=N)`, or
`cachetools.LRUCache` (cache hits **and** misses). Java: Caffeine/Guava `maximumSize(N)`.
TypeScript: `lru-cache`. Before adding a cache, check it isn't already cached a layer down (e.g.
`OpenMetadata._search_es_entity` is already `@lru_cache(maxsize=512)`).
**Comments explain *why*, never restate code.** Do NOT add comments that describe what obvious code
does (`// Create user` before `createUser()`). Only comment complex business logic, non-obvious
algorithms/workarounds, public-API JavaDoc, or `TODO/FIXME` with a ticket reference. If code needs a
comment to be understood, refactor it to be clearer instead.
**Testing philosophy.** Test real behavior, not mock wiring β if a test mocks 3+ of your own classes
to verify a method call, it tests the wrong thing. Prefer integration tests over heavily-mocked unit
tests (this project has real ITs: `OpenMetadataApplicationTest`, Docker, real OpenSearch). Mocks are
for boundaries (HTTP clients, third-party APIs), not internals. Ask "what breaks if this test passes
but the code is wrong?" β if the answer is "nothing", rewrite it. Assert on observable outcomes
(API responses, DB state), not internal `verify()` calls.
**License headers are per-module β copy one from a sibling file, never assume Apache.** UI TS/TSX:
Apache-2.0 (`yarn license-header-fix`). Python: `ingestion/` is **Collate Community License 1.0** (`ingestion/LICENSE`); `openmetadata-airflow-apis/` Python files use the same Collate header template.
Java: Apache-2.0, most files carry none.
Only the UI is enforced β the `ui-license-header` pre-commit hook and CI `ui-checkstyle`; spotless
and `py_format_check` never look at headers, so a wrong Python or Java header ships silently.
**Schema-first.** JSON Schemas in `openmetadata-spec/` are the single source of truth; all generated
code (Java POJOs, Pydantic models, TS types) is derived. **Edit the schema, then regenerate β never
hand-edit generated output.** Details in `.claude/rules/schema-first.md`.
**Output style.** Clean code blocks, no unnecessary explanation; assume an experienced reader; focus
on functionality over education. Do not add unnecessary blank lines between prose and code blocks.
**No wrappers around core-ui components.** If a wrapper component exists that wraps a core-ui component, skip it β use the `openmetadata-ui-core-components` component directly at the call site. Only create a wrapper if it adds genuine, non-trivial behaviour (custom hook logic, composed sub-components, domain-specific state). A wrapper that only passes props through is a maintenance liability: it hides which core component is in use and drifts from upstream updates.
**Icons and assets: always go through the design-system layer.** Import icons from `@openmetadata/ui-core-components/icons`, never directly from `@untitledui/icons` β the barrel ships only the design team's own curated SVG set, so add a missing glyph there via `yarn icons:generate` (see the core-components `ICONS.md`) rather than reaching for `@untitledui/icons`. For the same reason, do not import SVG icons directly from `assets/` paths; use the designated abstraction instead.
## Pointer index β when to reach for what
### Path-scoped rules (`.claude/rules/*.md`, auto-load on matching files)
| Rule file | Reach for it when you are editing⦠|
|---|---|
| `java.md` / `java-impact-map.md` | any `**/*.java` β style, spotless, no-wildcard, Kafka-grade method/class rules, ITs; own new code and ITs in `.github/java-tests/impact-map.json` before the PR |
| `frontend-react.md` | UI `*.{ts,tsx}` β components, hooks, state, types, and the CI lint code-rules |
| `frontend-styling.md` | UI `*.{ts,tsx,less,css}` β `tw:` prefix, design tokens, ringβborder, token-audit |
| `component-library.md` | UI `*.{ts,tsx}` β prefer `ui-core-components`, do not add Ant Design for new work |
| `frontend-performance.md` | UI `*.{ts,tsx}` β waterfalls, barrel imports, re-renders, bundle discipline |
| `frontend-permissions.md` | UI `*.{ts,tsx}` β `useEntityPermissions` and derived flags; never read a permission key directly |
| `frontend-a11y.md` | UI `*.{ts,tsx}` β semantics over `div`+`role`, keyboard, focus, contrast, targets |
| `i18n.md` | UI `*.{ts,tsx}` + `src/locale/**` β no string literals, `yarn i18n`, translate placeholders |
| `frontend-playwright.md` | UI `playwright/**` β E2E test constraints |
| `python-ingestion.md` | `ingestion/src/**/*.py` β pytest style, connector-specific-file rule, `model_str()` |
| `schema-first.md` | `openmetadata-spec/.../schema/**` and any `generated/**` β regen, never hand-edit generated |
| `migrations.md` | `bootstrap/sql/**` β append-only, native path, MySQL+Postgres, idempotent |
### Repo coding conventions (read before writing non-trivial code)
- `docs/design-patterns.md` β the design patterns this codebase uses idiomatically (Template Method
for repositories, Factory/Registry for dispatch, Strategy/Adapter/Observer, the ingestion
SourceβSink pipeline, β¦) with the canonical class to copy each from. Extend the established pattern
rather than inventing a parallel one.
- `openmetadata-ui/src/main/resources/ui/DEVELOPER_HANDBOOK.md` β **the UI folder structure and file
naming spec.** Read before creating any new file under `openmetadata-ui/.../ui/src/`. Layers stay
top-level (`components/`, `pages/`, `rest/`, `utils/`, `hooks/`) and are grouped inside by
`domain/feature/`; new files use one stem with a role suffix (`GlossaryList.tsx`, `.types.ts`,
`.utils.ts`, `.test.tsx`). Legacy `.component.tsx`/`.interface.ts` files stay as they are.
- `REVIEW.md` β **the review bar every PR is held to**: the logic defects, test gaps and flaky-test
shapes reviewers keep finding here. Read it before writing code; `pr-checklist` self-reviews against it.
- `docs/decisions/` β dated records of the choices that shape the code. **When a choice looks
arbitrary, find its record before re-deciding it; a PR that makes a durable decision adds the
record in the same PR.** Cite as `ADR:<date>-<slug>`; format and triggers in its `README.md`.
### Skills (`skills/<name>/`, linked into `.claude/skills/` and `.agents/skills/`; invoke by name)
| Skill | Reach for it when⦠|
|---|---|
| `dev-setup` | setting up / repairing a dev environment, a fresh clone, or a new worktree |
| `planning` | starting any non-trivial, multi-file feature or refactor |
| `tdd` | implementing a feature or bug fix (REDβGREENβREFACTOR) |
| `systematic-debugging` | a failing test/build/runtime issue whose cause isn't obvious |
| `test-enforcement` | before a PR β 90% changed-class coverage, ITs for new endpoints, Playwright for UI |
| `java-affected-tests` | before a PR touching Java/schemas/migrations β runs the impacted unit tests + ITs (PR CI runs no ITs) |
| `verification` | before claiming "done" β run real commands, show evidence |
| `code-review` | reviewing a local diff before it is a PR β `REVIEW.md` as the bar, spec compliance then code quality |
| `java-checkstyle` | after touching `.java` β runs `mvn spotless:apply` and verifies |
| `ui-checkstyle` | after touching UI `*.{ts,tsx,js,jsx,json}` β the exact CI ESLint+Prettier+organize-imports pass |
| `ui-core-components` | building UI layout/color before reaching for raw `<div>` + Tailwind |
| `test-locally` | spinning up the full local Docker stack to test a change/connector |
| `connector-standards` / `connector-building` / `connector-review` | building or reviewing an ingestion connector |
| `playwright` / `writing-playwright-tests` / `playwright-validation` | authoring or validating Playwright E2E tests |
| `pr-checklist` / `ui-pr-recording` | opening any PR β self-review against `REVIEW.md`, test evidence, the body; a UI feature PR also runs the build in Docker with sample data and uploads its screen recording |
| `openmetadata-pr-review` | reviewing a real GitHub PR (number/URL/batch) β maintainer merge verdict: live diff, CI, meaningful-test rubric |
> `openmetadata-workflow` is a meta-skill that routes tasks to the skills above; it is auto-loaded at
> session start when the `openmetadata-skills` plugin is installed.
### Harness integrity (CI, warnings-only)
A CI workflow (harness-integrity.yml) runs `scripts/harness/check_harness.py` on PRs β also `make harness-check` locally.
It **warns, never blocks** (promote to a gate only with maintainer sign-off) when the agent-facing config decays:
- **dead references** β a path, `make`/`yarn` target, or `mvn` goal named in this file,
ARCHITECTURE.md, `docs/index.md`, `.claude/rules/**`, or a SKILL.md that no longer resolves;
- **one instruction file** β a `CLAUDE.md` or `copilot-instructions.md` that is not a link to its `AGENTS.md`, another
harness's own file (`GEMINI.md`, `.cursorrules`, β¦), a rule missing from the index, a chain over Codex's 32,768 bytes;
- **skill symlinks** β a real file where a symlink into `skills/` is expected (`.claude/skills`,
`.agents/skills`), a skill missing from either of them, or two same-named SKILL.md that differ;
- **doc-size budgets** β this file > 200 lines, ARCHITECTURE.md > 300, any single rule > 100;
- **rule globs** β a `.claude/rules/**` `paths:` glob matching zero files;
- **generated-doc freshness** β `docs/generated/**` out of date with its source;
- **decision records** β a malformed record or a dangling `ADR:` citation (also a pre-commit hook).
## Git commit conventions
- Never add "Co-Authored-By" lines or any AI-attribution trailers to commit messages.
- The commit is authored by me; you are a tool, not a co-author.