<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Plekt</title>
    <description>The latest articles on DEV Community by Plekt (@plekt).</description>
    <link>https://dev.to/plekt</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F4118877%2Fe14f65bc-3e41-4671-9baa-376e9ad39aa9.png</url>
      <title>DEV Community: Plekt</title>
      <link>https://dev.to/plekt</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/plekt"/>
    <language>en</language>
    <item>
      <title>A sync ledger is only useful when it leads to a safe next step</title>
      <dc:creator>Plekt</dc:creator>
      <pubDate>Mon, 19 Oct 2026 14:23:03 +0000</pubDate>
      <link>https://dev.to/plekt/a-sync-ledger-is-only-useful-when-it-leads-to-a-safe-next-step-1bkn</link>
      <guid>https://dev.to/plekt/a-sync-ledger-is-only-useful-when-it-leads-to-a-safe-next-step-1bkn</guid>
      <description>&lt;p&gt;In &lt;a href="https://dev.to/plekt/designing-an-explainable-sync-ledger-for-watch-state-across-media-servers-5g89"&gt;my earlier post about designing an explainable sync ledger&lt;/a&gt;, I argued that one &lt;code&gt;synced&lt;/code&gt; or &lt;code&gt;failed&lt;/code&gt; flag hides too much. The implementation raises another question: after recording a result, can someone tell what to do safely?&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F7zlmlmobnlfyrcr5h7t5.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F7zlmlmobnlfyrcr5h7t5.png" alt="Plekt Sync Activity showing grouped history and per-destination results" width="800" height="391"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  One title, several outcomes
&lt;/h2&gt;

&lt;p&gt;A movie or episode may succeed on two destinations and need attention on a third. A single global result loses that detail.&lt;/p&gt;

&lt;p&gt;Plekt's Sync Activity groups history by movie or show while keeping each destination's result available behind the group. It records reasons for skipped destinations, exposes &lt;strong&gt;Fix match&lt;/strong&gt; for unresolved cross-platform matches, and targets retries at failed destinations. Expected missing-library skips are excluded from retries. Group logs can be downloaded for investigation.&lt;/p&gt;

&lt;p&gt;Those outcomes need different recovery paths:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;An unresolved identity needs a match decision.&lt;/li&gt;
&lt;li&gt;A failed destination may be retried.&lt;/li&gt;
&lt;li&gt;An expected missing-library skip should remain a skip.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Treating each as the same kind of failure would repeat work without resolving the underlying cause.&lt;/p&gt;

&lt;h2&gt;
  
  
  Fresh evidence before changing watch state
&lt;/h2&gt;

&lt;p&gt;Some problems start before delivery. In v2.1.1, the Plex watch-state path preserves completed state across stale resume data and protects against unwatch races. Suspected Plex unwatches are rechecked against live metadata, and cached episode state is kept aligned so stale reports do not reach manual watch review.&lt;/p&gt;

&lt;p&gt;These are targeted safeguards, not a claim that every provider-history edge case is solved. They address one important boundary: check that a signal is current before interpreting it as a change to watch history.&lt;/p&gt;

&lt;h2&gt;
  
  
  The question I keep asking
&lt;/h2&gt;

&lt;p&gt;For each item, can someone see what happened, which destination received it or why it did not, and whether the safe next step is to retry, fix a match, review, or leave it alone?&lt;/p&gt;

&lt;p&gt;That is the difference between a history table and an operational tool. The row is not the goal. A clear explanation and a safe recovery path are.&lt;/p&gt;

&lt;p&gt;This follows &lt;a href="https://dev.to/plekt/designing-an-explainable-sync-ledger-for-watch-state-across-media-servers-5g89"&gt;the original design post&lt;/a&gt;. The &lt;a href="https://github.com/Lasikiewicz/plekt/releases/tag/v2.1.1" rel="noopener noreferrer"&gt;v2.1.1 release notes&lt;/a&gt; include the Plex changes and the current known limitations.&lt;/p&gt;

&lt;p&gt;I'd be interested in how other media integrations distinguish a real unwatch from stale resume data.&lt;/p&gt;

&lt;h3&gt;
  
  
  Try Plekt
&lt;/h3&gt;

&lt;p&gt;Plekt is open source and self-hosted: &lt;a href="https://github.com/Lasikiewicz/plekt" rel="noopener noreferrer"&gt;project and source&lt;/a&gt;, &lt;a href="https://plekt.net" rel="noopener noreferrer"&gt;website&lt;/a&gt;, &lt;a href="https://demo.plekt.net/" rel="noopener noreferrer"&gt;live demo&lt;/a&gt;, &lt;a href="https://discord.gg/7ZmEGKcRC5" rel="noopener noreferrer"&gt;Discord community&lt;/a&gt;, and &lt;a href="https://www.reddit.com/r/plekt/" rel="noopener noreferrer"&gt;r/plekt&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Disclosure: I maintain Plekt. I used AI assistance to draft this post under my direction, checked the technical details against the current release notes and project docs, and AI is not part of the application at runtime.&lt;/p&gt;

</description>
      <category>selfhosted</category>
      <category>opensource</category>
      <category>architecture</category>
    </item>
    <item>
      <title>Designing an explainable sync ledger for watch state across media servers</title>
      <dc:creator>Plekt</dc:creator>
      <pubDate>Thu, 10 Sep 2026 10:23:06 +0000</pubDate>
      <link>https://dev.to/plekt/designing-an-explainable-sync-ledger-for-watch-state-across-media-servers-5g89</link>
      <guid>https://dev.to/plekt/designing-an-explainable-sync-ledger-for-watch-state-across-media-servers-5g89</guid>
      <description>&lt;p&gt;Update (27 October 2026): Replaced older screenshots with current Plekt UI captures.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fv43n5s8rsaop21ee4ebz.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fv43n5s8rsaop21ee4ebz.png" alt="Plekt Sync Activity issue view showing a provider failure and next step" width="797" height="136"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Watch-state synchronization looks simple until the same episode has four provider identities, a rebuilt library, a delayed webhook, and a user who watched it twice.&lt;/p&gt;

&lt;p&gt;A boolean watched flag is not enough to explain what happened. A useful sync system needs a canonical record, an event lifecycle, per-destination evidence, and a recovery path that a person can understand.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why watched and unwatched are insufficient
&lt;/h2&gt;

&lt;p&gt;A destination can be behind, unavailable, ambiguous, or based on a different item identity. Treating the latest provider response as absolute truth makes a sync layer overwrite information without showing why.&lt;/p&gt;

&lt;p&gt;A better model keeps the canonical record separate from delivery state. The canonical record answers what the local system believes. Delivery records answer what each destination received, when it was attempted, and why a retry is or is not needed.&lt;/p&gt;

&lt;h2&gt;
  
  
  Canonical identity and event lifecycle
&lt;/h2&gt;

&lt;p&gt;The first hard problem is identity. A movie or episode can be represented differently after a library rebuild or across Plex, Emby, Jellyfin, and Trakt. The sync layer needs a stable internal identity and an explicit review path when a match is uncertain.&lt;/p&gt;

&lt;p&gt;The second hard problem is lifecycle. A play can be new, repeated, partially watched, completed, corrected, or superseded by a newer state. Each transition should be idempotent so retrying a delivery does not create a second play by accident.&lt;/p&gt;

&lt;h2&gt;
  
  
  Per-destination delivery results
&lt;/h2&gt;

&lt;p&gt;A single sync status hides too much. For each destination, record the source event, destination, outcome, reason, attempt time, and retry context. This turns a vague failure into an actionable answer such as a missing match, a rate limit, a provider outage, or a deliberate manual review.&lt;/p&gt;

&lt;p&gt;That record is the basis of Plekt's Sync Activity view. It is designed as an operational ledger, not a green checkmark that asks the user to trust an invisible process.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fuv9jkhkh2a0705ymb3nz.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fuv9jkhkh2a0705ymb3nz.png" alt="Plekt watch history grouped by day" width="800" height="146"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Conflicts, rewatches, and rebuilt libraries
&lt;/h2&gt;

&lt;p&gt;Conflict handling needs to be explicit. When two sources disagree, the system should preserve the evidence, identify the newer or stronger signal, and explain the choice. Rewatches need their own event semantics so a repeat play is not mistaken for a duplicate import.&lt;/p&gt;

&lt;p&gt;A rebuilt library is a recovery case, not just another sync. Matching should be conservative, surface uncertainty, and let a person review before a large batch changes history.&lt;/p&gt;

&lt;h2&gt;
  
  
  Backup and recovery boundaries
&lt;/h2&gt;

&lt;p&gt;Backups are part of the feature. A safe workflow starts with a backup and a small library, then expands after the Sync Activity results look correct. Recovery documentation should state what can be restored, what needs to be reconnected, and which provider actions are not reversible.&lt;/p&gt;

&lt;h2&gt;
  
  
  Testing provider integrations
&lt;/h2&gt;

&lt;p&gt;Integration tests cover request and response handling, but browser flows matter too. A practical test pass checks onboarding, manual watch review, retries, backups, metadata, and the visible reason attached to each delivery outcome. The test data should include missing matches, provider failures, duplicate plays, partial progress, and a rebuilt library.&lt;/p&gt;

&lt;h2&gt;
  
  
  AI assistance and review
&lt;/h2&gt;

&lt;p&gt;Plekt was heavily AI-assisted under my direction. I reviewed the generated changes, tested the integrations and browser flows, and maintain the project. AI is not required at runtime. This article is a technical explanation of the design and its trade-offs, not a claim that the difficult edge cases are solved.&lt;/p&gt;

&lt;p&gt;Back up first and test with a small library.&lt;/p&gt;

</description>
      <category>selfhosted</category>
      <category>opensource</category>
      <category>architecture</category>
    </item>
  </channel>
</rss>
