title: Work with a coding assistant
description: Give the assistant a specific task, a separate test environment, and the repository's current instructions — the cold-start guide a coding agent reads before its first contribution.
url: https://legalquants.github.io/lq-ai/contribute/coding-agents/
checked-against: 671eab0d (2026-10-25)
status: draft
kind: how-to
sources: docs/contribute/coding-agent-onboarding.md

> **Purpose:** Get a fresh AI coding agent — or a new human contributor working alongside one — from zero to shipping a roadmap item correctly, the first time. This is the connective tissue between the docs that already exist; it does not duplicate them, it tells you the order to read them and the loop to run.
>
> **Read this once, in full, before your first contribution.** Then keep [CLAUDE.md](https://github.com/LegalQuants/lq-ai/blob/671eab0d/CLAUDE.md) open as your decision reference.

---

## In short

- Work from this guide and [CLAUDE.md](https://github.com/LegalQuants/lq-ai/blob/671eab0d/CLAUDE.md); read the cited files and confirm a request's problem is real before writing anything.
- Work one task at a time, in a separate test environment — verify migrations against a throwaway database, and never run `docker compose down -v` or other volume-wiping commands against the live dev stack; there is no throwaway alternative for that one, it is forbidden outright.
- When a task hides an architectural, scope, or authorization decision, stop and put the choice to a maintainer instead of deciding unilaterally.
- A finished task reports evidence, not a summary: what changed, how it was tested, and links to the files touched — say plainly whether that came from reading code, a run against simulated services, or the running app.
- Every commit carries a DCO sign-off; an external contributor pushes only to their own fork and opens a PR — a maintainer reviews the gates and merges it.

---

## 0. The 60-second orientation

LQ.AI is an **open-source, self-hosted AI platform for in-house legal teams**. Three services talk over HTTP via OpenAPI contracts — no shared in-process code:

- **`api/`** — FastAPI backend (sessions, chats, files, KBs, playbooks, tabular review, the autonomous layer). mypy *standard* mode.
- **`gateway/`** — the Inference Gateway: the **security boundary**, the only component holding privileged provider API keys. mypy **`--strict`**. Changes here get security review.
- **`web/`** — a SvelteKit fork of OpenWebUI. (Svelte only — no React in `web/`; the Word add-in is the only React surface.)

The project's reason for existing is **transparency**: every artifact that shapes the user experience (especially skills) is visible, debuggable, forkable work product. That is an architectural commitment, not marketing — it shows up in how you're expected to build (honest docs, no overclaiming, explicit decisions).

---

## 1. Read-order for a cold start

Read these in sequence before touching code. Each answers a different question:

| Order | Doc | The question it answers |
|---|---|---|
| 1 | [CLAUDE.md](https://github.com/LegalQuants/lq-ai/blob/671eab0d/CLAUDE.md) | How do I make decisions here? (routing, code style, pitfalls, the build loop) |
| 2 | [docs/HONEST-STATE.md](/lq-ai/trust/published-gaps/) | What is **actually** built right now vs. planned? (the truth map — trust it over older prose) |
| 3 | [README.md](/lq-ai/operate/install-docker-compose/) | What does the product claim to do, publicly? |
| 4 | [docs/architecture.md](/lq-ai/operate/architecture/) | How do the three services fit together? |
| 5 | [docs/ROADMAP.md](https://github.com/LegalQuants/lq-ai/blob/671eab0d/docs/ROADMAP.md) + [docs/PRD.md §9](https://github.com/LegalQuants/lq-ai/blob/671eab0d/docs/PRD.md#9-deferred-enhancements-and-identified-future-work) | What's next, and what's deliberately deferred (the DE-XXX list)? |
| 6 | [docs/contribute/EASIEST-CONTRIBUTIONS.md](/lq-ai/contribute/board/) + [mini-prds/](https://github.com/LegalQuants/lq-ai/tree/671eab0d/docs/contribute/mini-prds) | What's a good first item to pick up? |
| 7 | [CONTRIBUTING.md](/lq-ai/contribute/engineers/) | Code style, DCO, PR mechanics. |

For a specific task, then narrow: the PRD section for the capability, the OpenAPI sketch if it touches an endpoint, the DB schema if it touches a table, and **any existing code in the same area** (don't rebuild what exists). The full decision-routing priority order is in [CLAUDE.md § Decision routing](https://github.com/LegalQuants/lq-ai/blob/671eab0d/CLAUDE.md#decision-routing).

---

## 2. The build loop (the part that lived only in tribal memory)

This is how work actually gets shipped here. It is battle-tested across the M1–M4 milestones and the post-v0.4.0 integration run. Run it for every non-trivial change.

```
1. VERIFY THE ASK AGAINST THE CODE — FIRST, before writing anything.
   Nearly every incoming request has a wrong premise or a wider blast
   radius than reported. Read the cited files. Confirm the problem is
   real and is where the asker thinks it is. Report what you actually found.

2. SURFACE THE FORKS — don't unilaterally decide.
   If the task hides an architectural / product / authz decision, or a
   scope expansion, or a deferral — STOP and put the options to the human
   (a recommendation + the trade-offs). The maintainer makes those calls.
   The cost of asking is minutes; an undocumented decision costs hours later.

3. PLAN, then BUILD in reviewed increments.
   Break the work into independent tasks. For each: implement (TDD where it
   fits) → independent SPEC-compliance review → independent CODE-QUALITY
   review → fix → re-review. Fresh context per task keeps reviews honest.
   (The `superpowers:subagent-driven-development` skill encodes this; you
   can also run it by hand.)

4. RUN THE GATES YOURSELF — evidence before claims.
   Never report "done" without the command output. See §4.

5. SHIP.
   Commit with DCO (§5) → push the branch to your own fork → open a PR
   → respond to CI and review. A maintainer performs the merge (§6).
```

Two honesty rules that sit on top of the loop:

- **Surface deferrals and scope expansions as choices.** Don't quietly absorb extra work or quietly drop something. Name it.
- **Don't overclaim in code or docs.** "Handles PDF; DOCX is deferred" — not "handles all documents." If tests fail, say so with the output.

---

## 3. Hard rules about the dev environment (learn these before you run anything)

These have each bitten this codebase. Violating them corrupts the running dev stack or crashes CI.

- **NEVER run host-side `alembic upgrade` against the live dev DB** (`127.0.0.1:15432/lq_ai`). That IS the running stack's database; a host-side migration desyncs it and crash-loops the api trio. To **verify** a migration, use a throwaway container:
  ```bash
  docker run -d --name lqai-throwaway-pg \
    -e POSTGRES_USER=test -e POSTGRES_PASSWORD=test -e POSTGRES_DB=lqai_test \
    -p 55432:5432 pgvector/pgvector:pg16
  # then run pytest with DATABASE_URL=postgresql+asyncpg://test:test@127.0.0.1:55432/lqai_test
  # (conftest auto-migrates to head). Remove the container when done.
  docker rm -f lqai-throwaway-pg
  ```
  To **apply** a migration to the dev stack, rebuild the workers (next bullet) — don't reach for host alembic.
- **When a migration lands, rebuild `api` + `arq-worker` + `ingest-worker` together.** Stale sibling containers crash-loop with "Can't locate revision identified by …" after a daemon bounce — they're all built from the api image and pin the revision.
- **NEVER `docker compose down -v`.** It wipes volumes — including acceptance/review data that's expensive to recreate. Rebuild a single service (`docker compose build web && docker compose up -d web`), don't nuke volumes.
- **The `web` container serves a pre-built static bundle (no HMR).** Source can be many commits ahead of what the browser shows. If a UI change "isn't appearing," rebuild `web` before debugging the code — or, better, don't iterate against the container at all: see [web/docs/frontend-dev.md](https://github.com/LegalQuants/lq-ai/blob/671eab0d/web/docs/frontend-dev.md) for running `web/` natively with Vite HMR against the dockerized backend.
- **Run BOTH `ruff format` and `ruff check`** locally — CI runs them as separate gates. `ruff check` passing doesn't mean `ruff format` will.

---

## 4. The gates (what "done" means)

Before claiming a change is complete, run and read the output of:

```bash
# From the repository root after `make install`
make lint          # Python ruff/mypy + Web npm run check:lq-ai
make format-check  # Python ruff format --check
make test          # Python local-loop suites + Web Vitest

# Run Web gates independently when iterating on web/
make web-check
make web-test
```

Run API tests that need Postgres against the throwaway pgvector database from
§3; do not claim integration coverage from a run without it.

Current PR CI runs three jobs: **API** (`uv lock --check`, `ruff check api
scripts`, `ruff format --check api scripts`, `mypy app`, and `pytest -n auto -q`
with pgvector Postgres — each xdist worker gets its own session-scoped disposable
database via the conftest fixture, so workers are fully isolated),
**Gateway** (`uv lock --check`, ruff check/format, `mypy app`, and `pytest -q`),
and **Web** (`npm run check:lq-ai` and `npm run test:frontend -- --run`). The
path-triggered Stack smoke workflow also runs on PRs that change the specified
API/Gateway/Web dependency manifests and locks, Dockerfiles, compose, API
migrations, its script, or its workflow. PR CI enforces coverage as a ratchet
(`.github/workflows/ci.yml`): `--cov-fail-under=80` for `api/` and
`--cov-fail-under=88` for `gateway/`, each pinned at or below the measured
figure so a PR that lowers coverage fails. The deterministic Cypress track
runs nightly and on manual dispatch (`.github/workflows/e2e.yml`), not on
every PR. A new endpoint needs unit + integration + OpenAPI-conformance
tests; a bug fix needs a regression test.

### Test-suite collision guards (miss these and the **whole** api suite crashes at collection)

These are non-obvious and have each taken down CI at least once:

- **New fully-implemented route** → add it to `IMPLEMENTED_ROUTES` (`api/tests/test_endpoints.py`) **and** bump the exact path count + `EXPECTED_PATHS` set in `api/tests/test_openapi.py`. The count is pinned; an off-by-one fails collection for the entire suite.
- **New `DELETE` endpoint returning `204`** → use the `response_class=Response` recipe (FastAPI's default `JSONResponse` asserts at import time on 204 and collapses the whole suite). The full recipe is in [CLAUDE.md § Common pitfalls](https://github.com/LegalQuants/lq-ai/blob/671eab0d/CLAUDE.md#common-pitfalls-catch-at-write-time-not-test-time).
- **`docs/api/backend-openapi.yaml` does not parse with plain `yaml.safe_load`** (pre-existing). `api/tests/test_openapi.py` is the authoritative conformance check — run it; don't trust a hand-eyeball.
- **Decimal cost fields serialize as JSON strings** — schemas type them as `string`, not `number`.

---

## 5. Commit & PR mechanics

- **DCO sign-off on every commit:** `git commit -s`. Imperative mood ("Add X", not "Added X"). Reference issues in the body (`Closes #123`, `Refs DE-103`).
- **Stage files explicitly** — never `git add -A`. Inspect `git status` and add
  only the paths intended for the contribution.
- **External contributors:** push the contribution branch only to your own fork,
  open a PR against `LegalQuants/lq-ai`, and respond to CI and maintainer review.
  Do not push maintainer remotes or merge the PR yourself.
- **Maintainers only:** after merging, an authorized maintainer may synchronize
  upstream and configured mirror remotes and apply the project's branch-retention
  policy. These are not contributor steps.

---

## 6. Merge gating (who merges what)

External contributors never auto- or self-merge. CI green is necessary but not
sufficient: a maintainer reviews and merges the PR. External/community PRs also
receive adversarial vetting per
[docs/security/external-contribution-vetting.md](https://github.com/LegalQuants/lq-ai/blob/671eab0d/docs/security/external-contribution-vetting.md).

Changes to `gateway/**`, authentication, authorization, audit, or cryptography
require the applicable security review under
[.github/CODEOWNERS](https://github.com/LegalQuants/lq-ai/blob/671eab0d/.github/CODEOWNERS) before a maintainer merges them.

When in doubt, treat it as the stricter row and ask.

---

## 7. Picking up a roadmap item, end to end

A worked shape for taking an item from [ROADMAP.md](https://github.com/LegalQuants/lq-ai/blob/671eab0d/docs/ROADMAP.md) / a [mini-PRD](https://github.com/LegalQuants/lq-ai/tree/671eab0d/docs/contribute/mini-prds) to a merged PR:

1. **Confirm it's still open and still shaped as described** — read HONEST-STATE and the relevant code; roadmap text can lag reality. If it's already partly built, scope to the gap.
2. **Read the anchors** — PRD section, OpenAPI sketch, DB schema, neighbouring code.
3. **Surface any fork** (§2 step 2) before building.
4. **Branch** off `main` (`feat/...` or `fix/...`).
5. **Build in reviewed increments** (§2 step 3).
6. **Gates** (§4), including the collision guards and a throwaway-DB migration check if you added one.
7. **Docs are part of the change** — update the PRD section, OpenAPI yaml, and DB schema doc that your change touches, in the same PR. Reconcile the narrative docs (README, HONEST-STATE, the feature guide) if behavior changed. File new deferrals as **DE-XXX in PRD §9** rather than expanding scope.
8. **Ship** (§5–§6): push only to your own fork, open the PR, and respond to CI and review; a maintainer merges it.

### Special case: skills with legal substance

Skills that contain legal work product (playbook positions, drafting guidance) go through a claim → draft → **attorney attestation** → review → merge path ([skills/CONTRIBUTING.md](/lq-ai/skills/attestation/)). **Agents do not attest** — the human contributor is the attesting party, and the maintainer team does **not** review legal substance (the in-house attorney is the validator). Synthetic test fixtures are functional QA the maintainer team *does* own.

---

## 8. When to stop and ask (non-negotiable)

Stop and put the decision to the human when:

- The task surfaces an **architectural choice** that wasn't anticipated. (Don't decide unilaterally and continue.)
- A decision isn't anchored in any decision-routing doc.
- You'd be **expanding scope** to fold in a good idea (file it DE-XXX instead).
- You'd be **adding a dependency** (it's part of the SBOM / supply-chain surface — needs justification).
- A change touches the **gateway or an authz path** and you're unsure of the security implication.

The friction is the point. An undocumented decision compounds across every task that follows it.

---

## 9. Reading the documentation site as plain text

If your first contact with the project is the published documentation site rather than a
clone, you don't need to render HTML to read it. Per [ADR 0028, decision
4](https://github.com/LegalQuants/lq-ai/blob/671eab0d/docs/adr/0028-documentation-site-generator-and-hosting.md#decision), the site build owns
the machine-readable surface: every page is also served as plain Markdown at its own URL
with `.md` appended, so a page's URL and its machine-readable twin differ only by that
suffix. The build may also publish `llms.txt` at the site root (every page with a one-line
description, grouped by namespace) and `llms-full.txt` (every page's full text
concatenated in sidebar order), but ADR 0028 is explicit that the `.md` routes are the
guaranteed surface — the `.txt` files depend on a plugin and must not be assumed present
without checking.

---

*Next stop: [CLAUDE.md](https://github.com/LegalQuants/lq-ai/blob/671eab0d/CLAUDE.md). Keep it open while you work.*

## Next

- [Set up for development](/lq-ai/contribute/dev-environment/)
- [Contribute code](/lq-ai/contribute/engineers/)
