Skip to content

Add group pseudonymization support (fixes #14117)#15258

Merged
subhramit merged 3 commits into
JabRef:mainfrom
f0restron07:fix-14117-pseudonymize-groups
May 27, 2026
Merged

Add group pseudonymization support (fixes #14117)#15258
subhramit merged 3 commits into
JabRef:mainfrom
f0restron07:fix-14117-pseudonymize-groups

Conversation

@f0restron07

@f0restron07 f0restron07 commented Mar 3, 2026

Copy link
Copy Markdown
Contributor

Description

added group pseudonymization before this only entries were pseudonymized now groups get pseudonymized too for eg group-1,group-2,...etc

Changes

  • added pseudonymizeGroupTree() method which uses deepCopy() and nameProperty().set() so in order the original library stays unchanged and group types (SearchGroup, KeywordGroup etc) are been preserved
  • extracted groups field handling into separate method named as pseudonymizeGroupsField() instead of manual splitting
  • groups in entries which doesn't exist in the group tree also get pseudonymized with a counter
  • added changelog entry

Testing

  • All test pass
  • Added shouldPseudonymizeGroupTree() test
  • Added shouldPseudonymizeUnmappedGroups() test for edge case where group in entry doesnt exist in tree

Before (original groups):

image

After (pseudonymized groups):

image

Fixes #14117

AI Disclosure

I used Chatgpt AI to assist with understanding the codebase and debugging.

Checklist

  • I own the copyright of the code submitted and I license it under the MIT license
  • I manually tested my changes in running JabRef (always required)
  • I added JUnit tests for changes (if applicable)
  • I added screenshots in the PR description (if change is visible to the user)
  • I added a screenshot in the PR description showing a library with a single entry with me as author and as title the issue number
  • I described the change in CHANGELOG.md in a way that can be understood by the average user (if 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

@github-actions

github-actions Bot commented Mar 3, 2026

Copy link
Copy Markdown
Contributor

Hey @f0restron07! 👋

Thank you for contributing to JabRef!

We have automated checks in place, based on which you will soon get feedback if any of them are failing. We also use Qodo for review assistance. It will update your pull request description with a review help and offer suggestions to improve the pull request.

After all automated checks pass, a maintainer will also review your contribution. Once that happens, you can go through their comments in the "Files changed" tab and act on them, or reply to the conversation if you have further inputs. You can read about the whole pull request process in our contribution guide.

Please ensure that your pull request is in line with our AI Usage Policy and make necessary disclosures.

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

Copy link
Copy Markdown
Contributor

Review Summary by Qodo

Add group pseudonymization support for library anonymization

✨ Enhancement

Grey Divider

Walkthroughs

Description
• Add group tree pseudonymization to library anonymization process
• Groups renamed to sequential identifiers (group-1, group-2, etc.)
• Recursively pseudonymize group hierarchies preserving tree structure
• Remove file field from test data and update expected output
Diagram
flowchart LR
  A["BibDatabaseContext"] -->|extract groups| B["GroupTreeNode"]
  B -->|pseudonymizeGroupTree| C["Renamed Groups<br/>group-1, group-2..."]
  C -->|add to metadata| D["Pseudonymized<br/>BibDatabaseContext"]
  E["valueMapping"] -->|track mappings| D
Loading

Grey Divider

File Changes

1. jablib/src/main/java/org/jabref/logic/pseudonymization/Pseudonymization.java ✨ Enhancement +25/-0

Implement group tree pseudonymization logic

• Added imports for group handling classes (AbstractGroup, ExplicitGroup, GroupTreeNode, Optional)
• Extract and pseudonymize group tree from database metadata in pseudonymizeLibrary() method
• Implement new pseudonymizeGroupTree() method to recursively rename groups with sequential
 identifiers
• Store original group names in value mapping for reverse lookup

jablib/src/main/java/org/jabref/logic/pseudonymization/Pseudonymization.java


2. jablib/src/test/resources/org/jabref/logic/pseudonymization/Chocolate-pseudnomyized.bib 🧪 Tests +11/-6

Update test expectations with pseudonymized groups

• Remove file field entries from all article entries (8 occurrences)
• Add pseudonymized group metadata section with 8 StaticGroup entries (group-1 through group-8)
• Maintain existing pseudonymized entry field values

jablib/src/test/resources/org/jabref/logic/pseudonymization/Chocolate-pseudnomyized.bib


3. jablib/src/test/resources/org/jabref/logic/pseudonymization/Chocolate.bib 🧪 Tests +7/-9

Clean up test data and fix encoding issues

• Remove file field from multiple article entries (6 occurrences)
• Fix HTML entity encoding in journaltitle fields (replace &amp; with \&)
• Add fileDirectory and keypatterndefault metadata comments
• Preserve existing group structure for pseudonymization testing

jablib/src/test/resources/org/jabref/logic/pseudonymization/Chocolate.bib


Grey Divider

Qodo Logo

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

qodo-free-for-open-source-projects Bot commented Mar 3, 2026

Copy link
Copy Markdown
Contributor

Code Review by Qodo

🐞 Bugs (13) 📘 Rule violations (14) 📎 Requirement gaps (0)

Grey Divider


Action required

1. Optional isPresent() branching ✓ Resolved 📘 Rule violation ≡ Correctness
Description
The new code uses if (groups.isPresent()) { ... groups.get() ... }, which is an Optional
anti-pattern and violates the project guideline to use ifPresent/ifPresentOrElse for clearer
optional flows. This reduces readability and encourages null-like control flow.
Code

jablib/src/main/java/org/jabref/logic/pseudonymization/Pseudonymization.java[R45-48]

Evidence
PR Compliance ID 9 requires idiomatic Optional usage (prefer ifPresent/ifPresentOrElse) and
discourages isPresent() branching. The added block explicitly branches on isPresent() and then
calls get().

AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md
jablib/src/main/java/org/jabref/logic/pseudonymization/Pseudonymization.java[45-48]

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 group pseudonymization logic uses `Optional.isPresent()` branching and then calls `Optional.get()`, which is discouraged by the project&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;#x27;s Optional-usage guideline.
## Issue Context
The code currently does:
- `Optional&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;lt;GroupTreeNode&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;gt; groups = ...`
- `if (groups.isPresent()) { ... groups.get() ... }`
The compliance requirement prefers `ifPresent` / `ifPresentOrElse` to keep Optional flows idiomatic and avoid null-like branching.
## Fix Focus Areas
- jablib/src/main/java/org/jabref/logic/pseudonymization/Pseudonymization.java[44-49]

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


2. Group semantics destroyed ✓ Resolved 🐞 Bug ≡ Correctness
Description
pseudonymizeGroupTree converts every group node into an ExplicitGroup, discarding original group
type and configuration (e.g., SearchGroup query, KeywordGroup field/expression, Automatic/Tex group
details). This makes the pseudonymized library’s grouping behavior diverge from the original and can
prevent reproducing group-related issues accurately.
Code

jablib/src/main/java/org/jabref/logic/pseudonymization/Pseudonymization.java[R74-84]

Evidence
The PR unconditionally creates ExplicitGroup nodes for every group in the tree. ExplicitGroup
matches entries using StandardField.GROUPS and the group name as search expression, which is
fundamentally different from other group types. The exporter/serializer explicitly supports many
group subtypes, indicating that type preservation matters.

jablib/src/main/java/org/jabref/logic/pseudonymization/Pseudonymization.java[74-84]
jablib/src/main/java/org/jabref/model/groups/ExplicitGroup.java[10-22]
jablib/src/main/java/org/jabref/logic/exporter/GroupSerializer.java[114-138]

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

## Issue description
`pseudonymizeGroupTree` rebuilds the entire group tree as `ExplicitGroup`, which changes group behavior (Search/Keyword/Automatic/Tex groups) and drops group-specific configuration.
## Issue Context
Group serialization and parsing supports many concrete group types; replacing all of them with `ExplicitGroup` makes the output semantically different and undermines the goal of producing a usable pseudonymized library.
## Fix Focus Areas
- jablib/src/main/java/org/jabref/logic/pseudonymization/Pseudonymization.java[74-86]

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


3. Root group replaced ✓ Resolved 🐞 Bug ≡ Correctness
Description
The PR pseudonymizes the group-tree root node as well, replacing the expected AllEntriesGroup root
with an ExplicitGroup. Downstream code and documentation assume the root is AllEntriesGroup;
replacing it can change matching semantics and break logic that treats roots specially (e.g., merge
behavior).
Code

jablib/src/main/java/org/jabref/logic/pseudonymization/Pseudonymization.java[R44-49]

Evidence
The PR invokes pseudonymizeGroupTree on the current root node, and pseudonymizeGroupTree always
creates an ExplicitGroup, so the root is no longer AllEntriesGroup. MetaDataSerializer documents
that the root node is always the AllEntriesGroup; other logic relies on this structural invariant.

jablib/src/main/java/org/jabref/logic/pseudonymization/Pseudonymization.java[44-49]
jablib/src/main/java/org/jabref/logic/exporter/MetaDataSerializer.java[67-71]
jablib/src/main/java/org/jabref/logic/database/DatabaseMerger.java[102-109]
jablib/src/main/java/org/jabref/model/groups/AllEntriesGroup.java[7-31]

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 group-tree root is rebuilt into an `ExplicitGroup`, removing the expected `AllEntriesGroup` root.
## Issue Context
Several components assume the root is `AllEntriesGroup` (documentation and merge logic). The root should remain `AllEntriesGroup` while children can be renamed.
## Fix Focus Areas
- jablib/src/main/java/org/jabref/logic/pseudonymization/Pseudonymization.java[44-49]
- jablib/src/main/java/org/jabref/logic/pseudonymization/Pseudonymization.java[74-86]

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


View more (42)
4. Group membership breaks ✓ Resolved 🐞 Bug ≡ Correctness
Description
Groups are renamed to "group-N", but entries’ StandardField.GROUPS values are pseudonymized
independently as "groups-N" (field-based), so ExplicitGroup matching (which relies on the GROUPS
field containing the group name) will no longer work. The pseudonymized output will contain a group
tree whose groups match no entries (or wrong entries).
Code

jablib/src/main/java/org/jabref/logic/pseudonymization/Pseudonymization.java[R74-80]

Evidence
Entry pseudonymization uses the field name prefix (e.g., "groups-1" for StandardField.GROUPS), while
group pseudonymization uses the singular "group-1". ExplicitGroup is implemented as a
WordKeywordGroup that matches entries where StandardField.GROUPS contains the group name/search
expression, so the entry field and group name must align.

jablib/src/main/java/org/jabref/logic/pseudonymization/Pseudonymization.java[62-69]
jablib/src/main/java/org/jabref/logic/pseudonymization/Pseudonymization.java[76-81]
jablib/src/main/java/org/jabref/model/groups/ExplicitGroup.java[10-22]

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

## Issue description
Group names in metadata (&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;quot;group-N&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;quot;) and entry group assignments in `StandardField.GROUPS` (currently pseudonymized as &amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;quot;groups-N&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;quot;) diverge, breaking explicit group membership.
## Issue Context
`ExplicitGroup` matching relies on the `StandardField.GROUPS` field containing the group name.
## Fix Focus Areas
- jablib/src/main/java/org/jabref/logic/pseudonymization/Pseudonymization.java[33-69]
- jablib/src/main/java/org/jabref/logic/pseudonymization/Pseudonymization.java[44-49]
- jablib/src/main/java/org/jabref/logic/pseudonymization/Pseudonymization.java[74-86]

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


5. orElse("") for groups field 📘 Rule violation ≡ Correctness
Description
The new group-field pseudonymization path converts an absent Optional value into an empty string
using orElse(""), which can hide missing data vs. intentionally-empty data. Handle the Optional
using ifPresent(...)/ifPresentOrElse(...) or Optional transformations instead of default
substitution when absence is meaningful.
Code

jablib/src/main/java/org/jabref/logic/pseudonymization/Pseudonymization.java[R69-71]

Evidence
PR Compliance ID 8 discourages converting Optional absence into defaults like empty strings unless
explicitly intended. The new code reads the groups field via orElse("") and then branches on
isBlank().

AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md
jablib/src/main/java/org/jabref/logic/pseudonymization/Pseudonymization.java[69-71]

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

## Issue description
`Optional` is converted to an empty string via `orElse(&amp;amp;amp;amp;quot;&amp;amp;amp;amp;quot;)`, which can mask absence and is non-idiomatic per project conventions.
## Issue Context
This is part of the new logic that rewrites `StandardField.GROUPS` values to pseudonymized group names.
## Fix Focus Areas
- jablib/src/main/java/org/jabref/logic/pseudonymization/Pseudonymization.java[69-71]

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


6. Committed test-output.txt artifact 📘 Rule violation ≡ Correctness
Description
The PR adds test-output.txt, which appears to be a local Gradle/test run output (including
binary/encoded characters), and is unrelated to the feature change. Committing build artifacts adds
noise and can cause repository hygiene and tooling issues.
Code

test-output.txt[R1-3]

Evidence
PR Compliance ID 1 requires avoiding unnecessary churn and keeping changes aligned with repository
conventions. Adding a local test output artifact is unrelated to the feature and should not be
committed.

AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md
test-output.txt[1-3]

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

## Issue description
A local build/test output artifact (`test-output.txt`) was committed, creating unrelated churn and potential repository hygiene issues.
## Issue Context
The file content appears to be Gradle/test output (including encoded/binary characters) and is not part of the pseudonymization feature.
## Fix Focus Areas
- test-output.txt[1-3]

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


7. Groups not pseudonymized 🐞 Bug ⛨ Security
Description
pseudonymizeEntries bypasses the generic pseudonymization for StandardField.GROUPS and only
replaces names found in groupNameMapping, so if metadata has no group tree (or an entry references
an unmapped group) the original group name is copied into the pseudonymized output.
Code

jablib/src/main/java/org/jabref/logic/pseudonymization/Pseudonymization.java[R69-83]

Evidence
groupNameMapping is only populated when metadata groups are present; otherwise it stays empty, but
pseudonymizeEntries still special-cases GROUPS and writes back the original group names when no
mapping is found, causing a pseudonymization data leak.

jablib/src/main/java/org/jabref/logic/pseudonymization/Pseudonymization.java[33-45]
jablib/src/main/java/org/jabref/logic/pseudonymization/Pseudonymization.java[69-83]

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

## Issue description
`StandardField.GROUPS` is handled via `groupNameMapping` only. When the library has no group tree metadata (or entries reference group names not present in the tree), the pseudonymization output keeps original group names in the entry field, leaking identifying information.
### Issue Context
- `groupNameMapping` is only filled from the group tree.
- The `GROUPS` field is excluded from the generic field-value pseudonymization path.
### Fix Focus Areas
- jablib/src/main/java/org/jabref/logic/pseudonymization/Pseudonymization.java[33-45]
- jablib/src/main/java/org/jabref/logic/pseudonymization/Pseudonymization.java[69-83]
- jablib/src/main/java/org/jabref/logic/pseudonymization/Pseudonymization.java[99-119]

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


8. Optional isPresent() branching ✓ Resolved 📘 Rule violation ≡ Correctness
Description
The new code uses if (groups.isPresent()) { ... groups.get() ... }, which is an Optional
anti-pattern and violates the project guideline to use ifPresent/ifPresentOrElse for clearer
optional flows. This reduces readability and encourages null-like control flow.
Code

jablib/src/main/java/org/jabref/logic/pseudonymization/Pseudonymization.java[R45-48]

Evidence
PR Compliance ID 9 requires idiomatic Optional usage (prefer ifPresent/ifPresentOrElse) and
discourages isPresent() branching. The added block explicitly branches on isPresent() and then
calls get().

AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md
jablib/src/main/java/org/jabref/logic/pseudonymization/Pseudonymization.java[45-48]

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 group pseudonymization logic uses `Optional.isPresent()` branching and then calls `Optional.get()`, which is discouraged by the project&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;#x27;s Optional-usage guideline.
## Issue Context
The code currently does:
- `Optional&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;lt;GroupTreeNode&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;gt; groups = ...`
- `if (groups.isPresent()) { ... groups.get() ... }`
The compliance requirement prefers `ifPresent` / `ifPresentOrElse` to keep Optional flows idiomatic and avoid null-like branching.
## Fix Focus Areas
- jablib/src/main/java/org/jabref/logic/pseudonymization/Pseudonymization.java[44-49]

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


9. Group semantics destroyed ✓ Resolved 🐞 Bug ≡ Correctness
Description
pseudonymizeGroupTree converts every group node into an ExplicitGroup, discarding original group
type and configuration (e.g., SearchGroup query, KeywordGroup field/expression, Automatic/Tex group
details). This makes the pseudonymized library’s grouping behavior diverge from the original and can
prevent reproducing group-related issues accurately.
Code

jablib/src/main/java/org/jabref/logic/pseudonymization/Pseudonymization.java[R74-84]

Evidence
The PR unconditionally creates ExplicitGroup nodes for every group in the tree. ExplicitGroup
matches entries using StandardField.GROUPS and the group name as search expression, which is
fundamentally different from other group types. The exporter/serializer explicitly supports many
group subtypes, indicating that type preservation matters.

jablib/src/main/java/org/jabref/logic/pseudonymization/Pseudonymization.java[74-84]
jablib/src/main/java/org/jabref/model/groups/ExplicitGroup.java[10-22]
jablib/src/main/java/org/jabref/logic/exporter/GroupSerializer.java[114-138]

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

## Issue description
`pseudonymizeGroupTree` rebuilds the entire group tree as `ExplicitGroup`, which changes group behavior (Search/Keyword/Automatic/Tex groups) and drops group-specific configuration.
## Issue Context
Group serialization and parsing supports many concrete group types; replacing all of them with `ExplicitGroup` makes the output semantically different and undermines the goal of producing a usable pseudonymized library.
## Fix Focus Areas
- jablib/src/main/java/org/jabref/logic/pseudonymization/Pseudonymization.java[74-86]

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


10. Root group replaced ✓ Resolved 🐞 Bug ≡ Correctness
Description
The PR pseudonymizes the group-tree root node as well, replacing the expected AllEntriesGroup root
with an ExplicitGroup. Downstream code and documentation assume the root is AllEntriesGroup;
replacing it can change matching semantics and break logic that treats roots specially (e.g., merge
behavior).
Code

jablib/src/main/java/org/jabref/logic/pseudonymization/Pseudonymization.java[R44-49]

Evidence
The PR invokes pseudonymizeGroupTree on the current root node, and pseudonymizeGroupTree always
creates an ExplicitGroup, so the root is no longer AllEntriesGroup. MetaDataSerializer documents
that the root node is always the AllEntriesGroup; other logic relies on this structural invariant.

jablib/src/main/java/org/jabref/logic/pseudonymization/Pseudonymization.java[44-49]
jablib/src/main/java/org/jabref/logic/exporter/MetaDataSerializer.java[67-71]
jablib/src/main/java/org/jabref/logic/database/DatabaseMerger.java[102-109]
jablib/src/main/java/org/jabref/model/groups/AllEntriesGroup.java[7-31]

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 group-tree root is rebuilt into an `ExplicitGroup`, removing the expected `AllEntriesGroup` root.
## Issue Context
Several components assume the root is `AllEntriesGroup` (documentation and merge logic). The root should remain `AllEntriesGroup` while children can be renamed.
## Fix Focus Areas
- jablib/src/main/java/org/jabref/logic/pseudonymization/Pseudonymization.java[44-49]
- jablib/src/main/java/org/jabref/logic/pseudonymization/Pseudonymization.java[74-86]

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


11. Group membership breaks ✓ Resolved 🐞 Bug ≡ Correctness
Description
Groups are renamed to "group-N", but entries’ StandardField.GROUPS values are pseudonymized
independently as "groups-N" (field-based), so ExplicitGroup matching (which relies on the GROUPS
field containing the group name) will no longer work. The pseudonymized output will contain a group
tree whose groups match no entries (or wrong entries).
Code

jablib/src/main/java/org/jabref/logic/pseudonymization/Pseudonymization.java[R74-80]

Evidence
Entry pseudonymization uses the field name prefix (e.g., "groups-1" for StandardField.GROUPS), while
group pseudonymization uses the singular "group-1". ExplicitGroup is implemented as a
WordKeywordGroup that matches entries where StandardField.GROUPS contains the group name/search
expression, so the entry field and group name must align.

jablib/src/main/java/org/jabref/logic/pseudonymization/Pseudonymization.java[62-69]
jablib/src/main/java/org/jabref/logic/pseudonymization/Pseudonymization.java[76-81]
jablib/src/main/java/org/jabref/model/groups/ExplicitGroup.java[10-22]

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

## Issue description
Group names in metadata (&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;quot;group-N&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;quot;) and entry group assignments in `StandardField.GROUPS` (currently pseudonymized as &amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;quot;groups-N&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;quot;) diverge, breaking explicit group membership.
## Issue Context
`ExplicitGroup` matching relies on the `StandardField.GROUPS` field containing the group name.
## Fix Focus Areas
- jablib/src/main/java/org/jabref/logic/pseudonymization/Pseudonymization.java[33-69]
- jablib/src/main/java/org/jabref/logic/pseudonymization/Pseudonymization.java[44-49]
- jablib/src/main/java/org/jabref/logic/pseudonymization/Pseudonymization.java[74-86]

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


12. Optional isPresent() branching ✓ Resolved 📘 Rule violation ≡ Correctness
Description
The new code uses if (groups.isPresent()) { ... groups.get() ... }, which is an Optional
anti-pattern and violates the project guideline to use ifPresent/ifPresentOrElse for clearer
optional flows. This reduces readability and encourages null-like control flow.
Code

jablib/src/main/java/org/jabref/logic/pseudonymization/Pseudonymization.java[R45-48]

Evidence
PR Compliance ID 9 requires idiomatic Optional usage (prefer ifPresent/ifPresentOrElse) and
discourages isPresent() branching. The added block explicitly branches on isPresent() and then
calls get().

AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md
jablib/src/main/java/org/jabref/logic/pseudonymization/Pseudonymization.java[45-48]

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 group pseudonymization logic uses `Optional.isPresent()` branching and then calls `Optional.get()`, which is discouraged by the project&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;#x27;s Optional-usage guideline.
## Issue Context
The code currently does:
- `Optional&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;lt;GroupTreeNode&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;gt; groups = ...`
- `if (groups.isPresent()) { ... groups.get() ... }`
The compliance requirement prefers `ifPresent` / `ifPresentOrElse` to keep Optional flows idiomatic and avoid null-like branching.
## Fix Focus Areas
- jablib/src/main/java/org/jabref/logic/pseudonymization/Pseudonymization.java[44-49]

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


13. Group semantics destroyed ✓ Resolved 🐞 Bug ≡ Correctness
Description
pseudonymizeGroupTree converts every group node into an ExplicitGroup, discarding original group
type and configuration (e.g., SearchGroup query, KeywordGroup field/expression, Automatic/Tex group
details). This makes the pseudonymized library’s grouping behavior diverge from the original and can
prevent reproducing group-related issues accurately.
Code

jablib/src/main/java/org/jabref/logic/pseudonymization/Pseudonymization.java[R74-84]

Evidence
The PR unconditionally creates ExplicitGroup nodes for every group in the tree. ExplicitGroup
matches entries using StandardField.GROUPS and the group name as search expression, which is
fundamentally different from other group types. The exporter/serializer explicitly supports many
group subtypes, indicating that type preservation matters.

jablib/src/main/java/org/jabref/logic/pseudonymization/Pseudonymization.java[74-84]
jablib/src/main/java/org/jabref/model/groups/ExplicitGroup.java[10-22]
jablib/src/main/java/org/jabref/logic/exporter/GroupSerializer.java[114-138]

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

## Issue description
`pseudonymizeGroupTree` rebuilds the entire group tree as `ExplicitGroup`, which changes group behavior (Search/Keyword/Automatic/Tex groups) and drops group-specific configuration.
## Issue Context
Group serialization and parsing supports many concrete group types; replacing all of them with `ExplicitGroup` makes the output semantically different and undermines the goal of producing a usable pseudonymized library.
## Fix Focus Areas
- jablib/src/main/java/org/jabref/logic/pseudonymization/Pseudonymization.java[74-86]

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


14. Root group replaced ✓ Resolved 🐞 Bug ≡ Correctness
Description
The PR pseudonymizes the group-tree root node as well, replacing the expected AllEntriesGroup root
with an ExplicitGroup. Downstream code and documentation assume the root is AllEntriesGroup;
replacing it can change matching semantics and break logic that treats roots specially (e.g., merge
behavior).
Code

jablib/src/main/java/org/jabref/logic/pseudonymization/Pseudonymization.java[R44-49]

Evidence
The PR invokes pseudonymizeGroupTree on the current root node, and pseudonymizeGroupTree always
creates an ExplicitGroup, so the root is no longer AllEntriesGroup. MetaDataSerializer documents
that the root node is always the AllEntriesGroup; other logic relies on this structural invariant.

jablib/src/main/java/org/jabref/logic/pseudonymization/Pseudonymization.java[44-49]
jablib/src/main/java/org/jabref/logic/exporter/MetaDataSerializer.java[67-71]
jablib/src/main/java/org/jabref/logic/database/DatabaseMerger.java[102-109]
jablib/src/main/java/org/jabref/model/groups/AllEntriesGroup.java[7-31]

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 group-tree root is rebuilt into an `ExplicitGroup`, removing the expected `AllEntriesGroup` root.
## Issue Context
Several components assume the root is `AllEntriesGroup` (documentation and merge logic). The root should remain `AllEntriesGroup` while children can be renamed.
## Fix Focus Areas
- jablib/src/main/java/org/jabref/logic/pseudonymization/Pseudonymization.java[44-49]
- jablib/src/main/java/org/jabref/logic/pseudonymization/Pseudonymization.java[74-86]

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


15. Group membership breaks ✓ Resolved 🐞 Bug ≡ Correctness
Description
Groups are renamed to "group-N", but entries’ StandardField.GROUPS values are pseudonymized
independently as "groups-N" (field-based), so ExplicitGroup matching (which relies on the GROUPS
field containing the group name) will no longer work. The pseudonymized output will contain a group
tree whose groups match no entries (or wrong entries).
Code

jablib/src/main/java/org/jabref/logic/pseudonymization/Pseudonymization.java[R74-80]

Evidence
Entry pseudonymization uses the field name prefix (e.g., "groups-1" for StandardField.GROUPS), while
group pseudonymization uses the singular "group-1". ExplicitGroup is implemented as a
WordKeywordGroup that matches entries where StandardField.GROUPS contains the group name/search
expression, so the entry field and group name must align.

jablib/src/main/java/org/jabref/logic/pseudonymization/Pseudonymization.java[62-69]
jablib/src/main/java/org/jabref/logic/pseudonymization/Pseudonymization.java[76-81]
jablib/src/main/java/org/jabref/model/groups/ExplicitGroup.java[10-22]

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

## Issue description
Group names in metadata (&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;quot;group-N&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;quot;) and entry group assignments in `StandardField.GROUPS` (currently pseudonymized as &amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;quot;groups-N&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;quot;) diverge, breaking explicit group membership.
## Issue Context
`ExplicitGroup` matching relies on the `StandardField.GROUPS` field containing the group name.
## Fix Focus Areas
- jablib/src/main/java/org/jabref/logic/pseudonymization/Pseudonymization.java[33-69]
- jablib/src/main/java/org/jabref/logic/pseudonymization/Pseudonymization.java[44-49]
- jablib/src/main/java/org/jabref/logic/pseudonymization/Pseudonymization.java[74-86]

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


16. orElse("") for groups field 📘 Rule violation ≡ Correctness
Description
The new group-field pseudonymization path converts an absent Optional value into an empty string
using orElse(""), which can hide missing data vs. intentionally-empty data. Handle the Optional
using ifPresent(...)/ifPresentOrElse(...) or Optional transformations instead of default
substitution when absence is meaningful.
Code

jablib/src/main/java/org/jabref/logic/pseudonymization/Pseudonymization.java[R69-71]

Evidence
PR Compliance ID 8 discourages converting Optional absence into defaults like empty strings unless
explicitly intended. The new code reads the groups field via orElse("") and then branches on
isBlank().

AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md
jablib/src/main/java/org/jabref/logic/pseudonymization/Pseudonymization.java[69-71]

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

## Issue description
`Optional` is converted to an empty string via `orElse(&amp;amp;amp;amp;amp;quot;&amp;amp;amp;amp;amp;quot;)`, which can mask absence and is non-idiomatic per project conventions.
## Issue Context
This is part of the new logic that rewrites `StandardField.GROUPS` values to pseudonymized group names.
## Fix Focus Areas
- jablib/src/main/java/org/jabref/logic/pseudonymization/Pseudonymization.java[69-71]

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


17. Committed test-output.txt artifact 📘 Rule violation ≡ Correctness
Description
The PR adds test-output.txt, which appears to be a local Gradle/test run output (including
binary/encoded characters), and is unrelated to the feature change. Committing build artifacts adds
noise and can cause repository hygiene and tooling issues.
Code

test-output.txt[R1-3]

Evidence
PR Compliance ID 1 requires avoiding unnecessary churn and keeping changes aligned with repository
conventions. Adding a local test output artifact is unrelated to the feature and should not be
committed.

AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md
test-output.txt[1-3]

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

## Issue description
A local build/test output artifact (`test-output.txt`) was committed, creating unrelated churn and potential repository hygiene issues.
## Issue Context
The file content appears to be Gradle/test output (including encoded/binary characters) and is not part of the pseudonymization feature.
## Fix Focus Areas
- test-output.txt[1-3]

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


18. Groups not pseudonymized 🐞 Bug ⛨ Security
Description
pseudonymizeEntries bypasses the generic pseudonymization for StandardField.GROUPS and only
replaces names found in groupNameMapping, so if metadata has no group tree (or an entry references
an unmapped group) the original group name is copied into the pseudonymized output.
Code

jablib/src/main/java/org/jabref/logic/pseudonymization/Pseudonymization.java[R69-83]

Evidence
groupNameMapping is only populated when metadata groups are present; otherwise it stays empty, but
pseudonymizeEntries still special-cases GROUPS and writes back the original group names when no
mapping is found, causing a pseudonymization data leak.

jablib/src/main/java/org/jabref/logic/pseudonymization/Pseudonymization.java[33-45]
jablib/src/main/java/org/jabref/logic/pseudonymization/Pseudonymization.java[69-83]

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

## Issue description
`StandardField.GROUPS` is handled via `groupNameMapping` only. When the library has no group tree metadata (or entries reference group names not present in the tree), the pseudonymization output keeps original group names in the entry field, leaking identifying information.
### Issue Context
- `groupNameMapping` is only filled from the group tree.
- The `GROUPS` field is excluded from the generic field-value pseudonymization path.
### Fix Focus Areas
- jablib/src/main/java/org/jabref/logic/pseudonymization/Pseudonymization.java[33-45]
- jablib/src/main/java/org/jabref/logic/pseudonymization/Pseudonymization.java[69-83]
- jablib/src/main/java/org/jabref/logic/pseudonymization/Pseudonymization.java[99-119]

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


19. orElse("") for groups field 📘 Rule violation ≡ Correctness
Description
The new group-field pseudonymization path converts an absent Optional value into an empty string
using orElse(""), which can hide missing data vs. intentionally-empty data. Handle the Optional
using ifPresent(...)/ifPresentOrElse(...) or Optional transformations instead of default
substitution when absence is meaningful.
Code

jablib/src/main/java/org/jabref/logic/pseudonymization/Pseudonymization.java[R69-71]

Evidence
PR Compliance ID 8 discourages converting Optional absence into defaults like empty strings unless
explicitly intended. The new code reads the groups field via orElse("") and then branches on
isBlank().

AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md
jablib/src/main/java/org/jabref/logic/pseudonymization/Pseudonymization.java[69-71]

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

## Issue description
`Optional` is converted to an empty string via `orElse(&amp;amp;amp;quot;&amp;amp;amp;quot;)`, which can mask absence and is non-idiomatic per project conventions.
## Issue Context
This is part of the new logic that rewrites `StandardField.GROUPS` values to pseudonymized group names.
## Fix Focus Areas
- jablib/src/main/java/org/jabref/logic/pseudonymization/Pseudonymization.java[69-71]

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


20. Committed test-output.txt artifact 📘 Rule violation ≡ Correctness
Description
The PR adds test-output.txt, which appears to be a local Gradle/test run output (including
binary/encoded characters), and is unrelated to the feature change. Committing build artifacts adds
noise and can cause repository hygiene and tooling issues.
Code

test-output.txt[R1-3]

Evidence
PR Compliance ID 1 requires avoiding unnecessary churn and keeping changes aligned with repository
conventions. Adding a local test output artifact is unrelated to the feature and should not be
committed.

AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGENTS.md: AGE...

@github-actions github-actions Bot added the good first issue An issue intended for project-newcomers. Varies in difficulty. label Mar 3, 2026
Comment thread jablib/src/main/java/org/jabref/logic/pseudonymization/Pseudonymization.java Outdated
Comment thread jablib/src/main/java/org/jabref/logic/pseudonymization/Pseudonymization.java Outdated
Comment thread jablib/src/main/java/org/jabref/logic/pseudonymization/Pseudonymization.java Outdated
Comment thread jablib/src/main/java/org/jabref/logic/pseudonymization/Pseudonymization.java Outdated
@github-actions github-actions Bot added the status: changes-required Pull requests that are not yet complete label Mar 3, 2026
@testlens-app

This comment has been minimized.

@github-actions github-actions Bot mentioned this pull request Mar 3, 2026
@testlens-app

This comment has been minimized.

@github-actions github-actions Bot added status: no-bot-comments and removed status: changes-required Pull requests that are not yet complete labels Mar 3, 2026
@f0restron07

Copy link
Copy Markdown
Contributor Author
2026-03-03.15-14-59.mp4

@koppor koppor left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

  • Tests missing
  • file field removed
  • int[] is a hack. Should be made differentlt

"Steps to test" removed. Indicator for too much AI usage. Therefore only brief review.

@github-actions github-actions Bot added status: changes-required Pull requests that are not yet complete and removed status: no-bot-comments labels Mar 3, 2026
@f0restron07

f0restron07 commented Mar 3, 2026

Copy link
Copy Markdown
Contributor Author

Thanks for the review.. I'll address each of the pt:

  1. Add unit tests
  2. Restore file fields
  3. Replace int[] with AtomicInteger
    Will push the fixes soon

@testlens-app

This comment has been minimized.

@f0restron07

f0restron07 commented Mar 3, 2026

Copy link
Copy Markdown
Contributor Author

Hey @koppor
I have done the changes:

  1. Replaced int[] with AtomicInteger
  2. Added a unit test shouldPseudonymizeGroupTree() for group pseudonymization
    Regarding the file field: The pseudonymizeLibrary test was already failing before my code changes because the output does not include file fields. I removed the file fields from the expected file

Should I change this back and leave this as a separate issue?

@koppor

koppor commented Mar 3, 2026

Copy link
Copy Markdown
Member
  1. Replace int[] with AtomicInteger

Is strange. Isn't it possible with normal recursion?

In tests we use "withField", not setField

"pt" in comment text strange

@testlens-app

This comment has been minimized.

@subhramit

Copy link
Copy Markdown
Member
  • All existing tests pass
  • Groups are correctly pseudonymized in output

No screenshot or video recording is provided in the description. Please add them.

@testlens-app

This comment has been minimized.

@testlens-app

This comment has been minimized.

@github-actions github-actions Bot added status: no-bot-comments and removed status: changes-required Pull requests that are not yet complete labels Mar 4, 2026
@f0restron07

Copy link
Copy Markdown
Contributor Author

@koppor
I have implemented all the suggestions:

  • Normal recursion instead of using AtomicInteger
  • Used withField in tests
  • Fixed the comment
  • Screenshots added

@koppor koppor added status: ready-for-review Pull Requests that are ready to be reviewed by the maintainers and removed status: no-bot-comments labels Mar 7, 2026
@Siedlerchr Siedlerchr removed the status: ready-for-review Pull Requests that are ready to be reviewed by the maintainers label Mar 7, 2026
@github-actions github-actions Bot added status: changes-required Pull requests that are not yet complete and removed status: stale Issues marked by a bot as "stale". All issues need to be investigated manually. status: no-bot-comments labels May 18, 2026
@github-actions

Copy link
Copy Markdown
Contributor

Your pull request conflicts with the target branch.

Please merge with your code. For a step-by-step guide to resolve merge conflicts, see https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/addressing-merge-conflicts/resolving-a-merge-conflict-using-the-command-line.

@f0restron07 f0restron07 force-pushed the fix-14117-pseudonymize-groups branch from da5ee4c to ac1dfa8 Compare May 24, 2026 16:06
@github-actions github-actions Bot added status: no-bot-comments and removed status: changes-required Pull requests that are not yet complete labels May 24, 2026
@f0restron07 f0restron07 force-pushed the fix-14117-pseudonymize-groups branch from ac1dfa8 to 1a08b09 Compare May 24, 2026 16:16
@f0restron07 f0restron07 force-pushed the fix-14117-pseudonymize-groups branch from 1a08b09 to 965e5b7 Compare May 24, 2026 16:43
@subhramit

Copy link
Copy Markdown
Member

please don't rebase/force-push.
Make any changes you want in subsequent commits.

@f0restron07

Copy link
Copy Markdown
Contributor Author

@subhramit @koppor sorry for the force push stuff earlier didnt intend to do it but i needed to recover from corrupted merge state had pulled a bad rebase of unrelatd commit from main .
also i have fixed the keyword separator threaded through constructor applied to multi keyword field and keyword serialization using keywordlist#parse+keywordlist.tostring to handle separator instead of hardcoded ","

@f0restron07 f0restron07 requested a review from koppor May 25, 2026 19:00
String originalName = keyword.toString();
String pseudoName = groupNameMapping.get(originalName);
if (pseudoName == null) {
pseudoName = "group-" + (groupNameMapping.size() + 1);

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

"group-" should become a constant GROUP_PREFIX

@subhramit

Copy link
Copy Markdown
Member

I get
image

When opening a pseudonymized Chocolate.bib.

Also follow-up issue to be created: let a user choose the name of the pseudonymized bibfile (out of scope for this PR)

@subhramit

Copy link
Copy Markdown
Member

I get image

When opening a pseudonymized Chocolate.bib.

Reason: the line

@Comment{jabref-meta: groups-search-syntax-version:6.0-alpha_1}

is not preserved by the pseudonymization

@subhramit

subhramit commented May 25, 2026

Copy link
Copy Markdown
Member

Reason: the line

@Comment{jabref-meta: groups-search-syntax-version:6.0-alpha_1}

is not preserved by the pseudonymization

@f0restron07 could you fix that? I know this PR has been hanging around for long - so feel free to just address @InAnYan's comments here and fix this in a follow-up PR.

Edit - I am fine if this search groups is left as-is. As per discussion with @InAnYan on call, this needs to be thought about a bit more if it carries any semantic meaning (and thus if it also needs to be pseudonymized).

@f0restron07

Copy link
Copy Markdown
Contributor Author

@InAnYan i have extracted the group_prefix const and also reformatted Pseudonymizationtest setup/action/check blocks with blank lines and @subhramit i have left the search group syntax fix as is pending and lmk if any changes needed further.

@subhramit subhramit enabled auto-merge May 27, 2026 08:26
@subhramit subhramit added this pull request to the merge queue May 27, 2026
@github-actions github-actions Bot added the status: to-be-merged PRs which are accepted and should go into the merge-queue. label May 27, 2026
Merged via the queue into JabRef:main with commit 6f01b56 May 27, 2026
64 checks passed
Siedlerchr added a commit to InAnYan/jabref that referenced this pull request May 28, 2026
* upstream/main: (29 commits)
  Chore(deps): Bump dev.langchain4j:langchain4j-bom in /versions (JabRef#15853)
  Chore(deps): Bump org.glassfish.jaxb:jaxb-runtime in /versions (JabRef#15854)
  Chore(deps): Bump com.gradleup.shadow:shadow-gradle-plugin (JabRef#15852)
  Chore(deps): Bump com.gradleup.shadow:shadow-gradle-plugin (JabRef#15849)
  Chore(deps): Bump com.autonomousapps:dependency-analysis-gradle-plugin (JabRef#15850)
  Update dependency org.apache.maven.plugins:maven-surefire-plugin to v3.5.6 (JabRef#15844)
  Fix reset and import of AiPreferences (JabRef#15843)
  Fix Comparable Contract Violation in SharedBibEntryData (JabRef#15806) (JabRef#15842)
  Chore(deps): Bump com.dlsc.gemsfx:gemsfx from 4.0.5 to 4.1.0 in /versions (JabRef#15841)
  Chore(deps): Bump jablib/src/main/resources/csl-styles (JabRef#15840)
  New Crowdin updates (JabRef#15839)
  Add group pseudonymization support (fixes JabRef#14117) (JabRef#15258)
  Feature parse MeSH terms in PubMed MEDLINE records (JabRef#15529)
  Fix/non latin author parsed as name prefix (JabRef#15823)
  Fix not on fx thread cleanup (JabRef#15835)
  Fix garbled BibEntry Javadoc example (JabRef#15834)
  Revert "Fix cleanup operationn setFiles not on fx thread causes exceptiosn"
  Revert "changelog"
  changelog
  Fix cleanup operationn setFiles not on fx thread causes exceptiosn
  ...
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

component: groups component: JabKit [cli] first contrib good first issue An issue intended for project-newcomers. Varies in difficulty. status: no-bot-comments status: to-be-merged PRs which are accepted and should go into the merge-queue.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Pseudonymize groups

6 participants