LibreChat/.github/CONTRIBUTING.md
Danny Avila f20b978075
🎯 ci: Retarget Pull Requests Opened Against main to dev (#15416)
* 🎯 ci: Retarget Pull Requests Opened Against `main` to `dev`

Contributions land on `dev`; `main` only moves at release time. With `main` as
the repository's default branch, pull requests default to it, and the mismatch
has been resolved by hand at merge time.

Adds a `pull_request_target` hook that switches a newly opened or reopened pull
request's base from `main` to `dev` and posts a one-time explanation, plus a
manual `workflow_dispatch` sweep (dry-run by default) for the existing backlog.

Release-bound upstream branches keep `main` as their base: `dev`, `release/*`,
`hotfix/*`, `backport/*` and `*-main`. The `target: main` label exempts any
pull request. The exemptions apply only to branches on this repository, so a
fork branch that happens to match one of those names is still retargeted.

The job checks out the trusted base commit rather than the pull request head,
so the write-scoped token never runs contributor code.

`pull_request_target` reads its workflow definition from the base branch, so
this must exist on `main` to take effect.

* 📝 docs: Record the `dev`-First Branch Policy for Agents

`CLAUDE.md` and `AGENTS.md` never stated which branch work targets, and the
default branch is `main` — so `gh pr create` and the GitHub UI point at `main`
unless told otherwise, and an agent following the defaults gets it wrong.

States the policy, the reason a backport to `main` is never needed (`main` is
kept as a strict ancestor of `dev`), and the exemptions the retarget workflow
honors.

* 📐 docs: Record Typecheck, Formatting and Issue-Closing Gotchas for Agents

Four repeat cycle-wasters that neither file stated, each verified against the
current tree:

- `tsdown` emits without typechecking, so `npm run build` proves nothing about
  types in `packages/api`, `packages/client` or `packages/data-schemas`; only
  `packages/data-provider` runs `tsc` in its build.
- `packages/client/tsconfig.json` excludes `*.spec.ts(x)` and `*.test.ts(x)`,
  so type errors in its specs surface only at run time.
- `npm run sort-imports` with no arguments rewrites all six source roots.
- `Fixes #N` never closes an issue here, because closing keywords are honored
  only on merges into the default branch and everything merges to `dev`.

Also notes that worktrees share one stash stack, and points at
`npm run static-checks -- --against origin/dev` as the local mirror of CI.

* 🔁 fix: Address Codex Review on the Retarget Workflow

- **Backport branches are no longer exempt.** Exempting `backport/*` and
  `*-main` contradicted the invariant the policy rests on: `main` is a
  fast-forward of `dev`, so a backport merged straight to `main` is precisely
  what breaks it. `target: main` remains the deliberate, explicit escape hatch;
  `dev`, `release/*` and `hotfix/*` stay exempt as release-bound branches.
- **`npm run update` does not sync `dev`.** `config/update.js` hard-codes
  `git checkout main` and `git pull origin main` — it is the self-host
  deployment updater, not a branch-sync tool. Telling contributors to refresh
  `dev` with it would have branched them off `main` and recreated the very
  flow this change exists to prevent. Replaced with the explicit git commands.
- **A dry run no longer reports work it did not do.** The summary said
  "Retargeted N" on a dispatch that wrote nothing, which could read as a
  completed sweep.
- **A failed explanation comment is recorded, not swallowed.** The base edit
  has already landed at that point, so a later run would skip the pull request
  and the contributor would never receive the note. It now counts, fails the
  run, and names the retry: `EXPLAIN_MISSING=true`, which posts a missing
  explanation onto an already-retargeted pull request.
- **The sweep paginates.** `gh pr list --limit 500` is a cap, not a page size,
  and would have silently ignored the remainder while reporting success.

Verified against the stubbed `gh` boundary: `*-main` now retargets, the dry run
performs zero writes and says so, a failing comment yields a non-zero exit, and
`EXPLAIN_MISSING` posts onto a pull request already based on `dev`. The
paginated query returns all 258 open pull requests based on `main`.

* 🧷 fix: Address the Second Codex Round on the Retarget Workflow

- **`already_explained` now separates "lookup failed" from "no marker".** A
  transient comments-API failure returned the same status as a clean miss, so
  the run would post a second explanation and report success — defeating the
  deduplication it exists for. A failed read now declines to post and is
  recorded.
- **`CONTRIBUTING.md` syncs from the canonical remote.** The previous fix
  pulled `dev` from `origin`, which in the fork-based flow described two
  sections later is the contributor's own fork — so it would fail outright or
  silently sync a stale branch, landing them on the wrong base again.
- **The hook retries on `synchronize`.** If the `opened` run failed before
  `gh pr edit` landed, nothing retried and the pull request stayed on `main`.
  A retargeted pull request no longer matches `branches: [main]`, so this adds
  no redundant runs — and the existing backlog now drains on contributor
  activity rather than only by sweep.
- **Sweeps serialize against each other** via a shared concurrency group, while
  the hook stays keyed per pull request. Deliberately not one global group:
  GitHub keeps a single pending job per group, so a burst of openings would
  cancel queued retargets — trading a rare duplicate comment for dropped
  retargets is the wrong way round.
- **`main` is an ancestor of `dev`, not a strict one.** Right after a sync the
  two refs are equal, which the previous wording described as a violated
  invariant.

Verified: a failing comments lookup edits the base but posts nothing and exits
non-zero.
2026-08-31 11:29:34 -04:00

10 KiB

Contributor Guidelines

Thank you to all the contributors who have helped make this project possible! We welcome various types of contributions, such as bug reports, documentation improvements, feature requests, and code contributions.

Contributing Guidelines

If the feature you would like to contribute has not already received prior approval from the project maintainers (i.e., the feature is currently on the roadmap), please submit a request in the Feature Requests & Suggestions category of the discussions board before beginning work on it. The requests should include specific implementation details, including areas of the application that will be affected by the change (including designs if applicable), and any other relevant information that might be required for a speedy review. However, proposals are not required for small changes, bug fixes, or documentation improvements. Small changes and bug fixes should be tied to an issue and included in the corresponding pull request for tracking purposes.

Please note that a pull request involving a feature that has not been reviewed and approved by the project maintainers may be rejected. We appreciate your understanding and cooperation.

If you would like to discuss the changes you wish to make, join our Discord community, where you can engage with other contributors and seek guidance from the community.

Our Standards

We strive to maintain a positive and inclusive environment within our project community. We expect all contributors to adhere to the following standards:

  • Using welcoming and inclusive language.
  • Being respectful of differing viewpoints and experiences.
  • Gracefully accepting constructive criticism.
  • Focusing on what is best for the community.
  • Showing empathy towards other community members.

Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that do not align with these standards.

To contribute to this project, please adhere to the following guidelines:

1. Development Setup

  1. Use Node.js v24.16.0.
  2. Run npm run smart-reinstall to install dependencies (uses Turborepo). Use npm run reinstall for a clean install, or npm ci for a fresh lockfile-based install.
  3. Build all compiled code: npm run build.
  4. Setup and run unit tests:
    • Copy .env.test: cp api/test/.env.test.example api/test/.env.test.
    • Run backend unit tests: npm run test:api.
    • Run frontend unit tests: npm run test:client.
  5. Setup and run integration tests:
    • Create .env: cp .env.example .env.
    • Install MongoDB Community Edition, ensure that mongosh connects to your local instance.
    • Run: npx install playwright, then npx playwright install.
    • Copy config.local: cp e2e/config.local.example.ts e2e/config.local.ts.
    • Copy librechat.yaml: cp librechat.example.yaml librechat.yaml.
    • Run: npm run e2e.

2. Development Notes

  1. Before starting work, sync dev from this repository. You are working in a fork, so origin is your fork — add the canonical remote once and sync from it:
    • git remote add upstream https://github.com/danny-avila/LibreChat.git
    • git fetch upstream dev && git checkout -B dev upstream/dev
    • npm run update is the self-host deployment updater — it checks out main and rebuilds your containers. Do not use it to refresh a development branch.
  2. Run linting command to find errors: npm run lint. Alternatively, ensure husky pre-commit checks are functioning.
    • npm install sets the hooks up for you; set HUSKY=0 to opt out.
    • The pre-commit hook runs the Static Checks CI job locally, scoped to the files in the commit. Run it by hand with npm run static-checks, against a base ref with npm run static-checks -- --against origin/dev, or with the slow gates (TypeScript, config migration tests, unused i18n keys, unused npm packages) via npm run static-checks:full.
    • Every commit gets ESLint, Prettier, import order and circular-dependency detection; the slower gates stay opt-in so commits stay fast.
  3. After your changes, reinstall packages in your current branch using npm run reinstall and ensure everything still works.
    • Restart the ESLint server ("ESLint: Restart ESLint Server" in VS Code command bar) and your IDE after reinstalling or updating.
  4. Clear web app localStorage and cookies before and after changes.
  5. To check for introduced errors, build all compiled code: npm run build.
  6. Run backend unit tests: npm run test:api.
  7. Run frontend unit tests: npm run test:client.
  8. Run integration tests: npm run e2e.

3. Git Workflow

We utilize a GitFlow workflow to manage changes to this project's codebase. Follow these general steps when contributing code:

  1. Fork the repository and branch off dev with a descriptive slash-based name (e.g., new/feature/x). All contributions target dev; main only moves at release time, and pull requests opened against it are retargeted automatically.
  2. Implement your changes and ensure that all tests pass.
  3. Commit your changes using conventional commit messages with GitFlow flags. Begin the commit message with a tag indicating the change type, such as "feat" (new feature), "fix" (bug fix), "docs" (documentation), or "refactor" (code refactoring), followed by a brief summary of the changes (e.g., feat: Add new feature X to the project).
  4. Submit a pull request against dev with a clear and concise description of your changes and the reasons behind them.
  5. We will review your pull request, provide feedback as needed, and eventually merge the approved changes into the dev branch.

4. Commit Message Format

We follow the semantic format for commit messages.

Example

feat: add hat wobble
^--^  ^------------^
|     |
|     +-> Summary in present tense.
|
+-------> Type: chore, docs, feat, fix, refactor, style, or test.

Commit Guidelines

  • Do your best to reduce the number of commits, organizing them as much possible. Look into squashing commits in order to keep a neat history.
  • For those that care about maximizing commits for stats, adhere to the above as I 'squash and merge' an unorganized and/or unformatted commit history, which reduces the number of your commits to 1,:
* Update Br.tsx

* Update Es.tsx

* Update Br.tsx

5. Pull Request Process

When submitting a pull request, please follow these guidelines:

  • Ensure that any installation or build dependencies are removed before the end of the layer when doing a build.
  • Update the README.md with details of changes to the interface, including new environment variables, exposed ports, useful file locations, and container parameters.
  • Increase the version numbers in any example files and the README.md to reflect the new version that the pull request represents. We use SemVer for versioning.

Ensure that your changes meet the following criteria:

  • All tests pass as highlighted above.
  • The code is well-formatted and adheres to our coding standards.
  • The commit history is clean and easy to follow. You can use git rebase or git merge --squash to clean your commit history before submitting the pull request.
  • The pull request description clearly outlines the changes and the reasons behind them. Be sure to include the steps to test the pull request.

6. Naming Conventions

Apply the following naming conventions to branches, labels, and other Git-related entities:

  • Branch names: Descriptive and slash-based (e.g., new/feature/x).
  • Labels: Descriptive and kebab case (e.g., bug-fix).
  • JS/TS: Directories and file names: Descriptive and camelCase. First letter uppercased for React files (e.g., helperFunction.ts, ReactComponent.tsx).
  • Docs: Directories and file names: Descriptive and snake_case (e.g., config_files.md).

7. Coding Standards

For detailed coding conventions, workspace boundaries, and architecture guidance, refer to the AGENTS.md file at the project root. It covers code style, type safety, import ordering, iteration/performance expectations, frontend rules, testing, and development commands.

8. TypeScript Conversion

  1. Original State: The project was initially developed entirely in JavaScript (JS).

  2. Frontend: Fully transitioned to TypeScript.

  3. Backend:

    • The legacy Express.js server remains in /api as JavaScript.
    • All new backend code is written in TypeScript under /packages/api, which is compiled and consumed by /api.
    • Shared database logic lives in /packages/data-schemas (TypeScript).
    • Shared frontend/backend API types and services live in /packages/data-provider (TypeScript).
    • Minimize direct changes to /api; prefer adding TypeScript code to /packages/api and importing it.

9. Module Import Conventions

Imports are organized into three sections (in order):

  1. Package imports — sorted from shortest to longest line length.

    • react is always the first import.
    • Multi-line (stacked) imports count their total character length across all lines for sorting.
  2. import type imports — sorted from longest to shortest line length.

    • Package type imports come first, then local type imports.
    • Line length sorting resets between the package and local sub-groups.
  3. Local/project imports — sorted from longest to shortest line length.

    • Multi-line (stacked) imports count their total character length across all lines for sorting.
    • Imports with alias ~ are treated the same as relative imports with respect to line length.
  • Consolidate value imports from the same module as much as possible.
  • Always use standalone import type { ... } for type imports; never use inline type keyword inside value imports (e.g., import { Foo, type Bar } is wrong).

Note: ESLint will automatically enforce these import conventions when you run npm run lint --fix or through pre-commit hooks.

For the full set of coding standards, see AGENTS.md.


Go Back to ReadMe