# Changelog

All notable, user-facing changes to auspex are recorded here. This file is the
**source of truth** for release notes: it is what publishes to the public
`/changelog` and what becomes each GitHub Release body (ADR 0046).

The format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
and auspex adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html)
— the `vX.Y.Z` git tag is the single source of the version (ADR 0033).

## How to maintain this

- **Every user-facing change** adds a bullet to `## [Unreleased]` under one of the
  categories below, in the same PR as the change (reviewed in the diff).
- Write for **users**, not for the git log: describe the observable change and why
  it matters, not the implementation.
- **Cutting a release** moves the `## [Unreleased]` bullets into a new
  `## [X.Y.Z] - YYYY-MM-DD` section (`release-preflight` refuses a tag whose
  section is missing or empty).
- Categories are fixed: **Added**, **Changed**, **Fixed**, **Security**,
  **Removed**, **Deprecated**. `validate-changelog` (in `make guard`) enforces the
  shape, ordering, dates, and that nothing internal-only leaks into a public file.

## [Unreleased]

## [0.13.0] - 2026-11-10

### Added

- **`auspex upgrade` keeps a self-install current** — it moves a self-installed auspex to the `stable`
  release, or to `--channel latest` or `--version X`, and `--check` reports what it would do without changing
  anything. Every file is checked against the release signer built into auspex before anything is replaced,
  the new build is test-run first, and if the new version doesn't come up healthy the previous one is put back.
  It never downgrades. Installs managed by an MDM or a package manager are turned away with the right way to
  upgrade them.
- **One digest pins a whole release** — each release's signed `manifest.json` is now also served by its own
  sha256 at `https://auspex.span.app/blobs/sha256/<digest>`. It lists every binary and package for every
  platform by digest, so pinning it pins them all, `auspex` and its capture binary included.
- **Git-snapshot changes show where HEAD really was** — each working-tree change delta now also carries the
  commit and branch git reports for the repo when the change shipped, beside the ones the session last saw.
  When the two differ, the session hadn't seen HEAD move when the change shipped. The move may be someone
  else's, such as a pull or a commit made in another terminal, so the change may include commits the session
  didn't make. It may also be the agent's own commit inside a command that was still running. A read that
  fails is marked as failed and never holds the change back.
- **Health warns while hooks still run the full `auspex`** — the new `capture_binary` check warns when
  `auspex-capture` is only a link to `auspex`, or when a tool's last event was captured by `auspex` itself,
  for example from a hook entry copied by hand into managed settings. It names the tools, never a path.
  Capture still works; it is just slower.
- **auspex captures OpenCode** — `auspex install` now places auspex's plugin in OpenCode 2.x's global
  plugins folder, `~/.config/opencode/plugins/auspex.ts`, and `auspex uninstall` removes it. It captures
  session starts, prompts, tool calls (failures included), turn ends, subagents, the model, assistant
  text, token usage, skill invocations with the skill's text, and file content around edits. If
  `OPENCODE_CONFIG_DIR` is set, OpenCode reads only that folder, so install writes the plugin into its
  `plugins/` instead. On a managed device, `auspex install --system` places it once for every user, in
  OpenCode's system config folder. Windows isn't verified yet. Events reach auspex and are sent on, but
  Span doesn't show OpenCode activity yet.

### Changed

- **`auspex version --check` verifies the channel document** — it now checks the document's signature
  against the release signer built into auspex, and refuses a document older than one it has already seen.
  A forged or replayed document exits 1 ("couldn't decide"), so scripts can trust the other exit codes.
- **Coding-tool hooks on Windows run a much smaller program** — `auspexw.exe`, which your coding tools run
  on every event, is now a capture-only build about a sixth of its former size, so each event loads less.
  It keeps the same name, location and signature, so hook settings and software allowlists that name it
  need no change.
- **Coding-tool hooks on macOS and Linux run `auspex-capture`, a small capture-only program** — about a
  sixth of the size of `auspex`, so each event starts in roughly half the time. Releases ship it beside
  `auspex` in every archive and package, and install rewires existing hooks to it. If you use Codex and
  installed auspex yourself, open Codex, run `/hooks` and trust auspex's hooks once after upgrading.
  **MDM admins on macOS:** deploy the updated configuration profile with this release; it adds a Full Disk
  Access rule for `/usr/local/bin/auspex-capture`. An install from the single `auspex` file still works,
  more slowly, and says how to get the faster program.
- **The organization config check now reports the auspex version and work email** — so Span can turn a
  feature on for a few devices first, or keep it off builds that lack it. The work email is the one already
  sent with your captured events, and is sent only when it is a valid address. auspex now applies a config
  whenever its contents change, so a device picks up a feature as soon as it qualifies.

### Fixed

- **A macOS `.pkg` upgrade restarts auspex for every signed-in user** — before, it restarted only the user at
  the screen. Another user signed in through fast user switching kept running the old version until they
  signed out and back in. Their capture kept working, but on the old daemon.
- **The Windows managed uninstall script removes each user's data** — `uninstall-managed.ps1`, which the
  Windows MDM guides run after the `.msi` uninstall, stopped with "Access to the path is denied" on the first
  user whose auspex had run. auspex keeps each user's data readable only by that user, which kept the script
  out too, so their undelivered events, logs and device id stayed behind. It now deletes them with the backup
  and restore rights an administrator holds, and still removes a link without following it, even one a user
  creates while the script runs.

## [0.12.1] - 2026-11-05

### Fixed

- **Git-snapshot diffs are redacted the same way as the files they change** — on-device redaction now
  checks the old and new versions of each changed block, not the mixed-together diff. Your organization's
  custom patterns that match at the start of a line now apply to diffs. Personal data that needs a nearby
  word to be recognized, such as an unformatted social security number below an `ssn:` label, is
  redacted even when removed lines sit between them.
- **Deleted transcripts no longer fill the error log** — when a coding tool deleted a session transcript
  auspex had already collected, every daemon restart logged an error for it, and auspex kept tracking the
  file forever. auspex now logs no error for a collected transcript that disappears, and stops following it
  within 30 minutes. A transcript auspex never managed to read is still reported.
- **Switching to another branch no longer credits its commits to the session** — when an agent ran
  `git switch -c new existing`, or `git checkout -b new origin/main` from `main`, and the target branch
  descended from the one it left, git snapshot shipped every commit between the two as the session's work.
  The branch it left now ships only the session's own work, including a commit made in the same command as
  the switch.

## [0.12.0] - 2026-11-04

### Added

- **`auspex version --check` tells you whether this build is current** — it compares the installed version with
  the `stable` release, or `latest` with `--channel latest`, and with the lowest version a device should run. Scripts
  can branch on its exit code (0 current, 10 behind, 11 below the minimum, 1 the check couldn't decide) or read
  `--json`. It fetches one small file from `auspex.span.app` when you run it; plain `auspex version` still makes
  no network call.

### Changed

- **Git snapshot capture follows the agent's shell into other repos** — when an agent's shell command
  works in another repo or worktree, through `cd`, `git -C` or a different working directory, git
  snapshot now captures the changes it makes there, including a commit made in the same command. The
  repo a session started in is still captured. When the agent never worked there, its change deltas
  carry a new `unobserved_repo` attribution caveat, so they can be filtered out. Covers Claude Code,
  Codex, Copilot CLI, VS Code and Cursor.

### Fixed

- **Redacted git-snapshot diffs keep their `+` and `-` line markers** — when on-device redaction masked
  something at the start of a diff line, the line could lose its leading `+` or `-`, and then couldn't be
  attributed to its pull request. Redaction now leaves each line's marker and the hunk's line numbers
  alone, and Python decorators such as `@pytest.fixture` are no longer redacted as email addresses.
- **A crash in auspex's hook can no longer block Claude Code** — if the hook that captures each event ever
  crashed, it would exit with the status Claude Code reads as "block" and stop your tool calls or prompts.
  It now exits cleanly and drops that one event, and the daemon logs and counts the crash.
- **A Windows repo is no longer captured twice when its drive letter changes case** — coding agents
  sometimes report one folder as both `c:\…` and `C:\…`, and each spelling registered as its own repo.
  The hook now writes every repo path with an upper-case drive letter, so its changes are captured once.

## [0.11.0] - 2026-11-02

### Added

- **`auspex uninstall --purge` deletes captured data and config** — after the normal uninstall, it deletes
  `~/.auspex` (`%USERPROFILE%\.auspex` on Windows): the event queue, the local copy, identity, logs and
  config. It refuses before changing anything when events haven't been delivered yet (add `--force` to
  delete them anyway), on a managed device, for a custom `--home`, or while a daemon started by hand is
  running. Without `--purge`, uninstall keeps your data as before, and its summary now names the folder.
- **`auspex status` reports a managed install that didn't finish** — the Windows installer doesn't fail when
  its managed-hook step does (failing would roll back the whole upgrade), so a half-finished device used to
  look installed. Now that step records the failure: `status` shows a failing `managed_converge` check naming
  what failed and how to fix it, and on Windows the failure is also written to the Application event log.
- **Managed Macs keep an install log** — the managed setup that the macOS `.pkg` runs, and repeats at
  each login, now records each run in `/Library/Logs/auspex/install.log`.
  Each record holds the auspex version, the user it set up, how many coding tools it wired or removed,
  and any error. Before, a device set up by an MDM kept no record of what its install did.
- **Managed Windows devices keep an install log** — the managed setup that the Windows installer runs
  now records each install and upgrade in `C:\ProgramData\auspex\logs\install.log`, with the same
  fields as on a Mac.
- **Support bundles include the install logs** — `auspex support` now collects the managed install
  log. On a Mac it also adds the auspex lines from the macOS installer log, which show a package
  install that failed before auspex could run. Secret-shaped values are masked there, and other
  packages' lines are left out.
- **Lost events now say which limit dropped them** — when auspex's on-device queue or its intake
  buffer is full and drops the oldest events, `auspex status` and the fleet heartbeat now say whether
  the size limit or the event-count limit did it: `undelivered_evicted_by_cap` for the queue, and the
  `cap_bytes` / `cap_entries` reasons in `spool_dropped_by_reason` for the buffer (which used one
  `cap_evicted` reason). The daemon also logs a warning when the queue drops events, at most once every
  10 minutes for each limit.
- **auspex reports how long it was down before it last started** — `auspex status --verbose` shows
  when the daemon started and how long it had been stopped before that, or that the computer had
  restarted in between. The fleet heartbeat carries the same facts, so a gap in a device's events can
  be explained. A computer that was off or restarting doesn't count as the daemon being down.
- **The daemon's error log is sent to Span** — every error the auspex daemon writes to `daemon.log` after this upgrade is uploaded in batches, with its time, the auspex version and the OS, once the device is enrolled. Support can then see why a device is failing without asking for a support bundle. Errors wait in the daemon's own log files until they upload, so nothing extra is stored on the device. Logs carry no prompts, file contents or tool output, so they are sent as-is; they can include local file paths. Nothing is sent while privacy mode is on, and errors logged during it are never sent. Set `error_log.disabled: true` to turn it off on a device, or set the org feature flag `errorLogEgressKillSwitch` to `enabled` to stop it for the whole organization; errors logged while it is set are never sent.

### Changed

- **`daemon.log` is always JSON** — `logging.format: text` now changes only what the daemon prints to the
  terminal. The log file stays one JSON record per line, so its errors can be read back and uploaded.
- **Captured tool calls hold the agent for less time on macOS** — auspex no longer makes the drive
  empty its whole write cache for each captured event. It still hands every event to the drive before
  the agent continues, so a system crash or restart puts no more events at risk than before. On a Mac
  this cuts the time a captured tool call waits on auspex from about 10 ms to about 6 ms. Events
  captured just before a sudden power loss or a forced shutdown may be lost if the drive hadn't stored
  them yet.
- **Files saved in a legacy encoding are captured instead of skipped** — when the agent edits or reads a
  file saved as Windows-1252 or Latin-1 rather than UTF-8, auspex used to record that the file was
  binary and attach nothing. It now attaches the text, with the parts that aren't valid UTF-8
  replaced by `�`, and marks it `DEGRADATION_DISK_READ_REPAIRED` so it isn't mistaken for an exact copy.
  The same applies to files referenced in prompts and to skill files. Files containing a zero byte
  are still treated as binary.
- **Buffered events are no longer thrown away after 10 minutes** — when the auspex daemon was
  stalled, still waiting for its first organization policy, or restarting after a short outage,
  events already buffered were deleted if they waited more than 10 minutes to be processed. They
  are now kept and processed once the daemon catches up. The buffer is still limited by size
  (about 128 MiB or 20,000 events); only when it is full are the oldest events dropped.
- **Repeated failures no longer flood the daemon log** — while the network was down, the spool
  couldn't be read, or another program kept locking new events, auspex wrote a warning on every
  retry, which could push older, more useful lines out of `daemon.log`. Each such warning now names
  the kind of failure, such as `timeout` or `dns`, instead of the error text. It is written at most
  once every 10 minutes for each kind, and says how many were skipped since the last one.
- **Transcript history is read back one day, not seven** — when auspex first finds a session
  transcript (from Codex, Copilot CLI, VS Code or Claude Code), it now sends only the events from the
  last day, by each event's own time. Before, it read a whole transcript if the file had changed in
  the last 7 days, so a long-running session could send weeks of history, much of which was then
  discarded and reported as lost. Change it with `tailer.lookback_horizon_hours`. On an existing
  install, the change takes effect after the next `auspex install` (see below), unless you changed the
  setting yourself.
- **`auspex install` no longer copies the defaults into `config.yaml`** — install used to write a full
  copy of the default settings there. A copy can't be told from a setting you chose, so it kept every
  setting at the release you installed, and later default changes (such as the larger queue limit and
  the shorter transcript read-back) never reached you. Install now writes no `config.yaml` unless you
  pass `--config-from`, and the daemon uses its built-in defaults. On a device that already has one,
  the next `auspex install` removes the settings it copied there, once, keeping a backup and any
  setting you changed. A value you set on purpose to exactly an old default is removed too; set it
  again to keep it.
- **Queued events are no longer thrown away after 7 days** — events waiting to upload were deleted once
  they were a week old, so a device that was offline, stalled, or failing to upload for over a week lost
  them. They are now kept until they upload. The queue is still limited by size (about 768 MiB or
  100,000 events); only when it is full are the oldest events dropped.
- **Events are no longer dropped while auspex restarts** — when the auspex daemon stopped, hooks
  dropped every event fired more than 60 seconds after it last checked in. On Windows, where a
  crashed daemon can take up to a minute to start again, that could lose events during an ordinary
  restart. Hooks now keep capturing for 10 minutes, so restarts, upgrades and short outages lose
  nothing, and the daemon processes the events when it is back. To pause capture, run
  `auspex privacy on`; stopping the daemon no longer pauses it at once.

### Fixed

- **Uninstalling a managed install now removes its hooks** — on Windows, uninstalling the `.msi` left the
  Claude Code, Codex, Copilot and Cursor hooks in place, still pointing at the auspex binary it deleted.
  It now removes them; an upgrade leaves them in place. On macOS and Linux, `auspex hooks uninstall
  --system` now also removes each user's VS Code hooks, which it used to leave behind. On Windows, each
  signed-in user's auspex removes its VS Code hooks as the uninstall stops it. Hooks that remain, for a
  user who wasn't signed in, now do nothing once auspex is gone, instead of showing a warning in VS Code
  chat on every event. On a device uninstalled before this version, deleting that user's
  `%USERPROFILE%\.auspex\vscode` folder stops them.
- **A misspelled setting is no longer silently ignored** — a key in `config.yaml` or `managed.yaml` that
  matches no setting, such as `logging.levle`, loaded and did nothing, so the setting looked applied. The
  daemon now logs a warning at startup naming the key and its file, and `auspex install --config-from`
  rejects such a file, listing each unknown key.
- **A `config.yaml` with no settings no longer stops auspex** — a `config.yaml` that was empty or held
  only comments, for example one with every setting commented out, wiped the built-in defaults, so the
  daemon couldn't load its configuration and `auspex install` failed. Such a file is now treated as if
  it weren't there.
- **One bad event no longer stops capture** — an event auspex couldn't process or store made the
  daemon retry it forever, and nothing captured after it was delivered. One cause was a git
  `user.name` saved in a non-UTF-8 encoding, which some older Windows tools write. auspex now repairs
  such text the way browsers do, drops only an event it still can't handle, counts it as `poisoned`
  in the spool drop counts `auspex status` shows, and carries on.
- **Code changes to files in a legacy encoding are captured** — a change to a file saved as Windows-1252
  or Latin-1, rather than UTF-8, couldn't be stored, so git-snapshot shipped nothing for it. Such
  changes are now captured, with the parts that aren't valid UTF-8 replaced by `�` the same way
  the pull request's own diff is decoded, so they still match it line for line.
- **Same-size edits made right after a commit are captured on Windows** — an edit that kept a file's
  size, made shortly after a commit or `git add` in that repo, could look unchanged to git-snapshot,
  which then left it out of the captured change. Such edits are now captured.
- **Uploads no longer fail after a quiet spell on a VPN** — some VPNs drop idle connections without
  telling the device, so the first upload after a pause failed, was retried about a second later, and
  logged `egress: span sink request failed`. auspex now resends that upload at once on a new
  connection, so it succeeds without the delay or the logged failure.
- **Large backlogs upload without being rejected** — after a stall or a stretch offline, auspex sent up
  to 100 queued events per upload with no size limit. A backlog of large events, such as big file
  edits, made uploads the server rejected, so they were delivered only after minutes of retries, and on
  a slow connection they could time out every time. Each upload now carries at most 4 MiB, so a backlog
  drains steadily; an event larger than that is sent on its own.
- **Capture resumes on its own after a silent Windows upgrade** — when an MDM upgraded auspex with the
  `.msi` (`msiexec /i … /qn`), the new version could start while the old one was still shutting down,
  find it still running, and exit. Nothing was delivered and `auspex status` stayed red until the user
  signed out and back in, and a standard user couldn't restart it. auspex now waits up to 30 seconds
  for the previous version to finish shutting down before starting. Hooks kept capturing throughout,
  so nothing was lost.
- **Upgrading with the Windows installer wizard now updates the managed hooks** — when an admin ran the `.msi`
  from their own session (the wizard, or `msiexec` from an elevated prompt), auspex couldn't read that user's
  own VS Code hook file, so it skipped updating every managed hook while reporting a successful install. Worse,
  it could leave a folder in that user's profile that stopped their own auspex from starting. The installer now
  leaves VS Code to the user's own auspex, which wires it when it starts, and updates the other managed hooks.
- **A missing Codex hook file on a managed device names its fix** — `auspex status` now tells you to run
  `auspex install --system` (elevated) when a managed device's machine-wide hooks are missing; the next Windows
  installer upgrade also restores them.
- **Uninstalling on Windows waits for auspex to stop** — the `.msi` ended auspex and removed its
  files without waiting for it to finish shutting down. A removal script that deleted the auspex
  folder straight after `msiexec /x` could fail because the files were still in use. Uninstall now
  waits for auspex to exit first.

### Security

- **Standard users can no longer add files to the Windows managed configuration folder** — the
  Windows installer left `C:\ProgramData\auspex` open to any user for creating files and folders.
  A user could also create the folder before the install and keep control of it. A user could
  then plant managed configuration before your MDM placed it. The installer now makes SYSTEM
  the folder's owner and replaces its permissions on every install and upgrade, so only SYSTEM and
  administrators can change it. Upgrading fixes a device that already has auspex. Anything a user
  created there before the upgrade keeps its permissions, so review the folder on devices you
  don't trust (see the fleet rollout guide).

### Removed

- **The `process.spool_max_age_seconds` setting is retired** — auspex no longer discards buffered
  events by age, so this setting (and the `AUSPEX_PROCESS_SPOOL_MAX_AGE_SECONDS` environment
  variable) has no effect. A configuration that still sets it keeps loading, including a locked
  managed configuration, now and in future releases. auspex logs a warning at startup, and the new
  `retired_config_keys` check in `auspex status` names the setting and where it is set, so you can
  remove it. If `auspex install` wrote the setting into your `config.yaml` and you never changed it,
  it isn't reported, and running `auspex install` again removes it.
- **The `store.max_age_days` setting is retired** — auspex no longer deletes queued events by age, so
  this setting (and the `AUSPEX_STORE_MAX_AGE_DAYS` environment variable) has no effect. A configuration
  that still sets it keeps loading, including a locked managed configuration, and `auspex status` names
  it in the `retired_config_keys` check so you can remove it. As with the setting above, a value
  `auspex install` wrote and you never changed isn't reported, and the next `auspex install` removes it.

## [0.10.0] - 2026-10-22

### Added

- **Files you point the agent at in a prompt are captured** — when a prompt references a file
  (`@notes/plan.md` in Claude Code, Copilot CLI and Cursor, a bare path picked in Codex, or
  a file attached or typed with `#file:` in VS Code), auspex now records which files were referenced and, for text
  files inside your repository, their content as it was when the prompt was processed. File
  content is redacted like the rest of a prompt. Only the prompt's own references are read:
  nothing linked from inside those files is followed, directories are noted but not listed, and
  files outside the repository, non-text files and anything over 1 MiB are noted without their
  content.
- **Claude Code sub-agent token usage is now reported** — tokens spent by sub-agents (including
  forked and workflow agents) were previously left out, so a session that delegated work showed
  only the main agent's usage. Each sub-agent's usage is now reported against its parent session
  and tagged with the sub-agent's id, so it can be counted in the session total or broken out.
  Each usage record also carries the response's stop reason, which shows when a Claude Code
  version recorded a response's output tokens only partially.

- **auspex now checks the git it runs** — the daemon finds its git the first time it needs it, and the
  health report (`auspex status`) gains a `git` check that warns when git is missing or older than
  2.45, the version auspex now requires for every repository. On a device whose system git is
  older (Ubuntu 24.04 ships 2.43, Debian 12 ships 2.39), point auspex at a newer one with
  `capture.git_binary` in its config or `AUSPEX_CAPTURE_GIT_BINARY`; it must be an absolute path.

### Changed

- **Installing on WSL tells you how to enable systemd** — on Windows Subsystem for Linux,
  `auspex install --service` needs WSL 2 with systemd enabled. When systemd isn't running, install now
  stops before registering the service and prints the `/etc/wsl.conf` change that fixes it, instead of a
  generic `systemctl` error. Other Linux hosts are unaffected.
- **Built with Go 1.27** — auspex moves from Go 1.26 to Go 1.27.3. macOS 13 Ventura is still the
  minimum. On macOS and Windows, when `SSL_CERT_FILE` or `SSL_CERT_DIR` is set, auspex now loads
  trusted root certificates only from that file or those directories instead of the system store,
  as it already did on Linux. If you set either variable, make sure it includes any proxy or private
  root certificate auspex needs to reach Span.
- **auspex now logs when it can't read a coding tool's session file** — if a session file stays
  unreadable for a minute (missing, blocked, or locked), auspex logs it once at error level on the
  device, with the path and the error.
- **Hooks give up faster when something is stuck** — a coding-tool hook that can't finish (for example
  waiting on a hung disk or network drive) now stops itself after 1 s instead of 2 s, so a stuck
  capture holds up your agent for about a second at most.
- **Hook timeouts are now reported** — when a hook has to stop itself, auspex logs an error in its daemon
  log saying where it was stuck (the source line, and the call it was waiting in), and counts it
  in its health report, instead of silently losing the event. The log line is written at most once every
  10 minutes, so a device whose hooks keep timing out doesn't crowd other lines out of the log.

### Changed

- **Very large degraded code-change snapshots are sent as one summary** — when a snapshot of your
  working tree is too large to send with its content and covers more than 500 files, auspex now
  sends a single record with the file count, a count per kind of change and the directories
  holding most of the files, instead of one content-free record per file. This mostly affects
  snapshots of an entire repository, which would otherwise crowd out the rest of the session's
  activity.

- **Faster coding-tool hooks on Windows** — Claude Code, Copilot, Cursor and Codex hook events now run
  `auspexw.exe`, a windowless build of auspex installed next to `auspex.exe` (the same pairing as Python's
  `python.exe` and `pythonw.exe`), instead of the `auspex` command itself. Windows no longer starts a hidden
  console for every event, cutting each hook's cost by about a third (roughly 23 ms to 15 ms per Claude Code
  tool call on a typical laptop). VS Code runs it too. Existing hook entries are switched over
  automatically the next time auspex updates the tools' hook settings; the `auspex` command you run yourself is
  unchanged. The `.msi`, the `.zip` and the standalone download all ship it, and a self-install must download
  `auspexw.exe` into the same folder as `auspex.exe` before running `install` (without it, install fails).
  **Admins:** if your fleet allowlists software by path, hash or publisher (AppLocker, WDAC, EDR), allow
  `auspexw.exe` too, or the coding tools capture nothing.

- **Every Windows download is signed** — `auspex.exe` and `auspexw.exe` in the Windows `.zip` and the
  standalone `.exe` downloads now carry the same Authenticode signature as the `.msi` installer, so Windows
  and security software see a verified publisher whichever way auspex is installed.

### Fixed

- **Your own Claude Code hooks are kept when they share a group with auspex's** — if you added a hook
  inside the same `settings.json` hook group as auspex's, installing, updating or uninstalling auspex
  removed your hook along with auspex's. auspex now removes only its own hook and leaves yours in place.
- **Hooks auspex stops using are removed from Claude Code, Codex and Cursor** — when a release stops
  subscribing to a coding-tool hook, the next update of the tools' hook settings now deletes auspex's
  old entry for it, so it no longer runs, and uninstalling auspex removes it too instead of leaving it
  pointing at a removed program. Your own hooks on that event are kept. In Codex, if you added a hook
  after auspex's under that same event, its position changes, so Codex asks you to approve it again in
  `/hooks` once.
- **Codex is captured on managed Windows installs** — on Windows, Codex runs hooks through PowerShell,
  which couldn't run the hook command auspex wrote when the path to auspex needed quoting: every managed
  (`.msi`/MDM) install, under `C:\Program Files`, and per-user installs whose profile path contains a space.
  On those devices Codex reported `hook: … Failed` and its activity wasn't captured. auspex now writes a
  command PowerShell runs, and rewrites existing Codex hooks when it updates. A user-installed auspex asks
  you to approve its Codex hooks again in `/hooks`; managed installs need nothing.
- **auspex recovers on its own if a background step gets stuck** — if processing captured events,
  sending them, following transcripts, or a periodic task stops responding, auspex now logs where it
  was stuck and restarts itself within a few minutes, then processes the events captured in the
  meantime instead of losing them. `auspex status` shows a `worker_wedge` check when this has
  happened. A daemon started by hand (not as a service) logs it and keeps running instead.
- **Event processing no longer stalls on a stuck git process** — on Windows, a git process
  that kept running after auspex gave up on it could block event processing until the daemon
  restarted, and events captured in the meantime were dropped as too old. The programs the
  daemon runs are now cut off within a few seconds of their time limit, so processing carries on.
- **Claude Code token usage is attributed to the right turn** — every token usage record in a
  Claude Code session carried the id of the session's first turn, so usage from later turns
  could not be matched to the turn that spent it. Each record now carries its own turn's id.
  Re-invoking the same skill in a later turn is also now counted as a new invocation.
- **Cursor reasoning effort is recorded for Grok and Gemini models** — Cursor names the effort
  setting differently per model family, and auspex only recognized the GPT and Claude names. A
  Cursor prompt sent with a Grok or Gemini model now records its effort (for example `high`) like
  the others.
- **Claude Code sub-agent counts no longer include Claude's internal helper agents** — recent
  Claude Code versions run small internal agents of their own (for example, progress summaries
  while a background sub-agent works) and report each one as a finished sub-agent. auspex counted
  them as sub-agents, inflating sub-agent counts by up to about ten times. They are now left out;
  sub-agents your session actually delegated to are still reported.
- **Files ignored by your global git ignore file are no longer reported as code changes** — if
  `core.excludesFile` is set in your global or system git config (for example
  `~/.gitignore_global`), files it ignores were still picked up as new files the agent created.
  auspex now honors that file the same way `git status` does.
- **Codex token usage is no longer double-counted** — Codex sometimes records the same token
  count twice with no model request in between, and auspex reported both, overstating a
  session's input and output tokens by a few percent. A repeated record is now counted once.

- **VS Code token usage is reported once per request, with its final numbers** — VS Code rewrites
  a request's token counts while it runs, and auspex reported each rewrite, sometimes under
  another request's index, so adding up VS Code usage overstated it. auspex now reports each
  request once, after it finishes. A VS Code sub-agent's final answer is now captured too: it was
  usually missing because the sub-agent's completion fired before VS Code finished writing its
  transcript. VS Code doesn't record a sub-agent's (`runSubagent` or `execution_subagent`) own
  token usage, so a request that used one reports only the parent's tokens.
- **Token usage and Claude Code version for sessions run from Claude Desktop on Windows** — when
  Claude Desktop is installed as a packaged app (for example from the Microsoft Store or pushed by
  your MDM), Windows keeps the session files of its agent mode in the app's private storage, where
  auspex couldn't find them. Those Claude Code sessions reached Span without token usage, version
  or session details, for the main session and its sub-agents alike. auspex now finds the files where
  Windows actually keeps them.
- **Copilot CLI sub-agent completion events now include the sub-agent's final message** — on
  current Copilot CLI versions the final message was usually missing, because sub-agents were
  matched by an id those versions no longer use. auspex now matches them by the agent id that
  Copilot reports, and falls back to the message Copilot sends with the completion hook when the
  session log has none.
- **Repos with many branches, or using git's newer reftable format, no longer report the whole repository as
  changed by the agent** — when git's housekeeping had moved the current branch into its packed refs file, or
  the repo stores its branches in the reftable format, auspex could not read the commit the agent started from
  and treated the repo as brand new, so every file appeared as created by the agent. auspex now reads the
  commit from the packed refs file however large it is, and for reftable repos asks git for the commit the agent
  was on when each event happened. In the rare case neither can say for sure, auspex records that the starting
  point is unknown and holds back that session's code-change details until it can, rather than reporting them
  wrongly. Events from reftable repos also no longer report a branch named `.invalid`; the branch is left
  empty when auspex can't read it.

## [0.9.1] - 2026-10-08

### Fixed

- **Cursor no longer blocks tool calls when it also runs your Claude Code hooks** — with
  Cursor's third-party hooks setting on, Cursor runs auspex's Claude Code hooks without their
  arguments, and auspex answered with its help text. Cursor read that as an invalid hook
  response and blocked the agent's shell commands, file reads and MCP calls ("returned
  invalid JSON. The command was blocked for safety."). auspex now stays silent when it is
  started that way.

## [0.9.0] - 2026-10-06

### Added

- **Codex sub-agents are now attributed** — a sub-agent's tool calls, prompts and token
  usage carry its identity, and its start and finish are reported, as for Claude Code.
  Its copy of the parent's history is no longer reported twice.
- **Codex shell commands report their exit code** (Codex 0.148 and later), so failures can
  be measured. `apply_patch` edits report theirs too.
- **Interrupted Codex turns are reported as interrupted** instead of going unreported.
- **Codex: approve the new hooks once** — a user-installed auspex captures sub-agents and
  interrupts after you approve its three new hooks in `/hooks`. Managed installs need nothing.

### Changed

- **macOS and Linux: Claude Code hooks no longer start a shell** — as on Windows since
  0.8.1, auspex now registers its Claude Code hooks so that Claude Code launches auspex
  directly instead of through `/bin/sh`. Each hook starts slightly faster, most of all on
  devices where security software inspects every new process. This needs **Claude Code
  2.1.139 or later**. Upgrading auspex updates the hooks: a managed install rewrites its
  hook file, and a self-install picks the change up when `auspex install` or
  `auspex hooks install` runs again.
- **GitHub Copilot CLI hooks no longer start a shell, on every OS** — auspex now registers
  them so that the Copilot CLI launches auspex directly instead of through bash or
  PowerShell. On Windows that saves a PowerShell start on every hook. This needs **Copilot
  CLI 1.0.61 or later**: older versions reject the whole hook file, so update Copilot
  before or with auspex. Upgrading auspex rewrites the hook file automatically.
- **On-device redaction is about six times faster** — scrubbing a captured event before it
  leaves the device now takes a fraction of the CPU it did (a 1 MiB event: ~7.7 s → ~1.3 s),
  with exactly the same result. Large events and change snapshots reach their destination
  sooner and are less likely to run past their time budget.

### Fixed

- **Codex: your own hooks keep their approval when auspex updates its hooks** — a hook you
  added after auspex's was moved, and Codex then skipped it until you re-approved it.
- **Windows: git change capture works again with Git for Windows 2.56** — Git for
  Windows 2.56 refuses the null device as a config file, which made every
  git snapshot fail and stopped edits from being attributed on Windows machines
  running it. auspex now points git at an empty config file instead.
- **Windows: a pre-release `.msi` no longer blocks later upgrades** — an installer built
  from a pre-release tag (such as `0.8.0-rc2`) registered a garbled version number that
  made every later 0.x installer report "A newer version of auspex is already installed".
  Pre-release installers now register their final version number, so the final release
  upgrades them in place. A device that still has such an older pre-release installed
  needs it uninstalled once before upgrading.
- **Claude Code background-job reports keep their link to the call that started them** —
  when a background command or sub-agent finishes, Claude Code reports it in a message that
  names the tool call that launched it. On-device redaction mistook about half of those
  call ids for secrets and masked them, so the report could not be matched back to its
  call. Those ids now pass through unchanged; everything around them is still redacted as
  before.
- **Claude Code skill invocations now report the skill's file path** — Claude Code names
  an invoked skill but not where its `SKILL.md` lives, so its skill events arrived without
  a file path, unlike other coding tools'. auspex already finds the file on the device to
  capture its contents; it now also reports that location (repo-relative, or `~/…` for a
  personal skill), and the skill's repository when it lives in a different one.

## [0.8.1] - 2026-10-03

### Changed

- **Windows: Claude Code hooks no longer start PowerShell** — auspex now registers its
  Claude Code hooks so that Claude Code launches auspex directly instead of through a
  PowerShell call. Starting PowerShell was most of each hook's cost, and Claude Code
  waits for these hooks on every tool call, so sessions on Windows run noticeably
  faster — most of all on devices where security software inspects every new process.
  This needs **Claude Code 2.1.139 or later**. Upgrading auspex updates the hooks: the
  `.msi` rewrites the managed hook file on upgrade, and a self-install picks the change
  up when `auspex install` or `auspex hooks install` runs again. macOS and Linux are
  unchanged.

## [0.8.0] - 2026-09-29

This release has two themes. **Edit capture is now complete** across Claude Code, Cursor,
Codex, the GitHub Copilot CLI and VS Code (Copilot Chat): every edit names the right file
and repository — from a repository subdirectory, across repositories and through renames —
with its before/after content, including notebook edits and edit tools that previously
recorded nothing. And **events are no longer lost when another process briefly holds
auspex's temporary capture files open** (for example a file-monitoring or scanning tool):
auspex retries them within bounds and in order, and an event it still has to give up on is
counted, logged as an error naming the process holding the file where the OS allows it,
reported on the fleet heartbeat, and surfaced by the `delivery` check.

### Added

- **The daemon names the process holding a capture file open** — when another process keeps
  a just-captured event's file open until the daemon gives up on it, the error names that
  process where the OS allows it; the processes seen also appear under `lock_holders` in
  `auspex status --json` and the support bundle. The lookup only asks the OS who has the file
  open, and your org admin can turn it off.
- **Events lost before storage are now counted** — events the daemon could not read, and
  events the capture hook could not write (`hook_write`), count as delivery loss, survive
  restarts, and appear under `queue.spool_dropped` / `queue.spool_dropped_by_reason` in
  `auspex status --json`.
- **The fleet heartbeat reports capture-file loss and retries** — lifetime counts of events
  dropped before storage (total and by reason) and of failed reads of held-open files, so
  affected devices show up without a support bundle (`queue.spool_lock_retries` locally).

### Changed

- **The `delivery` check warns only on recent loss** — it warns while events were lost
  (evicted, dead-lettered or dropped before storage) in the last 3 days, or while undeliverable
  events are still retained, and clears on its own after that. Lifetime totals and the time of
  the last loss stay in the check's detail; `auspex status --json` adds `queue.recent_loss`.

### Fixed

- **Edits from a repository subdirectory report repo-relative paths** — a Codex or GitHub
  Copilot CLI session started in `pkg/sub` records `pkg/sub/file.txt` instead of `file.txt`
  (and captures that file's content, not a same-named file at the root). An edit outside any
  repository reports its absolute location, with the home directory shown as `~`.
- **Renames report clean paths** — a Codex rename carries its pre-edit content on the rename
  itself instead of a spurious extra entry, and a rename inside another repository reports
  both paths relative to that repository.
- **Cursor edits are recorded even when the content can't be read** — an edit to a file
  outside the workspace, over 1 MiB, binary or unreadable still names the file and why its
  content is missing, and a new file's pre-write event names the file it is about to create.
- **Claude Code notebook edits are captured** — `NotebookEdit` records the notebook's path,
  repository and full before/after content. Reading a notebook or PDF, or re-reading an
  unchanged file, no longer records a spurious change.
- **GitHub Copilot CLI `create` / `edit` edits are captured** — the tools Copilot uses with
  Claude models now record the file, the kind of change and its before/after content, and an
  edit that failed no longer reports a change.
- **VS Code (Copilot Chat) edits are captured in full** — `replace_string_in_file`,
  `multi_replace_string_in_file`, `insert_edit_into_file` and `edit_notebook_file` report each
  edited file, its repository and its before/after content; after-edit content is no longer
  flagged as unverified; and a failed `apply_patch` reports the tool error instead of a change.
- **VS Code events fired through the Copilot CLI hooks file are captured as VS Code** — with
  the VS Code tool name and file edits, and kept once when both registrations fire.
- **Events are no longer lost when another process briefly holds a capture file open** —
  the capture hook retries its write once after about 10 ms, and the daemon retries reading
  the event while keeping events in order. A burst of held files stalls capture about 30
  seconds in total, then each further held file gets only a few quick tries; events that were
  merely waiting, such as a backlog after the daemon was down, aren't penalised. An event
  still given up on is logged as an error, as are events shed at the local buffer's size or
  age limit.
- **A stored event is never sent twice after a restart** — when another process kept an
  event's capture file open so it couldn't be deleted, the daemon remembers the file across
  restarts and only finishes deleting it. `auspex status --json` shows how many files are
  still awaiting deletion as `queue.spool_sealed`.

## [0.7.0] - 2026-09-24

Hardening is the theme of this release. Code-change capture moves off the daemon's shared
background scheduler into its own supervised routine, with its own time budget, cadence and
health signal, so a slow or large repository can no longer hold up privacy, config and health
work — and the failure modes seen in the field are now bounded, self-healing and visible in
the daemon log and `auspex status --json`. Reasoning-effort capture rounds out the release.

### Added

- **Reasoning effort is captured for coding-tool sessions** — Cursor, VS Code (Copilot Chat),
  Claude Code, Codex and the GitHub Copilot CLI record the requested effort level (e.g. low /
  medium / high) on turn and token-usage telemetry, verbatim, and never inferred.

### Changed

- **Code-change capture runs as its own supervised routine** with a multi-minute per-pass
  budget (was a 30-second slot on the shared scheduler), its own liveness line in health
  checks, and isolation from the privacy/config/health jobs. Its cadence setting moves to
  `capture.git_snapshot_capture_seconds` / `AUSPEX_CAPTURE_GIT_SNAPSHOT_CAPTURE_SECONDS`
  (the former `scheduler.*` key is no longer read).
- **Daemon git work is throttled and de-prioritized** — at most two daemon git processes run
  at once across capture and ingest, at lowered OS priority, so capture yields to your own work.
- **Default on-device store cap raised to ~768 MiB** (was ~512 MiB) to hold code-change data
  alongside hook events during an upstream outage; disk footprint only, still tunable via
  `store.max_bytes` / `AUSPEX_STORE_MAX_BYTES`.
- **Clearer code-change capture diagnostics** — a pass that runs out of time names the
  session, worktree, step and change size, plus elapsed time; unusually slow changes log a
  diff/emit timing split; the "remote-reachable commit" warning includes the commit range.

### Fixed

- **An oversized change can no longer stall capture until restart** — it gets three full
  attempts, then is given up with a warning (a content-free "over budget" placeholder is sent
  for a committed range) and counted as `flushes_abandoned` / `settles_abandoned`.
- **A deleted git worktree is no longer retried for hours** — capture drops it the first time
  it is found missing, logs it once and counts it as `worktrees_retired`, not as a failure.
- **One failing background job no longer takes down the others** — a crash in a single
  scheduled job is contained to that job.
- **A stuck git subprocess can no longer wedge code-change capture**, and a large change's
  write to the on-device store no longer starts after the capture time budget has run out.

## [0.6.0] - 2026-09-21

Sharper, more disciplined code-change capture is the theme of this release: changes captured
from a repository snapshot now carry a few lines of context around each edit for a steadier
attribution anchor, and the capture lifecycle tightens up — a session's repositories stop
being snapshotted promptly when it ends, a session's final change is reliably flushed when it
goes idle, and a repository open in several worktrees is no longer captured from all of them
at once.

### Changed

- **Code-change snapshots now capture a few lines of context around each edit.** A change
  captured from a repository snapshot now includes the surrounding lines on either side of
  each edit (matching git's default diff view) instead of only the changed lines. The extra
  context gives downstream attribution a more stable anchor for matching a captured change to
  its pull request, and reduces false matches on small, common edits.

### Fixed

- **Code-change capture now stops promptly when a coding session ends.** When a tool
  reports a session has ended, auspex stops snapshotting that session's repositories on
  the next capture pass — instead of keeping them in the discovery set until other
  sessions crowded it out. Its final change is settle-flushed first, so nothing captured
  before the end is lost. (Behind the code-change capture feature; sessions from tools
  that report no end still age out automatically.)
- **A session's last code change is reliably captured when it goes idle.** When a coding
  session falls quiet, its final working-tree change is now flushed at that point rather
  than left to incidental capture timing — closing a gap where the tail of an idle
  session could go uncaptured until (and unless) the session resumed. (Behind the
  code-change capture feature.)
- **The same repository is no longer captured from several checkouts at once.** When a
  session works across multiple worktrees of one repository (e.g. a main checkout plus a
  linked worktree), auspex now captures only the checkout currently being edited rather
  than snapshotting all of them concurrently — removing duplicate change-delta volume and
  the inflated line counts it caused. Switching focus to another checkout hands off cleanly,
  flushing the outgoing one's final change first. (Behind the code-change capture feature.)

## [0.5.0] - 2026-09-17

Attribution fidelity is the theme of this release: the code changes auspex captures from a
repository snapshot now carry the same tool, author, and branch as the rest of the work
session, and commits pulled in from elsewhere are recognised as a sync rather than counted as
locally authored work. Every captured event also records *how* auspex observed it, and Cursor's
headless CLI now contributes prompts and agent responses, not just tool activity.

### Added

- **Code-change snapshots now carry full session attribution.** A code change captured
  from a repository snapshot is now tagged with the same coding tool (and version), git
  author, and branch as the editor-hook events from the same work session — so a change
  made by a shell command, a script, or an agent is attributed to the same tool, author,
  and branch as the rest of the session instead of arriving unattributed.
- **Every captured event records how auspex observed it.** Events now carry their capture
  method — an editor/tool hook, the session-log reader, or the repository snapshot — so
  downstream analysis can tell events apart and weight them by how they were captured.
- **Cursor prompts and agent responses are now captured in headless mode.** When you run
  Cursor's headless CLI (`cursor-agent -p`), auspex now records the prompt you submit and the
  agent's response for each turn by reading the session transcript — previously these were
  captured only in the Cursor IDE, where the editor hooks fire, so headless sessions recorded
  tool activity but not the prompt or the turn itself.

### Fixed

- **Pulled commits are no longer attributed to the session that pulled them.** When a `git pull`
  or fast-forward merge of a base branch brings in commits authored elsewhere, snapshot-based
  code-change capture now recognises that as a **sync** rather than as locally authored work: it
  re-baselines on the pulled state, so the incoming content drops out of the change attributed to
  the session. Locally authored commits still count — including ones already pushed, which a naive
  "is it on the remote?" test would have wrongly discarded. If a foreign commit does slip into a
  captured range, the record is flagged as degraded rather than quietly crediting outside code to
  the session.

## [0.4.0] - 2026-09-14

Result-based code-change attribution is the theme of this release: when enabled by your
organization, auspex now attributes code changes by what actually landed in a repository —
captured from a non-invasive working-tree snapshot — not only by the editing tool that made
them, so an edit written by a shell command, a script, or an agent that no editor hook
observes is still recorded. The capability is off by default and enabled only by
organization policy. On-device secret redaction also becomes time-bounded, so a pathological
input can never stall capture.

### Added

- **Code-change attribution from a repository snapshot (opt-in)** — when enabled by your
  organization, auspex now attributes code changes by their **result**, not only by the
  editing tool that made them. It periodically takes a non-invasive snapshot of each
  active repository's working tree and captures the net change — as a unified diff —
  since the current work session began, so an edit is recorded however it was made,
  including changes written by a shell command, a script, or an agent that no editor hook
  observes. The snapshot **never modifies your repository** — it writes nothing to your
  working tree, index, or history, and reads only from within the repository itself (never
  files it is configured to ignore, never anything outside the repo). Work is grouped
  into activity sessions anchored at a starting commit, re-anchored when you switch
  branches or rewrite history and segmented across long idle gaps, so a change is
  attributed to the session that produced it rather than smeared across unrelated work.
  The captured diff rides the redacted content plane — passing through the same on-device
  secret and PII scrubbing as any other captured content, never the always-kept
  metadata — and very large or generated files are summarized (line counts kept, raw
  contents withheld) rather than shipped wholesale. The whole capability is **off by
  default**: it is enabled only by organization policy and is suppressed entirely while
  `auspex privacy` is on.

### Changed

- **On-device secret redaction is now time-bounded and can't stall capture.** The pass
  that scrubs secrets out of captured content before it leaves your device now runs under
  a wall-clock budget scaled to how much content it is scrubbing, so a healthy scrub of a
  large change is never cut short. If a single record's scrub ever runs far longer than
  its size warrants — an unexpected pathological input — that record's content is dropped
  (its always-kept metadata is retained, and the record is flagged as redaction-timed-out)
  rather than holding up the rest. Everyday redaction is unchanged.

## [0.3.0] - 2026-09-10

Skill capture is the theme of this release: auspex now records when an agent reaches
for a skill, across all five supported tools, and captures the skill definition itself
on the redacted content plane. On-device redaction also covers five more secret
formats, and `auspex auth show` now reports what the running daemon actually resolved.

### Added

- **Skill-invocation capture** — auspex now records when an agent uses a *skill* (a
  reusable capability defined by a `SKILL.md` file) across all five supported tools:
  Claude Code, Codex, GitHub Copilot, Cursor, and VS Code. Both ways of triggering a
  skill are captured — an explicit `/name` command and the model reaching for one on
  its own. Each invocation reports the skill's name and, where the tool exposes it, the
  path to its `SKILL.md`; when that file lives in a different git repository than the
  work in progress, the skill's own repository is attributed too. The `SKILL.md`
  **contents** are captured too — lifted directly when the tool hands them over
  (Cursor, GitHub Copilot), or read from the skill file on device otherwise: by its
  known path when the tool exposes one (VS Code, Codex), or discovered from the skill's
  name under that tool's standard skill directories when only the name is known (Claude
  Code, and VS Code's explicit `/name`). Discovery searches each tool's skill locations
  in the tool's own precedence order — including per-user, per-project, and, for tools
  that support them, enterprise/managed directories (Claude Code's system-managed skills,
  Codex's admin skills) — and captures the skill the tool would actually run when the
  same name exists at more than one level. On-device reads stay confined to those
  sanctioned skill directories and the repository being worked in (following a symlinked
  skill directory to its real file, but only ever to another skill file), are size-capped,
  and time out rather than stall. The contents ride the redacted content plane — passing
  through the same secret scrubbing as any other captured content, never the always-kept
  metadata.

- **Five more secret formats are now redacted on-device, by name.** The scrubber
  gained rules for LangSmith keys, Stripe webhook signing secrets, Cursor `crsr_`
  keys, Slack `xoxs-` tokens and LLM gateway keys issued under a known `sk-`
  prefix (`sk-proj-`, `sk-svcacct-`, `sk-admin-`, `sk-bf-`), including OpenAI keys
  that lack the older infix. Hugging Face tokens containing a digit are now matched
  too, where previously only all-letter tokens were. Each is replaced by a marker
  naming the format, so you can see which kind of secret was scrubbed.

### Changed

- **`auspex auth show` now reports the running daemon's own identity** — it asks the
  daemon for the token and work email it actually resolved (the values it stamps on
  your events), so what `auth show` prints matches what leaves the device. Previously
  the command resolved these itself and printed a confusing always-on note warning that
  its values "may differ" from the daemon's; that note is gone. When the daemon is not
  running, `auth show` falls back to resolving the identity locally and says so in a
  single, plain note.

## [0.2.0] - 2026-08-27

Windows support is the theme of this release: auspex now installs, captures, and
updates correctly on Windows for both a self-install and a managed (MDM) deployment.
Organizations also gain a fleet-wide privacy kill switch, and releases become
verifiable by content digest.

### Added

- **Organization privacy kill switch** — an organization can now force capture off
  across its fleet from org policy. It only ever tightens: a local `auspex privacy off`
  cannot turn capture back on while the org enforces it, and the effective posture
  reaches the on-device hook, not just the daemon. Both `auspex privacy` and
  `auspex status` explain why capture is off — reporting it as "enforced by your
  organization" rather than a plain "on" — so it is clear a local toggle cannot lift it,
  including when the daemon is down.
- **Windows: `auspex` is on the PATH for every user** — a managed (`.msi`) install now
  adds auspex to the system PATH, so anyone who signs in to the device can run
  `auspex status`, `auspex privacy on|off` and the rest by name instead of typing the
  full install path. Open a new terminal after installing to pick it up. (macOS and
  Linux already resolved a bare `auspex`; this brings Windows in line.)
- **Content-addressed release manifest** — every release now ships a cosign-signed
  `manifest.json` (an OCI image-index) that binds the version to each artifact's
  SHA-256 digest, so downloads can be pinned and verified by content, not only by
  version tag.
- **By-digest download URLs** — publishing now dual-stores each artifact at a global,
  version-free content-addressed URL (`/blobs/sha256/<digest>`) alongside the existing
  version path, so an install can be pinned to exact bytes. The one-hop version URL is
  unchanged (the version path still serves its own bytes; no redirect).

### Changed

- **`unpublish` now de-indexes rather than deletes bytes** — it removes a version's
  resolvable surface (version tree, signed manifest, and the `latest` alias when it
  points there) but **retains the content-addressed blobs**, so a consumer pinned by
  digest keeps working. A real byte takedown is now the explicit `withdraw` (a whole
  version) or `purge-blob` (a single digest).

### Fixed

- **VS Code capture on a managed (MDM) device** — VS Code is the only tool whose hook
  registration is not a manageable enterprise policy, so it has to be written into each
  user's own settings. On a device set up by an organization's management system this
  failed three ways over: the install could not tell which person to register for, the
  hooks named an install location that does not exist there, and they were written in a
  form Windows could not run. Nothing was captured from VS Code while `auspex status`
  reported it as wired. Each user's own agent now completes the registration for itself,
  naming the machine-wide install, in a form Windows runs — including for someone who
  first signs in after the device was set up.
- **A managed re-deploy no longer signs the device out** — reinstalling or upgrading
  auspex through an organization's management system could delete the organization
  identity file it had been given, leaving the device unauthenticated with no error: the
  install reported success, and the only symptom was uploads quietly stopping. On a
  device where no individual user can be identified (the normal case for a management
  system running as the machine), the shared identity file is now the one that gets
  written and kept, so it keeps working for everyone who signs in — and rotating the
  organization token there actually takes effect.
- **Windows: the background daemon no longer shows a console window** — a terminal
  window stayed open for the daemon's whole life, and closing it stopped capture. The
  daemon is now started in a way that never creates a window at all, on both a
  self-install and a managed install; the per-event git lookup it makes can no longer
  flash a window either. **An existing install needs its service re-registered** to pick
  this up — re-run `auspex install --service`, or reinstall the `.msi` on a managed
  device.
- **Windows: Claude Code and GitHub Copilot capture** — both tools run their hooks
  through a shell that discarded the backslashes in auspex's path, so the hooks failed
  and nothing was captured. They are now written as a correctly quoted PowerShell call.
  Re-run `auspex hooks install` to refresh an existing install.
- **Windows-encoded credentials are accepted** — a token or `identity.json` saved from
  PowerShell (UTF-16, or UTF-8 with a byte-order mark) used to poison the auth header,
  so every upload failed and `auspex status` blamed an unreachable control plane. Such
  files are now decoded correctly; a token that is still unusable is rejected at
  `auspex auth set` with the exact offset and cause, and `auspex status --check-token`
  reports a malformed token instead of a connectivity error.
- **Windows: `auspex status` now correctly reports a stopped daemon** — a daemon that
  wasn't running could be misreported as running-but-unavailable (an absent local socket
  can surface as a "dead network" error there). As a result `auspex privacy on|off` and
  `auspex set echo-local on|off` again fall back to writing the setting directly to disk
  while the daemon is down, instead of failing.
- **Windows: the `auspex support` bundle is now a portable archive** — rotated
  daemon-log entries were stored under backslash paths (`logs\daemon.log`), which some
  tools couldn't extract alongside the archive's other `logs/` files; entries now always
  use forward slashes.
- **Windows: the session-log tailer no longer risks hanging** — during shutdown the
  tailer could block indefinitely while setting up its filesystem watcher; it now
  proceeds without one (falling back to polling), so the daemon stops cleanly.

## [0.1.0] - 2026-08-18

Initial release of auspex — a per-user capture agent that records how AI coding
tools are used, with privacy controls and on-device redaction built in.

### Added

- **Capture across AI coding tools** — Claude Code, Cursor, Codex, GitHub Copilot,
  and VS Code, via editor/tool hooks and session-log tailing: prompts and assistant
  responses, tool calls, token usage, and file edits attributed to the repository
  each one belongs to.
- **On-device redaction** — secrets and PII are scrubbed on the device, before any
  data is stored or sent.
- **Privacy mode** — `auspex privacy on|off` discards all capture while engaged, and
  a user's privacy choice always wins over org policy.
- **`auspex install`** — puts auspex on your PATH and scaffolds config; `--service`
  registers a per-user login service (launchd on macOS, systemd on Linux, Scheduled
  Task on Windows), and `--supervise` runs it under an auspex-owned supervisor for
  dev containers and cloud agents.
- **Fleet / MDM deployment** — `auspex install --system` converges a managed device
  to the fleet tier; installers ship for macOS (`.pkg`), Windows (`.msi`), and Linux
  (`.deb`/`.rpm`/`.apk`), plus a raw per-OS/arch binary for scripted installs, each
  covered by a published checksum.
- **Organization identity & policy** — `auspex auth set` / `auspex auth show`
  provision the org token and work email; the daemon syncs org configuration on a
  tighten-only basis (an org can add redaction or privacy, never relax it).
- **`auspex hooks`** — place, remove, and inspect capture hooks in your coding tools
  (user or system tier).
- **`auspex status` and `auspex support`** — report daemon health and device facts,
  and bundle logs, redacted config, version, and status into a shareable,
  content-free archive for troubleshooting.
- **Cross-platform background daemon** — resource-bounded, with a durable on-device
  store (capped, with retry/back-off and dead-lettering) that reliably forwards
  captured events to Span.
