# Deprecation tracker — schema and audit process

Source of truth: [`docs/deprecated.json`](./deprecated.json) — array of deprecation entries the `cleanup-deprecated` skill walks weekly.

Each entry documents one deprecated symbol / config field / endpoint, why it
exists, when (and under what signal) it is safe to remove, and which tests
pin its contract until the removal lands.

## Common fields

The schema is loose — entries grow fields as their gating story matures. The
following are all optional unless called out as required.

- `id` — **required**, kebab-case unique identifier (e.g. `completion-fallback-parsers`).
- `description` / `reason` — free-form prose explaining what is deprecated and why.
- `file` / `location` / `code` — pointer(s) to the deprecated symbol(s). `code` is
  the structured form: an array of `{ file, lines, note }` objects.
- `lines` — comma-separated string of line numbers (drift-prone; cross-check before
  acting).
- `deprecated` / `targetRemovalDate` — calendar dates (YYYY-MM-DD) marking when
  the entry was registered and when it may be removed. `targetRemovalDate: null`
  signals a non-calendar gate (telemetry / signal-based).
- `removalGate` — prose describing the signal that must clear before removal.
- `notes` — free-form post-removal scope or rollout caveats.

## Counter-gated entries (telemetry sentinels)

When the removal gate is "this counter must read 0 across a sweep window," the
entry uses these fields:

- `telemetryGate` — string referencing the metrics counter that must read zero.
- `sweepWindowDays` — positive integer (window length in days).
- `sweepStartDate` — `YYYY-MM-DD` window start.
- `enforcingTest` / `enforcingSweepWindowTest` — paths to the unit tests that
  pin the gate contract.

## Static call-site audit fields (P-c5d9e7b4)

Counter-gated entries also opt in to the static call-site audit
(`test/unit/deprecated-call-site-audit.test.js`). The audit enumerates live
call sites of the entry's symbols across `engine/`, `dashboard/`, and the
root-level `.js` files, then asserts every discovered call site is declared
in either `removalSites` or `allowedCallers`.

- `symbols` — **required for the audit to run**. Array of bare identifier
  strings (e.g. `["parseStructuredCompletion", "parseCompletionFieldSummary"]`).
  The audit builds a `\b<symbol>\s*\(` regex per name and skips matches that
  sit immediately after the `function` keyword (i.e. it does not count the
  definition itself as a caller). If `symbols` is missing or empty, the audit
  fails the entry — there is no fallback parse of `description`.
- `removalSites` — array of `{ file, line, symbol?, reason? }` objects listing
  the call sites that WILL be removed when the deprecation gate clears (the
  "expected, planned" callers). Paths are forward-slash relative to the repo
  root.
- `allowedCallers` — array of `{ site, reason }` objects (or
  `{ file, line, reason }` — both shapes are accepted) listing call sites that
  are intentionally preserved past the gate (e.g. a sibling deprecation that
  will retire separately). Defaults to `[]` when absent.

The audit assertion is `discoveredCallers ⊆ (removalSites ∪ allowedCallers)`.
On failure, the assertion message names every unexpected `file:line` so the
audit author can immediately classify each one as a new `removalSites` entry
or a new `allowedCallers` entry.

Entries WITHOUT a `telemetryGate` field are skipped by the audit — calendar-
gated entries (e.g. `qa-json-sidecars`) and signal-gated entries that don't
hang on a counter (e.g. `config-claude-binary-override`) don't carry the
`symbols` / `removalSites` / `allowedCallers` triple and are not enforced by
the static audit. Add them later if the gate evolves into a counter sentinel.
