jabref

GitHub

Graphical Java application for managing BibTeX and BibLaTeX (.bib) databases

RAW Rules

AGENTS.md

## 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 -->