Conversation
The outbound half of DirectoryLibrarySynchronizer subscribes to entry events through a CoarseChangeFilter (mirroring the shared-SQL seam) and persists user changes read-modify-write into the Hayagriva sidecars, debounced per file: the first edit of an entry without a sidecar creates one (next to its PDF, sharing the base name, or named after the citation key), a citation-key edit renames the YAML map key, and deleting an entry removes it from its file — the file is trashed or deleted once its last entry is gone, the paired PDF is never touched. Own writes are fingerprinted so the inbound watcher ignores them. Ctrl+S now flushes the pending sidecar writes instead of asking for a .bib path; "Save as" remains the explicit .bib snapshot. Directory tabs no longer prompt on close and drop the modification marker, and generated citation keys are set with a non-local event source so system-initiated changes never materialize sidecars. 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>
19 tasks
# Conflicts: # jablib/src/main/java/org/jabref/logic/directorylibrary/DirectoryLibrarySynchronizer.java
New sidecars are now created as Markdown files: the Hayagriva data as YAML frontmatter, the comment fields as a markdownlint-clean notes body (# Notes intro, one ## comment-<name> section per per-user comment); body sections under foreign headings survive rewrites. Plain .yml sidecars keep being written as pure Hayagriva. The write debounce is now trailing-edge and re-armed by every change event, including the keystroke events the CoarseChangeFilter marks as filtered. Previously only the first keystroke after a field switch scheduled a write, so the write captured a mid-typing value and the rest of the burst was never persisted (and Ctrl+S had nothing left to flush). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Q5Y5GTihE9rZxKrvPqnHV4
19 tasks
Resync layer 4. Conflict: upstream changed SaveDatabaseAction.save() to return SaveResult instead of boolean — kept the directory-library flush-instead-of-.bib early return, now returning SaveResult.SUCCESS. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01DDcHNMt9fPWnpYaHheFvry
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Vr3E1Gg5DRU4LQDDVnhPys
… write-back Failed sidecar writes stay pending and are reported on save and close instead of being lost; metadata extraction for a dropped PDF no longer runs under the synchronizer's monitor; deleting an entry keeps its file until the debounced write, so an undo lands it back in the same file; an external edit that arrives before a pending write is merged field-wise instead of overwritten; writes are debounced per file, use AtomicFileOutputStream, and are drained before the final flush on shutdown. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Vr3E1Gg5DRU4LQDDVnhPys
# Conflicts: # jablib/src/main/java/org/jabref/logic/exporter/HayagrivaEntryWriter.java
Fixes the CI format check. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01DDcHNMt9fPWnpYaHheFvry
Adapt to upstream: save() gained a mayAutoCommit parameter (directory early-return unaffected), the change-event filtered flag was renamed to setFiltered, and the unwritable-sidecar test is disabled on Windows (setWritable(false) is a no-op there for the owner). 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
…back # Conflicts: # jabgui/src/main/java/org/jabref/gui/exporter/SaveDatabaseAction.java
31 tasks
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
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 3 of the "directory as library" plan (
PLAN.md), stacked on #738 (base branchdirectory-inbound-sync; the diff also contains the programmatic Hayagriva writer commits of #736, which this phase is the first consumer of). No issue is closed by this PR.PR Description
🤖 A directory library now saves straight into its Hayagriva sidecar files — Ctrl+S never writes a
.bib. The first edit of an entry creates a sidecar beside its PDF or named after its citation key; deleting an entry removes it from its file, leaving the PDF untouched. Hand-written content JabRef does not understand survives every write, and directory tabs no longer prompt on close because nothing is left unsaved.jabref-contrib-policy:4.2:reviewed:okAnalogies
Like honey sealed back into its own comb, every change flows into the cell it came from — the sidecar next to the paper. Like a chocolate mold that shapes only the squares you pour, the read-modify-write merge touches only the fields JabRef owns and leaves the rest of the bar untouched. And like the moon completing its cycle, the round trip is closed: what the importer reads, the writer returns to the very same file.
Steps to test
Open a folder as library, create an entry (toolbar +): a sidecar
.ymlappears immediately.Type a title and a citation key: within half a second the file contains them, the YAML key follows the citation key:
Ctrl+S shows "Library saved" — no
.bibfile is created anywhere.Delete the entry: the sidecar disappears, PDFs stay.
Guarded by seven new
DirectoryLibrarySynchronizerTestcases (rewrite preserving unknown YAML, sidecar creation for stubs and new entries, key rename, disposal semantics, multi-entry rewrite, self-echo suppression) plusHayagrivaEntryWriter.mergeIntoDocument.AI usage
Claude Code (model claude-fable-5).
AI CHECKLIST.md walkthrough
1. Code self-review
== null/!= nullbeyond nullable JDK/Jackson returns; new members@NullMarkedvia their classes;Optionalconsumed idiomatically; noObjects.requireNonNull.IOException,JacksonException); none ofRuntimeException/IllegalStateExceptionthrown; exceptions last in log calls.BibEntrys in tests; records,SequencedMap,List.of(); no new threads (existing per-library executor reused); no commented-out code; Markdown Javadoc.Library savedkey; no new strings.@TempDir, deterministic (direct handler calls +flush(), injected clock), no sleeps.2. Verification commands
./gradlew :jablib:check— full suite; only the known environmentalRemote*Testport-conflict failures on this machine (a running JabRef occupies the port; green on CI)../gradlew :jablib:checkstyleMain :jablib:checkstyleTest :jabgui:checkstyleMain../gradlew :jablib:modernizerand./gradlew traceRequirements(newreq~directory-library.write-back~1covered)../gradlew --no-configuration-cache :rewriteDryRun— no changes.npx markdownlint-cli2on changed Markdown.3. Documentation
CHANGELOG.mdentry (TODO replaced with this PR's link after creation).req~directory-library.write-back~1added with impl markers.4. Pull request
gh api(stacked cross-repo).Checklist
.bib)CHANGELOG.mddescribing the change from the user's point of view (if the change is visible to the user)🤖 Generated with Claude Code