Changelog
1,076 changes across 26 active days in the last 30.
Every line below is a commit message, published as written. Merge commits, tooling commits and planning notes are left out; nothing is summarised or rewritten. Today is still in progress and will grow until the next build.
Sunday 20 September 2026
Athenana
- iOS capture: record the fix wave's three Swift traps, trim the outbox's forgotten set to the file, and tolerate spaces in an entitlements plist
- iOS capture fix wave, Swift minors: one ladder for the link sheet, a merged outbox move, sized refusals, Debug-only strings
- iOS capture spec and docs: the built handler contract, §10 as the ledger, the measured counts
- iOS capture fix wave: sweep dot-files out of the staged appex, drop the unread fromNativeHost, pin the fourth client and CORS credentials
- Share sheet: re-encode a HEIC through the one ServerImage rule the intent already used
- build-ios.sh --extensions: match each group under its own entitlements key, and read the closure off the stager
- Outbox: an id this instance removed is never read back off a file whose rewrite failed
- iOS capture: pin the three clients' field sets on the server, and record what the build learned
- Safari staging: rewrite the manifest with /usr/bin/python3, since an Xcode script phase has no node on its PATH
- Safari extension on iPhone: the existing extension, staged by the Mac build's rule, with the app's session answered by a native handler
- UI test: retry the article-link tap, since the reader's position restore moves the link under it
- Extension: only a session-shaped reply is a native host's answer, so an echoing handler cannot read as a signed-out phone
- Extension: take the session from a native host when there is one, so the iPhone build needs no pasted token
- App Intents fix round: a HEIC is re-encoded as JPEG since IntentFile will not, and Continue reading opens the article on a warm app too
- iOS app: Save to Athenana and Continue reading as App Intents, so Shortcuts and Siri keep their composability
- Outbox: a purge re-reads the file first, so a picture the extension parked goes with it
- UI test: wait for the surface's single card to settle before tapping it
- Outbox: re-read outbox.json before every read and write, so the share extension's entries survive the app's next save
- iOS share extension: share → saved → done, with a note one tap away and an outbox for the plane
- iOS app: an App Group for the outbox and a Keychain access group for the token, ahead of the extensions
- CapturePayload: key an image on its bytes, not its name; a caption's trailing slash still marks the link
- AthenanaKit: one payload for every capture, a multipart save, and an outbox entry that carries a file
wyome.com
- Refresh the banked archive from the server
- Make the reclassify() archive write atomic like the harvest write already is
- Classify the two spam families in the generator too, and re-file what was already banked
- Say that Athenana's reader now shows an item's pictures, from copies on its own server
- Restore "no hidden image" on the weekly-email promise
- Promise that no image in the weekly email reports an open, not only no hidden one
Saturday 19 September 2026
Athenana
- Drop a forwarded newsletter's unsubscribe footer from the reader
- Bring the reader's own tests onto the new picture rule, and stop "kept" meaning two things
- Move the worker's --timeout with the budget FetchArticleImages raised
- Reader: stop running blocks together, drop page furniture, and show an article's pictures again
wyome.com
- Refresh the banked archive from the server
- Say what the 403 double fault actually cost: refusals logged as 500
- Serve Apache's own 403 page on the other three docroots too
- Serve Apache's own 403 page to a refused request
- Name both candidate paths in the missing-spam-list warning, and fix two facts
- Test the Matomo wiring with a host that is not also in SPAM_HOSTS
- Filter the Matomo referrer-spam list on the statistics page
- Carry the referrer-spam block on the other three docroots
- Refuse a request whose Referer names a known spam family
- Record a referral only on a 200 or 304, so a refused request never reaches the page
- Refresh the banked archive from the server
Wednesday 16 September 2026
Athenana
- CSP: let the IndieAuth consent form redirect to the native apps' scheme
- iOS app review fixes: pictures over the API with a bearer, a 304 keeps the reader's block, snapshots respect the purge, notes through ReadableHtml, and a lens keeps the way back
- iOS app: a purged outbox takes nothing and the open reader flushes first; a failed refresh keeps the deck paging and spends no window
- iOS app: offline deck and outbox replay on foreground, sign-out that purges, a release script, screenshots, and the record
- iOS app: the back flipped over the surface's reader has Open original; a list jump pages; a dead state dropped
- iOS app: the card back with share-out, lenses with their list and chip, and the surface with search
- iOS reader: a flushed position names its rendering, so reopening the card resumes; the trailing report is the latest build's
- Reading API and iOS reader: a card's position names its rendering, and the reader opens at the block only for that rendering
- iOS reader: the article in a WebView, a block-indexed position through the outbox, tap-to-reveal chrome, Keep, the link sheet, typography per device
- iOS app: Next mutates before it awaits, history is ids, and a refused token signs out with the full purge
- iOS app: a swipe is Next and Back — it passes and remembers the card, as the compass does
- iOS app: sign in, the deck and its compass, cards by type, and the stopping-point card
- iOS reader: exclude the reparented <script> from position tracking, and clean up the postbuild strip
- iOS reader: the page template and a script that reports the block index, tested with Node
- AthenanaKit: the outbox and the article cache survive a purge or a newer position landing while a request is in flight, and a drop is reported
- AthenanaKit: an article cache that revalidates by ETag, and an outbox that replays in order and keeps a position's own clock
- AthenanaKit: pin the session's departures from the brief, and refuse a callback from another issuer
- AthenanaKit: the session — PKCE, the IndieAuth flow that refuses a foreign state, and a token that lives only in the Keychain
- AthenanaKit: models and a client for every reading endpoint, with fixtures the PHP suite holds to the presenter
- iOS app: a generated Xcode project, the AthenanaKit package, and a build script that reads xcodebuild's own status
- Reading API: a note reads from its body, and an image card carries the full picture
- Newsletter review follow-ups: aspect-ratio note, access-log clause, comment reword
- Newsletter view polish: one issue date, block @php, text escaping, lowercase wordmark, bulletproof Read pill
- Send CORS on the woff2 fonts so Apple Mail renders them
- Harden the newsletter body-identical guards against a smuggled value
- Record what the weekly email fetches, and why that is the promise now
- Say on the privacy page what the weekly email now fetches, and hold the view to it
- Set the weekly issue in the app's own typefaces, served from this host
- Show each emailed link's preview picture, through the public route, and pin that every reader gets one body
- Fix two newsletter tests that were silently asserting nothing
- Send the weekly issue as a designed page, with a text part beside it
wyome.com
- Say what the weekly email now fetches: pictures and a typeface, the same for every reader
- Refresh the banked archive from the server
Tuesday 15 September 2026
Athenana
- Reading API review fixes: positions in UTC and on every card, a bad host lens is refused, and four tidy-ups
- Reading API review fix: one position per item, the search test fails rather than passes under a transaction, and only the card back's own echo of visibility is exempt from the principle-6 guard
- Reading API: prune passes nightly, hold principle 6 with a test, and record what the build learned
- Deck settings: the switches, over the API and on a web screen
- IndieAuth: our own apps as a client, returning to a custom scheme the client page lists
- Reading API review fix: the card back gates Rec League behind the same switches as its share list
- Reading API: the card back, share-out through one action both controllers call, and a share scope
- Reading API: keep a passage over the API, and bind every highlight to its source item
- Reading API review fix: the resurfaced shelf resolves switches in one query, not one per item
- Reading API: the surface's two shelves, and one lens for tag, host and search
- Reading API review fix: a tampered cursor is a 422 and not a 500, base64url for the iOS client, and a walk never re-deals what became in-progress after it began
- Reading API review fix: a highlight in progress is dealt once, at the front; a switch off removes an in-progress item too
- Reading API: deal the deck — in-progress first, then the operator's pattern over seeded ingredients
- Reading API review fix: read items.host instead of re-deriving it, make both upserts race-safe, and cover ReadingCard
- Reading API: the card, a block-indexed position that the older device cannot overwrite, and passes
- Reading API review fix: drop the scope by name in RecordOpen, and two unused test imports
- Reading API: the reader as blocks, with a hash and an ETag, and the open recorded
- Deck switches: what a person may leave out, and the operator's defaults
- Reading API: positions, passes, a highlight's source item, and per-user deck switches
WyHealth
- Read a silent probe as no answer, not as absence
Hosting monitor
- Stop not-served and dir-listing passing on a server nobody reached
- Say the Mac was offline instead of rendering eight sites down
Monday 14 September 2026
Athenana
- Run the feed cards the full column, the width the board's list view has
- Select every tag on the manage-tags page with one press, and clear them with the next
Sunday 13 September 2026
Athenana
- Prune a fetched feed with no entries whatever its age, and name each feed the prune takes
- Raise the narration ceiling to 60,000 characters, and count the saved text on the item page
- Keep a Day One entry whose URL line is longer than the save form allows as a note
- Hand the Reeder job test the Day One action too, and untangle the format tests after the rebase
- Bound the Day One decode and the text columns from the bytes, and anchor the guards
- Name Day One on the import screen, and count its photos and skipped entries
- Ask the zip for its journal before reading the upload, and bound the rest by size on disk
- Route a Day One export through the import job, zip open for its photos
- Own the Day One test zips, and say nothing per entry when there is no zip
- Put a Day One entry's photos in the note's gallery, the first as its card picture
- Say what keeps a binned Day One entry binned, bound the uuid, count tags as the vocabulary does
- Import a Day One entry as a private note, or a private link when it is a share-sheet save
- Refuse a URL line that carries prose, and read a CRLF body the same as LF
- Read a Day One entry's text: the escapes, the share-sheet shape, the note body
- Read the journal JSON out of a Day One zip, in place
- Detect a Day One JSON export
- Bound the import upload by an archive ceiling of its own
- Run athenana:prune-feeds daily at 03:30
- Forget feeds silent for a year: athenana:prune-feeds
- Make the feeds page a list scroll.js extends fifty rows at a time
- Move the Share panel under the saved page text
- Drop the fill from the item page's Share panel; it keeps its outline
- Put Previous / Next and Back on one row: the nav on the left, Back on the right
- Put Read / Listen on the tags' own row, right-aligned; the chips wrap beside them
- Move the add-a-tag field under the Title and note form; the chips stay under the title
- Reorder the item page: title, tags, Read/Listen right-aligned, picture, share, labelled text
- Call the first two tabs In and Out, under the Import / Share heading
- Make site: search the saved URL's domain, and count a feed's saves the same way
- Head the four sharing tabs "Import / Share", to match their sidebar row
- Label the settings sidebar's Sharing row "Import / Share"
- Walk a source from inside an item: Next and Previous stay on the source you came from
- Tidy the source board after review: honest host test, no tag-timeline link on a source strip, prefixed helpers
- Link the source named on an item page to everything saved from that domain
- Draw a source's history above its board, against every save as the baseline
- List every item from one source domain at /from/{host}, on the tag board's template
- Record each item's source host, written on save and backfilled from the URL
- Define the source domain of a URL once, in PHP
- Correct what the branch review found overstated or understated in the Reeder import's comments, copy and spec
- Charge scalars in the JSON Feed decode estimate, and stop truncating an untitled long url into a permanent title
- Detect a JSON Feed export whose slashes are escaped, as Reeder writes them
- Name Reeder wherever the import formats are listed, and import both files end to end
- Detect a Reeder export, either shape, and hand it to its parser
- Read a JSON Feed file as private links tagged with the feed's name
- Bound a Reeder export's containers at three a link, which is what the shape costs
- Read a Reeder Classic Read Later export as private, tagged, untitled links
- Record in the backlog that the narrowed-board walk plucks the whole list
- Walk a search from inside an item too, and make the narrowed board one context
- Walk a tag from inside an item: Next and Previous stay in the tag you came from
- Say what the JSON decode estimate cannot see, rather than claiming it never under-counts
- Tidy the JSON Feed branch after review
- Say on the standards page that JSON Feed is read as well as published, and anchor it
- Never throw out of a JSON feed's date
- Measure a JSON body before decoding it, and decode it in one place
- Read a JSON Feed wherever a feed body is parsed, and record json as its kind
- Read past a byte-order mark when deciding which feed parser to use
- Dispatch a feed body to the JSON or XML parser on its first byte
- Parse JSON Feed 1.1 into the shape the feed pipeline already reads
- Pick the axis's label density from the pixels it has, in both directions
- Say on the privacy and features pages what a file import does, and hold both policies to it
- Label a long axis by a stride of years, so a twenty-year library reads
- Thin a tag axis past four years to its Januarys, and give the first tick its room
- Bound a Pinboard JSON backup's element count by its commas before decoding
- Run the retry backfill's tests as an administrator, now the command is gated
- Name Pinboard where the import formats are listed, and make two fixtures honest
- Count a Pinboard JSON backup's entries before decoding it
- Hold both backfills to the same key gate as enrich-pending
- Say what the Sharing In link means for the phone gap, and make the keyless copy test discriminate
- Move the import onto Sharing In and 301 the old address
- Read an import's pages only for an account that pays for it
- Import a Pinboard bookmark that was not shared as private
- Detect a Pinboard XML or JSON backup and hand it to its parser
- Read a Pinboard XML backup
- Read a Pinboard JSON backup
- Say on both public pages that the Share block can be switched off
- Exercise forget on a still-hidden target
- Show a switched-off share refusal outside the panel the switch removes
- Put a switch on every share target and a master switch above them
- Add the sharing-out switch and per-target hide and show actions
- Refuse a share to a hidden target, or any share with sharing out off
- Make the listing share-icon negations actually discriminate
- Fix User::sharesOut() to default true for an unrefreshed model
- Honour the sharing-out switch and the hidden list in the one share-target gate
- Give an account a sharing-out switch and a list of hidden share targets
- Wash the trash's thumbnails like the board row's
- Show each trashed item its listing picture, and serve it
- Make a feed due a day less the run's length, so no feed skips a night
- Name every trashed item: one list-title rule for the row and the trash
wyome.com
- Name Reeder among the file imports on the privacy page, beside Athenana's
- Say JSON Feed can be followed from Athenana
- Say what a bookmarks-file import into Athenana does with the file
- Refresh the banked archive from the server
Saturday 12 September 2026
Athenana
- Tighten the posting targets after the branch review
- Stop /shares calling micro.blog the one target that posts
- Keep a rejected share token out of old input
- Say on the privacy page that Share sends MyMind a link, beside the import's promise
- Refuse a redirect on every posting share request, so a user-typed host cannot bounce this server
- Record the competitor share targets as built
- Hold both privacy pages to naming the four bookmarking apps
- Name the five targets the server checks on connect, since MyMind is not one of them
- Say on the privacy page which of eleven share targets the server posts to, and guard all seven
- Reframe the /shares stance as no lock-in and list the five bookmarking apps
- Draw a mark for each bookmarking-app share target
- Share a saved link back to MyMind through the import key
- Require a real host in a Karakeep address, with no query string or sign-in
- Share a saved link to a self-hosted Karakeep
- Share a saved link to Readwise Reader
- Share a saved link to Raindrop
- Name every posting endpoint in the parity guard so a new target file counts as a poster
- Share a saved link to Pinboard
- Add the four bookmarking-app credentials and the Karakeep address to users
- Connect and forget any posting target from its declared fields
- Draw posting share targets from the registry rather than a block each
- Move Are.na onto the shared posting controller
- Move the posting-share ladder into one controller, with micro.blog as its first target
- Keep every item-page form inside the Revisit draw it was opened from
- Size the Send to Device headings like the other tabs
- Key the Send to Device section rhythm on its two ids, not on every section
- Restore the column rhythm inside the Send to Device sections and fix the tab strip's overflow
- Count the Send to Device POSTs correctly: six, not five or seven
- Replace five settings rows with one Sharing row
- Put the Sharing tab strip on its four pages
- Put Podcast and Kindle on one Send to Device page
- Give the five-Ws pass five minutes by default, not two
- Close the five-Ws review: skip headline-only stories, echo the ceiling on the privacy page, pin the third nonce
- Say on the privacy page that ticked feeds' stories are summarised, and the article fetched when the feed gives a line
- Show the five Ws of the ticked feeds' stories under the briefing on Discover
- Run the five-Ws step inside the discovery build, and prune its rows with the rest of the morning
- Write a story's five Ws from the feed's summary, or from the article when the summary is a line
- Name the five-Ws article fixture apart from FullChainTest's
- Ask Claude for a story's five Ws, and let FeedSource narrow the window to what still owes one
- Extract the article-text read from ExtractContent into ReadableText
- Add the feed_summaries table and its three ceilings
- Toggle a feed's daily five-Ws summary from its card
- Let a feed be marked for a daily five-Ws summary when it is added
- Send the item's picture with an Are.na share instead of letting Are.na screenshot the page
- Stop the privacy page promising nothing goes to third parties
- Anchor wyome's WyHealth logging promise to WyHealth's code, with a skip for an absent checkout
- Say the Kindle send carries your note and can be downloaded; re-read the chart's row
- Rank the artist's own channel above a nearer stranger
- Name what the privacy page had missed: federation, MyMind, IndieAuth, the host
- Match a song on its title as well as its length, and read a single off its page
- Name the six share targets on the privacy page, and hold the promise to the code
- Say the download is a rebuild, and match the small print to the box
- Say that the note goes to the Kindle only if you tick the box
- Offer the note, and refuse a second press
- Let the file be downloaded once it has been sent
- Strip <img> from the Kindle note, in the DOM
- Build the Kindle file in one place, and let a send carry the note
wyome.com
- Name the five bookmarking apps a saved link can be shared to
- Say that an Are.na share carries the item's picture, never a screenshot
- Refresh the banked archive from the server
- Say the stylesheet serves every page, not three
- Name WyHealth in the three page descriptions that still listed four apps
- Say where WyHealth's data lives and what the app sends; drop the flows it does not yet have
- Refresh the banked archive
- Drop bot and redirect-follower referrals from the statistics page
- Say what Athenana's blog delivers to followers, and name MyMind, IndieAuth and the host
- Name the six places a saved link can be shared to
- Say that a Kindle send carries your note only if you ask
Friday 11 September 2026
Athenana
- Make a cover-shaped cover from a page's picture
- Give the EPUB a place for the reader's own note
- Hold wyome.com/privacy to the Send to Kindle promise as well
- Keep the Kindle address out of laravel.log and failed_jobs
- Say that Send to Kindle mails an article to Amazon, and hold it to the code
WyHealth
- Take Next 16.3.4 for the AVIF image-optimizer RCE
wyome.com
- Refresh the banked archive from the server
- Say that a saved article can be sent to a Kindle
Hosting monitor
- Bless standards/ in the wyome.com docroot
Thursday 10 September 2026
Athenana
- Downgrade three Send to Kindle cells from no to unknown
- Say in public that an article can be sent to a Kindle
- Let the Kindle refusal show for the two cases that could never see it
- Put Send to Kindle on the reader, and say what the last send did
- Record a Kindle send the catch never saw, and test the passages stay the owner's
- Mail an item to a Kindle, and write down what happened
- Keep a Kindle address on the account, refused unless it is Amazon's
- Fail loudly on a bad EPUB write instead of shipping empty bytes
- Build an EPUB from the reader's output, as a pure function
- Take the "no other bookmarking apps" block off the shares page
- Say on the privacy page that a PDF attachment is kept, and guard the claim
- Say what a document is on the board card and the list row
- Tell every cache not to keep a document
- Close four things a review of the branch found
- Draw page one with poppler, not ghostscript
- Save a PDF attached to a forwarded message as a document of its own
- Show a document on its own page, and give the board the right picture
- Run the extraction, and serve a document as a download
- Let a PDF be uploaded from the form that already saves everything else
- Point the new tests at tests/fixtures, which is its real name
- Read a PDF into the column search already reads
- Share the link, never the note — and write down the inbound account wall
- Refuse a double-clicked share, and stop listings privileging one service
- Stop the documented URL running over the column beside it
- Let other sites share INTO Athenana, and document it on /shares
wyome.com
- Say that a PDF attached to a forwarded message is kept
Wednesday 9 September 2026
Athenana
- Add a saved item to Are.na, and link out from the shares page
- Post a saved item to micro.blog
- Share a saved item to Bluesky, Mastodon and Hacker News
- Remove one episode, give the feed a face, and write down what was learned
- Move the share panel up under the buttons it belongs with
- Play the narration where the article is, and prefer it to a robot voice
- Make the share a panel like Picture and Photos
- Put the share box above the published sentence, not below it
- Say at the form that a credential is saved, because it always was
- Give the item screen's share its own box under the action row
- Derive the narration timeout from the length it is allowed to narrate
- Give narration long enough to finish, and say why when it does not
- Say in public that articles are narrated, and hold it to the code
- Reserve the row's share slot on rows that have nothing to share
- Narrate a saved article into a private podcast feed
- Put the Rec League share on the board card and the list row
- Offer a share to Rec League from a saved item
- Read an article aloud with a voice that is already on the device
- Show which client made each save, in counts and nothing else
- Record which client saved an item, not only that it started with ios
- Show which hosts actually speak Webmention, off the ledger that already knew
- Say on the standards pages that outbound Webmention almost never lands
wyome.com
- Say that an article can be narrated into a private podcast feed
- Say that the sending half of Webmention rarely finds anyone home
Tuesday 8 September 2026
Athenana
- Owe no webmention for a public item that has no url to mention
- Strip an authorship label off a name, and deliberately not the form that eats names
- Re-measure the microformats census against the whole corpus, and decline h-entry on it
- Hold wyome's new standards page to the same code facts as our own
- Guard the standards page the way the policies are guarded
- Put every public page in the sitemap, and date the one that can be dated
- Compare this against six others in public, and say where they win
wyome.com
- Put Standards in the nav, and let the nav wrap so it fits
- Restate the standards Athenana speaks, now that a guard holds the copy honest
Monday 7 September 2026
Athenana
- Say in public which standards this speaks, and which it does not
- Pin wyome's front page, the last surface nothing checked
- Say on /features that podcasts exist, and pin that page to the code
- Show an episode's publish date beside its duration
- Constrain the tag history strip's numerator to an active search
- Pin a posted highlight to private, since the deploy check could not
- Give the two leak tests a temp directory nobody else can reach
- Let a client keep a passage, not just a page
- Move the tag history strip above Jump to, and let it go full width
- Stop the play overlay hiding the cover it sits on
- Stop the podcast facade showing a player that cannot play
- Hold the row you are reading in the middle of the screen
- Stop the settings screen asserting a publication that is not happening
- Lift the discovery row by its cells: Safari will not lift a row
- Fix the review's four findings: dead http:// play buttons, a button that hides on failed playback, an implicit-nullable deprecation, and og:site_name missing the name= fallback its siblings gained.
- Make the reading scrim actually dim, and say so on Discover
- Rewrite the stale-uniqueness needle in PrivacyPolicyTest for podcasts
- Stop promising a summary is never shown publicly
- Correct three things the final review found
- Put the summary consent on the screen that decides publishing
- Say on both policies that playing an episode contacts the show's host
- Pin the six withheld surfaces with the flag switched ON
- Dim the page around the item j and k are on
- Show an opted-in account's summary on its own blog
- Read the shows_summaries default through the database, not a model default
- Cover the preview suppression on the three views that do it
- Deepen tag strip band backdrop from accent-100 to accent-300
- Show the podcast facade on the four surfaces the video facade uses
- Paint the tagstrip band as a pale backdrop, not a solid bar
- Emit no tag-strip bar for a month with zero uses
- Thin a long tag axis by month count, not viewport width
- Play a saved podcast episode behind a facade
- Give /login and /forgot-password the meta a public page needs
- Give an account a switch for publishing its own summaries
- Rephrase tagstrip comment to avoid the section-header marker
- Store the podcast episode a saved link describes
- Name the column this line actually renders
- Draw the tag strip on the timeline's own grid
- Recognise a podcast episode from a saved Overcast or Apple link
- Put a tag's own history above its board
- Unescape Apple's captured stream url as JSON, not with stripslashes
- Write down a page that carried no article, and keep it out of the failures
- Re-indent the axis and band-caption partials naturally
- Read a page's episode audio url
- Give the axis and the band caption one home each
- Read name= Open Graph tags for title and description
- Pin the two screens to the same numbers on different axes
- Measure one tag's history over its own months
- Say where the timeline's other whereNull guard lives
- Give the timeline denominator one home instead of two
- Correct the features page, and stop the parity guard passing on nothing
- Amend the privacy policy for the weekly newsletter's click counting
wyome.com
- Point at the standards Athenana speaks, rather than restating them
- Say that a saved song or podcast episode plays in place
- Say that playing a saved podcast episode contacts the show's host
- Refresh the banked archive from the server
- Say that Athenana counts front-page link follows, and offers an email
Hosting monitor
- Say at the top of the panel how old it is
Sunday 6 September 2026
Athenana
- Guard the half of the axis fix that had no test
- Thin the timeline axis to quarters instead of hiding it
- Drop class="input" from the front-page newsletter field
- Give the newsletter-state lookup a total order
- Guard the newsletter settings panel on having an email address at all
- Make the unsubscribe idempotency test actually test idempotency
- Make the clicked-branch tiebreak test actually reach the clicked branch
- Bind newsletter user_id only when the address is the account's own
- Widen PruneClicks guard test to match PruneTrash's input matrix
- Add athenana:prune-clicks and schedule it (Task 7: click retention)
- Send the weekly newsletter: five links, dedup by week, no tracking
- Make the newsletter reachable: a front-page form, a settings toggle
- Let a reader leave, in a way a prefetch cannot do for them
- Open the newsletter list, with one confirm flow for both audiences
- Send the front page's outbound links through the counter
- Count a follow of a front-page link, and refuse what is not one
- Unify this hook with the copy in the other six repos
- Refuse a push that restores an older version of a file
- Give a two-band caption its separators back instead of five undifferentiated tokens
- Restore the charset fix, and stop the MyMind client calling itself self-hosted
- Mark year boundaries on the timeline axis before the live library crosses one
- Fix three timeline tests that named a guard they never exercised, and add a fourth that had none
- Record that the worktree isolation stops at sys_get_temp_dir()
- Honour a charset declared after the first non-ascii byte
- Declare a default for the month count the markup supplies
- Fix two numbers review caught: a stale test count and a stale fixture percentage
- Close two holes in the colour guard
- Draw the strip as a grid, in tokens, with no colour literal
- Put the timeline behind /tags, and say what it left out
- Read the series with two joins that never touch a global scope
- Separate a project from a tag that had one busy month
- Make the peak-not-mean test able to fail
- Measure a tag's month against its own share, not against a count
- Give the timeline a dense month axis and its eight thresholds
- Drop every imagedestroy(): a no-op since 8.0, deprecated in 8.5
- Add the mail capture to the policy parity guard
- Guard the two privacy policies against the code, not against each other
WyHealth
- Ask deployed-ref about web/, not about HEAD
- Bless the header set the server-rendered theme produces
- Take an atomic lock before writing the audit's state
- Refuse a push that restores an older version of a file
Wy5
- Add nosniff to the API docroot
- Refuse a push that restores an older version of a file
WyAuto
- Refuse a push that restores an older version of a file
WyHome
- Refuse a push that restores an older version of a file
wyome.com
- Track this account's other .htaccess files, and stop rsync publishing them
- Refresh the banked archive from the server
- Stop saying a saved email's pictures are shown when you read it
Hosting monitor
- Bless nosniff on six sites and the two .htaccess files that carry it
- Probe the three .app domains, which serve the placeholder too
- Correct today's done entry: the placeholder is not gone from the box
- Bless the two keys e06f9cb predicted, and nothing else
- Give --only its own scratch paths, and refuse --baseline --only
- Assert that /data/ still refuses anonymous requests
- Both placeholder roots now serve the account; record what the probe cost
- Read the shortest certificate from the run, not from memory
Saturday 5 September 2026
Athenana
- Stop the privacy page promising pictures the reader no longer shows
- Fix the two review-blocking gaps left by dropping <img> from the reader
- Make the reader text-only: drop every image, for every item
- Make the overlay actually scroll, and stop promising a button the markup cannot deliver
- Add a scrollable overlay for the mail capture, keeping the JPEG link as fallback
- Make the reading link a button, matching the capture's own weight
- Escalate the mail capture probe once instead of raising the ceiling
- Decide the tag board and the search board on total(), and come back to the first page of the same list
- Skip Readability entirely for saved mail, not just when it throws
- Point FetchMail at logContext(), so the log shape has one copy
- Keep the proposed-tag tail on a page past its end, and clamp against the tail the screen renders
- Decide the tag manager on total(), and come back to a page that exists
- Give logContext() FetchMail::describe()'s exact shape, trace included
- Log a rejected row's shape, never the row, from every catch-all that logs
- Scrub the refused mail insert through DatabaseErrorReport
- Teach the second timeout derivation about the capture stage too
- Fix mail-capture safety gaps: scoped photography, timeouts, temp-file leaks
wyome.com
- Refresh the banked archive from the server
Hosting monitor
- Rebuild the panel on a schedule, and record two fixes in it
- Pin the remote locale, after a Terminal run cried backdoor
- Retire the partner watch, and say what retiring one costs
- Bless four changes: two header sets, dropped favicons, vanished partner
Friday 4 September 2026
Athenana
- Decide the blog and the webmention list on total(), and come back to a page that exists
- Log a refused mail insert without the mail it refused
- Stop paying for followers on servers that stopped answering
- Configure the mail capture, and write down what the probes taught
- Report a database failure without the row it failed on
- Give a test file its opening tag, so PHP runs it instead of printing it
- Decide who is allowed to speak X-Forwarded-*, and trust nobody by default
- Decide what is public on total(), and come back to a page that exists
- Make installing the Safari app something you ask for, and prove the release script
- Stop an older save's timer from wiping a newer save's failure badge
- Stop the fade erasing a short capture, and stop calling a truncated one whole
- Clean up only the temp files a test made, not a shared glob
- Show the capture in the item body, capped, with a way into the whole thing
- Put the mail capture in the enrichment chain, behind the screenshot
- Fix Task 5 review findings: doc the capture env keys, and match their names to the plan
- Write down what eighteen branches learned, and fix what went stale
- Photograph a forwarded message, and refuse to store a photograph of an error page
- Stop a private message being written where the whole box can read it
- Cut an over-long capture to fit its column instead of losing the save
- Accept a capitalised h-entry instead of refusing it as an unknown type
- Prepare a stored message for a renderer reading from disk
- Test the three parsers nothing named, and fix four holes they found
- Catch the throw a hung convert produces in CaptureImage
- Test the extension's one security decision, and the four scripts that had none
- Read the two API clients' JSON against the ceiling instead of buffering it
- Stop the inbox fetching a stranger's URLs on a stranger's connection
- Encode the capture at the size it is worth storing
- Name what a received mention points at, instead of calling it the front page
- Fix CaptureImage::trimBottom() — ImageMagick reads trim colour from the corner pixel, not -background
- Cover athenana:normalise-tag-case, the last untested command
- Measure a capture with ImageMagick, since GD cannot hold the probe
- Fix two review findings on the HeadlessChrome extraction
- Pin the clock in the three test files that straddle midnight
- Record a stage's error on a model the failed save has not dirtied
- Stop composer setup migrating whatever database .env happens to name
- Give the browser one driver, since there are about to be two callers
- Ask for a credential before wiping the library, not just the word "delete"
- Stop the digest sorting a whole day's entries
- Make the Micropub read half token-only too, not just decoratively scoped
- Page the dismissal register, and ask for a day sargably
- Make the MyMind dry run classify, and fail a run that imported nothing
- Ask one method for kept passages, so both screens carry both guards
- Ship only the manifest's closure, and check the keys the manifest uses
- Stop federating as an account that has been removed
- Say on the privacy page that the server now fetches a message's pictures
- Give each token minter its own flash key, and put the API panel back
- Store the capture as JPEG, width-capped
- Carry the new remote floor through ForwardedMailTest's limits helper
- Put the two text-extraction docblocks back on the methods they describe
- Stop calling every remote-image failure an SSRF refusal
- Bound one message's picture pass by a wall clock, not only by a count
- Count outbound requests against the remote-image ceiling, not stored pictures
- Refuse a DOCTYPE by where the grammar allows one, not by the first 2 KB
- Refuse only a declared 1x1 or 0x0 remote picture, not everything under 8x8
- Fold the approval path's reindex into the one helper the other tag paths use
- Document the two remote-image env keys the merge brought a test for
- Give the capture path its three units: localise, measure, close the holes
- Bank the remote-image work, which the owner has since declined
wyome.com
- Refresh the banked archive from the server
- Say that forwarding an email in fetches its pictures
Thursday 3 September 2026
Athenana
- Document the two follower ceilings in .env.example
- Pin every outbound credential in phpunit.xml with force="true"
- Fail a test that makes an unfaked HTTP request, instead of letting it out
- Make a disk that refuses a file say so, instead of returning false
- Send a Content-Security-Policy, so the reader has a second layer
- Cap how many followers a blog, and one host, can put on it
- Say what went wrong on a passkey instead of swallowing every failure
- Read xcodebuild's own exit status, so a failed build cannot install a stale app
- Document every env key config/ reads, and default the session cookie to Secure
- Ask the database for the entries the digest keeps, not for the whole day
- Expire a cache-first entry when the server said it would, not when the LRU does
- Claim the arrow keys only where there is a list to walk
- Approve a proposed tag as one transaction instead of a loop per item
- Rebuild search blobs a page at a time instead of hydrating every item
- Make one idempotency key one item, in the database rather than in a SELECT
- Bind a verified signature to this host, this actor and one use
- Fit page metadata and tag names to their columns before the save
- Make recrop-captures' dry run answer the question the real run acts on
- Bind the Apple state token to the browser that started the flow
- Give prune-trash a dry run, and stop an empty env from meaning "purge everything"
- Stop an unreachable host wiping the verdict a reachable one gave
- Refuse a tag rename onto a slug another tag holds, instead of 500ing
- Stop a second spelling of one address buying a fresh limiter bucket
- Compare prose, not padding, when deciding the note can go
- Drop the flattened note when the page really carries the words
- Correct the fourth cache comment, and name the marker test for what it proves
- Say what the tagging cache really does, and log the number that proves it
- Give the model calls a real ceiling instead of a share of the job's
- Stop the reader printing a blank line where a picture was not asked for
- Save a forwarded email the way a saved page is saved
- Stop the sidebar overwriting the layout it was meant to return you to
- Let an account read its forwarding address back instead of once
- Give the enrichment job a budget it can actually finish inside
- Stop a lock that lives exactly as long as the gap to the next run
- Give an untitled link a readable headline, and stop tags sizing the text column
- Take the feed images too, which are not under items/
- Stop Apple sign-in putting the relay address back over a chosen one
- Make the list's tag column stand still, which was the eyesore rather than the chips
- Save a forwarded email as a note, from a secret address per account
- Give the list row the board's picture and the API's description
- Widen the discovery thumbnail to the shape publishers actually send
- Bound every guarded fetch while it reads, not after it has allocated
- Say what the logo fetch actually does on failure, which is not retry
- Say once what a URL this app stores as a link, and ask it at every door
- Stop a reset link registering a passkey, because the row never said what it was for
- Give five paged queries the id tiebreak, and a guard so the sixth cannot skip it
- Walk the library instead of hydrating it, and pin the loop that does the work
- Scope --restore to one account, which it never was
- Take the files with the account, on the path where the promise is legal
- Guard the discovery table's shape, which no content assertion can see
- Show the picture a feed already published, on Discover and on the feeds page
- Raise the briefing cap to 1,000, because 500 was about to become a working limit
- Import the two classes browsingDraw() named in full
- Show the description a source already supplied on Discover
- Pin the revisit browsing clock, which failed for five minutes a day
- Put a daily briefing on what the feeds carried at the top of Discover
WyHealth
- Replace a decorative test of my own with one that bites
- Show wakes since the last completed sync, not just each count alone
- Ask iOS for time to finish a drain, and end the assertion properly
- Bound each step of a drain, so a stalled call cannot outlive it
- Complete the background refresh task instead of going silent on expiry
- Make the sync gate a lease, so a suspended drain cannot stall forever
- Make the choice reach every route, not just the three with tokens.css
- Move the cookie write out of the component, and test its format
- Charts read the palette instead of one theme's literals
- Make the active pill readable in dark mode
- Add the control, and keep it out of the URL
- Count media rules, not mentions of the phrase
- Swap the lockup in one place instead of two that can disagree
Wednesday 2 September 2026
Athenana
- Pin the feed rotation to its seed, verified by removing it
- Draw the discovery list from the reader's own feeds first
- Rewrite the two reset tests the email-address index made unstorable
- Render the message in a test, because Mail::fake() never does
- Record the second retirement, and what the DNS actually says
- Let an account set its own email address, so the password is usable
- Fix the guard tripping bin/worktree, and a false claim in its own message
- Stop the redemption screen telling a returning account it is being created
- Throttle the reset request to protect an inbox, not an account
- Stop a worktree reaching the database that belongs to another checkout
- Let somebody ask for a reset link, and say plainly it may not arrive
- Give one link mechanism two lifetimes, and make spending one spend the rest
- Retire the ban on a reset route, and keep the ban on a second token table
- Draw the two destinations as buttons, and lead with the wait
- Beat the .authcard form margin reset, which was eating the gap
- Put the wordmark and the wait caveat where the signing in happens
- Stop the sign-in caveat collapsing under a shorter button
- Point the front page at the login screen, not at one provider
- Record in CLAUDE.md that the no-password property was retired, and by whom
- Let an admin hand out a one-time link for somebody who is locked out
- Set, change and clear a password from its own settings screen
- Sign in with an email address and a password, without needing any script
- Give an account somewhere to keep a password, and retire the test that said it never would
- Assert structurally that there is still no password, and no key in an export
- Let an admin invite somebody who has no Apple ID, without sending anything
- Sign in with a passkey, and apply UserStatus before any session exists
- Let an account keep several passkeys, and refuse to remove its only way in
- Verify a passkey for real, and prove the verification can fail
- Give a passkey somewhere to live, and let an account exist without Apple
- Resolve DNS through the container, so a test can answer the lookup
WyHealth
- One colour table that a choice can override, not two that drift
- Let the server render the theme it is going to serve
- Give charts a two-column palette, held to a measured floor
- Parse the theme choice, and say what the server writes for it
Tuesday 1 September 2026
Athenana
- Stop double-escaping the meta tags, and measure the title not its markup
- Precache the fonts at the URL the browser actually asks for
- Write down the effort coupling and the resolution table
- Register the worker, tell it whose caches these are, and say when offline
- Add the screen for setting, replacing and removing an AI key
- Assemble and serve the service worker
- Resolve the key, model and effort for the acting account
- Put the worker's decisions in a plain module, and test them
- Serve a manifest, and a page for when the network is gone
- Render the manifest icon set from the mark already in the repo
- Build the SDK client through a factory, so a test can see which key was sent
- Store a per-account AI key, and make the model carry its own effort
- Grey a dismissed discovery row, at the dimmest value that stays readable
- Address branch review: restore proof, mass-assignment fix, backlog note
- Show what has been dismissed, and let it back
- Fix round 1: replace the URL-only dismissal-view test
- Dismiss a discovery link, and take it back
- Fix round 1: pin discovery ranking mode in the slot-back test
- Stop offering a link that was dismissed
- Record a dismissed discovery link
- Record that production runs Haiku again, and why
- Make the 65-character title bound true, not usually true
- Close the Micropub create path to a browser session
WyHealth
- Stop the chart inventing months nobody measured
- Say the pair even when nothing collapsed, and draw the window rather than the rows
- Narrate the series the charts actually draw, and stop overclaiming the thresholds
- Delete topActivities, and make the fake client see the RPC it is asked for
- Task 7 fix round 1: honest empty state, drop the in-progress trailing bucket
- Close two saveProfile coverage gaps from review
- Panel 10: insights, bounded to computed single-metric findings
- Cover saveProfile, the one web write path with no test
- Fix round 1 on the insights computation
- Task 5 fix round 2: complete log truncation, mirror the sessions-missing case
- Stop the session log overflowing, and say when totals are missing
- Add the insights computation, with no I/O and a banned-word test
- Actually pin the dev server to Node 22
- Panel 4: activity and training over collapsed sessions
- Fix double-counted Other and unstable colours in StackedActivity
- Add the stacked activity chart
- Fix nullable test in fetchActivityBuckets to catch copy-paste regression
- Add fetch function tests to workouts test suite
- Add the workouts wrapper over the two session RPCs
- Count the sessions each total was actually summed over
- Aggregate collapsed sessions in Postgres, per bucket per activity
- Bound the session window at both edges, not just p_from
Monday 31 August 2026
Athenana
- Micropub action=delete and action=undelete, through the trash
- Add public/js test harness and fix scroll.js announcement + count
- Micropub action=update, with the refusals written down
- Micropub q=source: read one post back as the mf2 that made it
- Refuse an address the page declares about a different document
- Four from the backlog: archive 404/410, a disclosed restore, a trashed blob, a dialog colour
- Stop an over-long image_url costing the whole save
- Say in the privacy policy that a saved video is not only a song
- Pin FetchVideoThumbnail into the enrichment chain
- Admit a video's own cover to the front page, and pin youtu.be into the fetch guard
- Keep SoftDeletingScope in the backfill's item query
- Backfill covers for videos saved before they could play
- Narrow the permalink/front-page fix to url-sourced video only
- Unify the video/preview suppression rule across all three public surfaces
- Fall back to the item's own picture when a video has no cover
- Fix Task 3 Round 1: Replace inert tests with ones that verify the YouTube guard
- Stop photographing a video we already have the cover of
- Fetch the cover for a video the url names
- Fix Task 1 Round 2: restrict href to anchors in expectNothingFetchedFromGoogle(), add helper test
- Fix Task 1: revert view changes, add expectNothingFetchedFromGoogle() helper, update test
- Correct the plan: a page about youtube may name youtube
- Play a video the url itself names
WyHealth
- Collapse double-recorded workouts into sessions
- Record who wrote a row, by a name a rename cannot move
- Correct the sleep docstring: 72 minutes, not 24
- Average sleep over the nights that measured it
- Pin the dev server to Node 22, and rule on the in-bed months
- Retire the pre-RPC aggregation path and record what Phase 3b proved
- A profile form, because two panels and all of Phase 4 need one
- Panel 6: what the watch recorded, and nothing about what it means
- Panel 5: sleep, in stages, and the ten years it was dropping
- Panel 2: cardio fitness, and what it cannot say yet
wyome.com
- Say that a saved YouTube link gets a play button too
- Refresh the banked archive from the server
Hosting monitor
- Panel: record the SSH kex finding, correct a stale cert note
- Lock the state directory, after a collision nearly ate the baseline
- Bless four intended additions to wyome.com
Sunday 30 August 2026
Athenana
- Stop a video saving as "YouTube", and read a page in its own encoding
- Make a fileless image save say what it DID receive
- Count each account's items on the admin screen, without the scope lying
- Stop a three-letter search matching everything, and let short items rank
- Add a second search mode, for describing a thing rather than naming it
- Make the discovery lede follow the ranking setting instead of asserting one
- Shrink an image before sending it, so a phone photo can be described at all
- Stop telling an account with nothing waiting that it is at the limit
- Give the image backfill a --force, for a backlog somebody has decided to pay for
- Describe saved images, so they can be found by describing them
- Read author and publication date from a page's structured data
- Correct the dates the session's midnight crossing put wrong
- Add IndieAuth, so a Micropub client can actually get a token
- Count what the conditional requests actually save
- Show the topics the day was built from, not a fresh guess at them
- Ask feeds whether anything changed before downloading them again
WyHealth
- Panel 3: fifteen years of body composition
- Round the value axis, which was printing raw floats
- Panel 1: today, against its own thirty days
- Give the dashboard a shell, a window, and an honest freshness panel
- One panel shell and one chart vocabulary, in the brand's colours
- Move the colour table out of the landing page
- One window for every panel, snapped to local midnight
- Let one instrument describe a night, and union what it wrote
- Return a night whole when the window starts mid-sleep
- Sessionise sleep where the rows are, and say why that moved
- Say why a panel is empty when the RPC fails
- Bucket in Postgres, because 25,511 rows do not fit through PostgREST
wyome.com
- Refresh the banked archive from the server
Saturday 29 August 2026
Athenana
- Correct the date I got wrong in ten places, and record the audit on the board
- Put the last month's features on the features page
- Name the three sources the morning reading list searches
- Move the ignored feeds onto their own page
- Make j and k walk the draw, and stop the draw moving while you read it
- Revisit: the things you saved and stopped opening
- Offer the reader when the page is archived, not when body_text happens to exist
- Say on the board that the keyboard shortcuts exist
- Put a kept passage's own visibility switch beside it
- Show a kept passage where you go looking for it, and say it is private
- Keep a passage from the reader
- List every blog tag page in the sitemap
- Fold ?page=1 into the bare blog URL instead of self-canonicalising
- Give every public page a real og:image instead of none
- Stop the search box swallowing every keyboard shortcut, and make j work on an item
- Read the article, rebuilt from the page already on disk
- Give each tagged blog page its own title and description
- Say 'not yet' rather than printing an infinity sign
- Stop the summary estimate promising a date it cannot reach
- Give the keyboard a way through the board, and say what the keys are
- Keep yesterday's discovery list, and let the page look back
- List /features, /blog and every blogger's page in the sitemap
- Give public pages real titles, descriptions and Open Graph tags
- Self-canonicalise a paginated blog instead of collapsing it into page one
- Play a YouTube Music share without searching for it
- Draw the day's discovery list at random, a few links per topic
- Mark a failure that is an answer, so it is not re-billed nightly
- Take summary and summary_hash back out of the fillable allowlist
- Carry the summary into the CSV export
- Record the attempt when the model has nothing to say
- Stop the summary backfill spending an enrichment retry it never makes
wyome.com
- Name Athenana's three reading-list sources here too
- Refresh the banked archive from the server
- Redirect http to https, which nothing was doing
- Regenerate sitemap.xml with today's lastmod dates
- Fix SEO monitor findings: titles, descriptions, canonicals, og tags, sitemap
Thursday 27 August 2026
Athenana
- Retire the publiccard vocabulary, and restore the rule it took with it
- Backfill summaries for text already stored
- Show the summary on the item page, labelled and private
- Cover the summarise()-returns-null skip branch in SummariseItem
- Summarise the whole article, once per version of its text
- Delete the dead public/_card.blade.php, and stop citing it as a surface
- Ask Claude what a whole article argues
- Let each Claude call site carry its own effort setting
- Fence the summary out of every public surface before it exists
- Move Feature/Summaries to correct alphabetical position in DB_TRANSACTIONAL_DIRS
- Give an item its own summary, apart from the publisher's blurb
- Tag with Sonnet 5, and correct a config note that described fiction
- Name the import formats where the link to them is
- Say 1 tag, and let bold mean the service you came from
- Say which reader an import came from, and stop promising Pocket HTML
- Open a zipped export and take the CSV out of it
- Refuse an unreadable import instead of reporting zero rows
- Read Instapaper and Pocket exports, and detect which is which
- Record that an unknown import format silently imports nothing
- Exempt the API by the request line, not the rewritten URI
- Stop the canonical-host redirect breaking every iOS Shortcut
WyHealth
- Backfill percentages that were stored as fractions
- Backfill percentages that were stored as fractions
- Show imperial without storing it
- Send a percentage as a percentage
- Make a stored percentage mean percent, at the one place all data passes
Wednesday 26 August 2026
Athenana
- Put Sign out against the sentence it acts on
- Name the JSON feed too, and drop the two settings pills
- Give the account screen the blog's feed address, and four boxes instead of two
- Recover production's HSTS block into git before a deploy eats it
- Every public page declares its own canonical address
- A video on a blog post hides the page preview, not every picture
- Bring the feeds up to parity with the blog page
- Send www to the apex, and plan the canonical tag that backs it up
- Import an OPML to drop feeds you already read elsewhere
- Stop re-enrichment renewing an approval's thirty days
- Say what the ceiling spared, not only what stopped it
- Let a hand edit still promote an approved tag to manual
- Stop re-enrichment deleting a tag the person typed
- Name the signature headers that arrived when it cannot read them
- Make the detach that this branch depends on actually run
- Say what the inbox drops, and why it refuses
- Stop the nightly enrichment vouching for its own tags
- Stop overstating the no-downgrade guarantee, and pin two untested lines
- Stop a machine sync from taking back a hand-typed tag
- Show the tags on a blog permalink, for every type
Wy5
- Apply migrations from deploy.sh, and never ship code while reading
- Run the whole group suite against the harness, not production
- Strip the space with AUTO_INCREMENT when normalising
- Make topics from inside a group, not from Browse
- Select the group board from topics, not from ballots
- Seed 116 curated topics, with a guard that proves they do not collide
- Show a group its own topics at one vote
- Make a topic from inside a group, and never demote on merge
- List topics by listing state, not by ballot count
- Give a topic an explicit listing state
- Add a disposable local API harness for integration tests
- Raise the seed set to ~118 and get density from featuring instead
- Singularize the -es plurals that were splitting topics from themselves
WyHome
- Use American spelling throughout
wyome.com
- Take the video's own cover, not the feed's poster
- Close the last closeable gaps against Athenana, and classify the rest
- Match Athenana's blog entry, and build the video facade ahead of the feed
- Host permalinks on wyome, add a tag cloud, and spec the video gap
- Track .htaccess, close the directory listings, serve a real 404
- State the permalink on blog entries, and plan it for RSS
- Render the feed's image on the blog, and correct two false comments
- Add the Search Console verification token for wyome.com
- Stamp the stylesheet at 6959b781
- Take the wordmark to 48px
- Scale the wordmark up to the site's header size
- Redraw the logo o as one ring instead of three
Tuesday 25 August 2026
Athenana
- Make the tag-provenance backfill atomic and self-preserving
- Sweep every public route for queries that match nothing signed out
- Re-source the tags MyMind gave us
- Render the blog's tag navigation for the people who read it
- Judge a tag's recency by the item's save date
- Stop calling every imported tag manual
- Alternate the bands down a person's blog, one to a post
- Drop the stand-in picture, and band the blog pages
- Band the front page too, and sweep the vocabulary it replaced
- Build the features page as bands
- Record the owner's ruling: a trashed item does not count as a use
- Put the sign-in caveat under the button, at the button's width
- Spell the features page out, all of it
- Write down why the protections see trashed items and the counting does not
- Give the vocabulary test tags the window can actually see
- Make the counting-trap test actually read a use count
- A top bar on the marketing pages, and the blogs line after the stream
- Refuse to delete a hand-applied tag in the applier, not just the decider
- Reject a --target that is not a positive integer
- Move budgetTag to tests/Pest.php so the ceiling command's tests run alone
- Run the tag ceiling nightly at 03:25
- Require --user with --target so an override always names its account
- Add athenana:enforce-tag-ceiling
- A video on any item, and the song's video under its cover
- Decide which tags must go for an account to fit under its ceiling
- Give each account a configurable tag ceiling, defaulting to 900
- Extract tag removal so prune and ceiling suppress identically
- Show the model the most-used tags rather than the first 200 alphabetically
- Stop collecting feeds that could never carry anything
- Put the edit box with the tags, not at the foot of the page
- Draw the tags once, and say which public page it means
- Move InstallPageTest off the real Safari build
- Assert the stamp against the instance that produced it
- Truncate on the way out, not just on the way in
WyHealth
- Count background wakes, because a timestamp could not survive being read
- Show the lockup at its documented minimum size
- Make background delivery say whether it is actually on
- Rewind the sync cursor when a drain finishes
- Keep a baseline run from overwriting the last real report
- Audit the deployment daily, and keep the baseline free of secrets
- Start HealthKit where a session is guaranteed, not at launch
- Stop advertising Next.js in the x-powered-by header
- Send signed-in users to wyhealth.app, not to the socket we bound
- Refuse to deploy uncommitted web/ code, and stamp what shipped
- Send HSTS, so the first request is never the insecure one
- Build the landing page out of the mark's own geometry
Wy5
- Show people their own topics in Browse, whatever the list count
- Open a newly created topic, which the browse threshold had made unreachable
WyAuto
- Fetch the EPA figures, and record that they were fetched
- Offer camera readings on the entry screen, confirmed before they land
WyHome
- Make a second property reachable, and pick the state from a list
wyome.com
- Say why the NUL-wrapped record separator is actually safe
- Refuse to publish an empty changelog over a good one
- Close three ways a suppressed name still reached the public page
- Rebuild the changelog every morning, from a laptop that may be asleep
- Put Blog in the footers too, where it never was
- Put Changelog in the nav, and the Blog link the statistics page never got
- Write the page, stamp the stylesheet, and put it on the server
- Make the two suppression lists disagree loudly instead of quietly
- Keep a client's name off a public page, whole commit at a time
- Group the commits by day and render them, escaping every subject
- Make a record boundary something a commit message cannot forge
- Read paths as git stores them, and fail loudly on a record we cannot parse
- Read each repo's log, with merges judged by parent and not by word
- Judge a commit by the files it touched, not by how it was worded
- Drop tooling and process commits, and keep the ones about merging
- Name the seven projects, and refuse two worktrees of one repo
- Ignore the SDD scratch, and put referrers/ back in the nav task
- Add wyhealth.app to the statistics, and stop counting HTTPS twice
- Ignore the panel's meta.json, but keep the panel
Hosting monitor
- HSTS on all four .com hosts, and a correction
- Send HSTS where the vhost will take it
- Record what got fixed, and question how
- Render the checks and what to do about them
- Look at directory modes, not just file modes
- Watch the whole box, not just the one site on it
Monday 24 August 2026
Athenana
- Drop the stand-in tile above a video, and stop cropping the frame
- Size the song facade on every surface, not only the blog
- Show a matched song's video on the front page and the permalink
- Recognise the non-breaking space Apple actually sends
- Let a song entry sit with the posts around it
- Write down the key the song-video feature needs
- Keep the play marker legible over a dark video still
- Collapse the third copy of the image-storing body
- Stop recording an undecodable answer as "there is no video"
- Make the facade's privacy assertion able to fail
- Correct what the privacy policy says about Apple and about Google
- Name Google in the privacy policy, and say what pressing play discloses
- Keep a song's own metadata when re-reading it
- Move the song-video play handler out of an inline onclick
- Move feedFor() into Pest.php so a parallel run can see it
- Show a matched video behind a facade that calls nobody
- Move song test helpers into tests/Pest.php so a single file runs alone
- Publish the preview image in RSS, not only in JSON Feed
- Copy the video cover locally so the facade never calls Google
- Narrow the FindSongVideo test catch-all and forbid stray requests
- Find a song's video during enrichment, and only ever ask once
- Match a song to a video by duration, or not at all
- Security: redact API key from exception messages
- test isTopicChannel() directly instead of through HTTP searches
- Add a YouTube client that is inert without a key
- Add the song_video column, which must record a miss as well as a hit
- Recognise a shared Apple Music track without asking a model
- Make the feeds screen usable at three hundred subscriptions
- Let another site read a blog's feeds
- Send the picture with a photo
- Deliver only what a receiver can show, and keep the outbox correct
- Federate the blogs: Activity Streams and ActivityPub
- Match public image geometry to the private area
- Stop one numerically-named tag from breaking the whole bulk reject
- Show received mentions on a post, once the owner says so
- Fix entry image sizing, blog back-links and tag navigation
- Correct the privacy policy for what shipped today
- Let each page advertise its own feeds, and only its own
- Give each person their own blog
- Give the writing somewhere to live
- Give images and highlights a page, and raise their cap to blog
- Do not finish with an item on a question that was never put
- Replace the private/public boolean with a three-rung ladder
- Carry links only in the anonymous stream
- Stop publishing a link's private annotation in the feeds
- Fall back to the archive when a site refuses to talk to a server
- Say how much "Reject everything below" would actually reject
- Gate the front page on consent rather than on role
- Accept posts over Micropub
- Publish the front page as RSS and JSON Feed
- Emit the XML declaration from PHP, because this server runs short tags
- Publish a sitemap, generated from what is actually public
WyHealth
- Close the plaintext port, and move the deploy off Passenger
- aggregate respiratory_rate and oxygen_saturation daily
- let a user update their own sample rows
- never 500 because one page repeated a bucket key
- Build the dashboard here, ship the result to DreamHost
- aggregate high-volume metrics into daily buckets on the device
- add a reversible ingest circuit breaker
- carry the device timezone on the profile instead of hardcoding Denver
- show per-page sync progress and the brand lockup on the home screen
- background delivery and BGAppRefreshTask drive sync without the app open
- sync sleep stages and heart events
- sync workouts through the same paged anchored path
- paged resumable sync — backfill and delta share one anchored path
- iOS payload types and the full 23-type quantity mapping table
- ingest workouts, sleep stages and heart events alongside samples
- full v1 metric type list with per-type canonical unit enforcement
- schema for workouts, sleep stages, heart events and profile timezone
- the dashboard logo lost half its wordmark in dark mode
- adopt the WyHealth logo system — app icon, favicons, dashboard lockup
- a single reading rendered as an invisible dot, reading as missing data
- iOS app — native Sign in with Apple, HealthKit sync engine, XcodeGen project
- dashboard steel thread — steps and weight charts, last-synced indicator
- ingest edge function — validated, idempotent, RLS-bound
- Next.js app with Apple sign-in, protected dashboard, RLS isolation tests
- core schema — profiles, samples, sync_state with RLS
wyome.com
- Show a rolling month, and stop hiding data behind a gap
- Privacy: the song play button, and the third party it reaches
- The statistics page said one site was a real network hop; none is
- Name WyHealth's domain, without linking to it
- Add tools/crop.py, because sips crops from the centre
- Scroll the page before capturing, or lazy images never load
- Add tools/shoot.py — screenshot a page in both colour schemes
- Publish the Athenana blog on wyome.com
- Give WyHealth its real icon
- Rewrite Athenana's description for the consent model and blogs
- Add WyHealth to the site, ahead of its launch
- Refresh the banked archive from the server
- Commit the sitrep panel; keep only its local server scratch ignored
- Move the referral totals down beside the list they total
- Move the referrer prose down to the referral list it describes
- Refresh the banked archive from the server
- Show whether the sites are actually up, on the statistics page
- Run the archive pull daily, so it does not depend on remembering
- Add --pull, so the banked archive cannot rot unnoticed
- Refresh the banked archive from the server, adding 24 August
- Ignore the local sitrep-panel scratch directory
Sunday 23 August 2026
Athenana
- Give the test suite enough memory to finish
- Show a note's own comment on its public page, and promote a photo to header
- Offer only your own photos to a gallery, never a link's preview
- Add a photo to a gallery from the web, or from your own library
- Give a post a photo gallery
- Show a note's own picture on its card
- Let a note be published, with its own entry style and a permalink
- Fetch photo thumbnails from MyMind, because GD will not make them
- Fetch MyMind photos from /objects/{id}/blob
- Read entityType when importing, so photos stop becoming empty notes
- Serve the item page's picture from the owner's own route
- Stamp asset URLs so a replaced picture is actually seen
- The photo Shortcut is installable by anyone now
- Keep the summary when the page cannot be read again
- Drop the "all N tags" link from the jump-to row
- Stop claiming a page is fine when the site would not answer
- Rewrite the summary when a link is read again
- Read a saved link again, and say so when the page has gone
- Move Discovery topics onto Discover and out of the menu
- Stop the suggestion count displacing the tag icon
- Offer a 90% threshold on the suggested-tag bulk reject
- Move Manage tags onto the Tags screen and out of the menu
- Actually send the page a suggested-tag decision was made on
- Give the board's search help a gap under it
- Ship the photo Shortcut that actually works, and drop the diagnostic
- TEMPORARY: log the shape of a refused image save
- Capture the working photo Shortcut
- An image action with no input produces nothing
- Offer all five Shortcuts on the Extensions page
- Capture the Song Shortcut — all five now exist
- Capture the Notes Shortcut, and check the input is actually read
- Check that a capture's body matches the type it declares
- Capture the News and Photo Shortcuts, and check every capture
- Drop the Suggested tags entry now that Tags carries the way in
- Make suggested tags reachable, and visible, on a phone
- Serve a Shortcut that saves links, and stop the capture clobbering
- Pin the payloads for five single-purpose shortcuts
- Treat a save that carries a link as a link, whatever came with it
- Space the wordmark's dot, and enlarge the mark in the icons
- Use the real wordmark on the public page, and let it follow the theme
- Give every checkout its own test database
- Owe a mention to every public link, as the owner decided
- Source Webmentions from the permalink, and stop withdrawing on rotation
- Note the pictures a page carried, so one can be chosen later
- Let a picture you set by hand win on the board too
- Set an item's picture from a URL as well as a file
- Let an item be given its own picture, or have it replaced
- Receive Webmentions: accept, verify, store, display nothing
- Give every public item a page, and read the front page as a blog
wyome.com
- Add wyome.com's sitemap, and move two credential backups out of a web root
- Add robots.txt to all five sites that were missing one
- Add broken links, missing files and most-requested pages
- Add the bot/human split and bandwidth to the statistics page
- Move the statistics page to /statistics/, leaving a redirect behind
- Add a country map to the referrers page, geolocated offline
- Publish the referrers page, add a traffic chart, and build it nightly
- Add an unlisted referrer page built from every site's access logs
Built 2026-09-21 06:00 from the last 30 days of git history. This page is rebuilt daily from a laptop, so if that date is old, the laptop was off — not the projects.