Skip to content

Releases: nurkamol/replay-swift

Replay 0.9.10

Choose a tag to compare

@github-actions github-actions released this 31 Jul 20:05

Install

Homebrew is the recommended way, and the only one with nothing to click
through — the tap clears the quarantine flag on install and on every upgrade:

brew trust --tap nurkamol/tap
brew install --cask nurkamol/tap/replay-app

brew trust is required by Homebrew 6 before it will load any third-party tap.
Skipping it looks like a broken tap rather than a consent prompt.

Also in the tap: nurkamol/tap/replay (the command-line reader) and
nurkamol/tap/replay-app-source, which compiles on your Mac and needs a full Xcode.

Or the zip

Replay-0.9.10.zip — unzip it and drag Replay.app to Applications.

macOS will refuse to open this one the first time. Replay has no Apple Developer
ID yet, so a downloaded copy shows "Apple could not verify Replay is free of
malware"
, offering only Move to Trash or Cancel. That message is about a
missing certificate, not about anything found in the app.

To open it:

  1. Try to open Replay once and dismiss the warning.
  2. Go to System Settings ▸ Privacy & Security, scroll down, click Open Anyway.
  3. Confirm. It opens normally from then on.

Or one line in a terminal, which does the same thing:

xattr -dr com.apple.quarantine /Applications/Replay.app

Right-click ▸ Open does not work. That bypass existed for years and Apple
removed it in macOS 15. It is still the advice in most projects' READMEs.

A signed, notarised disk image — no warnings on any route — appears here the day
there is a certificate. The workflow that builds it is already written.


Added

  • The scope controls read in your language. "Today / This Week / This Month" on Apps,
    "All time / Week / Month" on Search, "Last 7 Days / Last 30 Days" on the Timeline, every
    page subtitle beneath them, and the section headings on Apps. The enums that define them
    are compared against the reference by the parity suite, so they still return English and
    the translation happens at the view layer — the same seam SessionTitle.english uses.

  • The rituals on Story read in your language — the first of the runtime-assembled
    surfaces to be converted. "Morning tends to lead with" was built by pasting the day part
    onto an English tail, which is untranslatable into any language that orders it differently;
    it is one format string with a positional argument now, and the part goes through the same
    spelled-out switch the day's story uses.

  • Story, Chapters, Autobiography, the Museum and the Legacy view read in your language.
    NarrativeCopy holds the prose those five surfaces are made of, and every one of its
    sentences was already a key and already translated — the views just never looked any of
    them up. Text(NarrativeCopy.storyTitle) renders the English constant whatever language
    the app is in, so five surfaces stayed English while status reported Uzbek complete and
    the Uzbek sat in the catalogue unused. Thirty-three render sites, now looked up.

  • The command palette reads in your language. All of it: the commands, their
    descriptions, and the group headings above them. It was rendering Text(item.title) with
    no lookup at all, so ⌘K opened an English window in a translated app — and matching was
    against English too, which is why searching it in Uzbek found nothing.

  • Counted nouns follow the language's own plural rules. "3 sessions" was chosen by
    n == 1, which is English's rule rather than a rule. Russian has four forms — 21 takes
    the same one as 1, and 11 takes neither — and Arabic has six, so every counted noun in
    either would have been wrong however carefully the two English forms were translated. Each
    is now a .stringsdict entry with one form per category, resolved by Foundation against
    the locale's CLDR rules.

    • The categories come from ICU rather than a hand-kept table: translate.mjs asks
      Intl.PluralRules, the same CLDR data Foundation uses, so Arabic scaffolds 36 plural
      rows, Russian 24 and Japanese 6, and nothing here can fall behind a CLDR release.
    • English ships no .stringsdict — its rule is n == 1 — so the two-form path stays as
      the fallback for any noun a language has not finished, and the contract is untouched.

Fixed

  • Eleven counted nouns were built by appending "s" in code. count == 1 ? "session" : "sessions", spelled out across eight files, bypassing Loc.count entirely — so they were
    untranslated and carried the English-only rule this release exists to remove. All now go
    through Loc.count.

  • Five counted nouns were never in the catalogue at all. %@ day, %@ visit,
    %@ visits, %@ switch and %@ switches reach Loc through Loc.count rather than
    Loc.t, which the key scanner does not read — so they were missing from keys.txt, from
    every CSV, and rendered in English while status reported Uzbek at 100%. The count could
    not see them, which is the third distinct way scanning source for strings has been wrong
    here. Now collected, and asserted by a test.

  • status no longer calls the new plural rows orphaned. Twelve rows per language
    reported as dead would be an invitation to delete them — and a bulk orphan sweep has
    already destroyed correct translations in this project once.

Replay 0.9.9

Choose a tag to compare

@github-actions github-actions released this 31 Jul 12:02

Install

Homebrew is the recommended way, and the only one with nothing to click
through — the tap clears the quarantine flag on install and on every upgrade:

brew trust --tap nurkamol/tap
brew install --cask nurkamol/tap/replay-app

brew trust is required by Homebrew 6 before it will load any third-party tap.
Skipping it looks like a broken tap rather than a consent prompt.

Also in the tap: nurkamol/tap/replay (the command-line reader) and
nurkamol/tap/replay-app-source, which compiles on your Mac and needs a full Xcode.

Or the zip

Replay-0.9.9.zip — unzip it and drag Replay.app to Applications.

macOS will refuse to open this one the first time. Replay has no Apple Developer
ID yet, so a downloaded copy shows "Apple could not verify Replay is free of
malware"
, offering only Move to Trash or Cancel. That message is about a
missing certificate, not about anything found in the app.

To open it:

  1. Try to open Replay once and dismiss the warning.
  2. Go to System Settings ▸ Privacy & Security, scroll down, click Open Anyway.
  3. Confirm. It opens normally from then on.

Or one line in a terminal, which does the same thing:

xattr -dr com.apple.quarantine /Applications/Replay.app

Right-click ▸ Open does not work. That bypass existed for years and Apple
removed it in macOS 15. It is still the advice in most projects' READMEs.

A signed, notarised disk image — no warnings on any route — appears here the day
there is a certificate. The workflow that builds it is already written.


Fixed

  • brew upgrade no longer swaps the bundle out from under a running Replay. The cask had
    no uninstall quit:, so an upgrade replaced the application directory while the process was
    still in it — holding an open SQLite handle it writes to every few seconds. A launcher
    survives that; a recorder can lose the tail of the day, and the record is the product, with
    no cloud copy to restore from. Homebrew now quits Replay first and reopens it afterwards.

  • The in-app updater no longer overwrites a Homebrew-managed copy. It recognised a
    formula build by its /Cellar/ path, but a cask moves the app into /Applications,
    where it is indistinguishable from a copy dragged there by hand. So from the day 0.9.8
    shipped a cask, the updater would replace it — leaving brew describing a version it no
    longer had, and the next brew upgrade putting the older one back. It now checks
    Homebrew's own receipt under both prefixes.

Changed

  • Every build carries the same signature, so an update stops asking again. Replay was
    signed ad-hoc, and an ad-hoc signature has no identity: its designated requirement is the
    code hash, so it changed with every build. Homebrew 6 compares that requirement across an
    upgrade and carries a reader's Gatekeeper approval forward only when it matches — otherwise
    printing "the signer changed so macOS will prompt at next launch". That branch was taken
    every time, so each update meant another trip through System Settings, forever rather
    than once. A stable self-signed certificate fixes it, and fixes the same churn in the
    in-app updater. It is not a Developer ID and does not make the first launch any quieter.
    docs/SIGNING.md has the whole argument and the commands.

  • The tap clears the quarantine flag, and says so. Installing the cask no longer ends at
    a dialog offering only Move to Trash. What that trades is stated in the cask's caveats, in
    the README and on the site rather than buried: macOS did not check the app, Homebrew checked
    the download against the cask's SHA-256 instead, and that is a check published by the same
    project that publishes the app. The source build remains the route for anyone who would
    rather Apple's check applied.

  • brew trust --tap nurkamol/tap is now the first line of every install instruction.
    Homebrew 6 refuses to load a third-party tap until it is trusted, and the refusal reads like
    a broken tap rather than a consent prompt — which is what several confusing install errors
    turned out to be.

  • The cask's version and checksum are bumped by the release workflow, from the artefact it
    just built. They used to be copied between two repositories by hand, and 0.9.8 duly shipped
    for a while carrying 0.9.7's checksum against a 0.9.8 URL: every install failed, and failed
    in a way that reads as a corrupted download rather than a mistake in the tap.

Added

  • Memories reads in your language. The surface that exists to be read was the one still in
    English: its frame translated and every card inside it said "Your longest focus", "A late
    night"
    , "Where it began". All seven now follow the chosen language, along with the
    on-this-day names, the heatmap's months and weekday initials, and the dates throughout.

    • Word order moves with it. In Uzbek the application's name leads — "Firefox ilovasida 2h
      13m…"
      — which is what positional arguments are for and what glueing clauses together
      could never produce.
    • The English is untouched: it is the reference's own wording, and a facts payload on each
      moment is what lets the sentence be assembled a second time rather than replaced.
  • Update messages are translated too. The eleven sentences that appear when a check or an
    install fails were English in every language — which is the worst moment to meet a language
    you did not choose.

  • A day you reopen reads in your language. Six English strings were sitting on that
    surface — the time-of-day word in the story, the headline date, the toolbar subtitle, the
    reflection prompt, and both Export menus — and nobody had ever seen them, because the
    screenshot harness drove 21 surfaces and a past day was not one of them. It drives 25 now:
    a reopened day, the welcome screen's two pages, and What's New.

    • One of the six was self-inflicted, and is the useful half. The day parts are looked up
      through a variable, which the key scanner cannot see, so their translations looked
      orphaned and a tidy-up deleted them. docs/TRANSLATING.md now carries the rule: never
      sweep orphans in bulk.
  • Every SF Symbol is checked against the oldest macOS this app supports. A symbol newer
    than the deployment target draws nothing — no crash, no warning, a gap where an icon
    should be, on a Mac the developer does not own. tools/symbol-audit.mjs reads Apple's own
    availability manifest and fails the build instead. All 52 in use are safe on macOS 14; that
    was true by luck until now.

Replay 0.9.8

Choose a tag to compare

@github-actions github-actions released this 29 Jul 19:20

Download

Replay-0.9.8.zip — unzip it and drag Replay.app to Applications.

macOS will refuse to open it the first time. Here is why, and what to do.

Replay has no Apple Developer ID yet, so macOS marks it as unverified and shows
"Apple could not verify Replay is free of malware" — offering only Move to
Trash
or Cancel. That message is about a missing certificate, not about
anything found in the app
. Every unsigned app gets it.

To open it:

  1. Try to open Replay once and dismiss the warning.
  2. Go to System Settings ▸ Privacy & Security, scroll down, click Open Anyway.
  3. Confirm. It opens normally from then on.

Or one line in a terminal, which does the same thing:

xattr -dr com.apple.quarantine /Applications/Replay.app

Right-click ▸ Open does not work. That bypass existed for years and Apple
removed it in macOS 15. It is still the advice in most projects' READMEs.

Or skip all of that

Homebrew compiles it on your machine, so it is never downloaded, never quarantined,
and opens with no warning at all:

brew tap nurkamol/tap
brew install nurkamol/tap/replay-app    # the application
brew install nurkamol/tap/replay        # the command-line reader

A signed, notarised disk image — no warnings, nothing to click through — appears
here the day there is a certificate. The workflow that builds it is already written.


Added

  • Reinstall, in Settings ▸ About. Downloads the published build of the version you are
    already on and replaces this copy.

    • A version number stops being the whole truth when a release is rebuilt under it. Two
      builds then share a number, "check for updates" correctly answers "you are up to date", and
      a copy of the earlier one has no way forward. That happened to this very release, twice.
    • Also the repair for a bundle that is merely damaged — right version, wrong contents.
    • Never automatic and never suggested. The same download, checksum and swap as an ordinary
      update, with only the "is it newer?" test skipped. Your record is untouched.
    • It says so afterwards. A reinstall comes back at the version it left, so the note that
      normally follows an update — decided by comparing versions — had nothing to compare and
      said nothing at all. Pressing a button and getting silence is the one outcome that leaves
      you no better off, so the banner now reads Reinstalled Replay 0.9.8.
  • The permissions Replay does not use now have rows, in Settings ▸ Privacy and on the welcome
    screen.
    Each says where it stands and what it is for; Reset takes back anything granted
    earlier, which was previously impossible from inside the app.

    • Accessibility's button asks macOS to prompt, which puts Replay in the list — so all
      that is left is the switch. The two buttons here before opened System Settings and stopped,
      leaving somebody to press + and find Replay in /Applications by hand.
    • App Management has no button of that kind, because macOS offers none. No app can read
      or request it, so the row says so rather than showing a tick that would be a guess.
    • Every row offers a way in and, where there is something to undo, a way out — so a
      permission can be granted, taken back and granted again without leaving Settings. App
      Management always offers Reset, precisely because macOS will not say whether it is on:
      hiding the button until a permission "looks" granted would have made the one thing nobody
      can check also the one thing nobody could undo.
    • Nothing here is required and the rows never imply otherwise: no red, no warnings, no
      "action needed". Every row off is the state Replay is built for.
    • No password is ever asked for. macOS collects authorisation in its own window; an app that
      put up a password field to approve a toggle would be collecting a credential it has no
      business seeing, and it would not work anyway.
  • Replay runs on macOS 14 Sonoma. It required macOS 26 before, which is a version most
    Macs are not on. Exactly two calls in the interface needed something newer — the system's
    glass material (26) and a colour blend (15) — found by building the app against each floor
    in turn rather than by reading the code. Both are guarded, so three OS generations gained
    the app and nothing about it changed on a current Mac.

    • Below macOS 26 the Surfaces setting offers Solid and Frosted, and not Glass. A
      setting that silently did nothing would be worse than one that is not there.
    • A Glass preference carried over from a newer Mac — in a backup, or across an account —
      draws as Frosted rather than as nothing.
    • macOS 13 is not reachable the same way: the models are built on Observation, which begins
      at 14. That is a rewrite rather than a guard, and it is not planned.
  • A report on a schedule. Settings ▸ Data writes a report of the period that has just
    finished — yesterday, or the week just gone — into a folder you choose, as Markdown or
    HTML, keeping the eight most recent and removing only its own. The sibling of the scheduled
    backup and deliberately the same shape, with one difference worth stating: a backup is
    insurance you hope never to open, and this is the one file in the app meant to be read.

    • Past tense on purpose. A report of a day at two in the afternoon is wrong by six.
    • A period with nothing in it writes no file. A folder that fills with "nothing happened"
      is a folder somebody stops opening.
    • Not PDF: that is one page with a pointer to HTML, which is right for a document somebody
      asked for and wrong for a file that arrives on its own and might cover a busy week.

Changed

  • The parts of the app you read most are now translated. A session is called "Late night
    in Terminal" and a gap "8m not recorded" — sentences the app assembles at runtime, which had
    no whole string for a translator to be given and so stayed English in every language. Each
    is now a format string whose pieces a translation can reorder, which matters: in Uzbek the
    application's name comes first and the time of day after it. Session titles, the gaps
    between them, the day's headline figures, the sidebar sections, the resume card, the
    reflection prompt and the relative day and time labels all follow the chosen language.
    Uzbek is complete at 519 strings.
    • Still English everywhere: Story, the autobiography, Memories, the morning briefing,
      Collections and Projects. Those are the same kind of work and are listed as undone in
      docs/TRANSLATING.md rather than left to be discovered.

Fixed

  • The schedules section reported one schedule as if it were both. Its footer was the
    backup's, so "Nothing has been written yet" sat directly under a line saying a report had
    just been written.

  • A VoiceOver label was built from a malformed format string. One argument was handed to
    a two-placeholder format on the Chapters screen, mixing positional and non-positional
    specifiers — both undefined behaviour, and the label is read aloud.

Replay 0.9.7

Choose a tag to compare

@github-actions github-actions released this 29 Jul 14:12

Download

Replay-0.9.7.zip — unzip it and drag Replay.app to Applications.

Running 0.9.3 or newer already? Settings ▸ About ▸ Check for updates will offer this one and
install it for you, checking it against the SHA-256 published beside the zip. A Homebrew copy
is left to brew upgrade, as it should be.

macOS will refuse to open it the first time. Here is why, and what to do.

Replay has no Apple Developer ID yet, so macOS marks it as unverified and shows
"Apple could not verify Replay is free of malware" — offering only Move to
Trash
or Cancel. That message is about a missing certificate, not about
anything found in the app
. Every unsigned app gets it.

To open it:

  1. Try to open Replay once and dismiss the warning.
  2. Go to System Settings ▸ Privacy & Security, scroll down, click Open Anyway.
  3. Confirm. It opens normally from then on.

Or one line in a terminal, which does the same thing:

xattr -dr com.apple.quarantine /Applications/Replay.app

Right-click ▸ Open does not work. That bypass existed for years and Apple
removed it in macOS 15. It is still the advice in most projects' READMEs.

Or skip all of that

Homebrew compiles it on your machine, so it is never downloaded, never quarantined,
and opens with no warning at all:

brew tap nurkamol/tap
brew install nurkamol/tap/replay-app    # the application
brew install nurkamol/tap/replay        # the command-line reader

A signed, notarised disk image — no warnings, nothing to click through — appears
here the day there is a certificate. The workflow that builds it is already written.


Added

  • Replay can be read in another language, and there is now a way to translate it.
    Settings ▸ General ▸ Language, listing only the languages this build actually carries —
    and the switch takes effect immediately rather than at the next launch.
  • A translation kit, because the two things that translate text — a person and a service —
    both want a table, and neither wants a .strings file. node tools/translate.mjs new fr
    writes translations/fr.csv, a service or a translator fills the second column, build fr
    turns it into the catalogue that ships. Re-running new keeps what is already translated
    and adds rows for anything the app has gained since. status says how far each language is.
    Instructions, including what a translator needs to know about %@, are in
    docs/TRANSLATING.md.
  • A language ships only when it is complete. build refuses below 100% unless --partial
    is passed: an .lproj tells macOS "Replay speaks this", and a Mac set to it would then show
    translated and English lines mixed with no way to tell which is which.
  • Uzbek, complete — all 423 strings, so it ships without --partial. It is machine-made
    and wants a native reader: corrections belong in translations/uz.csv, and rebuilding is one
    command. Twelve more languages are scaffolded and empty, ready for a service or a person.

Fixed

  • Swift unicode escapes were being carried into the table as text. Three of the longest
    strings in the app contain \u{2019} in the source; the extractor took them literally, so
    those keys could never have matched the string the app actually looks up. Found by
    translating them — nothing else would have.
  • The sidebar was never translatable, and no audit could have said so: its names arrive as
    Navigation.Surface.rawValue, and tools/strings-audit.mjs looks for literals. The most
    visible column in the app was English in every language while the audit read clean. It goes
    through Loc now, and the extractor knows about the helpers that take copy as an argument.

Replay 0.9.6

Choose a tag to compare

@github-actions github-actions released this 29 Jul 10:28

Download

Replay-0.9.6.zip — unzip it and drag Replay.app to Applications.

Running 0.9.3 or newer already? Settings ▸ About ▸ Check for updates will offer this one and
install it for you, checking it against the SHA-256 published beside the zip. A Homebrew copy
is left to brew upgrade, as it should be.

macOS will refuse to open it the first time. Here is why, and what to do.

Replay has no Apple Developer ID yet, so macOS marks it as unverified and shows
"Apple could not verify Replay is free of malware" — offering only Move to
Trash
or Cancel. That message is about a missing certificate, not about
anything found in the app
. Every unsigned app gets it.

To open it:

  1. Try to open Replay once and dismiss the warning.
  2. Go to System Settings ▸ Privacy & Security, scroll down, click Open Anyway.
  3. Confirm. It opens normally from then on.

Or one line in a terminal, which does the same thing:

xattr -dr com.apple.quarantine /Applications/Replay.app

Right-click ▸ Open does not work. That bypass existed for years and Apple
removed it in macOS 15. It is still the advice in most projects' READMEs.

Or skip all of that

Homebrew compiles it on your machine, so it is never downloaded, never quarantined,
and opens with no warning at all:

brew tap nurkamol/tap
brew install nurkamol/tap/replay-app    # the application
brew install nurkamol/tap/replay        # the command-line reader

A signed, notarised disk image — no warnings, nothing to click through — appears
here the day there is a certificate. The workflow that builds it is already written.


Added

  • A travelling border while an update installs. The one moving border in the app, on the
    one stretch where Replay is working on something you should not interrupt and cannot be told
    how long it will take. Built here rather than taken from a package — CLAUDE.md forbids
    dependencies, and the effect is thirty lines. Off entirely under Reduce Motion.

  • Pausing can end by itself. "Pause for…" in the menu bar offers 15 minutes, an hour, or
    until tomorrow, and recording comes back on its own — including after a quit or a night
    asleep, because a timed pause is a promise about a span of time rather than about this
    process. The menu bar and Settings ▸ Privacy both say when it ends. Pausing with no end is
    still there and still indefinite; the problem this fixes is not pausing, it is forgetting.

  • After an update, Replay says what you got. The updater replaces the bundle and restarts,
    so "after the update" is really the next launch — and until now that launch said nothing at
    all. It now compares the version it is running against the one that ran last time and, if it
    is newer, answers in one of two ways.

    • You pressed Update: the What's New window opens by itself. You asked a question thirty
      seconds ago, the app disappeared and came back, and this is the answer.
    • It arrived some other waybrew upgrade, a bundle dragged into place — a banner in
      the same slot the update offer uses: "Updated to Replay 0.9.6", a What's New button and
      a dismiss ✕. Nobody is waiting on an answer, so nothing takes the screen. SPEC §8's "never
      interrupt" is exactly this distinction.
    • A downgrade says nothing: putting an older copy back is deliberate, and announcing the
      release somebody just escaped would be the app arguing with them.
    • The note is true for one launch and remembers nothing. Read or dismissed, it is gone.

Fixed

  • Replay Story moved in lurches. Each stop ran three movements where it should run one: a
    flight that started at full speed, a lean toward the next stop, and another flight that
    started at full speed. It arrived, stopped, crept, stopped and jerked away — two stops and a
    lurch per hop. The lean was this port's own idea and has gone; the dwell is still, and a
    hop now eases at both ends, because it begins from a camera at rest and nobody asked for it
    in the instant it happens. The reference's timings are untouched and still contract-checked.
  • Compacting no longer freezes the window. VACUUM ran on the app's own connection, on
    the main actor, wrapped in defer { busy = false } — so "Compacting…" could never draw a
    frame and the window simply stopped answering for the length of a whole-file rewrite. It runs
    on its own connection now, off the main actor, with recording paused around it so no tracker
    write meets an exclusive lock. Importing a backup is still synchronous; it is seconds rather
    than tens of seconds, and it is on the backlog.
  • A pinned ambient screen switched auto-start off. The idle watch refused to raise anything
    while any display was open, so ambient mode left running on a second monitor stopped the
    screensaver ever arriving on the screen you work on. The guard is about the screen now: two
    displays on one screen is what it was for, and two displays on two screens is the setup.
  • The timeline beside the Canvas now travels with the story. It stayed on whatever was
    selected when the story began, so a story was a camera moving through one memory beside a
    list describing another — the Glaze version keeps the two in step, and now so does this.

Replay 0.9.5

Choose a tag to compare

@github-actions github-actions released this 29 Jul 00:39

Download

Replay-0.9.5.zip — unzip it and drag Replay.app to Applications.

Running 0.9.3 or newer already? Settings ▸ About ▸ Check for updates will offer this one and
install it for you, checking it against the SHA-256 published beside the zip. A Homebrew copy
is left to brew upgrade, as it should be.

macOS will refuse to open it the first time. Here is why, and what to do.

Replay has no Apple Developer ID yet, so macOS marks it as unverified and shows
"Apple could not verify Replay is free of malware" — offering only Move to
Trash
or Cancel. That message is about a missing certificate, not about
anything found in the app
. Every unsigned app gets it.

To open it:

  1. Try to open Replay once and dismiss the warning.
  2. Go to System Settings ▸ Privacy & Security, scroll down, click Open Anyway.
  3. Confirm. It opens normally from then on.

Or one line in a terminal, which does the same thing:

xattr -dr com.apple.quarantine /Applications/Replay.app

Right-click ▸ Open does not work. That bypass existed for years and Apple
removed it in macOS 15. It is still the advice in most projects' READMEs.

Or skip all of that

Homebrew compiles it on your machine, so it is never downloaded, never quarantined,
and opens with no warning at all:

brew tap nurkamol/tap
brew install nurkamol/tap/replay-app    # the application
brew install nurkamol/tap/replay        # the command-line reader

A signed, notarised disk image — no warnings, nothing to click through — appears
here the day there is a certificate. The workflow that builds it is already written.


The update check spending its own attempts badly, and one claim measured rather than
believed.

Fixed

  • A refused check counted as the day's check. The "last checked" stamp was written the
    moment a reply arrived, whatever it said — so a rate-limited attempt recorded a check and
    the next one was 24 hours away, while an attempt that failed because the Mac was offline
    threw before the stamp and retried freely. Backwards in both directions: the failure that
    clears within the hour burned the day, and the one that might last all day did not. Only a
    200 or a 304 counts now.

Added

  • It says when the limit clears. GitHub names the moment in x-ratelimit-reset; the
    message reads "It clears at 6:05 AM" rather than "try again later", and the daily check will
    not ask again before then — an attempt inside a window GitHub has already closed can only be
    refused, and spends one of the sixty an hour that were the problem.
  • The check asks a smaller question. It sends If-None-Match with the tag from the last
    answer, so a day when nothing has changed comes back as an empty 304 rather than a release
    it already had. The release it did have is kept between launches, so that empty reply is
    still a complete answer.

Measured, and it went the other way

A 304 does not save rate limit on this endpoint. GitHub documents conditional requests
as exempt, and that is written for authenticated ones — on the unauthenticated per-IP cap
this app uses, three requests decremented the counter three times whether they answered 304
or 200. The table is in docs/FINDINGS.md. If-None-Match stays for the
two reasons that survived measurement, bytes and clarity, and this entry exists because the
first draft of it claimed the saving.

Replay 0.9.4

Choose a tag to compare

@nurkamol nurkamol released this 28 Jul 23:51

Download

Replay-0.9.4.zip — unzip it and drag Replay.app to Applications.

macOS will refuse to open it the first time. Here is why, and what to do.

Replay has no Apple Developer ID yet, so macOS marks it as unverified and shows
"Apple could not verify Replay is free of malware" — offering only Move to
Trash
or Cancel. That message is about a missing certificate, not about
anything found in the app
. Every unsigned app gets it.

To open it:

  1. Try to open Replay once and dismiss the warning.
  2. Go to System Settings ▸ Privacy & Security, scroll down, click Open Anyway.
  3. Confirm. It opens normally from then on.

Or one line in a terminal, which does the same thing:

xattr -dr com.apple.quarantine /Applications/Replay.app

Right-click ▸ Open does not work. That bypass existed for years and Apple
removed it in macOS 15. It is still the advice in most projects' READMEs.

Or skip all of that

Homebrew compiles it on your machine, so it is never downloaded, never quarantined,
and opens with no warning at all:

brew tap nurkamol/tap
brew install nurkamol/tap/replay-app    # the application
brew install nurkamol/tap/replay        # the command-line reader

A signed, notarised disk image — no warnings, nothing to click through — appears
here the day there is a certificate. The workflow that builds it is already written.


Added

  • A screen to show them on. Settings ▸ Display ▸ Show on names which display the
    screensaver and ambient mode take. Replay follows the keyboard unless told otherwise, and a
    named screen that is unplugged falls back to the keyboard's without forgetting the choice.
  • Ambient mode can be left open while you work. On a screen other than the one Replay's
    window is on, it stops taking the keyboard and stops closing when you type — the second
    monitor the mode was always for. It never applies on the screen you are working in, because
    a display that covers your work and takes no keys is a trap rather than a feature.
  • Quiet hours. The idle drift can be kept to a span of the day. Spans that run through
    midnight work, which is the case the obvious implementation gets wrong.
  • Backups nobody has to remember. Settings ▸ Data writes the same full backup every day or
    every week into a folder you choose, atomically, keeping the eight most recent and removing
    only its own older ones. Off until a folder and a schedule are chosen; nothing leaves the Mac.
  • A note and a bookmark on the stretch you are in, from the menu bar — or ⇧⌘N from
    anywhere. The moment worth marking is the one you are in, and it passes while you are
    finding the session in a window.
  • Ambient mode can drift in on its own. The idle delay used to have one outcome — the
    screensaver — because that is the only thing the reference's timer raises
    (openAmbient("screensaver"), hard-coded). Settings ▸ Display now opens with an Auto-start
    section: which of the two displays the delay raises, then the delay itself. The default is
    the screensaver, so an existing install behaves exactly as it did.
  • What starts on its own leaves on its own. An ambient screen raised by the timer
    dismisses on any key, click or movement. One opened by hand still stays, which is the whole
    point of a display left on a second monitor — upstream says so in as many words, and this is
    the case that comment does not cover.

Fixed

  • "Theme: Light" left half the app dark. The appearance was applied by the main window and
    the menu bar panel and by nothing else, so Settings, What's New and the note panel followed
    the system instead of the setting. It is applied once now, where every window picks it up.
    Found by looking at the light-mode screenshots; every check in the suite passed throughout.
  • VoiceOver read an unlabelled image before every application name — three of them per
    session on the Timeline. Application icons are decoration beside a name that is always
    there, and are marked as such.
  • "Exit on mouse movement" had nothing to listen to. Neither full-screen window set
    acceptsMouseMovedEvents, and AppKit does not deliver .mouseMoved to a window that has not
    asked for it — so the monitor watching for it could never fire. The switch is off by default,
    which is why a setting that did nothing survived this long.

Changed

  • The line under "Auto-start when idle" now says which display it will raise. The screensaver's
    wording is still the reference's, read from the contract rather than restated.

Replay 0.9.3

Choose a tag to compare

@github-actions github-actions released this 28 Jul 19:58

Download

Replay-0.9.3.zip — unzip it and drag Replay.app to Applications.

macOS will refuse to open it the first time. Here is why, and what to do.

Replay has no Apple Developer ID yet, so macOS marks it as unverified and shows
"Apple could not verify Replay is free of malware" — offering only Move to
Trash
or Cancel. That message is about a missing certificate, not about
anything found in the app
. Every unsigned app gets it.

To open it:

  1. Try to open Replay once and dismiss the warning.
  2. Go to System Settings ▸ Privacy & Security, scroll down, click Open Anyway.
  3. Confirm. It opens normally from then on.

Or one line in a terminal, which does the same thing:

xattr -dr com.apple.quarantine /Applications/Replay.app

Right-click ▸ Open does not work. That bypass existed for years and Apple
removed it in macOS 15. It is still the advice in most projects' READMEs.

Or skip all of that

Homebrew compiles it on your machine, so it is never downloaded, never quarantined,
and opens with no warning at all:

brew tap nurkamol/tap
brew install nurkamol/tap/replay-app    # the application
brew install nurkamol/tap/replay        # the command-line reader

A signed, notarised disk image — no warnings, nothing to click through — appears
here the day there is a certificate. The workflow that builds it is already written.


Added

  • Updates install themselves. The banner's button was a link to a release page, which
    left somebody to download a zip and drag a bundle over the one they were running. It now
    downloads the zip, fetches the SHA-256 the release publishes beside it, hashes the download
    and compares, extracts with ditto, checks the bundle is signed and is this application
    and is the version that was advertised, then replaces itself and restarts.
  • What the trust rests on, said out loud: HTTPS to a repository named in the source, plus
    a checksum published in the same release — the Homebrew-formula model. It proves the bytes
    are the ones that release carries, not that the release is trustworthy. Anyone who can
    publish to the repository can publish an update. A Developer ID would add authorship and is
    still the thing worth buying.
  • It refuses more often than it installs, which is the design rather than a limitation. A
    Homebrew copy is left to brew upgrade, since replacing the bundle would leave brew
    believing it has a version it no longer has. A copy running from macOS's read-only
    translocation mount — what an app still sitting in Downloads gets — has nothing to replace.
    A read-only location refuses rather than half-installing.

Fixed

  • Rate limiting says something useful. Testing exhausted GitHub's sixty-per-hour
    unauthenticated cap, and "GitHub replied 403" tells nobody anything they can act on.

Replay 0.9.2

Choose a tag to compare

@github-actions github-actions released this 28 Jul 19:03

Download

Replay-0.9.2.zip — unzip it and drag Replay.app to Applications.

macOS will refuse to open it the first time. Here is why, and what to do.

Replay has no Apple Developer ID yet, so macOS marks it as unverified and shows
"Apple could not verify Replay is free of malware" — offering only Move to
Trash
or Cancel. That message is about a missing certificate, not about
anything found in the app
. Every unsigned app gets it.

To open it:

  1. Try to open Replay once and dismiss the warning.
  2. Go to System Settings ▸ Privacy & Security, scroll down, click Open Anyway.
  3. Confirm. It opens normally from then on.

Or one line in a terminal, which does the same thing:

xattr -dr com.apple.quarantine /Applications/Replay.app

Right-click ▸ Open does not work. That bypass existed for years and Apple
removed it in macOS 15. It is still the advice in most projects' READMEs.

Or skip all of that

Homebrew compiles it on your machine, so it is never downloaded, never quarantined,
and opens with no warning at all:

brew tap nurkamol/tap
brew install nurkamol/tap/replay-app    # the application
brew install nurkamol/tap/replay        # the command-line reader

A signed, notarised disk image — no warnings, nothing to click through — appears
here the day there is a certificate. The workflow that builds it is already written.


0.9.1 shipped an app that would not open. This is that fixed, and the check that should
have caught it.

Fixed

  • The app died on launch if it could not find its strings catalogue. Bundle.module calls
    fatalError when the bundle is missing, and Loc reached for it on a path that runs during
    startup. Every other path in Loc degrades to English by design — a missing .lproj, an
    unreadable table, a key nobody has translated — and missing the whole catalogue should
    degrade the same way rather than take the app down. It resolves the bundle by hand now and
    gives up quietly.
  • How it got through, because that is the useful part. The resource bundle has two shapes:
    SwiftPM's native build produces a flat one with Info.plist at the root, Xcode's produces
    a deep one with Contents/Resources/. Every local build was deep and worked; the release
    was built flat and did not. No test sees either — they run where the bundle is always found.
    951 contract checks, 115 behaviour cases and four audits all passed on a build nobody could
    open.
  • The release workflow now runs the app it is about to publish and fails if it exits on
    its own. The only way to catch this class of failure is to launch the artefact.

Replay 0.9.1

Choose a tag to compare

@github-actions github-actions released this 28 Jul 18:46

This release is withdrawn. Its download would not open.
Bundle.module traps when it cannot find its resource bundle, and the release build
shaped that bundle differently from every local build, so the app died on launch. The zip
has been removed. Use v0.9.2, which fixes it and adds a check that runs the app before
publishing it.


Download

Replay-0.9.1.zip — unzip it and drag Replay.app to Applications.

macOS will refuse to open it the first time. Here is why, and what to do.

Replay has no Apple Developer ID yet, so macOS marks it as unverified and shows
"Apple could not verify Replay is free of malware" — offering only Move to
Trash
or Cancel. That message is about a missing certificate, not about
anything found in the app
. Every unsigned app gets it.

To open it:

  1. Try to open Replay once and dismiss the warning.
  2. Go to System Settings ▸ Privacy & Security, scroll down, click Open Anyway.
  3. Confirm. It opens normally from then on.

Or one line in a terminal, which does the same thing:

xattr -dr com.apple.quarantine /Applications/Replay.app

Right-click ▸ Open does not work. That bypass existed for years and Apple
removed it in macOS 15. It is still the advice in most projects' READMEs.

Or skip all of that

Homebrew compiles it on your machine, so it is never downloaded, never quarantined,
and opens with no warning at all:

brew tap nurkamol/tap
brew install nurkamol/tap/replay-app    # the application
brew install nurkamol/tap/replay        # the command-line reader

A signed, notarised disk image — no warnings, nothing to click through — appears
here the day there is a certificate. The workflow that builds it is already written.


Everything below happened after 0.9.0 was tagged the same day. Two of them are the reason to
take this build rather than that one: a data-loss bug, and the last functional gap against
the reference closing.

Added

  • PDF export, which the ledger had recorded as a decision rather than an omission after
    three WebKit routes died. The fourth route is not WebKit: ImageRenderer returns a
    CGContext, a PDF context is a CGContext, and a SwiftUI view draws straight into a page
    with no browser and nothing to paginate. Every earlier failure was a pagination failure, and
    the reference caps its own PDF at one page — so the honest shape is a summary that counts
    the whole span and names what it left off, rather than the HTML report again.
  • A menu bar popover in place of the menu. The menu answered two questions well and could
    not answer more, because a menu is rows of text. This adds the day's total, the focus goal
    as a bar, the last three sessions with their applications' icons, and a pause control.
  • tools/screenshots.sh — every surface captured in one command, about two minutes. Not a
    UI test and it makes no assertions: 951 contract checks had never caught a layout bug, and
    the bottleneck was never whether to look but that looking cost a dozen commands each time.
  • Localisation, to the point where it needs a translator rather than a task. Every string
    a reader sees goes through Loc, whose key is the English text — so a missing translation
    falls back to correct English rather than a raw identifier, and the contract keeps comparing
    the strings it always did. tools/strings-audit.mjs holds it there.
  • A landing page at nurkamol.github.io/replay-swift, and a v0.9.0 release with a
    zipped app, since Homebrew and a source build are not routes everyone has.

Fixed

  • Two copies of Replay could zero each other's live session. ActivityStore.open() closes
    any session left with no end — right after a crash, destructive while another copy is
    running, since it sets ended_at = started_at, duration = 0 on the first instance's
    in-flight session. Both then wrote to one SQLite file with no busy timeout, so a contended
    write returned SQLITE_BUSY and the event was lost with no error anywhere. A second launch
    now hands the front to the first and exits; the store waits five seconds for a lock. Found
    from a question, not from a check
    — whether running the Glaze app alongside this one could
    explain a crash. It could not. This was underneath the question.
  • The menu bar panel opened mid-sentence. The popover is reused so the button can toggle
    it and remembered its last size, while the content changes height — so taller content was
    clipped off the top, taking the header and the day's total with it.
  • The year heatmap's weekday key sat a hundred points from the year it labelled. The grid
    anchors trailing so it opens on today, which is right; applied unconditionally it also shoves
    a grid that fits against the right edge. True at every window from about 1000pt, which is
    every default window, and found by the first light-mode render in the project's history.
  • The week's rhythm strip left a dead gap before the durations at any wide window. Its
    560pt cap was redundant — the page measure already bounds the row — and was concealing that
    the strip is twenty-four bars that each want to fill, so removing the cap pushed the duration
    column off the edge entirely until it was given layout priority.
  • A page never grew with its window: every surface was capped at the prose measure and
    pinned left, so widening the window grew a dead column against the scroll bar.
  • The Canvas field is still. Its sway was this port's own, cost a permanent 30fps redraw,
    and diverged from a reference whose graph does not move.

Known gaps

  • Still not signed. The download is a zip and macOS will refuse it once; the release notes
    say why and how. Homebrew and a source build avoid it entirely.
  • One language. The mechanism is built and every string goes through it; nobody has
    translated anything. Loc.count has two plural forms, which is English — Russian needs
    three, Arabic six. A .stringsdict is the seam waiting for a language to be chosen.