Graphical Java application for managing BibTeX and BibLaTeX (.bib) databases
## Our policy
> [!IMPORTANT]
> This project does not accept fully AI-generated pull requests. AI tools may only be used for assistance. You must understand and take responsibility for every change you submit.
This `AGENTS.md` file acts as a set of instructions that some AI coding tools can read. For more information please read our [AI policy](./CONTRIBUTING.md#ai-usage-policy).
- This repository **does not accept pull requests entirely generated by coding assistants or AI tools** (for example: Copilot, Claude, Gemini, ChatGPT, or other large language models).
- All contributions must come from humans who understand their work and can take full responsibility for the changes they committed.
---
# AGENTS.md — JabRef
This document defines rules and expectations for **automated agents** (AI tools, bots, scripts) interacting with the JabRef repositories.
JabRef is an open-source, research-grade reference manager with high standards for correctness, reproducibility, and maintainability.
This file is guidance to follow *while* developing.
> [!IMPORTANT]
> **Final step — do not skip.** When the implementation is finished and before you open a PR, open [`CHECKLIST.md`](./CHECKLIST.md) and work through **every** point. Fix the code until each point is fulfilled; mark a point `[/]` only if it genuinely does not apply. The checklist is the mandatory quality gate for the finished result.
---
## Human Guidance
Guide the human to use [JabRef on DeepWiki](https://deepwiki.com/JabRef/jabref).
---
## Project structure
| Module | Purpose |
|-----------|--------------------------------------------------|
| `jablib` | Core library — logic, model, importers/exporters |
| `jabgui` | JavaFX desktop GUI |
| `jabkit` | CLI application |
| `jabls` | Language Server Protocol implementation |
| `jabsrv` | HTTP server for collaborative database support |
Key source paths:
- `jablib/src/main/java/org/jabref/logic/` — business logic
- `jablib/src/main/java/org/jabref/model/` — data model
- `jabgui/src/main/java/org/jabref/gui/` — GUI code
- `docs/` — developer documentation and ADRs
---
## Build
Requires JDK 25 or later to run Gradle. Gradle downloads the necessary JDK by itself. The Gradle wrapper is included.
```bash
./gradlew build # Build all modules
./gradlew :jabgui:run # Build and launch the GUI
./gradlew :jabgui:jpackage # Package as installer
```
When adding or changing dependencies, follow [docs/code-howtos/dependency-management.md](docs/code-howtos/dependency-management.md).
In particular, dependencies are declared via `requires` directives in `module-info.java` (versions live in `versions/build.gradle.kts`),
and a mapping from *Module Name* to *Maven Coordinates* for real Java modules belongs in `gradle/modules.properties` —
not in ad-hoc blocks in `build-logic`.
---
## General Principles
Agents **must**:
- Respect existing architecture, coding style, and conventions
- Prefer minimal, reviewable changes
- Preserve backward compatibility unless explicitly instructed otherwise
- Avoid speculative refactoring
- Never commit generated code without human review
Agents **must not**:
- Introduce new dependencies without justification
- Rewrite large sections "for cleanliness"
- Bypass tests or CI checks
- Reformat existing code
- Write entire PRs
- Write replies to PR review comments
- Submit code the contributor doesn't understand
- Generate documentation or comments without contributor's review
- Automate the submission of code changes
---
## Code Quality Requirements
### Java / JVM
- Target the configured **Gradle toolchain**
- Use **Java 25+ features**
- Use modern Java best practices, such as Arguments.of() instead of new Object[] especially in JUnit tests or Path.of() instead of Paths.get(), to improve readability and maintainability.
Using JavaFX Observable lists is considered best practice, too.
- Use modern Java data structures
BAD: new HashSet<>(Arrays.asList(...))
GOOD: Set.of(...)
- Java 21 introduced SequencedCollection and SequencedSet interfaces. Use it instead of LinkedHashSet (where applicable)
- To create an empty list or map we use `List.of()` and `Map.of()` instead of `Collections.emptyList()` and `Collections.emptyMap()`.
- Use Java Text blocks (\"\"\") for multiline string constants
### General Java style
- Follow existing formatting
- Match naming conventions exactly
- Keep methods small and focused
- New methods (and new classes) should follow the Single-responsibility principle (SRP).
- Avoid code duplication
- Avoid premature abstractions
- Follow JabRef's code style rules as documented in [docs/getting-into-the-code/guidelines-for-setting-up-a-local-workspace/intellij-13-code-style.md](docs/getting-into-the-code/guidelines-for-setting-up-a-local-workspace/intellij-13-code-style.md)
- Follow the principles of "Effective Java"
- Follow the principles of "Clean Code"
- Ensure that tests are green before committing
### Java code style
- Correctly spelled variable names (meaning: no typos in variable names).
- Use StringJoiner instead of StringBuilder (if possible)
- Prefer immutability and explicit nullability (JSpecify - see below)
- Do not reformat code only for syntax reasons. Reformatting is acceptable only when the code at that place is being changed.
- Remove commented code. (To keep a history of changes git was made for.)
- No \"new Thread()\", use \"org.jabref.logic.util.BackgroundTask\" and its \"executeWith\"
- Use compiled patterns (Pattern.compile)
Examples:
NOT: x.matches(\".*\\\\s{2,}.*\")
BUT:
private final static PATTERN = ...
and then PATTERN.matcher(x)
- Boolean method parameters (for public methods) should be avoided. Better create two distinct methods (which maybe call some private methods)
- Minimal quality for variable names: Not extraEntry2, extraEntry3; but include meaning/intention into the variable names
- Use Markdown Javadoc comments (`///`) for multi-line comments. Within them, use Markdown syntax, not JavaDoc inline tags: `` `code` `` instead of `{@code code}`, and `[ClassName]` instead of `{@link ClassName}`.
### Comments
- Do not add trivial comments just restating the code line in plain English.
- When commenting, focus on the "why" and general idea.
Example for trivial comments (to be avoided):
```java
// Commit the staged changes
RevCommit commit = git.commit();
fieldName = fieldName.trim().toLowerCase(); // Trim and convert to lower case
```
Both comments must not be added.
### Favor Optionals over nulls
- Use the methods of java.util.Optional. `ifPresent`.
NOT
```java
Optional<String> resolved = bibEntry.getResolvedFieldOrAlias(...);
String value = resolved.orElse(\"\");
doSomething(value)
```
Following is fine:
```java
bibEntry.getResolvedFieldOrAlias(...)
.ifPresent(value -> doSomething(value));
```
- If the `java.util.Optional` is really present, use one of the following:`get()`
```java
opt.ifPresent(...)
opt.map(...)
opt.orElseThrow(...)
```
but never just `orElse({someValueNeverUsed})`. You can add `assert ...isPresent();` in the line before.
- Use `ifPresentOrElse` instead of `if ...isPresent() { ... } else { ... }`
### Dealing with `null`
- New public methods should not return `null`. They should make use of `java.util.Optional`. In case `null` really needs to be used, the [JSpecify](https://jspecify.dev/) annotations must be used.
- Use JSpecify annotations (`@Nullable`, `@NullMarked`, `@NonNull`, ...) instead of `null` checks
- Annotate every new class with `@NullMarked` (`org.jspecify.annotations.NullMarked`) so members default to non-null.
- `null` should never be passed to a method (except it has the same name).
- DO NOT use `Objects.requireNonNull`, use JSpecify's `@NullMarked` and `@NonNull` annotations.
### Exceptions
- try blocks should cover as less statements as possible (and not whole methods)
- Do not throw unchecked exceptions (e.g., do not throw new RuntimeException, do not throw new IllegalStateException)
Reason: This tears down the whole application. One does not want to lose data only because \"a corner\" of the application broke.
- Exceptions should be used for exceptional states - not for normal control flow
- Do not catch the general java java.lang.Exception. Catch specific exceptions only.
- At exception, always `LOGGER.debug` (or higher level)
- BAD:
```java
try {
// do some actions
} catch (IOException e) {
LOGGER.info("Failed to push: ".concat(e.toString()));
}
```
This code converts an error to string and then concatenates it with a message. This is not how it's done in JabRef.
GOOD:
```java
try {
// do some actions
} catch (IOException e) {
LOGGER.info("Failed to push", e);
}
```
In JabRef, we use logging capabilities. The last argument of the logger call should be an exception.
- Logging may include other arguments. But the exception should be the last in arguments. Example: `LOGGER.info(\"Error. Var1: {}, Var2: {}\", var1, var2, e)`.
### JabRef-specific
- If code in org.jabref.model or org.jabref.logic has been changed, tests need to be adapted or updated accordingly.
Note: This rule does not apply for import statements.
- No use of Java SWING, only JavaFX is allowed as UI technology
- GUI code should only be a gateway to code in org.jabref.logic. More complex code regarding non-GUI operations should go into org.jabref.logic. Think of layered architecture.
- Labels should not end with \":\"
BAD: `<Label text="%Git Username:"/>`
GOOD: `<Label text="%Git Username"/>`
#### Localization
- Fix localization before committing. See `docs/code-howtos/localization.md`
- The `LocalizationConsistencyTest` failure output is actionable — follow it literally instead of guessing:
- `findMissingLocalizationKeys` failing → its output lists ready-to-paste `key=value` lines to **add** to `jablib/src/main/resources/l10n/JabRef_en.properties`. Place each near semantically related keys; reuse an existing similar key when one exists.
- `findObsoleteLocalizationKeys` failing → its output lists keys to **remove** from `JabRef_en.properties` (after confirming each is truly unused).
- Only edit `JabRef_en.properties`. Translated `JabRef_<lang>.properties` files are maintained by translators via Crowdin — never hand-edit them.
- JabRef is a multilingual program, When you write any user-facing text, it should be localized.
To do this in Java code, call `Localization.lang` method, like this:
```java
Localization.lang(\"Ok\")
```
More information at: <https://devdocs.jabref.org/code-howtos/localization.html>.
Note: This rule is not applied for logging. Logging strings should stay in English. I.e., LOGGER.error(\"...\") should contain English text.
- All labels and texts in the UI should be sentence case (and not title case)
- Avoid exclamation marks at the end of a sentence. They are more for screaming. Use a dot to end the sentence.
- Use "BibTeX" as spelling for bibtex in Java strings. In variable names "Bibtex" should be used.
- New strings should be consistent to other strings. They should also be grouped semantically together.
- Existing strings should be reused instead of introducing slightly different strings.
- User dialogs should have proper button labels: NOT yes/no/cancel, but indicating the action which happens when pressing the button
- Use placeholders if variance is in localization:
BAD: Localization.lang(\"Current JabRef version\") + \": \" + buildInfo.version);
GOOD: Localization.lang(\"Current JabRef version: %0\", buildInfo.version);
#### GUI
- One should use jabref's dialogService (instead of Java native FileChooser)
dialogService.showFileOpenDialog(fileDialogConfiguration).ifPresent(path -> ...)
and with FileDialogConfiguration offers the Builder pattern.
(see e.g NewLibraryFromPdfAction)
#### Testing / JUnit
- Name test classes `...Test` (singular), not `...Tests` — e.g. `JabSrvArchitectureTest`, not `JabSrvArchitectureTests`. This holds even for ArchUnit classes that bundle several `@ArchTest` rules.
- In JabRef, we don't use `@DisplayName`, we typically just write method name as is. The method name itself should be comprehensive enough.
- Instead of `Files.createTempDirectory` `@TempDir` JUnit5 annotation should be used.
- If `@TempDir` is used, there is no need to clean it up
Example for wrong code:
```java
@AfterEach
void tearDown() throws IOException {
FileUtils.cleanDirectory(tempDir.toFile());
}
```
- Assert the contents of objects (assertEquals), not checking for some Boolean conditions (assertTrue/assertFalse)
Example for wrong code:
```java
assertTrue(
entry.getFiles().stream()
.anyMatch(file -> file.getLink().equals(newFile.getFileName().toString()) ||
file.getLink().endsWith(\"/\" + newFile.getFileName().toString()))
);
```
- Do not catch exceptions in Test - let JUnit handle
BAD: try {...code...} catch (IOException e) {
throw new AssertionError(\"Failed to set up test directory\", e);
}
GOOD: ...code...
- When creating a new BibEntry object \"withers\" should be used: Instead of `setField`, `withField` methods should be used.
- Whenever you include a text in FXML (text labels, buttons, prompts in text fields, window titles, etc.), it should be localized.
To localize a string in FXML, prefix it with `%`.
Bad example:
```xml
<Label text="Want to help?"/>
```
In this code `text` property is the field that is used to show text to the user. This must be localized.
Fix:
```xml
<Label text=\"%Want to help?\"/>
```
- Plain JUnit assert should be used instead of org.assertj (if possible)
BAD: assertThat(gitPreferences.getAutoPushEnabled()).isFalse();
GOOD: assertFalse(gitPreferences.getAutoPushEnabled());
---
## Tests
Agents must:
- Add or update tests when behavior changes
- Keep tests deterministic and fast
- Respect existing JUnit parallelization and resource locks
- Never disable or weaken assertions
- Follow the rules at `docs/code-howtos/testing.md`
If a change cannot be reasonably tested, explain **why**.
### Linting checks
```bash
./gradlew checkstyleMain checkstyleTest checkstyleJmh
./gradlew modernizer
./gradlew --no-configuration-cache :rewriteDryRun || git diff
./gradlew javadoc
npx markdownlint-cli2 "docs/**/*.md"
npx markdownlint-cli2 "*.md"
```
### Fix formatting issues
- Run `./gradlew rewriteRun` to fix Java formatting issues.
- Run `docker run -v $(pwd):/github/workspace ghcr.io/leventebajczi/intellij-format:master "*.java" "" ".idea/codeStyles/Project.xml"` to fix more Java formatting issues.
### Logic tests
```bash
# Recommended during development (core library only)
./gradlew :jablib:check
# Full check (all modules)
./gradlew check
# Per-module
./gradlew :jablib:test
./gradlew :jabgui:test
# Single test class
./gradlew test --tests "org.jabref.logic.l10n.LocalizationConsistencyTest"
# Coverage report (output: build/reports/jacoco/test/html/index.html)
./gradlew jacocoTestReport
```
Tests requiring external resources have dedicated tasks:
- `./gradlew databaseTest` — requires PostgreSQL
- `./gradlew fetcherTest` — hits live external APIs
Quick check of core library:
```bash
./gradlew :jablib:check -x checkstyleJmh -x checkstyleMain -x checkstyleTest -x modernizer
```
---
## Requirements tracing (OpenFastTrace)
JabRef uses [OpenFastTrace](https://github.com/itsallcode/openfasttrace) to trace requirements to implementation and tests.
For a new feature or significant bug fix, **at minimum add the requirement** to the appropriate `docs/requirements/<area>.md` file. Full tracing (`Needs: impl` + implementation comments) is encouraged but can be skipped if the effort is disproportionate.
**Defining a requirement** in `docs/requirements/<area>.md`:
```markdown
### Example
`req~ai.example~1`
Description of the requirement.
```
The identifier must follow the heading with no blank line between them. Add `<!-- markdownlint-disable-file MD022 -->` at the end of the file.
**Optionally — linking an implementation** to a requirement (full trace):
```markdown
Needs: impl
```
```java
// [impl->req~ai.example~1]
```
**Checking coverage:**
```bash
./gradlew traceRequirements # output: build/tracing.txt
```
See `docs/requirements/` for existing requirements and `docs/requirements/index.md` for full guidance.
---
## Architecture decisions (MADR)
When a significant design or implementation decision is made, create a new MADR in `docs/decisions/`:
1. Copy `docs/decisions/adr-template.md` to `docs/decisions/<NNNN>-<short-title>.md` (next free number).
2. Fill in **Context and Problem Statement**, **Considered Options**, and **Decision Outcome**.
3. Add an entry to `docs/decisions/index.md`.
See [ADR-0000](docs/decisions/0000-use-markdown-architectural-decision-records.md) for the rationale and [adr-template.md](docs/decisions/adr-template.md) for the full template.
---
## Git & PR Etiquette
### Syncing with upstream
- **Never** use `git rebase`, `git pull --rebase` / `-r` / `--rebase-merges`, or any force-push (`--force`, `--force-with-lease`, `--force-if-includes`, `-f`, or `+`-prefixed refspecs). Rebasing rewrites commit SHAs already pushed and breaks review threads pinned to commits; force-push would then be required to publish the rewritten history.
- **Preferred** sync via explicit fetch + merge:
```bash
git fetch upstream --prune
git merge upstream/main
```
- Plain `git pull` is acceptable for updating the branch as long as your local config does not set `pull.rebase=true` (the enforcement hook blocks the explicit rebase variants regardless).
- Resolve conflicts inside the merge commit. Do not squash or reorder existing commits.
### Commits
- One logical change per commit
- Clear, technical commit messages
- Do not reference issues in commits
- Avoid force-pushes
- No generated artifacts unless required
### Pull requests
PR title:
- Contains a short title of the issue fixed (or what the PR addresses), not just \"Fix issue xyz\".
PR body — **must** be built from `.github/PULL_REQUEST_TEMPLATE.md`:
1. Read `.github/PULL_REQUEST_TEMPLATE.md`.
2. Fill every section: \"Related issues and pull requests\", \"PR Description\", \"Steps to test\", \"AI usage\".
3. The PR Description must explain **intent**, not implementation trivia. Do not list modified classes one by one.
4. Fill \"AI usage\": disclose every AI tool used **and the exact model ID** (for example `Claude Code (model claude-opus-4-7)`).
5. Keep **all** checklist items. Mark each `[x]` (done), `[ ]` (TODO), or `[/]` (not applicable). Never `[ x]` or `[.]`.
6. Remove **all** HTML comments before opening the PR.
7. Write the body to a temp file and run `gh pr create --body-file <file>` — never `--body`, which bypasses the template.
8. Only if the CHANGELOG.md entry used a `TODO` placeholder (meaning no issue has been confidently identified yet — an existing issue link always stays): immediately after the PR is created replace `TODO` with the real PR-number link (`[#NUM](https://github.com/JabRef/jabref/pull/NUM)`), then commit and push that change. If an issue is identified or created later, switch the link to the issue per the precedence rule above.
---
## Documentation
- Add a CHANGELOG.md entry only if the change is visible to the user.
- The CHANGELOG.md entry should be for end users (and not programmers).
- Do not add extra blank lines in CHANGELOG.md
- CHANGELOG.md entries link the issue number when an issue exists; the PR number is used only as a fallback when there is no issue.
- When no issue is known and the PR is not yet created, use `TODO` as the issue/PR reference placeholder — never invent a fake number.
- Before using `TODO`, search <https://github.com/JabRef/jabref/issues> and <https://github.com/JabRef/jabref-koppor/issues> for a matching issue. Link it only on a confident match; otherwise list candidates for human review and keep `TODO`. Never use `closes`/`fixes` keywords for a merely-similar issue.
- User documentation is available in a separate repository <https://github.com/JabRef/user-documentation>.
- No AI-disclosure comments inside source code
### Developer documentation
When changing behaviour or adding features, update the relevant files under `docs/`.
For complex flows or new architecture, consider adding a Mermaid sequence or class diagram to the relevant `docs/` file.
- [devdocs.jabref.org](https://devdocs.jabref.org/) — full developer reference. Resides in `docs/`
- `docs/getting-into-the-code/` — workspace setup, code style, IntelliJ config
- `docs/code-howtos/` — localization, testing, fetchers, tools
- `docs/decisions/` — Architecture Decision Records
- `docs/requirements/` — Requirements (OpenFastTrace)
---
## Authority
Human maintainers have final authority.
Agents are assistants, not decision-makers.
When uncertain: **do nothing and ask**.
---
## License
All contributions must comply with JabRef's existing license (MIT).
Do not introduce incompatible licenses or code.
## Standard header block
Use this exact block for all generated files:
```text
> [!IMPORTANT]
> This project does not accept fully AI-generated pull requests. AI tools may only be used for assistance. You must understand and take responsibility for every change you submit.
>
> Read and follow:
> • [AGENTS.md](./AGENTS.md)
> • [CONTRIBUTING.md](./CONTRIBUTING.md)
```
### Placement and prominence
- The header must appear before any instructions for tools or contributors.
- Do not bury the header after long intros or tables of contents.
<!-- markdownlint-disable-file MD033 MD041 -->