You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Five Read-path prior-art patterns from advplyr/audiobookshelf land on harmonia's own stated gaps: Cardigann indexer definition coverage (#513), the unbuilt Phase 04 diagnostic visualizer, and the scanner/metadata subsystem generally. All five carry an adopt-now verdict from a search that treats audiobookshelf's library-scanning and metadata-precedence machinery as more transferable than anything audiobook-specific about it — none of the five patterns below depend on audiobook, narrator, or chapter vocabulary.
Every citation below was re-opened by an adversarial verifier against the source file:line before this pattern was allowed to survive; the verifier's corpus-wide reject rate on this pass was 23%. Two of the five patterns below carry a verifier correction to a claim that survived — both are stated inline in the pattern, not silently folded into the citation.
Candidate table
Repo
Licence
Extract for
Disqualifiers
advplyr/audiobookshelf
GPL-3.0
Scanner/watcher settle-detection and identity reconciliation (P1); metadata-precedence pipeline (P2); external provider contract shape, direct prior art for Cardigann #513 (P3); playback-position sync split (P4); polled-ingestion watermark + circuit breaker, direct prior art for Cardigann #513 (P5). Full path:line citations are in Patterns below; no commit SHA was captured in the routed evidence, so pin one before this table is copied into a phase PLAN.
GPL-3.0 is licence-compatible only inside harmonia, because harmonia is AGPL-3.0-or-later and GPLv3 §13 permits combining GPL-3.0 work into an AGPL-3.0-or-later work. This compatibility does not exist anywhere else in the fleet — no other fleet repo may cite advplyr/audiobookshelf as a source, by name or by pattern, even as reference. It also does not change the Read-path stance below: nothing here is copied, so the compatibility is dormant unless a later reader considers Depend or Carry, at which point this boundary is the first thing to check.
What: Two decoupled stages implement "auto-detects library updates, no need to re-scan." A live filesystem watcher does not scan on the first add/unlink/rename event: it polls each new file's mtime every 3s until it stops changing (600s timeout) before treating the file as settled, debounces all pending events behind a 10s reset-on-activity timer, and exposes an ignore-dir registry so the app's own writes (a podcast download in progress) never self-trigger a scan. Once settled, a separate batch reconciler walks the folder tree and matches found items against existing DB rows first by exact path, falling back to inode match at the item level and then the individual-file level — so a folder rename/move is recognized as the same item, not delete-then-re-add. Per matched item, a field-by-field diff (mtime/ctime/birthtime/path, each library file by path-then-inode) decides "nothing changed" vs "needs media rescan."
Verdict: adopt-now — as a from-scratch design for harmonia's scanner/watcher subsystem, targeting the settle-poll debounce, ignore-dir suppression, layered path→item-inode→file-inode identity resolution, and field-level diff as the mechanism to port. The Node/Sequelize implementation itself is not portable. The per-field diff events are also the structured before/after signal the unbuilt Phase 04 diagnostic visualizer would render per item.
Verifier correction: the disqualifier as mined described the watcher's dependency as "Node/chokidar/Sequelize." The watcher is not built on chokidar — server/Watcher.js requires a vendored, MIT-licensed library (server/libs/watcher, by Fabio Spampinato); chokidar appears in the lockfile only as an unrelated transitive dependency of unrelated packages. The mechanism claim itself is unaffected by this; strike "chokidar" from any restatement of the tech stack.
Disqualifiers: GPL-3.0 is licence-compatible for read-and-reference only inside harmonia specifically (see Candidate table); it must never be mined by name into any other fleet repo. The concrete implementation is JS plus Sequelize ORM idioms (mutable .save() model objects, EventEmitter) that don't map onto Rust — only the state machine and identity-resolution order are portable. The inode-based fallback assumes stable POSIX inodes and silently breaks (falls through to treating a moved item as new) on filesystems that recycle or fake inode numbers (some SMB/CIFS/FAT mounts); audiobookshelf does not appear to guard against that.
What: A library-level, user-configurable ordered list of named metadata sources (default folderStructure, audioMetatags, nfoFile, txtFiles, opfFile, absMetadata) drives a handler object whose methods are looked up and invoked by that string, each mutating one shared draft-metadata object in place; a later source's non-empty field silently overwrites an earlier one's. Ordering is pure config, never hardcoded in extraction logic. The scanner persists a last-scanned-precedence snapshot per library, and the moment the configured order no longer matches what a library was last scanned with, the whole library is forced through a rescan rather than left silently inconsistent between old- and new-precedence items.
Evidence:server/models/Library.js:84-86 (defaultMetadataPrecedence array); server/scanner/BookScanner.js:683-692 (precedence loop dispatching bookMetadataSourceHandlermetadataSource); server/scanner/BookScanner.js:709-772 (BookMetadataSourceHandler class, one method per named source); server/scanner/LibraryScanner.js:62-67 (precedence-drift forces forceRescan)
Verifier correction: the BookScanner.js:709-772 citation is narrower than the class it points at. The class's six methods (one per precedence source) actually span 709-817; the claim that all six exist as named methods is independently confirmed by grep, so this is a citation-precision defect, not a fabricated mechanism. Read the citation as the start of the class, not its full extent.
Verdict: adopt-now — the ordered-strategy-dispatch shape and the config-drift-forces-reprocess invariant are directly portable to harmonia's metadata-merge layer, e.g. as a Vec<Box<dyn MetadataSource>> reconciling embedded tags, sidecar files, and Cardigann-fed remote metadata into one media record. The merge rule itself — unconditional last-source-wins per field, no provenance kept — is naive and is a place for a harmonia design to improve rather than copy.
Disqualifiers: GPL-3.0 → AGPL-3.0-or-later compatibility is harmonia-only, as above; not usable as reference anywhere else in the fleet. No per-field confidence or provenance is retained on the final record — there is no way to later ask "which source set this field" — which a greenfield design should fix rather than inherit.
What: Third-party metadata providers are not code plugins; they are an OpenAPI-3.0-specified HTTP contract (search endpoint, query/author params, api-key header auth, a fixed response schema). Each configured provider is a DB row (base URL plus auth header) reached through a slug convention that is short-circuited ahead of all built-in provider logic — adding a provider is a config action, never a code change. The adapter never trusts or spreads the remote JSON: it destructures exactly the allowed keys, coerces each to its expected type, drops anything that fails validation, and only then builds the internal object.
Evidence:custom-metadata-provider-specification.yaml:1-141 (full OpenAPI contract, root of repo); server/providers/CustomProviderAdapter.js:21-145 (fetch plus field-by-field allowlist/coerce/strip); server/finders/BookFinder.js:382-384 (provider.startsWith('custom-') routes around the entire built-in provider list)
Verdict: adopt-now — as design prior for harmonia's Cardigann indexer gap (#513). Cardigann is the same idea (externally-defined scraper contracts) applied to indexers instead of metadata search; the same contract-first, config-registered-not-code-registered shape can back both. Adopt the contract-first-extensibility shape and the defensive-deserialization discipline — validating a third party's JSON field-by-field instead of trusting its declared schema, because the contract only defines the happy path. The literal OpenAPI schema is audiobook-specific (title/narrator/ASIN/duration-in-minutes) and would need a harmonia-appropriate schema written from scratch.
Disqualifiers: GPL-3.0 → AGPL-3.0-or-later, harmonia-only, as above. The YAML spec is a document rather than code but should still be rewritten rather than copied, since it encodes audiobookshelf's exact field set. The auth model is a single static bearer-style header per provider row with no rotation or scope — do not inherit that as the whole story, only the search-contract shape.
P4 — Playback-position sync: append-only session log decoupled from a last-write-wins progress register
What: Cross-device progress sync is two decoupled data shapes, not one. A session row is an append/update-always audit record of one listening session on one device — always written on sync, no conflict logic. Separately, a per-(user, media-item) progress row is the single canonical "current position" register, updated from an incoming session sync only if the incoming session's updatedAt is not older than the existing progress record's updatedAt, with the skip explicitly logged. Offline/local sessions get a temporary client-generated ID; the server holds a remap table translating those onto its own canonical session IDs the first time it sees each one, so a client that resyncs the same local session twice converges on one server-side row instead of duplicating it.
Evidence:server/managers/PlaybackSessionManager.js:150-170 (play_local_ id remap via oldPlaybackSessionMap); server/managers/PlaybackSessionManager.js:207-221 (session row always updated, no conflict check); server/managers/PlaybackSessionManager.js:230-247 (last-write-wins-by-updatedAt guard on the progress register, explicit skip-log at line 233)
Verdict: adopt-now — the two-shape decoupling and the idempotent local-id remap are directly portable to harmonia's cross-device state sync, if and when it tracks per-item playback or processing position across multiple clients. Last-write-wins by wall-clock timestamp is simple but trusts client-reported clocks; a monotonic or logical clock would be the sturdier choice if harmonia's clients cannot be assumed well-synced.
Disqualifiers: GPL-3.0 → AGPL-3.0-or-later, harmonia-only, as above. Conflict resolution trusts client-supplied updatedAt with no server-authoritative logical clock — verified true by inspection (session.updatedAt is a direct assignment from the client's JSON with no server-side monotonic stamp anywhere in this path) — a known weak point of pure last-write-wins that a greenfield design should improve on rather than copy verbatim.
What: New-episode detection against an RSS feed combines three independent safeguards. A watermark cursor — the latest already-owned episode's publish date, not simply "last time we checked" — bounds the search window so a feed re-fetch after downtime doesn't re-walk the whole history. Within that window, identity is checked by GUID-or-enclosure-URL match, because feed GUIDs are commonly absent, reused, or duplicated in the wild, while the audio enclosure URL is nearly always unique. A per-item failed-check counter trips a circuit breaker: after a fixed number of consecutive failed feed fetches, auto-download is disabled outright and the user is notified, instead of retrying forever silently or crashing the check loop.
Evidence:server/managers/PodcastManager.js:325-336 (pubDate-watermark cursor selection in runEpisodeCheck); server/managers/PodcastManager.js:339-352 (failedCheckMap circuit breaker, disables autoDownloadEpisodes after MaxFailedEpisodeChecks); server/managers/PodcastManager.js:401-402 (dedup filter combining watermark plus identity check in checkPodcastForNewEpisodes); server/models/Podcast.js:393-397 (checkHasEpisodeByFeedEpisode: GUID-or-enclosure-URL match)
Verdict: adopt-now — as prior art for harmonia's Cardigann indexer result polling (#513), and structurally reusable for any other polled-external-source ingestion harmonia adds. The watermark-plus-identity-plus-circuit-breaker combination is the correct general shape for polling any external, imperfectly-behaved feed/index: bound the poll window for efficiency, never trust one identity field alone for dedup, and make repeated poll failure a visible, self-limiting state rather than silent infinite retry or silent permanent death. All three safeguards are simple, independently well-evidenced, and map cleanly onto a from-scratch design regardless of language.
Disqualifiers: GPL-3.0 → AGPL-3.0-or-later, harmonia-only, as above; not portable to any other fleet repo even as reference. The specific dual-key check (GUID/enclosure-URL) is podcast-RSS vocabulary — the transferable lesson is "never rely on one declared identity field from an external feed," not the two literal field names.
Why this matters
harmonia names three open gaps that these five patterns bear on directly: Cardigann indexer definition coverage (#513), the unbuilt Phase 04 diagnostic visualizer, and the general scanner/metadata-merge machinery underneath both. P3 and P5 are direct prior art for #513 — a contract-first, config-registered provider shape (P3) and a watermark-plus-identity-plus-circuit-breaker polling loop (P5) together cover both halves of "define an indexer externally, then poll it safely." P1's per-field diff events are the structured signal the Phase 04 visualizer needs to render per-item before/after state, and P2 supplies the metadata-precedence pipeline that visualizer would sit downstream of. P4 is the least tied to a named gap today — cross-device playback/processing-position sync only becomes load-bearing once harmonia has more than one client surface reading and writing the same server state — but the two-shape decoupling it demonstrates (append-only log plus last-write-wins register) is worth having on file before that need arrives rather than designed under deadline once it does.
None of the five depend on audiobook-specific vocabulary. The scanner/watcher settle-detection, the precedence-as-config pipeline, the contract-first provider shape, and the watermark/circuit-breaker polling loop are all general answers to "reconcile local state against an external, imperfectly-behaved source," which is closer to harmonia's actual problem than to audiobookshelf's.
Desired correction
Record each pattern as a design-prior paragraph in the phase PLAN that owns its landing area, per the fleet prior-art standard's rule that a pattern settles at the phase-PLAN rung and work becomes a tracked issue only once that phase starts:
P1 → the phase PLAN for harmonia's scanner/watcher subsystem, carrying the verifier-corrected description (vendored MIT watcher library, not chokidar) and cross-referencing the Phase 04 diagnostic visualizer PLAN for the per-field diff event shape.
P2 → the phase PLAN for harmonia's metadata-merge layer, carrying the verifier-corrected citation range (BookScanner.js:709-817) and the "no per-field provenance" gap as a named improvement target rather than an inherited limitation.
P4 → held at this issue until harmonia has a second live client surface against the same server state; at that point it moves to whichever phase PLAN owns cross-device sync.
Before any row of the Candidate table is copied verbatim into a phase PLAN, pin the commit: the routed evidence above did not carry an advplyr/audiobookshelf SHA, and the standard requires owner/repo@<sha> on anything that lands.
Done when: P1, P2, P3, and P5 each appear as a design-prior paragraph in their owning phase PLAN, carrying their verdict, qualifier, and disqualifiers verbatim from this issue and a pinned advplyr/audiobookshelf@<sha> citation; P4 is either placed the same way or explicitly left in this issue with its trigger (a second live client surface against harmonia's server state) restated; and this issue is closed.
Provenance
Read path only: every pattern above is a design prior, not carried code. Nothing from advplyr/audiobookshelf is copied, ported, or transcribed into harmonia by this issue — what would land is the mechanism, described from scratch, per each pattern's verdict qualifier.
Source: advplyr/audiobookshelf, GPL-3.0. Destination: harmonia, AGPL-3.0-or-later. GPLv3 §13 permits combining GPL-3.0 work into an AGPL-3.0-or-later work, which is why this source is admissible here; that compatibility does not extend to any other fleet repository and does not, by itself, authorize anything beyond the Read path — see the Candidate table's Disqualifiers cell for the full boundary.
All five patterns were drawn from a corpus in which an adversarial verifier re-opened every cited path:line against the source and refuted 23% of candidates fleet-wide; the five here are the subset that survived that pass for this scope. Two carried a verifier correction to a claim that survived rather than a rejection: P1's tech-stack description named the wrong watcher library (chokidar, not the actual vendored MIT dependency), and P2's citation range understated the class it points at (709-772 rather than 709-817). Both corrections are stated inline in their pattern above and do not change either pattern's verdict.
Finding
Five Read-path prior-art patterns from
advplyr/audiobookshelfland on harmonia's own stated gaps: Cardigann indexer definition coverage (#513), the unbuilt Phase 04 diagnostic visualizer, and the scanner/metadata subsystem generally. All five carry anadopt-nowverdict from a search that treats audiobookshelf's library-scanning and metadata-precedence machinery as more transferable than anything audiobook-specific about it — none of the five patterns below depend on audiobook, narrator, or chapter vocabulary.Every citation below was re-opened by an adversarial verifier against the source file:line before this pattern was allowed to survive; the verifier's corpus-wide reject rate on this pass was 23%. Two of the five patterns below carry a verifier correction to a claim that survived — both are stated inline in the pattern, not silently folded into the citation.
Candidate table
advplyr/audiobookshelfpath:linecitations are in Patterns below; no commit SHA was captured in the routed evidence, so pin one before this table is copied into a phase PLAN.advplyr/audiobookshelfas a source, by name or by pattern, even as reference. It also does not change the Read-path stance below: nothing here is copied, so the compatibility is dormant unless a later reader considers Depend or Carry, at which point this boundary is the first thing to check.Patterns
P1 — Incremental library-update detection: settle-poll watcher + layered-identity reconciler
What: Two decoupled stages implement "auto-detects library updates, no need to re-scan." A live filesystem watcher does not scan on the first add/unlink/rename event: it polls each new file's mtime every 3s until it stops changing (600s timeout) before treating the file as settled, debounces all pending events behind a 10s reset-on-activity timer, and exposes an ignore-dir registry so the app's own writes (a podcast download in progress) never self-trigger a scan. Once settled, a separate batch reconciler walks the folder tree and matches found items against existing DB rows first by exact path, falling back to inode match at the item level and then the individual-file level — so a folder rename/move is recognized as the same item, not delete-then-re-add. Per matched item, a field-by-field diff (mtime/ctime/birthtime/path, each library file by path-then-inode) decides "nothing changed" vs "needs media rescan."
Evidence:
server/Watcher.js:239-258(waitForFileToAdd mtime-settle poll, 3s interval, 200-loop/600s timeout);server/Watcher.js:389-427(addIgnoreDir/removeIgnoreDir self-write suppression with 5s trailing delay);server/scanner/LibraryScanner.js:172-222(path-then-inode item reconciliation loop);server/scanner/LibraryScanner.js:647-653(ItemToFileInoMatch/ItemToItemInoMatch helpers);server/scanner/LibraryItemScanData.js:182-278(checkLibraryItemData per-field diff, including per-library-file path-then-inode matching at lines 224-247)Verdict: adopt-now — as a from-scratch design for harmonia's scanner/watcher subsystem, targeting the settle-poll debounce, ignore-dir suppression, layered path→item-inode→file-inode identity resolution, and field-level diff as the mechanism to port. The Node/Sequelize implementation itself is not portable. The per-field diff events are also the structured before/after signal the unbuilt Phase 04 diagnostic visualizer would render per item.
Verifier correction: the disqualifier as mined described the watcher's dependency as "Node/chokidar/Sequelize." The watcher is not built on chokidar —
server/Watcher.jsrequires a vendored, MIT-licensed library (server/libs/watcher, by Fabio Spampinato); chokidar appears in the lockfile only as an unrelated transitive dependency of unrelated packages. The mechanism claim itself is unaffected by this; strike "chokidar" from any restatement of the tech stack.Disqualifiers: GPL-3.0 is licence-compatible for read-and-reference only inside harmonia specifically (see Candidate table); it must never be mined by name into any other fleet repo. The concrete implementation is JS plus Sequelize ORM idioms (mutable
.save()model objects, EventEmitter) that don't map onto Rust — only the state machine and identity-resolution order are portable. The inode-based fallback assumes stable POSIX inodes and silently breaks (falls through to treating a moved item as new) on filesystems that recycle or fake inode numbers (some SMB/CIFS/FAT mounts); audiobookshelf does not appear to guard against that.P2 — Declarative, user-configurable metadata-precedence pipeline
What: A library-level, user-configurable ordered list of named metadata sources (default
folderStructure,audioMetatags,nfoFile,txtFiles,opfFile,absMetadata) drives a handler object whose methods are looked up and invoked by that string, each mutating one shared draft-metadata object in place; a later source's non-empty field silently overwrites an earlier one's. Ordering is pure config, never hardcoded in extraction logic. The scanner persists a last-scanned-precedence snapshot per library, and the moment the configured order no longer matches what a library was last scanned with, the whole library is forced through a rescan rather than left silently inconsistent between old- and new-precedence items.Evidence:
server/models/Library.js:84-86(defaultMetadataPrecedence array);server/scanner/BookScanner.js:683-692(precedence loop dispatching bookMetadataSourceHandlermetadataSource);server/scanner/BookScanner.js:709-772(BookMetadataSourceHandler class, one method per named source);server/scanner/LibraryScanner.js:62-67(precedence-drift forces forceRescan)Verifier correction: the
BookScanner.js:709-772citation is narrower than the class it points at. The class's six methods (one per precedence source) actually span709-817; the claim that all six exist as named methods is independently confirmed by grep, so this is a citation-precision defect, not a fabricated mechanism. Read the citation as the start of the class, not its full extent.Verdict: adopt-now — the ordered-strategy-dispatch shape and the config-drift-forces-reprocess invariant are directly portable to harmonia's metadata-merge layer, e.g. as a
Vec<Box<dyn MetadataSource>>reconciling embedded tags, sidecar files, and Cardigann-fed remote metadata into one media record. The merge rule itself — unconditional last-source-wins per field, no provenance kept — is naive and is a place for a harmonia design to improve rather than copy.Disqualifiers: GPL-3.0 → AGPL-3.0-or-later compatibility is harmonia-only, as above; not usable as reference anywhere else in the fleet. No per-field confidence or provenance is retained on the final record — there is no way to later ask "which source set this field" — which a greenfield design should fix rather than inherit.
P3 — Externally-specified custom metadata provider contract, with defensive response validation
What: Third-party metadata providers are not code plugins; they are an OpenAPI-3.0-specified HTTP contract (search endpoint, query/author params, api-key header auth, a fixed response schema). Each configured provider is a DB row (base URL plus auth header) reached through a slug convention that is short-circuited ahead of all built-in provider logic — adding a provider is a config action, never a code change. The adapter never trusts or spreads the remote JSON: it destructures exactly the allowed keys, coerces each to its expected type, drops anything that fails validation, and only then builds the internal object.
Evidence:
custom-metadata-provider-specification.yaml:1-141(full OpenAPI contract, root of repo);server/providers/CustomProviderAdapter.js:21-145(fetch plus field-by-field allowlist/coerce/strip);server/finders/BookFinder.js:382-384(provider.startsWith('custom-')routes around the entire built-in provider list)Verdict: adopt-now — as design prior for harmonia's Cardigann indexer gap (#513). Cardigann is the same idea (externally-defined scraper contracts) applied to indexers instead of metadata search; the same contract-first, config-registered-not-code-registered shape can back both. Adopt the contract-first-extensibility shape and the defensive-deserialization discipline — validating a third party's JSON field-by-field instead of trusting its declared schema, because the contract only defines the happy path. The literal OpenAPI schema is audiobook-specific (title/narrator/ASIN/duration-in-minutes) and would need a harmonia-appropriate schema written from scratch.
Disqualifiers: GPL-3.0 → AGPL-3.0-or-later, harmonia-only, as above. The YAML spec is a document rather than code but should still be rewritten rather than copied, since it encodes audiobookshelf's exact field set. The auth model is a single static bearer-style header per provider row with no rotation or scope — do not inherit that as the whole story, only the search-contract shape.
P4 — Playback-position sync: append-only session log decoupled from a last-write-wins progress register
What: Cross-device progress sync is two decoupled data shapes, not one. A session row is an append/update-always audit record of one listening session on one device — always written on sync, no conflict logic. Separately, a per-(user, media-item) progress row is the single canonical "current position" register, updated from an incoming session sync only if the incoming session's
updatedAtis not older than the existing progress record'supdatedAt, with the skip explicitly logged. Offline/local sessions get a temporary client-generated ID; the server holds a remap table translating those onto its own canonical session IDs the first time it sees each one, so a client that resyncs the same local session twice converges on one server-side row instead of duplicating it.Evidence:
server/managers/PlaybackSessionManager.js:150-170(play_local_ id remap via oldPlaybackSessionMap);server/managers/PlaybackSessionManager.js:207-221(session row always updated, no conflict check);server/managers/PlaybackSessionManager.js:230-247(last-write-wins-by-updatedAt guard on the progress register, explicit skip-log at line 233)Verdict: adopt-now — the two-shape decoupling and the idempotent local-id remap are directly portable to harmonia's cross-device state sync, if and when it tracks per-item playback or processing position across multiple clients. Last-write-wins by wall-clock timestamp is simple but trusts client-reported clocks; a monotonic or logical clock would be the sturdier choice if harmonia's clients cannot be assumed well-synced.
Disqualifiers: GPL-3.0 → AGPL-3.0-or-later, harmonia-only, as above. Conflict resolution trusts client-supplied
updatedAtwith no server-authoritative logical clock — verified true by inspection (session.updatedAtis a direct assignment from the client's JSON with no server-side monotonic stamp anywhere in this path) — a known weak point of pure last-write-wins that a greenfield design should improve on rather than copy verbatim.P5 — Podcast episode ingestion: watermark cursor, dual-key identity, and circuit breaker
What: New-episode detection against an RSS feed combines three independent safeguards. A watermark cursor — the latest already-owned episode's publish date, not simply "last time we checked" — bounds the search window so a feed re-fetch after downtime doesn't re-walk the whole history. Within that window, identity is checked by GUID-or-enclosure-URL match, because feed GUIDs are commonly absent, reused, or duplicated in the wild, while the audio enclosure URL is nearly always unique. A per-item failed-check counter trips a circuit breaker: after a fixed number of consecutive failed feed fetches, auto-download is disabled outright and the user is notified, instead of retrying forever silently or crashing the check loop.
Evidence:
server/managers/PodcastManager.js:325-336(pubDate-watermark cursor selection in runEpisodeCheck);server/managers/PodcastManager.js:339-352(failedCheckMap circuit breaker, disables autoDownloadEpisodes after MaxFailedEpisodeChecks);server/managers/PodcastManager.js:401-402(dedup filter combining watermark plus identity check in checkPodcastForNewEpisodes);server/models/Podcast.js:393-397(checkHasEpisodeByFeedEpisode: GUID-or-enclosure-URL match)Verdict: adopt-now — as prior art for harmonia's Cardigann indexer result polling (#513), and structurally reusable for any other polled-external-source ingestion harmonia adds. The watermark-plus-identity-plus-circuit-breaker combination is the correct general shape for polling any external, imperfectly-behaved feed/index: bound the poll window for efficiency, never trust one identity field alone for dedup, and make repeated poll failure a visible, self-limiting state rather than silent infinite retry or silent permanent death. All three safeguards are simple, independently well-evidenced, and map cleanly onto a from-scratch design regardless of language.
Disqualifiers: GPL-3.0 → AGPL-3.0-or-later, harmonia-only, as above; not portable to any other fleet repo even as reference. The specific dual-key check (GUID/enclosure-URL) is podcast-RSS vocabulary — the transferable lesson is "never rely on one declared identity field from an external feed," not the two literal field names.
Why this matters
harmonia names three open gaps that these five patterns bear on directly: Cardigann indexer definition coverage (#513), the unbuilt Phase 04 diagnostic visualizer, and the general scanner/metadata-merge machinery underneath both. P3 and P5 are direct prior art for #513 — a contract-first, config-registered provider shape (P3) and a watermark-plus-identity-plus-circuit-breaker polling loop (P5) together cover both halves of "define an indexer externally, then poll it safely." P1's per-field diff events are the structured signal the Phase 04 visualizer needs to render per-item before/after state, and P2 supplies the metadata-precedence pipeline that visualizer would sit downstream of. P4 is the least tied to a named gap today — cross-device playback/processing-position sync only becomes load-bearing once harmonia has more than one client surface reading and writing the same server state — but the two-shape decoupling it demonstrates (append-only log plus last-write-wins register) is worth having on file before that need arrives rather than designed under deadline once it does.
None of the five depend on audiobook-specific vocabulary. The scanner/watcher settle-detection, the precedence-as-config pipeline, the contract-first provider shape, and the watermark/circuit-breaker polling loop are all general answers to "reconcile local state against an external, imperfectly-behaved source," which is closer to harmonia's actual problem than to audiobookshelf's.
Desired correction
Record each pattern as a design-prior paragraph in the phase PLAN that owns its landing area, per the fleet prior-art standard's rule that a pattern settles at the phase-PLAN rung and work becomes a tracked issue only once that phase starts:
BookScanner.js:709-817) and the "no per-field provenance" gap as a named improvement target rather than an inherited limitation.Before any row of the Candidate table is copied verbatim into a phase PLAN, pin the commit: the routed evidence above did not carry an
advplyr/audiobookshelfSHA, and the standard requiresowner/repo@<sha>on anything that lands.Done when: P1, P2, P3, and P5 each appear as a design-prior paragraph in their owning phase PLAN, carrying their verdict, qualifier, and disqualifiers verbatim from this issue and a pinned
advplyr/audiobookshelf@<sha>citation; P4 is either placed the same way or explicitly left in this issue with its trigger (a second live client surface against harmonia's server state) restated; and this issue is closed.Provenance
Read path only: every pattern above is a design prior, not carried code. Nothing from
advplyr/audiobookshelfis copied, ported, or transcribed into harmonia by this issue — what would land is the mechanism, described from scratch, per each pattern's verdict qualifier.Source:
advplyr/audiobookshelf, GPL-3.0. Destination: harmonia, AGPL-3.0-or-later. GPLv3 §13 permits combining GPL-3.0 work into an AGPL-3.0-or-later work, which is why this source is admissible here; that compatibility does not extend to any other fleet repository and does not, by itself, authorize anything beyond the Read path — see the Candidate table's Disqualifiers cell for the full boundary.All five patterns were drawn from a corpus in which an adversarial verifier re-opened every cited
path:lineagainst the source and refuted 23% of candidates fleet-wide; the five here are the subset that survived that pass for this scope. Two carried a verifier correction to a claim that survived rather than a rejection: P1's tech-stack description named the wrong watcher library (chokidar, not the actual vendored MIT dependency), and P2's citation range understated the class it points at (709-772rather than709-817). Both corrections are stated inline in their pattern above and do not change either pattern's verdict.