AGENTS.md · git:20260922.30cc0bc · 2026-09-22 · sha256 6eb060e7019491d0

AGENTS.md git:20260922.30cc0bcA

Immutable. This exact content never changes and is served at /api/v1/blob/6eb060e7019491d0.

## AGENTS.md

This file provides guidance to AI coding agents when working with code in this repository.

NextRole is a GenAI career assistant. See `README.md` for product overview.

## Layout

Monorepo with three top-level apps. Each has its own `AGENTS.md` with stack-specific guidance:

- `backend/` — Python 3.13, `uv`, FastAPI-style agents (LangChain / LangGraph / DeepAgents). See `@backend/AGENTS.md`.
- `frontend/` — Next.js 16, React 19, TypeScript, Tailwind, `pnpm`. See `@frontend/AGENTS.md`.
- `analytics/` — Python 3.13, `uv`; dlt + dbt + Dagster pipelines into ClickHouse, Cube models, Superset bootstrap. See `@analytics/AGENTS.md`.
- `docker-compose.yml` runs the full local stack.

## `.ua/` — generated codebase graph (never read)

`.ua/` holds a generated [Understand-Anything](https://github.com/Egonex-AI/Understand-Anything) knowledge graph that humans browse in a dashboard (README → "Explore the codebase graph"). Never read, grep, glob, or index anything under `.ua/` — it is megabytes of generated JSON that wastes context and goes stale. Answer from the real source code and the checked-in `*.md` docs instead. Claude Code additionally hard-blocks it via the `Read(/.ua/**)` deny rule in `.claude/settings.json`, and Cursor via `.cursorignore`; for tools with no enforced ignore mechanism (e.g. Codex, which reads this file as its project doc), this instruction is the only guard.

## `docs/prd/` — feature PRDs (OKF knowledge bundle)

Per-feature design docs are an [OKF](https://github.com/GoogleCloudPlatform/open-knowledge-format) bundle: markdown + YAML frontmatter (`type`, `title`, `description`, `tags`, `status`, …). Read them with progressive disclosure to save tokens:

1. Start at `docs/prd/index.md` — one line per PRD (title + description). Don't bulk-read the folder.
2. Open only the PRDs relevant to your task; follow their relative `Extends:`/`Supersedes:` links for lineage.
3. Never read `docs/prd/viz.html` — the generated human-facing graph (guarded like `.ua/` via a deny rule and `.cursorignore`; for tools without an enforced ignore, this instruction is the guard). Humans view/regenerate it per `docs/prd/README.md`.

## `.claude/skills/` and `.agents/skills/` — mirrored agent skills

Every skill under `.claude/skills/<name>/` (Claude Code) has a byte-identical Codex mirror under `.agents/skills/<name>/`. Edit one copy, then `cp` it over the other; keep the wording agent-neutral (no "Claude"/"Codex" in the body) and reference the `AGENTS.md` files, which every agent loads natively. The `.agents` side may carry Codex-only extras (`agents/openai.yaml`, the `current-docs` skill) that are not mirrored back. The `skill-mirror-sync` pre-commit hook (`scripts/check-skill-mirrors.sh`, also run by the hygiene CI job) fails on any drift and prints the fix.

## Library docs — use Context7

Use the Context7 MCP server to fetch current documentation whenever the task involves a library, framework, SDK, API, CLI tool, or cloud service — even well-known ones like React, Next.js, Prisma, Express, Tailwind, Django, or Spring Boot. This covers API syntax, configuration, version migration, library-specific debugging, setup instructions, and CLI usage. Use it even when you think you know the answer — training data may not reflect recent releases. Prefer it over web search for library docs.

Don't use it for refactoring, writing scripts from scratch, debugging business logic, code review, or general programming concepts.

1. Call `resolve-library-id` with `libraryName` in its official spelling and punctuation (`Next.js`, not `nextjs`; `Three.js`, not `threejs`) and `query` describing what you're trying to accomplish — the query ranks the matches, so it isn't optional. Skip this step only when the user already gave an ID as `/org/project` or `/org/project/version`.
2. Pick the match on name similarity, description relevance, code-snippet count, source reputation (High/Medium are the authoritative ones), and benchmark score (100 is the top). Use the version-specific ID when the user names a version. If nothing fits, rephrase or try the alternate name — never invent an ID.
3. Call `query-docs` with that `libraryId` and a `query` scoped to a single concept. Split a multi-topic question into one call per concept: "how to set up JWT auth in Express" is right, "routing and auth and caching in Next.js" is too broad and "auth" is too vague. Keep them together only when the question is about how the concepts interact.
4. Answer from the fetched docs.

Both tools cap at **3 calls per question**; past that, work with the best result you have. Queries are sent to the Context7 API, so never put credentials, personal data (CVs, job descriptions, anything from `upload/`), or proprietary source into one.

## Local development

The local stack runs in Docker via `docker compose up -d`. To find host ports for any running service:

```bash
docker ps
```

Read the `0.0.0.0:<host>->...` mappings — host ports come from `.env` (`FRONTEND_LOCAL_PORT`, `LANGGRAPH_LOCAL_PORT`, `POSTGRES_LOCAL_PORT`, `REDIS_LOCAL_PORT`, `OBJECT_STORE_LOCAL_PORT`, `DAGSTER_LOCAL_PORT`, `SUPERSET_LOCAL_PORT`, …) and vary per machine, so don't assume defaults. Once you know the port, hit endpoints directly:

- `http://localhost:<LANGGRAPH_LOCAL_PORT>/docs` — backend API docs (LangGraph)
- `http://localhost:<LANGGRAPH_LOCAL_PORT>/files/list?prefixes=/upload/` — artifact files API (object storage)
- `http://localhost:<FRONTEND_LOCAL_PORT>/` — frontend UI
- `http://localhost:<OBJECT_STORE_UI_LOCAL_PORT>/buckets/next-role-artifacts/` — SeaweedFS filer UI (browse bucket objects)
- `http://localhost:<DAGSTER_LOCAL_PORT>/` — Dagster UI (analytics pipeline: lineage, runs, schedule)
- `http://localhost:<SUPERSET_LOCAL_PORT>/` — Superset BI (login: `SUPERSET_ADMIN_USERNAME`/`SUPERSET_ADMIN_PASSWORD` from `.env`)
- `http://localhost:<CUBE_LOCAL_PORT>/` — Cube Playground (semantic layer; REST at `/cubejs-api/v1/meta`)
- `http://localhost:<DBT_DOCS_LOCAL_PORT>/` — dbt docs (model/column catalog, compiled SQL, lineage)
- `http://localhost:<CLICKHOUSE_HTTP_LOCAL_PORT>/play` — ClickHouse query UI (warehouse)

Use the `agent-browser` skill for visual verification, or `curl` for API checks.

If a service connection fails or `docker ps` shows the stack isn't up, **remind the user to run `docker compose up -d`** instead of running it yourself. They may have stopped it intentionally, and silently restarting shared infrastructure can mask real bugs.

### Hot reload vs. restart vs. rebuild

Frontend and backend hot-reload on source edits — frontend via `pnpm dev` (Turbopack), backend via uvicorn `--reload` in the compose `command` (watches everything under `backend/`, agents and server packages alike). **Don't restart for plain code changes**; save the file and the running container picks it up.

Restart (`docker compose restart <service>`) when:

- Adding a frontend dependency (`pnpm --dir frontend add ...` — boot self-heals `node_modules` from the new lockfile).
- Changing `.env` values — use `docker compose up -d <service>`, **not** `restart`: env vars are baked in when the container is created, and `restart` reuses the old container, so only a recreate picks them up.
- Editing `docker-compose.yml` (use `docker compose up -d` to apply the diff).
- Editing `backend/server/core_server/` or `backend/server/grpc_common/` — **core-server has no hot reload**; run `docker compose restart core-server`.
- Editing `analytics/` pipeline or dbt code — **the Dagster services have no hot reload**; run `docker compose restart dagster-webserver dagster-daemon dbt-docs` (the boot commands re-parse the dbt manifest / regenerate the docs site). Superset bundle changes re-import via `docker compose up -d superset-init`.

Rebuild (`docker compose up -d --build <service>`) when:

- Adding a backend Python dependency (`uv add ...`) — deps are installed at image build time, not boot.
- Editing any `Dockerfile`.

## After editing files

Pre-commit is already installed (`pre-commit install` was run). Edit freely across many files in one task, then **once at the end** validate everything you touched in a single command:

```bash
pre-commit run --files $(git ls-files --modified --others --exclude-standard)
```

This picks up both modified-tracked files and new (untracked, non-ignored) files, and runs ruff (backend + analytics), sqlfluff (analytics dbt SQL), prettier + eslint (frontend), and the relevant type checker on just those paths. Don't run pre-commit after every individual edit — batch it.

If a hook auto-fixes a file (ruff format, prettier, EOL fixer), re-read the file before reporting the change to the user, since the on-disk content may differ from what was just written.

For a full repo sweep, use the `/quality` skill (runs `pre-commit run --all-files`).

## Working agreement — commits & PRs

- **Never commit, push, or open a PR unless I explicitly tell you to.** Make code changes in the working tree and leave them local so I can test first. When I'm ready I'll say "commit" / "open the PR".
- **One PR per feature/requirement**, even a large one — do not split a single feature across multiple PRs. Structure the work as **separate commits per logical phase**. Example (the multi-user feature): one PR with commits for *Identity foundation* → *Backend authn + authz* → *Per-user storage* → *Hardening + docs*.

## Commits

Conventional Commits: `feat:`, `fix:`, `chore:`, `docs:`, `refactor:`, `test:`. Lowercase subject, no trailing period. Match the style in `git log`.

## Shared conventions

- **100-char line length** in both backend (`ruff`) and frontend (`prettier`). Don't change one without the other.
- **Package managers are pinned**: `uv` for backend, `pnpm` for frontend. Don't introduce `pip` or `npm`/`yarn`.
- Secrets live in `.env` (gitignored intent — see `.env.example`). `gitleaks` runs on every commit.