Important
This is a mandatory final gate. When the implementation is finished and before you open a PR, work through every box below and tick it. If a box cannot be ticked, fix the code first; mark a box [/] only if the point genuinely does not apply.
AGENTS.md describes how to write code while developing — this file confirms the finished result. Do not skip the checklist and do not skip individual points.
Read your own diff once, top to bottom, and confirm each point.
- No
== null/!= nullchecks — JSpecify annotations (@NullMarked,@Nullable,@NonNull) used instead. - No
Objects.requireNonNull(...)— nullability expressed via JSpecify annotations. - New classes annotated with
@NullMarked(org.jspecify.annotations.NullMarked). - New packages annotated with
@NullMarked(org.jspecify.annotations.NullMarked). -
Optionalconsumed withifPresent/ifPresentOrElse/map/orElseThrow— neverorElse(unusedValue)nor anisPresent()+get()block. -
StringUtil.isBlank(...)used instead ofs == null || s.isBlank().
- No
catch (Exception e)— only specific exceptions are caught. - No
throw new RuntimeException(...)/IllegalStateException(...)— these tear down the whole application. - Logged exceptions are passed as the last logger argument (
LOGGER.info("...", e)), not concatenated into the message string.
- New
BibEntryobjects built with withers (withField, notsetField). - Modern Java used:
List.of()/Map.of()/Set.of(),Path.of(),SequencedCollection/SequencedSet, text blocks. - Regexes use a precompiled
Pattern.compile(...)constant, notString.matches(...). - Keep alphabetical ordering when adding variables or enum items.
- Background work uses
org.jabref.logic.util.BackgroundTask, notnew Thread(). - No commented-out code, no trivial comments restating the code, no AI-disclosure comments in source.
- Markdown Javadoc (
///) uses Markdown syntax, not JavaDoc inline tags:`code`instead of{@code},[ClassName]instead of{@link}.
- All user-facing text localized (
Localization.langin Java,%prefix in FXML). - Sentence case (not Title Case); no trailing
!; labels do not end with:. - Variance expressed with placeholders (
"...: %0"), not string concatenation.
- User-controlled data (request params, entry fields, file contents) is HTML-escaped before being written into any
text/htmlresponse — including exception/error messages, not just the success body (XSS).
- In case issue lists an example, this concrete example is covered by a test case.
- Behavior changes in
org.jabref.model/org.jabref.logichave added or updated tests. - Tests assert object contents (
assertEquals), use plain JUnit asserts (not AssertJ), have no@DisplayName, do not catch exceptions (let them propagate so JUnit reports setup/teardown failures directly), and use@TempDirinstead of manual temp directories. - Fetcher tests hit the live endpoints — the remote API is not mocked or stubbed (automated-review suggestions to mock it are rejected on purpose).
Run in this order — cheapest first. Each must pass.
-
./gradlew :jablib:check(or./gradlew checkfor all modules). -
./gradlew checkstyleMain checkstyleTest checkstyleJmh. -
./gradlew modernizer. -
./gradlew --no-configuration-cache :rewriteDryRunreports no changes (run./gradlew rewriteRunto fix). -
./gradlew javadoc. -
npx markdownlint-cli2 "docs/**/*.md" "*.md"(only if Markdown changed). -
npm ci && npm run textlintreports no misspellings (only if Markdown changed). - Only if formatting is still off after
rewriteRun:docker run -v $(pwd):/github/workspace ghcr.io/leventebajczi/intellij-format:master "*.java" "" ".idea/codeStyles/Project.xml".
-
CHANGELOG.mdentry added if the change is visible to the user (end-user wording, no extra blank lines, sorted in next to existing entries about the same component/feature). Link the issue if one exists; link the PR only when no issue exists. UseTODOas the placeholder when neither is known yet — never a fake number. No entry for fixes to changes that were themselves introduced after the last release (feature only in## [Unreleased]) — update the existing unreleased entry instead if needed. - Searched jabref/issues and jabref-koppor/issues for a related issue; linked only on a confident match, otherwise kept
TODO(nocloses/fixesfor merely-similar issues). - Requirement added to
docs/requirements/<area>.mdif the change is a new feature or significant bug fix (skip for refactors, minor fixes, and internal changes). - Developer documentation under
docs/updated if behavior or architecture changed.
- PR body built from
.github/PULL_REQUEST_TEMPLATE.md, every section filled. - All checklist items kept and marked
[x],[ ], or[/]. - "Steps to test" is a numbered list with a cropped screenshot of the result for every visible change; no video (only allowed when another program is involved, e.g. drag and drop or push to an external application).
- All HTML comments removed from the PR body.
- PR created with
gh pr create --body-file <file>(not--body). - If
CHANGELOG.mdused aTODOplaceholder (no issue confidently identified yet — an existing issue link always stays), the PR was opened as draft, the placeholder was replaced with the real PR-number link after PR creation, committed and pushed, and only then was the PR marked ready for review. If an issue is identified or created later, the link is switched to the issue.