Skip to content

Persist group membership in folder libraries and make pair renames opt-in - #769

Open
koppor wants to merge 15 commits into
directory-splitfrom
directory-groups-rename
Open

koppor wants to merge 15 commits into
directory-splitfrom
directory-groups-rename

Conversation

@koppor

@koppor koppor commented Sep 13, 2026 •

Copy link
Copy Markdown
Member

📚 Directory-as-library stack — bottom → top, each builds on the one below:

  1. Programmatic Hayagriva writer (directory-as-library, phase 0) #736 · Hayagriva YAML writer
  2. Open folder as library (directory as library, phase 1) #737 · Open folder as library
  3. Live inbound sync for directory libraries (phase 2) #738 · Inbound file sync
  4. Write-back to Hayagriva sidecars (directory as library, phase 3) #739 · Write-back to sidecars
  5. Groups panel mirrors the directory structure (phase 4, #10930) #740 · Directory-structure groups
  6. Pattern-driven pair renames for directory libraries (phase 5a) #741 · Pattern-driven pair renames
  7. Mirror directory libraries into a .bib with git-sync merge-back #743 · .bib mirror + merge-back
  8. Convert .bib libraries into directory libraries #744 · Convert .bib → directory
  9. Make directory libraries reachable through the REST API #745 · REST API support
  10. Split DirectoryLibrarySynchronizer into collaborators #760 · Split the synchronizer
  11. Persist group membership in folder libraries and make pair renames opt-in #769 · Group membership + opt-in renames ← this PR

Summary

🤖 Adding an entry to a group in a folder library was lost on restart, because explicit membership lives in the groups field and that field had no sidecar key; it is now written as a JabRef extension key. Sidecar and PDF pairs are no longer renamed on every metadata change: renames follow the global "Auto rename files if entry changes" preference, and the General tab of the library properties can override it per library (also for .bib libraries).

jabref-contrib-policy:4.2:reviewed​:ok

Analogies

Like honey, group membership now sticks to the sidecar instead of running off at restart. Like chocolate, the rename knob comes in three squares: global, always, never. And like the moon, the library override only shows its face when someone deliberately turns it.

Steps to test

  1. Open a folder as library, create a group, add an entry to it, quit JabRef, reopen the folder: the entry is still in the group.
  2. Change the title of an entry with a PDF: the PDF and sidecar keep their names (global preference off).
  3. Library properties > General > "Auto rename files if entry changes" = Yes, change the title again: the pair is renamed to the filename pattern.
  4. Same override in a .bib library: file renames follow the library setting instead of the global one.

No screenshot: the new row is a plain label + combo box below "Keyword separator" in the General tab.

Related issues and pull requests

Stacked on #760 (base branch directory-split). Closes NA

AI usage

Claude Code (model claude-fable-5-1), AIL4.

AI CHECKLIST.md walkthrough

1. Code self-review

Nullability and control flow
  • No == null / != null checks — @Nullable Boolean read through Optional.ofNullable.
  • No Objects.requireNonNull(...).
  • [/] No new classes.
  • Optional consumed with ifPresent / ifPresentOrElse / map / orElse.
  • [/] No null-or-blank checks needed.
Exceptions
  • No catch (Exception e).
  • No throw new RuntimeException(...) / IllegalStateException(...).
  • [/] No new logging.
Style and idioms
  • [/] Tests use HayagrivaMapping.toBibEntry and the existing synchronizer fixtures, no hand-built entries beyond the existing pattern.
  • Modern Java: Optional chains, method references.
  • No regexes.
  • No new background work.
  • No commented-out code, no trivial comments, no AI-disclosure comments.
  • Markdown Javadoc (///).
User-facing text
  • New l10n keys: Use global preference (%0), Yes, No; existing Auto rename files if entry changes reused.
  • Sentence case.
  • %0 placeholder for the current global value.
Security
  • [/] No trust-boundary input.
Tests
  • HayagrivaEntryWriterTest.mergeWritesGroupMembershipAndReadsItBack, MetaDataSerializerTest.serializeAutoRenameFilesOnChangeOverride, MetaDataParserTest.parsesAutoRenameFilesOnChangeOverride; DirectoryLibrarySynchronizerTest.userGroupsFromMirrorMetadataAreRestoredAtOpen extended to membership across a restart (fails without the fix, checked by mutation).
  • Plain JUnit asserts, no @DisplayName, @TempDir.
  • [/] No fetcher tests.

2. Verification commands

  • ./gradlew :jablib:test --tests '*HayagrivaEntryWriterTest' --tests '*MetaDataSerializerTest' --tests '*MetaDataParserTest' --tests '*DirectoryLibrarySynchronizerTest' --tests '*LocalizationConsistencyTest'
  • ./gradlew :jabgui:compileJava :jablib:checkstyleMain :jablib:checkstyleTest :jabgui:checkstyleMain
  • [/] ./gradlew modernizer — part of CI.
  • [/] :rewriteRun — part of CI.
  • ./gradlew traceRequirements
  • [/] Markdown: requirement doc only, checked by the CI lint.
  • [/] IntelliJ formatter (docker) unavailable on this machine; the CI format job is the backstop.

3. Documentation

  • CHANGELOG.md entry for the library override (visible in .bib libraries too); the sidecar fix itself is part of the unreleased feature.
  • [/] No related issue.
  • req~directory-library.pattern-rename bumped to ~2 (auto-rename opt-in), markers updated.
  • [/] No package changes.

4. Pull request

  • PR body built from .github/PULL_REQUEST_TEMPLATE.md, every section filled.
  • All checklist items kept and marked.
  • All HTML comments removed.
  • PR created with --body-file.
  • No TODO placeholder in CHANGELOG.md.

Checklist

  • I own the copyright of the code submitted and I license it under the MIT license
  • If AI tools were used, I disclosed them in the "AI usage" section and reviewed, understood, and take full ownership of all AI-generated code
  • I manually tested my changes in running JabRef (always required) — not yet: the restart round trip is covered headlessly by the extended synchronizer test; GUI check of the new properties row pending
  • I added JUnit tests for changes (if applicable)
  • [/] I added screenshots in the PR description (if change is visible to the user)
  • I added one sentence (max 20 words) to CHANGELOG.md describing the change from the user's point of view (if the change is visible to the user)
  • I checked the user documentation for up to dateness and submitted a pull request to our user documentation repository — folder-library docs are still pending as a whole

🤖 Generated with Claude Code

https://claude.ai/code/session_01H7bhV1r2H2XZvjHrYQkiJt

koppor and others added 3 commits September 13, 2026 23:59
Explicit group membership was lost across restarts because the `groups` field had no
Hayagriva key. Sidecar renames now follow "Auto rename files if entry changes", with a
library-level override in the General properties tab that also applies to .bib libraries.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01H7bhV1r2H2XZvjHrYQkiJt
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01H7bhV1r2H2XZvjHrYQkiJt
@koppor
koppor marked this pull request as ready for review September 13, 2026 22:01
@qodo-free-for-open-source-projects

qodo-free-for-open-source-projects Bot commented Sep 13, 2026 •

Copy link
Copy Markdown

PR Summary by Qodo

Persist folder groups and add per-library rename control

🐞 Bug fix ✨ Enhancement 🧪 Tests 📝 Documentation 🕐 40+ Minutes

Grey Divider

AI Description

• Persists explicit group membership in Hayagriva sidecars across folder-library restarts.
• Adds a tri-state library override for automatic linked-file renaming.
• Applies shared rename precedence to BibTeX and directory-library workflows.
Diagram

graph TD
  A["Global Preference"] --> C["Rename Policy"] --> D["Library Workflow"] --> E["Sidecar PDF Pair"]
  B["Library Override"] --> C
  F["Group Membership"] --> G["Hayagriva Sidecar"] --> H["Restart Restore"]
Loading
High-Level Assessment

The current approach is appropriate: Optional directly represents inherit, enabled, and disabled states, while one shared policy method keeps precedence consistent across library types. A dedicated tri-state enum or duplicated workflow-specific checks would add conversion or maintenance overhead without improving behavior.

Files changed (22) +222 / -21

Enhancement (10) +93 / -8
MetadataChangeDetailsView.javaDisplay rename override metadata changes +15/-2

Display rename override metadata changes

• Adds the rename override to metadata-diff labels and formats its inherited, enabled, and disabled values for users.

jabgui/src/main/java/org/jabref/gui/collab/metedatachange/MetadataChangeDetailsView.java

AutoRenameFileOnEntryChange.javaCentralize automatic rename precedence +13/-3

Centralize automatic rename precedence

• Resolves rename eligibility from the library override, global preference, and filename pattern. Skips direct event-based renaming for directory libraries because their write-back owns pair renames.

jabgui/src/main/java/org/jabref/gui/externalfiles/AutoRenameFileOnEntryChange.java

GeneralPropertiesView.javaConfigure the tri-state rename override +15/-0

Configure the tri-state rename override

• Populates and binds a combo box for inheriting the global rename preference or explicitly enabling or disabling it.

jabgui/src/main/java/org/jabref/gui/libraryproperties/general/GeneralPropertiesView.java

GeneralPropertiesViewModel.javaLoad and store the rename override +7/-0

Load and store the rename override

• Adds an optional Boolean property backed by library metadata and persists explicit values or clears the override when inheritance is selected.

jabgui/src/main/java/org/jabref/gui/libraryproperties/general/GeneralPropertiesViewModel.java

GeneralProperties.fxmlAdd automatic rename controls to library properties +4/-0

Add automatic rename controls to library properties

• Adds the automatic-rename label and tri-state combo box to the General properties grid.

jabgui/src/main/resources/org/jabref/gui/libraryproperties/general/GeneralProperties.fxml

MetaDataDiff.javaInclude rename overrides in metadata comparisons +2/-0

Include rename overrides in metadata comparisons

• Introduces a difference type for the automatic rename override and reports changes between inherited and explicit states.

jablib/src/main/java/org/jabref/logic/bibtex/comparator/MetaDataDiff.java

MetaDataSerializer.javaSerialize explicit rename overrides +2/-0

Serialize explicit rename overrides

• Writes the optional automatic rename setting into library metadata when an override is present.

jablib/src/main/java/org/jabref/logic/exporter/MetaDataSerializer.java

MetaDataParser.javaParse validated rename overrides +9/-0

Parse validated rename overrides

• Accepts explicit true or false metadata values and clears malformed overrides so the global preference remains effective.

jablib/src/main/java/org/jabref/logic/importer/util/MetaDataParser.java

MetaData.javaModel the optional library rename setting +22/-3

Model the optional library rename setting

• Adds the nullable rename override with accessors and change notifications. Includes it in metadata copying, equality, hashing, and diagnostics.

jablib/src/main/java/org/jabref/model/metadata/MetaData.java

JabRef_en.propertiesLocalize tri-state rename choices +4/-0

Localize tri-state rename choices

• Adds English strings for global inheritance and explicit yes or no selections.

jablib/src/main/resources/l10n/JabRef_en.properties

Bug fix (2) +12 / -5
OpenDirectoryLibraryAction.javaApply rename policy during directory write-back +7/-2

Apply rename policy during directory write-back

• Evaluates automatic rename eligibility lazily whenever a directory-library sidecar is written. Returning no generated filename suppresses pair renaming when disabled.

jabgui/src/main/java/org/jabref/gui/importer/actions/OpenDirectoryLibraryAction.java

HayagrivaMapping.javaMap group membership into Hayagriva sidecars +5/-3

Map group membership into Hayagriva sidecars

• Adds the JabRef extension key 'groups' to scalar field mapping so explicit memberships survive sidecar write and reload cycles.

jablib/src/main/java/org/jabref/logic/importer/fileformat/HayagrivaMapping.java

Tests (7) +109 / -3
AutoRenameFileOnEntryChangeTest.javaTest rename override precedence and directory delegation +41/-1

Test rename override precedence and directory delegation

• Covers library overrides winning over global preferences in both directions. Verifies directory libraries defer renaming to sidecar write-back.

jabgui/src/test/java/org/jabref/gui/externalfiles/AutoRenameFileOnEntryChangeTest.java

MetaDataDiffTest.javaTest rename override metadata diffs +13/-0

Test rename override metadata diffs

• Verifies that changing from inherited behavior to an explicit disabled override produces the expected difference.

jablib/src/test/java/org/jabref/logic/bibtex/comparator/MetaDataDiffTest.java

DirectoryLibrarySynchronizerTest.javaVerify group membership survives restart +7/-2

Verify group membership survives restart

• Extends the directory-library restart scenario to assign an entry to an explicit group, flush its sidecar, reopen the library, and assert restored membership.

jablib/src/test/java/org/jabref/logic/directorylibrary/DirectoryLibrarySynchronizerTest.java

HayagrivaEntryWriterTest.javaTest Hayagriva group membership round-trip +22/-0

Test Hayagriva group membership round-trip

• Verifies the writer emits the 'groups' extension key and the importer restores its value onto the entry.

jablib/src/test/java/org/jabref/logic/exporter/HayagrivaEntryWriterTest.java

MetaDataSerializerTest.javaTest rename override serialization +7/-0

Test rename override serialization

• Confirms an explicit disabled override is serialized under the new metadata key.

jablib/src/test/java/org/jabref/logic/exporter/MetaDataSerializerTest.java

MetaDataParserTest.javaTest valid and malformed rename metadata +18/-0

Test valid and malformed rename metadata

• Covers parsing an explicit false value and clearing a previous override when malformed input is encountered.

jablib/src/test/java/org/jabref/logic/importer/util/MetaDataParserTest.java

MetaDataOverwriteWithTest.javaCover rename overrides during metadata replacement +1/-0

Cover rename overrides during metadata replacement

• Adds the rename override to populated metadata fixtures so overwrite behavior includes the new property.

jablib/src/test/java/org/jabref/model/metadata/MetaDataOverwriteWithTest.java

Documentation (3) +8 / -5
CHANGELOG.mdDocument the per-library rename override +1/-0

Document the per-library rename override

• Adds a user-facing changelog entry for controlling automatic file renames from library properties.

CHANGELOG.md

directory-library.mdMake directory pair renaming conditional +6/-4

Make directory pair renaming conditional

• Revises the pair-renaming requirement to depend on the global preference or a library override. Bumps the requirement version accordingly.

docs/requirements/directory-library.md

SidecarWriteBack.javaReference the opt-in rename requirement +1/-1

Reference the opt-in rename requirement

• Updates the implementation trace marker to the revised directory-library pair-renaming requirement.

jablib/src/main/java/org/jabref/logic/directorylibrary/SidecarWriteBack.java

@qodo-free-for-open-source-projects

qodo-free-for-open-source-projects Bot commented Sep 13, 2026 •

Copy link
Copy Markdown

Code Review by Qodo

🐞 Bugs (1) 📘 Rule violations (1) 📎 Requirement gaps (0) 🎨 UX issues (0) 🔗 Cross-repo conflicts (0) 📜 Skill insights (0)

Grey Divider


Action required

1. New groups can corrupt old memberships ✓ Resolved 🐞 Bug ≡ Correctness
Description
groups now preserves the raw delimiter-separated field in each sidecar, but reopening a directory
library restores group objects without restoring the mirror's library-specific keyword separator
into the local metadata. If that separator differs from the global preference, a group created after
reopening parses and rewrites existing memberships with the wrong delimiter, after which previously
restored groups no longer recognize their entries.
Code

jablib/src/main/java/org/jabref/logic/importer/fileformat/HayagrivaMapping.java[115]

+        scalars.put("groups", StandardField.GROUPS);
Evidence
The new mapping imports groups unchanged as one scalar string. The scanner creates fresh metadata
and installs only an automatic group tree; mirror adoption moves parsed user-group objects but does
not copy the parsed keyword separator, while new explicit groups obtain their separator from local
metadata with a global fallback and use it to parse and rewrite the existing field.

jablib/src/main/java/org/jabref/logic/importer/fileformat/HayagrivaMapping.java[129-144]
jablib/src/main/java/org/jabref/logic/directorylibrary/DirectoryLibraryScanner.java[76-80]
jablib/src/main/java/org/jabref/logic/directorylibrary/BibMirror.java[183-200]
jablib/src/main/java/org/jabref/model/database/BibDatabaseContext.java[147-150]
jabgui/src/main/java/org/jabref/gui/groups/GroupDialogViewModel.java[313-320]
jablib/src/main/java/org/jabref/model/groups/WordKeywordGroup.java[68-76]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
Persisted group membership is delimiter-dependent, but a reopened directory library does not restore its library-specific keyword separator from mirror metadata. Creating or modifying groups afterward can rewrite the sidecar field using the global delimiter and invalidate existing memberships.
## Fix Focus Areas
- jablib/src/main/java/org/jabref/logic/importer/fileformat/HayagrivaMapping.java[112-115]
- jablib/src/main/java/org/jabref/logic/directorylibrary/BibMirror.java[183-200]
- jablib/src/main/java/org/jabref/model/database/BibDatabaseContext.java[147-150]
## Recommended Fix
Restore the mirror's keyword-separator metadata into the scanned directory-library context before adopted groups or newly created groups operate on persisted membership fields. Add a restart test where the library separator differs from the global separator, then create another explicit group and verify all old and new memberships remain recognized.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


2. Folder libraries forget rename overrides ✓ Resolved 🐞 Bug ≡ Correctness
Description
BibMirror.doInitialize() restores only group nodes via adoptUserGroups, discarding the mirrored
autoRenameFilesOnChange value before isEnabled reads the freshly scanned context’s metadata.
After reopening a folder library with an explicit Yes or No override, subsequent sidecar/PDF writes
and the properties dialog silently use the global preference instead.
Code

jabgui/src/main/java/org/jabref/gui/importer/actions/OpenDirectoryLibraryAction.java[R167-169]

+                AutoRenameFileOnEntryChange.isEnabled(databaseContext, preferences.getFilePreferences())
+                ? FileUtil.createFileNameFromPattern(databaseContext.getDatabase(), entry, preferences.getFilePreferences().getFileNamePattern())
+                : Optional.empty();
Evidence
Directory scanning creates a fresh BibDatabaseContext with default metadata, while the .bib
mirror serializes the directory library’s metadata, including autoRenameFilesOnChange. Mirror
initialization then invokes only adoptUserGroups, which moves group nodes without copying other
metadata, and the rename predicate consults only the scanned context’s metadata; therefore, the
persisted override cannot affect the reopened library.

jabgui/src/main/java/org/jabref/gui/importer/actions/OpenDirectoryLibraryAction.java[166-175]
jabgui/src/main/java/org/jabref/gui/importer/actions/OpenDirectoryLibraryAction.java[195-212]
jablib/src/main/java/org/jabref/logic/exporter/MetaDataSerializer.java[54-59]
jablib/src/main/java/org/jabref/logic/directorylibrary/DirectoryLibraryScanner.java[76-79]
jablib/src/main/java/org/jabref/logic/directorylibrary/BibMirror.java[108-125]
jablib/src/main/java/org/jabref/logic/directorylibrary/BibMirror.java[183-200]
jablib/src/main/java/org/jabref/logic/directorylibrary/DirectoryLibraryScanner.java[76-80]
jablib/src/main/java/org/jabref/logic/directorylibrary/BibMirror.java[116-126]
jabgui/src/main/java/org/jabref/gui/externalfiles/AutoRenameFileOnEntryChange.java[31-33]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
Directory-library startup parses the `.bib` mirror but restores only user-defined groups into the freshly scanned context, leaving the persisted `autoRenameFilesOnChange` override absent after reopening. Restore that metadata so the filename generator and properties dialog continue to use the library’s explicit setting rather than the global preference.
## Fix Focus Areas
- jablib/src/main/java/org/jabref/logic/directorylibrary/BibMirror.java[116-135]
- jablib/src/main/java/org/jabref/logic/directorylibrary/BibMirror.java[183-200]
- jabgui/src/main/java/org/jabref/gui/importer/actions/OpenDirectoryLibraryAction.java[166-169]
- jabgui/src/main/java/org/jabref/gui/externalfiles/AutoRenameFileOnEntryChange.java[31-33]
## Recommended Fix
During directory-library mirror initialization, copy `remote.getMetaData().getAutoRenameFilesOnChange()` into the freshly scanned context’s metadata, including clearing the local override when the mirror has none. Apply this independently of whether the mirror contains user-defined groups and use the existing model-update marshaller so metadata mutation occurs on the expected thread. Add a reopen/restart test covering explicit true and false overrides and verify that the reopened context retains each setting.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


3. Rename override calls hide intent 📘 Rule violation ⚙ Maintainability
Description
setAutoRenameFilesOnChange(boolean) introduces a public mode-setting API whose call sites express
the two alternatives only as true and false. Parser, properties, and test callers now depend on
this flag, so later changes must interpret each literal or computed boolean rather than invoke an
intention-revealing operation.
Code

jablib/src/main/java/org/jabref/model/metadata/MetaData.java[R193-195]

+    public void setAutoRenameFilesOnChange(boolean autoRenameFilesOnChange) {
+        this.autoRenameFilesOnChange = autoRenameFilesOnChange;
+        postChange();
Evidence
Compliance rule 6 prohibits new public boolean parameters where separate named methods can represent
the alternatives. The cited model method is newly added, public, and accepts exactly such a boolean
flag.

AGENTS.md: Avoid Boolean Parameters in Public APIs: AGENTS.md: Avoid Boolean Parameters in Public APIs: AGENTS.md: Avoid Boolean Parameters in Public APIs: AGENTS.md: Avoid Boolean Parameters in Public APIs: AGENTS.md: Avoid Boolean Parameters in Public APIs: AGENTS.md: Avoid Boolean Parameters in Public APIs: AGENTS.md: Avoid Boolean Parameters in Public APIs: AGENTS.md: Avoid Boolean Parameters in Public APIs
jablib/src/main/java/org/jabref/model/metadata/MetaData.java[193-195]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
The new public metadata API accepts a boolean mode flag, making callers encode enable and disable operations as boolean values.
## Fix Focus Areas
- jablib/src/main/java/org/jabref/model/metadata/MetaData.java[189-200]
## Recommended Fix
Replace the boolean setter with separate intention-revealing enable and disable methods while retaining the clear method for global fallback. Update parser, properties, and test call sites to select the appropriate named operation.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


View high (2)
4. Shared libraries keep cleared overrides 🐞 Bug ≡ Correctness
Description
MetaDataSerializer.getSerializedStringMap omits an unset override, while
DBMSProcessor.setSharedMetaData only upserts keys present in that serialized map and deletes
absent group-tree keys only. When a user selects “Use global preference” in a shared library, the
previous database row remains and is fetched back into reopened and remote clients, restoring the
old explicit setting.
Code

jablib/src/main/java/org/jabref/logic/exporter/MetaDataSerializer.java[R57-58]

+        metaData.getAutoRenameFilesOnChange().ifPresent(
+                autoRename -> stringyMetaData.put(MetaData.AUTO_RENAME_FILES_ON_CHANGE, List.of(autoRename.toString())));
Evidence
The new serializer emits the override only while present, and clearing it sets the model field to
null. Shared persistence merely upserts serialized entries and limits absent-key deletion to group
metadata, while remote synchronization parses the resulting stale database map back into the
existing metadata object.

jablib/src/main/java/org/jabref/logic/exporter/MetaDataSerializer.java[54-59]
jablib/src/main/java/org/jabref/model/metadata/MetaData.java[189-200]
jablib/src/main/java/org/jabref/logic/shared/DBMSProcessor.java[624-650]
jablib/src/main/java/org/jabref/logic/shared/DBMSSynchronizer.java[556-565]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
Clearing the library-specific auto-rename override omits its key from serialization, but shared metadata persistence never deletes that obsolete key. The stale database value is subsequently fetched and reapplied to clients.
## Fix Focus Areas
- jablib/src/main/java/org/jabref/logic/exporter/MetaDataSerializer.java[57-58]
- jablib/src/main/java/org/jabref/logic/shared/DBMSProcessor.java[624-650]
- jablib/src/main/java/org/jabref/logic/shared/DBMSSynchronizer.java[556-565]
## Recommended Fix
When a complete shared metadata snapshot omits the auto-rename override, delete its existing database row and emit the metadata notification. Also ensure applying a fetched snapshot clears the in-memory override when that key is absent, and add shared-database tests covering explicit-to-global transitions.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


5. Metadata copy tests now fail ✓ Resolved 📘 Rule violation ≡ Correctness
Description
MetaData adds autoRenameFilesOnChange, but MetaDataOverwriteWithTest.populated() does not
assign a non-default value for the reflectively enumerated field. When
everyCopiedFieldIsPopulated() compares that source with a fresh instance, both values are null,
so its assertNotEquals fails before the suite can validate the copy.
Code

jablib/src/main/java/org/jabref/model/metadata/MetaData.java[R104-105]

+    /// Library override of the global "auto rename files if entry changes" preference; absent = use the global one
+    @Nullable private Boolean autoRenameFilesOnChange;
Evidence
The new field is introduced by this PR, while the reflective test explicitly requires every copied
field to be populated and compares each source value against a fresh instance. Because populated()
never sets this field, both instances contain null, violating the requirements to update affected
model tests and keep the test suite green.

AGENTS.md: Add or Update Tests for Behavior and Logic Changes: AGENTS.md: Add or Update Tests for Behavior and Logic Changes: AGENTS.md: Add or Update Tests for Behavior and Logic Changes: AGENTS.md: Add or Update Tests for Behavior and Logic Changes: AGENTS.md: Add or Update Tests for Behavior and Logic Changes: AGENTS.md: Add or Update Tests for Behavior and Logic Changes: AGENTS.md: Add or Update Tests for Behavior and Logic Changes: AGENTS.md: Add or Update Tests for Behavior and Logic Changes: AGENTS.md: Add or Update Tests for Behavior and Logic Changes: AGENTS.md: Add or Update Tests for Behavior and Logic Changes: AGENTS.md: Add or Update Tests for Behavior and Logic Changes: AGENTS.md: Add or Update Tests for Behavior and Logic Changes: AGENTS.md: Add or Update Tests for Behavior and Logic Changes: AGENTS.md: Add or Update Tests for Behavior and Logic Changes: AGENTS.md: Add or Update Tests for Behavior and Logic Changes: AGENTS.md: Add or Update Tests for Behavior and Logic Changes: AGENTS.md: Add or Update Tests for Behavior and Logic Changes: AGENTS.md: Add or Update Tests for Behavior and Logic Changes: AGENTS.md: Add or Update Tests for Behavior and Logic Changes: AGENTS.md: Add or Update Tests for Behavior and Logic Changes: AGENTS.md: Add or Update Tests for Behavior and Logic Changes: AGENTS.md: Add or Update Tests for Behavior and Logic Changes: AGENTS.md: Add or Update Tests for Behavior and Logic Changes: AGENTS.md: Add or Update Tests for Behavior and Logic Changes: AGENTS.md: Add or Update Tests for Behavior and Logic Changes: AGENTS.md: Add or Update Tests for Behavior and Logic Changes: AGENTS.md: Add or Update Tests for Behavior and Logic Changes: AGENTS.md: Add or Update Tests for Behavior and Logic Changes: AGENTS.md: Add or Update Tests for Behavior and Logic Changes: AGENTS.md: Add or Update Tests for Behavior and Logic Changes: AGENTS.md: Add or Update Tests for Behavior and Logic Changes: AGENTS.md: Add or Update Tests for Behavior and Logic Changes
AGENTS.md: Keep the Project Build, Lint, Documentation, and Tests Green: AGENTS.md: Keep the Project Build, Lint, Documentation, and Tests Green: AGENTS.md: Keep the Project Build, Lint, Documentation, and Tests Green: AGENTS.md: Keep the Project Build, Lint, Documentation, and Tests Green: AGENTS.md: Keep the Project Build, Lint, Documentation, and Tests Green: AGENTS.md: Keep the Project Build, Lint, Documentation, and Tests Green: AGENTS.md: Keep the Project Build, Lint, Documentation, and Tests Green: AGENTS.md: Keep the Project Build, Lint, Documentation, and Tests Green: AGENTS.md: Keep the Project Build, Lint, Documentation, and Tests Green: AGENTS.md: Keep the Project Build, Lint, Documentation, and Tests Green: AGENTS.md: Keep the Project Build, Lint, Documentation, and Tests Green: AGENTS.md: Keep the Project Build, Lint, Documentation, and Tests Green: AGENTS.md: Keep the Project Build, Lint, Documentation, and Tests Green: AGENTS.md: Keep the Project Build, Lint, Documentation, and Tests Green: AGENTS.md: Keep the Project Build, Lint, Documentation, and Tests Green: AGENTS.md: Keep the Project Build, Lint, Documentation, and Tests Green: AGENTS.md: Keep the Project Build, Lint, Documentation, and Tests Green: AGENTS.md: Keep the Project Build, Lint, Documentation, and Tests Green: AGENTS.md: Keep the Project Build, Lint, Documentation, and Tests Green: AGENTS.md: Keep the Project Build, Lint, Documentation, and Tests Green: AGENTS.md: Keep the Project Build, Lint, Documentation, and Tests Green: AGENTS.md: Keep the Project Build, Lint, Documentation, and Tests Green: AGENTS.md: Keep the Project Build, Lint, Documentation, and Tests Green: AGENTS.md: Keep the Project Build, Lint, Documentation, and Tests Green: AGENTS.md: Keep the Project Build, Lint, Documentation, and Tests Green: AGENTS.md: Keep the Project Build, Lint, Documentation, and Tests Green: AGENTS.md: Keep the Project Build, Lint, Documentation, and Tests Green: AGENTS.md: Keep the Project Build, Lint, Documentation, and Tests Green: AGENTS.md: Keep the Project Build, Lint, Documentation, and Tests Green: AGENTS.md: Keep the Project Build, Lint, Documentation, and Tests Green: AGENTS.md: Keep the Project Build, Lint, Documentation, and Tests Green: AGENTS.md: Keep the Project Build, Lint, Documentation, and Tests Green
jablib/src/main/java/org/jabref/model/metadata/MetaData.java[104-105]
jablib/src/test/java/org/jabref/model/metadata/MetaDataOverwriteWithTest.java[36-37]
jablib/src/test/java/org/jabref/model/metadata/MetaDataOverwriteWithTest.java[61-70]
jablib/src/test/java/org/jabref/model/metadata/MetaDataOverwriteWithTest.java[74-100]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
The new `autoRenameFilesOnChange` field is included in the reflective metadata copy test, but the test fixture leaves it at its default `null` value. This causes `everyCopiedFieldIsPopulated()` to fail.
## Fix Focus Areas
- jablib/src/test/java/org/jabref/model/metadata/MetaDataOverwriteWithTest.java[74-100]
- jablib/src/main/java/org/jabref/model/metadata/MetaData.java[104-105]
## Recommended Fix
Set a non-default auto-rename override in `MetaDataOverwriteWithTest.populated()`, such as by calling `metaData.setAutoRenameFilesOnChange(true)`, so the reflective tests verify that `overwriteWith()` copies the new field.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


Grey Divider

Tip of the day
💡 Did you know, you can keep summaries lean with Finding overflow, which tucks the rest behind 'View more'

More tips ↗ | Customize Qodo ↗ | Qodo docs ↗

Grey Divider

Qodo Logo

Comment thread jablib/src/test/java/org/jabref/logic/exporter/HayagrivaEntryWriterTest.java Outdated
Comment thread jablib/src/main/java/org/jabref/logic/importer/util/MetaDataParser.java Outdated
@koppor
koppor marked this pull request as draft September 13, 2026 22:05
@koppor
koppor marked this pull request as ready for review September 13, 2026 22:07
}

storeKeywordSeparator(metaData);
autoRenameFilesOnChangeProperty.getValue().ifPresentOrElse(metaData::setAutoRenameFilesOnChange, metaData::clearAutoRenameFilesOnChange);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Action required

1. Shared libraries keep stale rename rules 🐞 Bug ≡ Correctness

storeSettings clears the optional override, but MetaDataSerializer then omits its key while
shared metadata persistence only upserts emitted keys and remote parsing never clears an absent key.
When a user switches back to the global preference in a shared library, other clients can
indefinitely retain the previous override and continue renaming or not renaming linked files against
the current setting.
Agent Prompt
## Issue description
Clearing `autoRenameFilesOnChange` omits it from serialized metadata, but shared SQL persistence does not delete omitted ordinary keys and parsing into an existing `MetaData` does not clear an absent override. Other shared-library clients therefore retain the previous library-specific rename behavior.

## Fix Focus Areas
- jabgui/src/main/java/org/jabref/gui/libraryproperties/general/GeneralPropertiesViewModel.java[125-125]
- jablib/src/main/java/org/jabref/logic/shared/DBMSProcessor.java[624-637]
- jablib/src/main/java/org/jabref/logic/importer/util/MetaDataParser.java[164-171]

## Recommended Fix
Treat absence of this optional key as an explicit clear throughout shared synchronization: delete the stored SQL metadata row and notify clients when a complete serialized snapshot omits it, and clear the existing `MetaData` value before or while applying a fetched snapshot that lacks it. Add a shared-database synchronization test covering a transition from an explicit override to the global setting.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🤖 Generated with Claude Code
Not done: pre-existing shared-DB limitation for every clearable key (keyword separator, file directory, git flags), not specific to this override. Separate PR if wanted.

@qodo-free-for-open-source-projects

Copy link
Copy Markdown

Code review by qodo was updated up to the latest commit 030485f

@koppor
koppor marked this pull request as draft September 13, 2026 22:12
@koppor
koppor marked this pull request as ready for review September 13, 2026 22:12
Comment thread jablib/src/main/resources/l10n/JabRef_en.properties Outdated
Comment thread jablib/src/main/java/org/jabref/model/metadata/MetaData.java
@qodo-free-for-open-source-projects

Copy link
Copy Markdown

Code review by qodo was updated up to the latest commit 030485f

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01H7bhV1r2H2XZvjHrYQkiJt
@koppor
koppor marked this pull request as draft September 13, 2026 22:17
@koppor
koppor marked this pull request as ready for review September 13, 2026 22:18
@qodo-free-for-open-source-projects

qodo-free-for-open-source-projects Bot commented Sep 13, 2026 •

Copy link
Copy Markdown

Code Review by Qodo

Grey Divider

New Review Started

This review has been superseded by a new analysis

Grey Divider

Qodo Logo

@koppor
koppor marked this pull request as draft September 13, 2026 22:21
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01H7bhV1r2H2XZvjHrYQkiJt
@koppor
koppor marked this pull request as ready for review September 13, 2026 22:22
@qodo-free-for-open-source-projects

Copy link
Copy Markdown

Code review by qodo was updated up to the latest commit 1f4c552

@koppor
koppor marked this pull request as draft September 13, 2026 22:27
…ange dialog

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01H7bhV1r2H2XZvjHrYQkiJt
@koppor
koppor marked this pull request as ready for review September 13, 2026 22:28
Comment on lines +167 to +169
AutoRenameFileOnEntryChange.isEnabled(databaseContext, preferences.getFilePreferences())
? FileUtil.createFileNameFromPattern(databaseContext.getDatabase(), entry, preferences.getFilePreferences().getFileNamePattern())
: Optional.empty();

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Remediation recommended

9. Long names split folder file pairs 🐞 Bug ≡ Correctness

The directory-library fileNameGenerator returns the raw pattern basename while
AutoRenameFileOnEntryChange sends the same entry through LinkedFileHandler, which truncates the
PDF filename to 255 characters. When an enabled library override processes a sufficiently long
generated name, the PDF moves to the truncated basename before SidecarWriteBack looks for it under
the old basename, so only the sidecar receives the untruncated name and the pair is no longer
associated.
Agent Prompt
## Issue description
Directory libraries can pass an untruncated pattern result to sidecar write-back while the ordinary linked-file renamer truncates the corresponding PDF filename. Long generated names can therefore split an equally named PDF and sidecar pair.

## Fix Focus Areas
- jabgui/src/main/java/org/jabref/gui/importer/actions/OpenDirectoryLibraryAction.java[166-169]
- jablib/src/main/java/org/jabref/logic/directorylibrary/SidecarWriteBack.java[191-203]
- jablib/src/main/java/org/jabref/logic/externalfiles/LinkedFileHandler.java[245-255]

## Recommended Fix
Centralize sanitization and length limiting for generated pair basenames, accounting for both PDF and sidecar extensions, and use the same resulting basename in the ordinary linked-file renamer and directory sidecar write-back. Add a directory-library test with a generated name exceeding the filesystem limit and verify that the PDF, sidecar, catalog, and entry link all retain one matching basename.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🤖 Generated with Claude Code
Fixed: the entry-change renamer now skips folder libraries, so only the write-back renames the pair (one basename, no overlap).

@qodo-free-for-open-source-projects

Copy link
Copy Markdown

Code review by qodo was updated up to the latest commit 176c3db

@koppor
koppor marked this pull request as draft September 13, 2026 22:32
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01H7bhV1r2H2XZvjHrYQkiJt
@koppor
koppor marked this pull request as ready for review September 13, 2026 22:33
Comment on lines +167 to +170
if ("true".equalsIgnoreCase(value) || "false".equalsIgnoreCase(value)) {
metaData.setAutoRenameFilesOnChange(Boolean.parseBoolean(value));
} else {
LOGGER.warn("Ignoring invalid value '{}' for {}", value, MetaData.AUTO_RENAME_FILES_ON_CHANGE);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Remediation recommended

8. Invalid overrides preserve prior renames 🐞 Bug ≡ Correctness

MetaDataParser.parse logs an invalid autoRenameFilesOnChange value but leaves the existing
override on the supplied MetaData unchanged. When a shared library previously held a valid
override and a remote update supplies a malformed value, applyRemoteMetaData parses into that same
object and file renaming continues under the stale rule instead of falling back to the global
preference.
Agent Prompt
## Issue description
Malformed `autoRenameFilesOnChange` metadata is logged and ignored without clearing an override already present on the supplied `MetaData`. Shared metadata updates parse into the existing object, so the prior rename rule remains active rather than reverting to the global preference.

## Fix Focus Areas
- jablib/src/main/java/org/jabref/logic/importer/util/MetaDataParser.java[164-171]
- jablib/src/test/java/org/jabref/logic/importer/util/MetaDataParserTest.java[117-123]

## Recommended Fix
Call `metaData.clearAutoRenameFilesOnChange()` when the supplied value is invalid, while retaining the warning. Extend the malformed-value test to parse into a `MetaData` instance that already has an override and assert that the override is cleared.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🤖 Generated with Claude Code
Fixed: malformed value clears the override; test parses into a MetaData that already had one.

@qodo-free-for-open-source-projects

Copy link
Copy Markdown

Code review by qodo was updated up to the latest commit 73ef5ad

@koppor
koppor marked this pull request as draft September 13, 2026 22:36
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01H7bhV1r2H2XZvjHrYQkiJt
@koppor
koppor marked this pull request as ready for review September 13, 2026 22:37
@qodo-free-for-open-source-projects

Copy link
Copy Markdown

Code review by qodo was updated up to the latest commit 7e49b42

@koppor
koppor marked this pull request as draft September 13, 2026 22:42
@koppor
koppor marked this pull request as ready for review September 13, 2026 22:44
Comment on lines +193 to +195
public void setAutoRenameFilesOnChange(boolean autoRenameFilesOnChange) {
this.autoRenameFilesOnChange = autoRenameFilesOnChange;
postChange();

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Action required

1. Rename override calls hide intent 📘 Rule violation ⚙ Maintainability

setAutoRenameFilesOnChange(boolean) introduces a public mode-setting API whose call sites express
the two alternatives only as true and false. Parser, properties, and test callers now depend on
this flag, so later changes must interpret each literal or computed boolean rather than invoke an
intention-revealing operation.
Agent Prompt
## Issue description
The new public metadata API accepts a boolean mode flag, making callers encode enable and disable operations as boolean values.

## Fix Focus Areas
- jablib/src/main/java/org/jabref/model/metadata/MetaData.java[189-200]

## Recommended Fix
Replace the boolean setter with separate intention-revealing enable and disable methods while retaining the clear method for global fallback. Update parser, properties, and test call sites to select the appropriate named operation.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🤖 Generated with Claude Code
Not done: boolean setter matches the existing setGitAutoPull(boolean) family; a differing API for one flag would be inconsistent.

Comment on lines +57 to +58
metaData.getAutoRenameFilesOnChange().ifPresent(
autoRename -> stringyMetaData.put(MetaData.AUTO_RENAME_FILES_ON_CHANGE, List.of(autoRename.toString())));

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Action required

2. Shared libraries keep cleared overrides 🐞 Bug ≡ Correctness

MetaDataSerializer.getSerializedStringMap omits an unset override, while
DBMSProcessor.setSharedMetaData only upserts keys present in that serialized map and deletes
absent group-tree keys only. When a user selects “Use global preference” in a shared library, the
previous database row remains and is fetched back into reopened and remote clients, restoring the
old explicit setting.
Agent Prompt
## Issue description
Clearing the library-specific auto-rename override omits its key from serialization, but shared metadata persistence never deletes that obsolete key. The stale database value is subsequently fetched and reapplied to clients.

## Fix Focus Areas
- jablib/src/main/java/org/jabref/logic/exporter/MetaDataSerializer.java[57-58]
- jablib/src/main/java/org/jabref/logic/shared/DBMSProcessor.java[624-650]
- jablib/src/main/java/org/jabref/logic/shared/DBMSSynchronizer.java[556-565]

## Recommended Fix
When a complete shared metadata snapshot omits the auto-rename override, delete its existing database row and emit the metadata notification. Also ensure applying a fetched snapshot clears the in-memory override when that key is absent, and add shared-database tests covering explicit-to-global transitions.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🤖 Generated with Claude Code
Not done: same pre-existing shared-DB gap for every clearable key (keyword separator, file directory, git flags), see earlier thread. Separate PR if wanted.

@qodo-free-for-open-source-projects

Copy link
Copy Markdown

Code review by qodo was updated up to the latest commit 7b2b3d7

@koppor
koppor marked this pull request as draft September 13, 2026 22:48
@koppor
koppor marked this pull request as ready for review September 13, 2026 22:48
@qodo-free-for-open-source-projects

Copy link
Copy Markdown

Code review by qodo was updated up to the latest commit 7b2b3d7

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant