Marked Read by Nobody but You
Written later and filed to this date, as part of filling in every day of the year.
When the lesson section was withdrawn, fifty-some files, a progress dashboard and the module that computed it left in one commit, and every browser that had ever pressed “mark complete” on a lesson kept its record. The site could not tell those readers anything. It has no accounts, no analytics, no cookies, no beacon, and that is a promise on the fine-print page, so the only copy of a reader’s progress is the one in their own browser’s local storage, and the site never sees it. A withdrawn section leaves a stranded key in some number of browsers I cannot count, pointing at pages that return nothing. There is no migration, because a migration needs a server that knows who to migrate.
The writing’s read marks were built two and a half weeks after the lessons’ progress, and with that constraint in mind. A post is marked read when you open it, once, automatically; there is no button and no undo, because “I opened this” is not something a reader would want to revert. The record is a sorted list of post slugs under one namespaced, versioned key, kept apart from anything the lessons stored. The index page puts a check on each visited card and prints “read X of Y” underneath, and that is the whole feature.
The part that matters lives in the Y. The summary counts only read slugs that still appear in the list of published posts the page itself rendered. A post withdrawn, or returned to draft, or renamed, falls out of the count on the next visit, silently, and the X can never exceed the Y. The parser is written to the same tolerance: a missing value, a non-JSON value, a hand-edited value, an array with something that is not a string in it, all collapse to an empty or filtered set and never throw, because the one thing worse than losing a reader’s marks is a script error on the index page over data the site never wrote. Twelve tests cover exactly that surface and nothing else.
The design follows from a constraint most sites do not have. If the site cannot see the storage, it cannot repair it, so the storage has to be something the site can be wrong about without consequence: a set of names, filtered at read time against what is true now. The lessons’ keys are still out there in browsers, a list of pages that no longer exist, harming nothing.
Read marks, then: a visitor’s progress through the writing, kept in their own browser only, counted against the current published list every time it is shown, and tolerant of any value at all, because the site that wrote the feature is the one party that can never read it back.