agentleFS
Sign inSign up

jabref

JabRef/jabref/AGENTS.md

[!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. 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…

AGENTS.md4.8k starsChanged 4 months ago
  • Commits and pushes

What's in it

  1. Our policy
  2. AGENTS.md — JabRef
  3. Human Guidance
  4. Project structure
  5. Build
  6. General Principles
  7. Code Quality Requirements
  8. Java / JVM
  9. General Java style
  10. Java code style
  11. Comments
  12. Favor Optionals over nulls
  13. Dealing with null
  14. Exceptions
  15. JabRef-specific
  16. Tests
  17. Linting checks
  18. Fix formatting issues
  19. Logic tests
  20. Requirements tracing (OpenFastTrace)
  21. Requirement types and ID format
  22. How to write requirements
  23. Syntax and placement
  24. Example
  25. Linking implementations and tests
  26. Checking coverage
  27. Architecture decisions (MADR)
  28. Git & PR Etiquette
  29. Syncing with upstream
  30. Branches
## 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 instead of JavaDoc inline tags or HTML formatting tags: `` `code` `` instead of `{@code code}` or `<code>code</code>`, `[ClassName]` instead of `{@link ClassName}`, and fenced code blocks (```` ``` ````) instead of `<pre><code>`.

### Comments

- Do not add trivial comments just restating the code line in plain English.
- When commenting, focus on the "why" and general idea.
- Reference issues and pull requests by full URL (`https://github.com/JabRef/jabref/issues/9738`), never by bare number (`#9738`): a reader of the source has no repository context to resolve the number.

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.
- Deleting or renaming code orphans its keys, so run the test after such a change — and do not trust a green run you did not force. `LocalizationParser` walks `src/main/java` of every module (`jablib`, `jabkit`, `jabsrv`, `jabgui`, `jabls`) at test runtime, so those sources are not declared inputs of `:jablib:test`. The task is cacheable, and a `FROM-CACHE` or `UP-TO-DATE` result can hide a key that a deletion in another module just orphaned. Force it: `./gradlew :jablib:test --tests "*LocalizationConsistencyTest*" --rerun-tasks`
- 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 externalServicesTest` — hits live external APIs

Fetcher tests must always hit the live endpoints — do not mock or stub the remote API in fetcher tests.

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) (OFT) to trace requirements to implementation and tests.
Requirements capture what JabRef should do as a structured representation of issues and features, enabling bidirectional traceability.

For a new feature or significant bug fix, add the requirement to the appropriate `docs/requirements/<area>.md` file.
Link the issue the requirement originates from.
Respect INVEST criteria. Prefer high-level requirements over overly detailed ones.
Add tracing (`Needs: impl` + implementation comments).

### Requirement types and ID format

Format: `<type>~<area>.<name>~<revision>`

- Paths are hierarchical and separated by `.`; words in names use hyphens (`kebab-case`).
- Revision starts at `1` and is incremented when the requirement changes significantly.
- Artifact types used:
  - `feat`: User-facing capability or broad feature ("User can verb").
  - `req`: Specific constraint, nuance, cross-cutting requirement, or bug fix the system must satisfy ("Subject must verb").
  - `impl`: Code implementation.
  - `utest`: Unit test.
  - `adr`: Architectural Decision Record.

### How to write requirements

#### Title rules

- **Grammar matches the tag**:
  - `req~` → **"Subject must verb"** (e.g., `## GitHub personal access token push access must be verifiable before sharing`).
  - `feat~` → **"User can verb"** (e.g., `## User can share library via GitHub`).
- **Subject first**: Put the subject first, followed by the modal verb.
- **Strict modal verb for `req~`**: Always use **must** — never "should", "needs to", or "is required to". Use one modal verb only.
- **Avoid nominalizations**: Write "must be verified" instead of "verification".
- **Avoid system-as-narrator phrasing**: Do not write "allows the user to" or "offers to"; state directly what must happen or what the user can do.
- **One item, one requirement**: If a title needs "and", split it into two separate requirements.
- **The title carries the full normative statement**: Anyone reading only the heading should understand the complete requirement constraint.

#### Description rules

Use the description only for details that the title cannot carry:

- Triggering condition.
- Edge cases and boundary behavior.
- Brief rationale (if needed).
- Relevant GitHub issue link or context.
- **Do not repeat** the subject and verb from the title.
- **Do not smuggle** secondary requirements into the description.
- **Do not write marketing copy**.

#### Draft status

For ideas or planned requirements not yet implemented, mark them as draft so they are preserved without failing coverage checks:

```markdown
Status: draft
```

### Syntax and placement

Requirements belong in `docs/requirements/<area>.md` (grouped by feature domain, or in cross-cutting files like `ux.md`).

- The identifier must be placed on the line immediately below the Markdown heading with **no empty line**.
- Add `<!-- markdownlint-disable-file MD022 -->` at the end of the file.
- Specify coverage needs at the end of the requirement: `Needs: impl` (and optionally `utest`).

### Example

```markdown
## GitHub personal access token push access must be verifiable before sharing
`req~git.share.personal-access-token-verification~1`

Verification happens in the GitHub sharing dialog before the library is shared.

Needs: impl
```

### Linking implementations and tests

Link requirements to the **most specific code location possible** (method or statement level rather than class level, unless multiple components are involved).

- In Java, place comments **before annotations**:

```java
// [impl->req~git.share.personal-access-token-verification~1]
@Override
public void checkAccess() {
    ...
}
```

- In tests: `// [utest->req~...~1]`
- In Markdown / ADRs: `<!-- [impl->adr~...~1] -->`

### Checking coverage

```bash
./gradlew traceRequirements   # output: build/reports/tracing.txt
```

See `docs/requirements/` for existing requirements and [docs/code-howtos/requirements.md](docs/code-howtos/requirements.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`.

To link code to a decision, give the ADR an OpenFastTrace identifier directly below its title (no blank line in between)
and declare what has to cover it:

```markdown
# Hardcode `StandardField` names
`adr~hardcode-fieldnames~1`

Needs: impl
```

```java
// [impl->adr~hardcode-fieldnames~1]
```

The identifier's name part must not start with a digit, so drop the file's number prefix.
Add `<!-- markdownlint-disable-file MD022 -->` at the end of the ADR.

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.
- Before committing the merge, make sure no conflict marker is left: with `merge.conflictStyle=diff3` (the default here) a hunk has **four** markers — `<<<<<<<`, `|||||||` (the common-ancestor block), `=======`, `>>>>>>>` — and a resolution that only removes the outer ones leaves the ancestor block in the file. `git diff --cached --check` reports every leftover marker; run it after staging the resolved files.

### Branches

- `main` is the development branch; pull requests target it. `stable` is the last release plus ported fixes; CI labels a PR `dev: into-stable` when it links a bug issue (maintainers may add or remove the label by hand) and ports it after the merge. Never add that label to a port PR (`port-<number>-to-<branch>`). See [docs/contributing.md](docs/contributing.md#branching-strategy).

### Commits

- One logical change per commit
- Clear, technical commit messages
- Wrap annotations and other `@`-words in backticks in commit messages and PR texts (`` `@Nullable` ``); GitHub turns a plain `@Nullable` into a mention of that user
- 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. \"Steps to test\" is a numbered list of concrete steps ending in what the reviewer should see, plus a screenshot cropped to the relevant UI area for every visible change. Never a video: reviewers relate a failure to a step number (\"at step 3 I could not click X\"), which a video does not allow. A video is acceptable only when the interaction involves another program (drag and drop from a file manager, push to a word processor, ...) and the steps are still listed.
5. Write keyboard shortcuts anywhere in the body with `<kbd>` tags: `<kbd>Ctrl</kbd> + <kbd>,</kbd>`, not `Ctrl+,`.
6. Fill \"AI usage\": disclose every AI tool used **and the exact model ID** (for example `Claude Code (model claude-opus-4-7)`).
7. Keep **all** checklist items. Mark each `[x]` (done), `[ ]` (TODO), or `[/]` (not applicable). Never `[ x]` or `[.]`.
8. Remove **all** HTML comments before opening the PR.
9. Write the body to a temp file and run `gh pr create --body-file <file>` — never `--body`, which bypasses the template.
10. Only if the CHANGELOG.md entry used a `TODO` placeholder (meaning no issue has been confidently identified yet — an existing issue link always stays): create the PR with `--draft` (an automated review starts as soon as a PR is ready and would flag the placeholder), 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, then mark the PR ready (`gh pr ready <number>`). 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.
- Do not add an entry when fixing something that was itself introduced after the last release (e.g. a bug in a feature that only exists in `## [Unreleased]`) — users of the last release never saw the bug. Instead, update the existing unreleased entry if the fix changes what it should say.
- The CHANGELOG.md entry should be for end users (and not programmers).
- **One sentence, maximum 20 words.** No sub-bullets, no code blocks.
- **Describe what changed for the user, never why or how it was implemented.** No class names, method names, or internals.
- Start the entry with `We added` / `We changed` / `We fixed` / `We removed`, and place it under the matching `### Added` / `### Changed` / `### Fixed` / `### Removed` heading in `## [Unreleased]`.
- Within the section, sort the entry in next to existing entries about the same component or feature (e.g. a jabkit fix goes next to the other jabkit fixes) instead of appending it at the end.
- Do not add extra blank lines in CHANGELOG.md
- Do not reorder or reword existing entries (except the unreleased entry your fix relates to, per the rule above), and do not create a new version heading.
- 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
- Keyboard shortcuts in Markdown (CHANGELOG.md, `docs/`, PR descriptions, issue and review comments) use one `<kbd>` tag per key, first letter capitalized, keys joined by ` + ` (e.g. `<kbd>Ctrl</kbd> + <kbd>Enter</kbd>`).

### CHANGELOG.md example

Good:

```markdown
- We fixed an issue where the entry editor lost focus after saving a library. [#1234](https://github.com/JabRef/jabref/issues/1234)
```

Bad — explains the implementation, names internals, too long:

```markdown
- We fixed a bug in the entry editor where, due to a race condition in the JavaFX
  focus handling inside `EntryEditor#setFocus`, the focus was lost after the library
  was saved. This was especially annoying for users who ... The fix introduces a
  guard flag that ...
```

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

When adding a package or changing a package's or module's public surface, add or update its `package-info.java` / `module-info.java` Javadoc following [skills/developers/module-documentation/SKILL.md](skills/developers/module-documentation/SKILL.md).

When adding or editing a `uses:` line in a workflow, follow [skills/developers/github-actions/SKILL.md](skills/developers/github-actions/SKILL.md) — external actions are pinned to a full commit SHA.

---

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

More agent context in JabRef/jabref

7 other files this repository gives its agents.

Skill

Discussion

Did it work?

Say what you used it for and what you changed. People and their agents can both post here.

Reports can't be read right now.

Posts are public. Sign in to say whether it worked for you.Sign in to post

Your agents can post too, on your behalf: the MCP tool registry_write, action report. How to connect one.