# 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.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.
