Skip to content

v0.3.2

Choose a tag to compare

@seriouslysean seriouslysean released this 23 Sep 18:49
· 202 commits to main since this release
d1fb8a8

Closes #19.

A patch: nothing in the API list moved, and the payload version stays 2.0.0.

Configuration and traps

  • AethergramConfiguration clamps transmitInterval and maxBackoffInterval to a ceiling far
    past any real schedule and below about 9.2e18 seconds, where a sleep that runs traps. An interval
    from there crashed the host at the first sleep that ran on it — the coalescing sleep its first
    record scheduled whenever batchSize is above 1, or a backoff sleep — and one past about 1.7e20
    seconds, where Duration.seconds itself traps, crashed wherever it was first made a Duration.
    That configuration now runs, and the property reads back the clamped value. An interval that is
    not finite, or not positive, still traps at construction, as in 0.3.1. queueLimit is not
    capped, and one that is not positive still traps there too.
  • TelemetryDeckTransport fails a precondition naming the background URLSession at the first
    send over one. The send aborted the host at the same moment before, with an NSGenericException
    about completion handlers that did not name the transport.
  • Calling back into the recorder from a seam it calls under its lock (clientUserProvider,
    environmentProvider, any RetentionStore call, SignalQueueStorage.load()) still terminates
    the process, now through a precondition at the recorder entry point that was re-entered rather
    than inside the lock's own recursion check.

Consent and erasure

  • A pending erase is never superseded by a snapshot submitted after it. The queue writer keeps the
    purge and the newest snapshot apart and runs the purge first, so a reset racing a record can no
    longer leave a queue the store could not read on disk for a later grant.
  • A purge never creates the queue file. A decline before anything was granted, in a directory that
    refuses the delete, used to be able to write one.
  • Across an erase, the cancelled send from before it may still be in flight while the drain after
    it sends a different batch. The erase takes the drain claim instead of waiting for the cancelled
    send to unwind. Sends were never promised to be serial.

Durability

  • A queue file that could not be read is retried on every write. Once a read succeeds, what the
    file held, up to the newest queueLimit signals, is kept ahead of the recorder's queue in every
    write, for the next process to load and send. A store wrapped in another conformance keeps the
    bound it already has: 1,000, unless it or a copy of it was also given to a recorder directly.
    Previously the writes stopped for the life of the process.
  • The look for the erasure mark checks the mark's reachability and no longer reads file
    attributes.

Retention counters and payload

  • Day strings (acquisition.firstSessionDate and the distinct-day history) are yyyy-MM-dd in the
    Gregorian calendar, in the time zone of the calendar the recorder was given. A device set to
    another calendar used to send its own year, such as a Buddhist year 543 ahead. A stored record
    is converted when the recorder loads it: a day that already reads as a Gregorian date between
    2015-01-01 and tomorrow is kept, any other is read in the recorder's calendar and kept converted
    only if the result lands in that window, and otherwise kept as written. So a rebuilt or
    downgraded record is never converted twice. A day written before 0.3.2 under the Ethiopic
    calendar's Incarnation-era numbering that also reads as a Gregorian date in that window — any
    day from Ethiopic year 2015 on outside the thirteenth month — cannot be told apart from a
    Gregorian one and is left as written; one that does not, such as 2018-13-01, is read as
    Ethiopic like any other. One written under the Chinese or Dangi calendar in a leap month carries
    no leap-month flag, so it converts to the same day of the ordinary month before it: 2025-07-25, written as 0042-06-01, converts to 2025-06-25.
  • A session's first recorded signal after beginSession() is checkpointed, so a session killed
    inside the ten-second checkpoint interval is measured to that signal rather than discarded.
  • endSession() closes only a session this recorder opened, and a record advances only that
    session's checkpoint. A session left open by another process is closed by the next
    beginSession() against its own last activity, rather than stretched to now.
  • For an app extension on iOS or Mac Catalyst (macOS reads no receipt),
    EnvironmentSnapshot.current() resolves the receipt that runContext.channel is read from
    against the containing app's bundle rather than the extension's own, falling back to the
    extension's when the app's bundle gives no receipt location. The .appex has to sit two
    directory levels under the .app, as in PlugIns/ or Extensions/; any other layout keeps the
    extension's own.
  • A calendar.hourOfDay value outside 0-23, which a caller can pass since caller parameters win,
    goes to the wire unchanged instead of trapping the send.
  • A caller key spelled as a vendor wire name wins over the package key mapped onto that name, on
    every run. The winner used to depend on dictionary order.

Scheduling and logging

  • The drain runs at utility priority rather than the priority of the call that scheduled it.
  • A queue overflow logs once when it begins and once when it ends, rather than on every record. It
    ends when a delivery or a permanent rejection leaves the queue below the limit; an erase clears
    it without the ending line. A store skipping writes logs when the skipping starts and when it
    stops or its reason changes, rather than on every write.

Tooling

  • CI builds the package for iOS release as extension-safe, and the core alone with no adapter in
    reach.
  • The history scan forgives the pull request number in the subject GitHub writes on a merge
    commit, and nowhere else. The scanner and the check runner are scanned line by line, with only
    their exact fixture lines allowed.