Ambient system companions over one privacy-preserving signal daemon (aggregate-only, no keystroke content): a git-driven terminal garden and IOKit hardware collectors. ambient daemon macos privacy terminal

CHANGELOG.org

v1.2.1
ambient-companions/CHANGELOG.org rendered · source · history · blame · raw

177 lines · 8100 bytes

Changelog

Notable changes per release. Dates are release dates; the repository's own history has the detail.

SCHEMA_VERSION is the wire contract between the daemon, the Swift collector and every renderer, so it is called out wherever it moves. From 1.0 onward SignalName discriminants are append-only: names may be added, never renumbered or removed.

Unreleased

1.2.1 — 2026-09-04

Added

  • packaging/net.krz.ambient-companions.menubar-pet.plist, a LaunchAgent for the menu-bar face. A Login Item stores a bookmark that macOS resolves to the versioned Cellar path at creation, so it breaks silently at the first brew cleanup after an upgrade; launchd stores a string and follows the opt symlink each time.

1.2.0 — 2026-09-04

Added

  • menubar-pet, the third face: a menu-bar pet with one life. It ages, neglect kills it over a week through visible stages, and death is permanent for that pet. A new one arrives when you come back, and the dead are kept in a cemetery that is never pruned.
  • pet-life, the Rust crate behind it. Everything derives from timestamps rather than accumulating, so death happens at last_activity + 7 days and not when a program noticed — sleep, reboots, daemon restarts and the app not running change nothing. Birth requires activity, so a fortnight away leaves one grave rather than a chain of pets born, never fed and dead.
  • History::open_read_only and History::last_activity_ms. Read-only because the ordinary open prunes, and a face polling every five seconds must not delete the daemon's history as a side effect of reading it.

Fixed

  • The privacy ship-gate's forbidden-symbol scan missed ${BUFFER}. The braced form is ordinary zsh and does not contain the substring $BUFFER, so the check enforcing the project's central claim was bypassable by two characters. $RBUFFER was absent entirely.
  • That scan also asserted nothing about having scanned. If the directory walk returned empty the test passed having read no files, reporting success while checking nothing.
  • The banned list widened by prefix where safe: CGEvent, IOHID, plus addLocalMonitorForEvents, AXObserver and AXUIElement. Bare NSEvent stays permitted — the keylogger-shaped API is the monitor, not the class, and a menu-bar face will need it to draw a UI.

1.1.0 — 2026-09-04

Added

  • terminal-pet --oneline: one line, then exit, for a shell prompt segment. About 2.7 ms per call including process start. With no daemon it prints nothing and exits non-zero, so a prompt segment hides itself rather than printing a diagnostic into the prompt.
  • signal_client::Frames::snapshot, which reads the daemon's last-value cache and returns without waiting out a timeout.

Fixed

  • The pet's mood ignored staleness, so a cached keystroke rate left it looking busy indefinitely. The terminal collector stops publishing once a session ages out of its active window, so the last rate sat in the cache with nothing to replace it. Mood now considers only current readings.

1.0.0 — 2026-09-04

The release that makes one claim: the daemon runs unattended, recovers from its own failures, and says so when it cannot. Not feature completeness — two of the five faces in the README exist, and the rest are explicitly post-1.0.

SCHEMA_VERSION 4 → 5.

Added

  • SignalName::CollectorUp (discriminant 11), the daemon's own health, published once per Source on state changes. A dead collector now reaches a face instead of a log file.
  • signald/src/supervisor.rs: collector liveness in one place. The hardware child is respawned with backoff from 1 s doubling to a 60 s cap, reset after a run surviving 30 s, with no retry limit.
  • terminal-pet, the second face: the shell, the machine, and collector health. Any collector down renders the pet sick, which outranks every other mood.
  • crates/signal-client, one definition of the socket path and of frame iteration, shared by the daemon and both renderers.

Fixed

  • A panic in the git or terminal collector killed the producer thread while the socket kept serving a frozen cache, so faces went on rendering numbers that had stopped being true. A panic now costs one tick.
  • The hardware collector was spawned once and never again. If the child died, the daemon stayed up, so launchd's KeepAlive never fired and hardware signals stopped for good.
  • run_git used .output() with no timeout, so a held index lock or a vanished network mount hung the producer thread forever while health still read up.
  • keys_per_min had no floor on its sample window: two prompts a millisecond apart extrapolated a handful of keys into a five-figure rate.
  • The zsh hook capped the spool at 1 MiB. Nothing consumes it while the daemon is away, and the daemon read the whole file on recovery.

Changed

  • The hub's last-value cache keys by name, source and tag. It keyed by name and tag, so two signals differing only by source overwrote each other.
  • Signal.ts no longer claims to be monotonic-corrected. It cannot be: it is persisted, retention prunes on it, and CommitsToday means since local midnight.

0.6.1 — 2026-09-04

Fixed

  • signald had no --help, and any unrecognised argument became a repository path — so signald --help started the daemon watching a directory named --help. Both binaries now have --help and --version, and an unknown option exits 2.

0.6.0 — 2026-09-04

Added

  • An install path. packaging/net.krz.signald.plist runs the daemon at login; a Homebrew formula in krz/homebrew-tap installs the binaries, the plist and the zsh hook, with brew services support.
  • ~/.config/signald/repos, read when no repository is given on the command line. launchd starts an agent in /, so without it a service install collected no git signals at all.
  • A README "Paths" table: the socket, history database and spool defaults in one place.

0.5.0 — 2026-09-04

The contract freeze. SCHEMA_VERSION 3 → 4.

Changed

  • SignalName cut from 29 variants to the 11 with a producer, and its discriminants renumbered from zero. The last release in which renumbering was possible.
  • Tag::bundle_id and Tag::ssh_host removed: both were todo!() panics whose only consumers were among the removed variants. Tag::repo_path is the sole constructor, and signald confines a tag to the roots it was told to watch.

Fixed

  • A frame the build could not decode ended the stream, so a renderer built before a metric was appended died on the first one it met. Such frames are now skipped; an error is reserved for a stream that cannot be framed at all.
  • Around 34 citations of a specification that was never committed, replaced by the rules they referred to.

0.4.0 — 2026-09-04

One bus: every collector reaching every subscriber by the same path.

Added

  • Hardware ingest. signald spawns macos-collector and reads its frames, so the five hardware signals reach the hub, the history store and every subscriber like any other collector's.
  • A 0BSD LICENSE.

Changed

  • The terminal spool is consumed each tick rather than re-read in full forever, and its records carry the shell's pid, so keys_per_min is each active shell's rate summed rather than a mix of interleaved sessions.
  • History rows older than --retention-days (default 7) are pruned.
  • CI was added and then removed within this release. The forge's runner is scoped to one repository, so builds queued here were never claimed; .githooks/pre-push runs the whole suite instead and is the only gate.

0.3.0 — 2026-08-10

First tagged release: the signal daemon, the git, terminal and IOKit collectors, and the privacy-gated f64-only schema at SCHEMA_VERSION 3.

The v0.1 and v0.2 milestones named in the README — the git collector and the garden, then the SQLite history store, live streaming and the terminal collector — were never tagged and are folded into this commit.