Conversation
A DirectoryLibrarySynchronizer registers with the Commons-IO DirectoryMonitor (promoted from jabgui to jablib, per ADR-0030) and applies external create/change/delete events for .yml/.pdf files to the open library: changed sidecars update the existing BibEntry instances field by field, renames are detected via a grace window over the monitor's delete+create events and keep the affected entries, appearing PDFs link to their sidecar entry or become stubs, and disappearing PDFs unlink or remove their stub. All mutations carry EntriesEventSource.SHARED, and recordWrittenFile lets the upcoming write-back direction fingerprint its own writes so they are not re-imported. Late-joining observers are baselined with a silent first poll, otherwise the running monitor reports every existing file as created and duplicates the scanned entries. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
DirectoryMonitor and DirectoryLibrarySynchronizer expose commons-io monitor types in exported-package signatures (flagged by checkModuleDirectivesScope). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Adapts the synchronizer to the new PdfEntryFactory: externally appearing PDFs without a sidecar now get metadata extracted from the PDF instead of a filename-only stub, same as during the initial scan. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Externally appearing PDFs also get a generated citation key after insertion, matching the scan behavior. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The enrichment task reuses the factory instance that also serves the synchronizer; externally dropped PDFs keep the synchronous extraction on the directory-sync thread. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
21 tasks
External creation, modification, deletion, and renaming of .md files with Hayagriva frontmatter now flows into the open directory library like the plain .yml sidecars; the notes body maps to the entry's comment fields. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Q5Y5GTihE9rZxKrvPqnHV4
19 tasks
Resync layer 3 of the stack (no conflicts). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01DDcHNMt9fPWnpYaHheFvry
A deletion undone within the grace window no longer removes live entries, renames of sidecars with a paired PDF keep the entry instances, an editor polled mid-write only stages the entries instead of dropping them, and events arriving during shutdown are discarded instead of failing the shared monitor thread. Sidecar precedence is deterministic; the baseline snapshot runs off the caller's thread. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Vr3E1Gg5DRU4LQDDVnhPys
# Conflicts: # jabgui/src/main/java/org/jabref/gui/importer/actions/OpenDirectoryLibraryAction.java # jablib/src/main/java/org/jabref/model/database/BibDatabaseContext.java
Fixes the CI format check. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01DDcHNMt9fPWnpYaHheFvry
Resync with upstream (no conflicts). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01DDcHNMt9fPWnpYaHheFvry
Upstream added a 0071 ADR after this stack claimed the number, so the MADR duplicate-ID check failed. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This was referenced Sep 7, 2026
31 tasks
This branch has not been deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Related issues and pull requests
Phase 2 of the "directory as library" plan (
PLAN.md), stacked on #737 (this PR's base branch isopen-directory-library; the diff shows only the sync commits). Related to JabRef#10930; no issue is closed by this PR.PR Description
🤖 An open directory library now follows external file changes live: creating, editing, renaming, or deleting a sidecar or PDF on disk is reflected in the open library within about a second, while selection, undo history, and group membership survive. Renames are recognised as moves rather than as a delete plus an unrelated add. The library's own writes are ignored, so its changes are not re-imported.
jabref-contrib-policy:4.2:reviewed:okAnalogies
Like honey trickling back into the comb, external edits now find their way into the open library without anyone carrying them. Like chocolate that snaps along the same grooves no matter who breaks it, the same entry instances survive renames and edits — the pieces keep their shape. And like the moon steadying the tides, the one-second poll quietly keeps two worlds — disk and table — moving together.
Steps to test
.yml, edit an existing one, rename one (mv a.yml b.yml), delete one, and add/remove a PDF next to a sidecar.Guarded by
DirectoryLibrarySynchronizerTest(10 tests: create/change/delete, multi-entry diff, rename grace window, self-echo fingerprint, PDF link/stub handling, non-Hayagriva takeover).AI usage
Claude Code (model claude-fable-5).
AI CHECKLIST.md walkthrough
1. Code self-review
Nullability and control flow
== null/!= nullin new code except nullable JDK returns (Path.getParent(), map lookups) expressed viaOptional.ofNullablewhere streamable.Objects.requireNonNull(...)—orElseThrowwith message for the constructor contract.@NullMarked.Optionalconsumed withifPresent/map/orElseThrow.StringUtil.isBlank(...)— not needed.Exceptions
IOException/NoSuchAlgorithmExceptioncaught specifically.RuntimeException/IllegalStateExceptionthrown (IllegalArgumentExceptionfor a violated constructor precondition;AssertionErroronly for the impossible missing-SHA-256 case).Style and idioms
BibEntrywithers used for new entries.List.of(),SequencedMap, streams.UiTaskExecutor::runInJavaFXThread), nonew Thread()in application flow (the executor's thread factory names its worker).///).User-facing text
Security
Tests
DirectoryLibrarySynchronizerTestdrives the handlers deterministically (injected clock for the grace window, direct-run marshaller); no sleeps, no polling.@TempDir, no@DisplayName, exceptions propagate.2. Verification commands
./gradlew :jablib:check(full suite; only the known environmentalRemoteCommunicationTestport-conflict failures on this machine)../gradlew :jablib:checkstyleMain :jablib:checkstyleTest :jabgui:checkstyleMain../gradlew :jablib:modernizer../gradlew :rewriteRunleft no diff../gradlew :jablib:javadoc../gradlew traceRequirements(req~directory-library.inbound-sync~1covered).npx markdownlint-cli2on changed Markdown.3. Documentation
CHANGELOG.mdentry added (TODO replaced with this PR's link after creation).docs/requirements/directory-library.md.4. Pull request
gh api(cross-repo head, stacked baseopen-directory-library).Checklist
CHANGELOG.mddescribing the change from the user's point of view (if the change is visible to the user)🤖 Generated with Claude Code