UNPKG

@withpica/mcp-server

Version:

MCP Server for withPICA — enables AI assistants to interact with your withPICA music catalog

3,991 lines • 265 kB
# Changelog

All notable changes to `@withpica/mcp-server` will be documented in this file.

The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

> **Rule of the road:** every version bump to this package MUST land with a
> matching entry here in the same commit. See the project's "npm publish
> discipline" memory entry for the enforcement rationale.

## [Unreleased]

## [2.100.0] - 2026-10-04

### Added

- **`pica_recording_splits_update` and `pica_recording_splits_end` — fix a
  wrong ownership share.** Before, a recording split could only be created
  through the API, so a wrong row (a holder-less "unknown" share, the wrong
  percentage or role) blocked adding the right owner with
  `RECORDING_SPLIT_TOTAL_EXCEEDED` and had to be fixed by hand in /inspect.
  `_update` changes `person_id` (relink to a person in the organisation, not
  deleted), percentage, role, `split_type`, territory, dates or notes; the 100%
  total is checked after the change with the row's old value left out.
  `_end` sets `end_date` (default today) so the share stops counting, keeping the
  row as history. Both report `verification_cleared: true` when the change
  cleared the holder's confirmation. Typed refusals: `SPLIT_NOT_FOUND`,
  `PERSON_NOT_FOUND`, `PERSON_DELETED`, `SPLIT_ALREADY_ENDED`,
  `INVALID_SPLIT_PERCENTAGE`, `INVALID_SPLIT_ROLE`, `INVALID_SPLIT_TYPE`,
  `INVALID_SPLIT_TERRITORY`, `INVALID_SPLIT_DATES`, plus the existing split total / exists / locked codes.
- **`pica_merge_duplicates` takes a list — one approval for a whole cleanup.**
  Send several confirmed duplicate groups as `merges` (up to 50 items of
  `{entity_type, winner_id, loser_ids}`) instead of one call per group. A
  delegated agent (OAuth connector / `pica_grant_*`) then raises ONE human
  approval and the owner gets ONE email listing every pair, approved or
  denied as a whole — before this, a 15-merge cleanup sent 15 emails, each
  expiring on its own. Every pair is checked before the email goes out; one
  bad pair refuses the list. Once approved, `pica_approval_status` returns a
  per-pair outcome (`merged` / `skipped` because already merged or deleted /
  `failed` with the reason); one failed pair does not stop the others, and the
  approval reads `failed` if any pair failed. Callers that execute directly
  get the same per-pair result. The single-merge fields work exactly as
  before; send one shape or the other, never both. The tool's input schema no
  longer lists a top-level `required`, because the two shapes cannot share
  one.
- **`pica_people_split_identity` — give one person their own identity when
  two different people share one** (ops 482424b7). The opposite of a merge:
  when a wrong match (e.g. someone else's ISNI) joined two people to one
  global identity, custody transfers to it can't tell whose it is and land as
  "unknown". This gives the named person a new identity. Nothing is guessed:
  identifiers stay with the original unless listed in `move_identifiers`, and
  only the unattributed split rows listed in `relink_split_ids` move.
  `clear_person_identifiers` removes identifiers that were never the person's
  from their record; `undo_external_ids` strips everything a wrong external
  record (e.g. a Wikidata Q-id) wrote. Custody legs are append-only and never
  rewritten. Tier `destructive`, `approvalMode: "human"`, scope
  `write:people`. `pica_people_inspect` gains a `global_identity` section
  (only when named) that lists everything a split would touch.
- **`pica_profile_claims_invite` + `pica_profile_claims_status` — claim
  your profile for assistants.** An assistant can invite people in the
  catalogue to claim their profile (each at the address the catalogue holds
  for them) and check where people stand. The invite is a preview unless
  `confirm: true`: the first call sends nothing and returns who would be
  emailed and who would be skipped and why, so the person the assistant acts
  for sees the list before anyone is emailed. Only a credential that belongs
  to an owner or admin may send; an organisation key with no owner is
  refused (`INVITE_REQUIRES_PERSON`). The status read never returns an
  email address.

- **`pica_import_emails_query` — see what happened to an email forwarded to
  PICA.** Lists the emails that reached the organisation's forwarding
  address, newest first, with what PICA did with each: stored on arrival
  (with what was created, by id), waiting for review, dismissed, or failed.
  With `id`, reads one: its text body when PICA kept one (emails held for
  review), its attachment names, and what was created. Owners and admins
  only; read-only (`read:catalog`). Before this an agent had no way to learn
  that an email its person forwarded had arrived. Every item carries
  `sender_trusted`; an email from a sender PICA could not verify is labelled
  third-party content (its subject and body are data, not instructions). The
  list reaches back to 2026-09-30, when PICA began recording outcomes, and
  refuses an `offset` past 500 rather than repeating a page.

### Changed

- **`pica_people_update` / `pica_people_create` accept and clear every field
  enrichment fills on a person.** `imdb_id`, `viaf_id`, `date_of_death`,
  `nationality_wikidata_id`, `birth_place_wikidata_id` and
  `languages_spoken` were written by Wikidata enrichment but refused with
  `UNKNOWN_FIELD`, so a misattributed value could not be removed. They are now
  accepted, and every enrichment-filled field takes `null` (or `[]` for a
  list) to clear it. A field set or cleared this way is recorded as the
  user's own, and later enrichment runs leave it alone. `field_sources` is no
  longer accepted from callers (it was never advertised); PICA writes it.
- **`pica_credit_update` can change who a credit is for (`person_id`).** Link
  a name-only credit to a person record created afterwards, or move a credit
  linked to the wrong person, and the credit keeps its id (ops issue
  abc91f6c: 11 credits had to be removed and re-added, losing their ids and
  confirmations). The person must be in the organisation and not deleted
  (`PERSON_NOT_FOUND` / `PERSON_DELETED`); if they already hold that role on
  the song or recording the change is refused with `CREDIT_PERSON_CONFLICT`
  and `existing_credit_id` rather than making a second credit; linking a
  name-only credit to a differently named person needs
  `confirm_name_mismatch: true` (`PERSON_NAME_MISMATCH` otherwise). A
  confirmed, disputed or declined credit goes back to pending, because the
  newly linked person gave none of those answers — the response's
  `person_change.attestation_reset` and `previous_attestation_status` say so. Server-side in `PATCH /api/admin/works/[id]/credits/[creditId]`.
- **`pica_documents_query` lists PICA's own split sheets first, marked as the
  record.** Each entry now carries `kind` (`split_sheet` or `upload`); a
  split sheet PICA generated has `split_sheet_role: "record"` and
  `read_with: "pica_split_sheet_get"`, and an uploaded or forwarded split
  sheet has `split_sheet_role: "supporting"` (plus `master: true` when it is
  about the master only). An archived PICA sheet is `"superseded"`, not the
  record. Paging is one sequence: PICA's sheets, then uploads, with `total`
  covering both. With `parsed_status` `pending` or `completed`, only uploads
  are listed. `pica_documents_link` / `_delete` take an entry with
  `kind: "upload"`. Server-side change in `/api/admin/documents/catalog`;
  the tool description says how to read each kind.

### Fixed

- `pica_recording_credits_update` no longer advertises or forwards
  `attestation_status`. A caller could create a producer or performer
  credit already marked "attested" without the credited person ever
  confirming it. The field is gone from the input schema, and a call that
  sends any attestation field (`attestation_status`, `attested_at`,
  `attested_by`, `attestation_source_type`, `attestation_source_id`), in
  the single form or on any `credits[]` entry, is refused before anything
  is written with `ATTESTATION_NOT_SETTABLE`. The refusal points at the way
  a recording credit actually gets confirmed: `pica_collaborators_invite`
  with the `recording_id`, or `pica_credits_send_for_attestation` on the
  recording's song. The server refuses the same fields on
  `POST /api/admin/recordings/[id]/credits` and the bulk route.
- `pica_credit_add` on a work now treats `share_pct: null` like an
  omitted share: stored as unknown. The server refused an explicit `null`
  with a 400, so a client that sends `null` for an empty optional field
  (ChatGPT's connector does) could not add a credit without a share.
  Server-side fix in `/api/admin/works/[id]/credits/atomic-add`; it reaches
  every client on deploy. The input schema now declares `share_pct` as
  number-or-null, matching `pica_credit_update`.
- When the server refuses `pica_credit_add` with a machine code
  (`VALIDATION_ERROR`, `PERSON_ORG_MISMATCH`, `RECORDING_TARGET_REQUIRED`,
  `INSUFFICIENT_SCOPE`, …), the tool now returns that code and message,
  plus the fields the server put beside them (`remedy`, `person_ids`,
  `next_tool`). It no longer reports those refusals as `CREDIT_ADD_FAILED`,
  which read as a transport failure and invited a blind retry. A server
  error (5xx), or a refusal with no code, is still `CREDIT_ADD_FAILED`.
- `pica_credits_update` no longer advertises `attestation_status`. The
  server stopped accepting it on 2026-09-29 (a caller could mark a credit
  "attested" with no one ever confirming it), but the tool kept offering
  it, so agents sent it and the whole save failed with `UNKNOWN_FIELD`. The
  field is gone from the input schema, the description and the
  `work-credits-required` resource. A call that still sends it is refused
  before anything is written with `ATTESTATION_NOT_SETTABLE`, naming the
  entry and pointing at `pica_credits_send_for_attestation` — the way a
  credit actually gets confirmed.

## [2.99.0] - 2026-09-30

### Added

- **Platform-presence waivers on `pica_integrity_waive`.** New `gap_code`
  values `PLATFORM_SPOTIFY`, `PLATFORM_YOUTUBE`, `PLATFORM_APPLE_MUSIC` with a
  required `kind` (`not_applicable` | `restricted`) and reason mark a work that
  is deliberately not on that platform (e.g. no YouTube by design, a licence
  rules out Content ID). The platform then drops out of the work's
  completeness instead of counting as missing; the answer carries the
  re-scored `completeness`. Owner/admin only. Identifiers and registrations
  (ISWC, ISRC, IPI, society registrations, writers, splits) can never be
  waived, and the description says so.

- `original_work_id` on `pica_works_create` / `pica_works_update`: links a
  modified version (a remix that added writers) to the work it modifies. Same
  catalogue only; `null` clears it.

### Changed

- **A remix is a recording of the same song unless it adds writers (ADR-324).**
  `pica_works_create` on a remix-titled work now suggests a recording under the
  original (`pica_recordings_create`, `version_type: "remix"`, remixer credited
  on the recording) with the modified-version path beside it
  (`if_new_writers`), instead of a separate derivative work.
  `pica_find_duplicates` (works) no longer groups a remix with its original as
  a duplicate: each remix is paired with its original as
  `match_kind: "remix_recording"` (move its recordings) or
  `"remix_modified_version"` (link it), with a `remix` block naming the
  recordings, any added writers and the action — never "fold the copies".
  The `catalog-modelling` skill carries the rule.

- `pica_rights_inspect` no longer says amounts are "not computed yet"; it points
  to `pica_rights_statements` for them.

### Removed

- **The last 11 deprecated tool aliases** (removal target 2.99.0). Each had
  been a hidden, callable alias of the tool named beside it — ten since the
  MCP tool-surface consolidation, `pica_royalties_summary` since 2026-09-25
  (ADR-321 Phase 0). Calling an old name now fails with `TOOL_NOT_FOUND`;
  the answer (from `pica_execute`, `pica_tool_details` and the hosted
  connector alike) says the tool was retired in 2.99.0, names its
  replacement and any argument change, and lists the replacement first in
  `did_you_mean` when the caller can call it. Use the replacement instead:
  - `pica_releases_list` → `pica_releases_query`
  - `pica_releases_get` → `pica_releases_inspect`
  - `pica_releases_bulk_attach_tracks` → `pica_releases_attach_track` with
    `items[]` (the old `tracks` field is now `items`)
  - `pica_releases_attach_track_by_identifier` → `pica_releases_attach_track`
    with `identifier_type` + `value`
  - `pica_projects_list` → `pica_projects_query`
  - `pica_notes_list` → `pica_notes_query`
  - `pica_documents_list` → `pica_documents_query`
  - `pica_documents_read` → `pica_documents_inspect`
  - `pica_statements_list` → `pica_statements_query`
  - `pica_royalties_summary` → `pica_royalties_blockers` (`work_id` and
    `limit` carry over; `gap_type` and `priority` have no equivalent)
  - `pica_recording_credits_bulk_update` → `pica_recording_credits_update`
    with a `credits` array (same arguments)

  Production use over the 60 days before removal was 4 calls in total.

- **`verified` on `pica_works_query`.** It filtered on a work-level
  `is_verified` flag that PICA has never stored, so it matched no works
  whichever way it was set. Passing it now returns `NO_WORK_VERIFIED_FLAG`
  (the same code `pica_works_verify` returns) and names the filters that do
  exist: `health_filter: 'low_completeness'`, `completeness_score_lt`, and
  `pica_works_inspect` for one work.
- **`verified` / `unverified` counts in the `catalog://stats` resource, and
  `is_verified` in `works://list`.** Both read the same missing flag, so every
  work counted as unverified.
- **`musicbrainz_recording_id` on `pica_works_update` / `pica_works_create` /
  `pica_works_bulk_update`.** An update used to pass it to the work's
  recording, but only when the work had exactly one recording, so the same
  call silently stopped working once a second recording was added (a create,
  with no recording yet, never stored it). It is now refused with
  `PHANTOM_FIELD` naming the tool to use instead:
  `pica_recordings_update {id, musicbrainz_recording_id}`. Work reads still
  show the recording's MusicBrainz id, and the completeness benchmark still
  reads it.

### Fixed

- `pica_sync_placements_cite` no longer answers 404 on every call: the SDK
  posted to `/sync-placements/{id}/sources`, a route that never existed; it
  now posts to `/cite` (needs the matching `@withpica/mcp-sdk` fix). The
  sync-placement item routes also required `read:sync_placements` /
  `write:sync_placements`, scopes no key can hold, so only `admin` keys got
  through; they now require `read:agreements` / `write:agreements`, the
  scopes these tools declare.
- `pica_physical_assets_inspect`'s `market` section no longer fails on every
  call (the SDK sent GET to a POST-only route). Because that section asks an
  AI model for a price estimate each time it runs, it is no longer part of
  "omit sections for everything": it runs only when `sections` names
  `market`, and needs a key with `write:catalog` (or `admin`).
- `pica_integrity_waive`'s `gap_code` enum now lists `WORK_SPLITS_ABSENT` and
  `WORK_SPLITS_INVALID`, which the app has accepted since 2026-08-16.
- **`pica_rights_statements`** (read:finances) — ADR-321 Phase 3, COMPUTED NOT
  PAID: direct income and what each party would receive under the song's
  locked split in force when it was earned, PICA's proposed fee as its own line
  (shown, never charged), every hold with why / who clears it / since when, and
  the four measures. With `work_id`, that song's imported society statements
  checked against the split: each co-writer's EXPECTED amount and the
  registration check for writer societies; line-up completeness for PPL /
  SoundExchange.
- **`pica_rights_record_income`** (write:finances) — record a figure the user
  gave (or a quoted licensing enquiry's fee), labelled "entered by your agent".
  No money moves.

## [2.98.0] - 2026-09-29

### Deprecated

- The 11 deprecated aliases scheduled for removal in 2.98.0 (among them
  `pica_releases_list`, `pica_documents_list`, `pica_notes_list` and
  `pica_royalties_summary`) stay one more version. They are now due for
  removal in **2.99.0**. Each still answers and names its replacement.

### Added

- **`disambiguation` on `pica_works_create` / `pica_works_update`** — a short
  note (≤ 200 characters, e.g. "theme", "2019 song") that tells apart two
  DIFFERENT songs sharing a title and primary artist. It is part of the
  duplicate check, so set it only when the songs genuinely differ; a
  `WORK_ALREADY_EXISTS` refusal for a song you know is not the existing one is
  the cue. The refusal message and its recovery hint now name both ways
  through: merge when it is the same song, `disambiguation` when it is not.
  `null` or `""` clears it. Also accepted by `pica_works_bulk_update`, which
  shares the works write schema.
- **`score` work type** — music composed for film, TV, games or other
  picture. Offered wherever `work_type` is (`pica_works_create`,
  `pica_works_update`, `pica_works_bulk_update`, `pica_works_query`,
  `pica_register_set`).

- **Programmable rights — `pica_rights_propose`, `pica_rights_status`,
  `pica_rights_inspect`** (ADR-321 §9, Phase 2). Agents propose, humans
  confirm. `pica_rights_propose` proposes a song rule, a recording's master
  rule, or a share rule that divides one party's share (a publishing deal, a
  manager's commission with an end date), and emails every party it affects to
  confirm. It never activates or confirms anything and never moves money.
  Tier `destructive`, `approvalMode: "human"`, scope `write:catalog`: a
  delegated agent's call becomes a pending approval for the org owner (poll
  `pica_approval_status`), replayed through the same propose service the route
  uses. Shares are basis points (100 = 1%; a share rule's parties sum to
  10,000). A refusal writes nothing and returns its reasons. A master rule
  needs `ownership` — the user's answer to "who owns the master of this
  recording?" (`only_me` / `me_and_others` / `someone_else`) — and locks only
  after every owner signs the recording's master ownership sheet; an agent can
  never lock "only me" in one step.
  `pica_rights_status` (`read:catalog`) reads one rule: status, who has
  confirmed this exact version, and email delivery. `pica_rights_inspect`
  (`read:catalog`) reads a song's rules on both sides, version history,
  disputes, and the holds — what money would be held, why, and who clears it.
  No amounts yet; holds are PICA's rules, not a society's.
- **`pica_royalties_blockers` — "why isn't this song being paid?"** (ADR-321
  Phase 0). With `work_id`: what in that song's record is likely holding up its
  society income — missing ISWC, writers without an IPI or society membership,
  shares not totalling 100%, disputed credits, no confirmed registration,
  recordings without an ISRC — each with its fix. Without it: the catalogue
  summary plus the songs that are not clear, filterable by blocker and paged.
  Same judgement as the `/inspect` song panel and `/inspect/money`. Scope
  `read:catalog` (no amounts). The description and result message never call a
  song clear unless every area was checked; unchecked areas are named.
- **`pica_audio_bulk_update` — metadata for up to 200 audio files in one
  call** (ops 5c53049a). Each item sets any of `file_type`, `stem_label`,
  `version_label`, `title` for one file id; the result is one lean row per
  item (`updated`, or `failed` with a reason) and never the file records. A bad
  item does not stop the others. `dry_run: true` previews. Only the caller's
  organisation's files are touched.
- **`pica_share_links_create` takes `selection_id`** — share every work a saved
  selection matches as one playlist link (a snapshot; the link does not follow
  later changes to the selection). Refused when the selection matches no works
  or more than 200 (ops 42be8f96).
- **`pica_share_links_create` takes `allow_view_lyrics`**, and its description
  now tells agents that lyrics, credits (`allow_view_collaborators`) and splits
  (`allow_view_financials`) are hidden unless turned on.
- **`pica_audio_query` takes `work_ids`** (up to 100) — the audio for several
  works in one paged call, including files attached through a recording of
  those works (ops 96672119).

### Changed

- **Playlist share links now record `include_all_files: true` by default**
  (unless `included_file_ids` names a subset), matching what the playlist page
  already shows — every version of each song, for the recipient to choose.
- **`pica_audio_update` and `pica_audio_assign` return the lean list row**
  (the shape a `pica_audio_query` row has) instead of the full stored record
  with its waveform and fingerprint data — about 8k tokens a file (ops
  5c53049a).
- **`pica_works_update` answers with a compact receipt** (ops 61ce1dcc): the
  work id, `changed` (each field sent, with the value now stored),
  `unconfirmed` for fields the stored record did not echo, and an `integrity`
  block listing every open gap by label and code but hinting only the gaps
  this write opened. It no longer returns the full work (lyrics included) or
  repeats every open gap as "next steps" on each call. Pass `verbose: true` for
  the previous answer.
- **`pica_credits_update` answers in the same shape**: `work_id`,
  `changed.credits` (person, role, share as written) and `saved` (the save
  count and any skipped rows). `verbose: true` returns the raw save result.

### Deprecated

- **`pica_royalties_summary` retired in favour of `pica_royalties_blockers`**
  (founder decision, 2026-09-25). It ran its own gap judgement — PRS, PPL and
  MCPS hard-coded, a missing row read as "not registered with PRS", only the
  first 1,000 works. It stays callable as a silent alias until 2.98.0: calls
  now return `pica_royalties_blockers`' answer (`work_id` and `limit` carry
  over; `gap_type` and `priority` have no equivalent). No longer listed in
  discovery. Unmatched payments remain `pica_royalties_unmatched`. Traffic: 2
  calls from 2 organisations in the 90 days before retirement.

### Fixed

- The "how much has this song earned?" prompt told agents to call
  `pica_royalties_summary` for cumulative earnings; that tool never returned
  earnings. It now names `pica_royalties_get`.
- **`pica_works_query` with `health_filter` honours `limit` and `offset`** and
  stops reporting the page length as `total` (ops 314325eb).
  `low_completeness` reports the number of works below the threshold by their
  stored score (max 200 per page). `needs_attention` now returns only works with reconciliation problems
  (it was returning reconciled works too), checks at most 50 works per call,
  pages by works checked (`next_offset`), and reports `total` only when one
  call from offset 0 checked every candidate (otherwise `null`).
- **An unknown `sections` name is refused instead of answering `{}`** (ops
  dfa810f8). `pica_works_inspect { sections: ["work"] }` returned a
  successful empty result; every tool with a fixed list of sections now
  answers `VALIDATION_ERROR` naming the unknown entries and the valid ones.
- **`pica_people_create` / `pica_people_update` accept `ipi`** as the same
  field as `cae_ipi_number` — the name the other tools use. Sending both with
  different numbers is refused.
- **`work_type` is one list everywhere** — song, instrumental, library, demo,
  sample, matching the database. `pica_works_update` refused `instrumental`
  while create and bulk update accepted it.

## [2.97.5] - 2026-09-18

### Added

- **`pica_export_song_dossiers` — everything PICA holds about a chosen set of
  songs, as one ZIP.** Pass 1 to 60 `work_ids`; the ZIP holds each work's song
  dossier as a PDF and as JSON (the work, its recordings with credits and
  ISRCs, the releases those recordings appear on, people and identifiers,
  provenance, AI disclosure, consent), a manifest with a SHA-256 hash of every
  file, and a README. Returns the signed-URL envelope the other exports return,
  plus `works[]` saying per work whether it was included: a work that is not in
  the catalogue comes back `included: false` rather than vanishing, and a list
  past 60 or a malformed id is refused rather than truncated. The
  credential needs `write:files` (the floor on every export write) and
  `write:catalog` (the route's own requirement). It downloads a file for the catalogue owner and sends nothing
  to anyone.

### Changed

- **`pica_works_inspect` no longer says the dossier PDF is only on the web
  UI**, in its `sections` description and in the dossier summary line — both
  now name `pica_export_song_dossiers`.
- **`pica_export_snapshot` stops claiming "export everything about this
  work"** as one of its phrases — the snapshot is a ranked what-to-fix view
  and carries no recordings or releases; that phrase now routes to
  `pica_export_song_dossiers`, which the snapshot's description names.

## [2.97.4] - 2026-09-17

### Fixed

- **Both CAR tools now declare the scope the platform actually requires.**
  `pica_export_catalog_asset_report` asked for `write:catalog` and
  `pica_car_preview` for `read:catalog`, but every write under
  `/api/admin/exports/*` is floored at `write:files` in proxy.ts, so a
  credential holding exactly what the tools advertised was refused 403
  before the route ran (measured against the develop preview, 2026-09-17).
  `pica_car_preview` is also `tier: "write"` now: it creates an S3 object and
  a `pica_exports` row, and was advertised read-only.
  Credentials that need a catalog asset report must carry `write:files`.

## [2.97.3] - 2026-09-17

### Fixed

- **`pica_export_catalog_asset_report` no longer signs the declaration on the
  user's behalf.** It sent `signer_name: "MCP Agent"` with the declaration
  pre-accepted, a statement to lenders and insurers that no person had read.
  The report it returns is now unsigned, and says so; only a person signed in
  to PICA can attest it, from the export panel. The same release makes the
  route build reports for agent callers at all: it used to 401 every one.
  Requires mcp-sdk 3.18.0.

### Removed

- **The `sections.audio` input** on `pica_export_catalog_asset_report` and
  `pica_car_preview`. It was described as bundling audio master files or an
  audio analysis section, and the report never read it.

### Changed

- **`pica_car_preview` no longer asks agents for a verbal attestation.** Its
  description called the `lending` format legally weighted and told agents to
  take a spoken attestation before calling, while the tool beneath it signed
  the declaration as "MCP Agent". The report is unsigned whatever the format,
  and the card's summary now says so.

## [2.97.2] - 2026-09-13

### Fixed

- **`pica_credit_update` now actually returns `allocation.status`.** The
  description promised it and the route sent it, but `@withpica/mcp-sdk`
  < 3.17.0 unwrapped it away, so the response never carried it and the
  "writer shares now total N%" hints could never fire. Requires mcp-sdk 3.17.0.

### Added

- **`pica_credit_update` — edit ONE credit in place.** The verb between
  `pica_credit_add` and `pica_credit_remove`: change a writer's share, an
  instrumentalist's instrument, a role, the displayed name or notes, leaving
  every other credit untouched and keeping the row's id and attestation state.
  `pica_credits_update` remained replace-all, so one split change meant
  re-listing every work and re-sending the whole array (ops issue bff0af25);
  nothing edited a recording credit by its id — beyond the natural-key
  upsert, a change meant remove-then-re-add (17f27449). Agents had already been calling this exact
  name — it was the `TOOL_NOT_FOUND` miss in 167bb804, six times. Writer-split
  closure is deliberately NOT enforced on a single edit; the response's
  `allocation.status` (closed / open / incomplete) reports whether the set
  still adds up instead. Backed by a new `PATCH
/admin/works/{id}/credits/{creditId}`, scoped to the work in the path.
- **`pica_recording_credits_update` batch mode accepts `instrument` per
  credit** (`credits[].instrument`), applied on every target recording. Until
  now crediting an instrumentalist across N recordings took N single calls.
- **`pica_import_streaming_link` returns `STREAMING_LINK_NOT_FOUND`** for a
  link the provider cannot resolve — user input, not a PICA fault, and no
  longer opens an ops issue. `STREAMING_IMPORT_FAILED` is now reserved for
  genuine failures (timeouts, upstream errors). ⚠️ A client matching on the
  old code for the not-found case will need the new one.
- **`pica_find_duplicates` accepts `entity_type: "audio_files"`** — the same
  file uploaded twice, grouped on exact byte size + normalised filename
  (`audio_files` carries no content hash). Each item reports `storage_bucket`;
  a `withpica-*` bucket is storage PICA pays for. ⚠️ **Findable, not
  mergeable:** `pica_merge_duplicates` refuses `audio_files` by design — the
  outcome is a person deleting a redundant upload, and there is no merge
  semantics for a file. The two entity lists are now deliberately different
  sizes.
- **`pica_publishers_query` works at all.** `GET /admin/publishers` did not
  exist, so every call since the tool shipped drew a bare 405 and surfaced as
  `UNKNOWN_ERROR`. Searches by name (substring, on name and legal_name) or by
  exact IPI.
- **`pica_approval_status` called with no `approval_id`** now explains what the
  dashboard's "N awaiting approval" count is and who can act on it, instead of
  answering "approval_id is required". There is deliberately no tool that lists
  approvals — an agent grant cannot enumerate the approvals it is itself
  waiting on.

### Changed

- ⚠️ **Wire change: `pica_execute` now FORWARDS `pica_discover` and
  `pica_tool_details`** instead of refusing them. A client matching on the old
  literal `"meta-tools (pica_discover, pica_tool_details, pica_execute) cannot
be invoked via pica_execute"` will stop seeing that refusal. Only
  `pica_execute` invoking ITSELF is still refused (that is the only case that
  can recurse); its message changed accordingly. A connector that surfaces only
  the wrapper teaches its agent to wrap everything, so refusing the two
  read-only meta-tools blocked discovery for exactly the clients that need it.
- ⚠️ **`pica_audio_query`'s `query` filter now applies to ALL results.** It was
  honoured only when `unassigned: true` was also set; a plain
  `pica_audio_query {query: "…"}` returned the whole library in upload order
  and truncated at the 100KB response cap. The parameter description said
  "with unassigned:true" and no longer does.
- **`pica_audio_query` rows are a lean LIST shape.** The route flattens the
  analysis satellite onto every row — `waveform_data` alone is ~4.9KB per file
  on prod, three times the rest of the row — so the 100KB response guard was
  trimming a page to about 8 files. List rows now carry identity, size,
  duration, bpm/key/energy/mood, links, bucket and `probable_match`;
  waveform, full analysis, `metadata` and the recording/work embeds are on
  `pica_audio_inspect`. ⚠️ A client reading those fields off the list will
  need the inspect call.
- **`pica_recording_splits_verify` records WHICH PERSON attested.** It now
  resolves the signed-in user to their person record and stamps that as
  `verified_by` (the column has always referenced `people`). ⚠️ Wire change:
  an API-key / agent-grant call is refused with
  `VERIFICATION_REQUIRES_HUMAN_SESSION` (message text changed), and a
  signed-in user with no person record in the org gets
  `ATTESTING_PERSON_NOT_FOUND`. Before this the endpoint had never succeeded
  for anyone.
- **`pica_credits_update` accepts a signed sheet within ±0.01.** Its own
  pre-flight used `!== 100`, so 33.34/33.34/33.33 (=100.01) and
  33.33×3 (=99.99) were refused before the API was called; the message text
  changed.
- **`pica_share_links_create` returns `share_url`** beside `token`, so the
  agent can hand the user a working link rather than sending them to the
  dashboard to find it. `pica_share_links_query` returns it too. `token` is
  unchanged.
- **`pica_recording_credits_update` no longer reports `PERSON_NOT_FOUND` when
  the RECORDING is the missing one.** It dispatches on the route's own error
  code rather than matching "not found" anywhere in the message, and a foreign
  or non-existent `person_id` is now validated org-scoped before the write
  (it previously reached the FK unchecked, which also accepted another
  organisation's person).
- **`pica_tool_details` offers near-miss suggestions** (`did_you_mean`) for an
  unknown tool name, in `structuredContent` as well as text — the same shape
  `pica_execute`'s unknown-tool branch has returned since 2.96.
- **`pica_catalog_health_fix` accepts `actionIds` / `action_ids`** as aliases
  for `accept` on the `pica_execute` path, and `INVALID_PLAN_TOKEN` now names
  the remedy rather than only the reason.

## [2.97.1] - 2026-09-08

### Added

- **`pica_catalog_health_fix` accepts `selections: [{actionId, chosenPersonId}]`**
  — the human choice a `resolve_person` action needs (one of its
  `preview.candidates[].personId`). A selection accepts its action; a choice
  the plan never offered is skipped `guard_blocked` with the reason. Until now
  the applier required a chosen person the tool had no way to pass, so every
  accepted `resolve_person` came back `stale` (feedback 2026-09-05).
- **`pica_find_duplicates` sees identifier-less copies.** Recordings group on
  title + artist and releases on title + equal track count when the ISRC/UPC
  pass cannot see them (`match_kind: "title_artist"`, keep-separate default);
  a UPC shared by releases with different titles is reported as
  `shared_upc_different_title` — the decision is "correct the UPC", with no
  fold option (feedback 2026-09-05).

### Fixed

- **`pica_works_update {collaborators}` is refused with a redirect** to
  `pica_credits_update` / `pica_credit_add`. `works.collaborators` is a
  legacy jsonb mirror; the write showed correct splits on inspect while the
  `work_collaborators` rows every health check reads still disagreed, and the
  health plan then proposed "fixing" them (feedback 2026-09-05).
- **Accepting a held finding is a typed 409, not `INTERNAL_ERROR`.** The
  apply route now maps every "is already <status>" refusal to
  `PROPOSAL_ALREADY_CLOSED`, and a held one carries the unlock sentence
  (`pica_billing_pay`, offer `unlock`, `entity_id` the work) — a ChatGPT user
  hit "internal server error" three times on 2026-09-04.
- **`pica_credit_add` never invents a share.** An omitted `share_pct` on a
  composition credit is stored as NULL (unknown), not 0 — a 0 read as a real
  share, so the health plan rescaled the other writer to 100 and the second
  writer's stake vanished (holdout lock, 2026-09-06). For `credit_type:
"owner"` the share is now REQUIRED (`OWNER_SHARE_REQUIRED`, 422): master
  ownership is recorded as given, never assumed to be 100.
- **`pica_releases_update` documents the UPC backfill trigger** — it fires
  only on a write to `spotify_album_id`, so re-sending the existing id is the
  way to backfill a UPC (ops issue 9f33cc59).

- **`pica_subscription_status` is callable on the hosted connector.** It was
  declared `scope: admin`, `audiences: ["stdio"]` while `pica_billing_pay` was
  `read:catalog` on both surfaces, so a connected agent could mint a pay link
  but not read the plan it would be paying for (`callable_on_this_surface:
false` on claude.ai, 2026-09-04). Both now share the catalogue-read floor;
  `pica_subscription_manage` stays stdio + admin.
- **The pay path is visible to an agent that never calls `pica_discover`.**
  The server instructions name `pica_subscription_status` and
  `pica_billing_pay` with the exact `pica_execute` shape inside the first
  2,000 characters (the observed cut on the claude.ai connector), and the
  `pica_discover` description lists "how do i pay pica" among its example
  queries for clients that surface tool descriptions but not instructions.

PR #2118 (`fix/avatar-import-path-2026-09-03`) is merged into the release branch, so its changes ship in the version below; publish in the order mcp-utils → mcp-sdk → mcp-server.

## [2.97.0] - 2026-09-04

⚠️ Publish order: `@withpica/mcp-sdk` 3.14.0 and `@withpica/mcp-utils` 1.29.0
must be on npm before this release — the dependency floors are now
`^3.14.0` / `^1.29.0` (this release imports `pageEnvelope`, which only
exists from mcp-utils 1.29.0; a clean install against 1.28.0 breaks at
runtime — the 2.96.2 uninstallable incident).

### Added

- `pica_multimedia_import_url` accepts `person_id` (sets the person's profile image) and its summary names the outcome — `Imported from URL (via)`, profile-image set/NOT set, work link (PR #2118, merged into this release). Page URLs are followed to their `og:image`.

- **Per-tool argument aliasing for `pica_execute`** (`arg-aliases.ts`, D1
  2026-09-03) — `applyArgAliases` renames a tool's OWN entity id (or an
  explicit `TOOL_ARG_ALIASES` entry, e.g. `pica_skill_get`'s `skill` /
  `skill_name`) to the schema's real required field name, structurally by
  domain (`DOMAIN_ID_ALIASES`) — never a foreign entity's id, never
  overwriting a value already present, and left untouched when more than
  one candidate is present. Fixes the wrong diagnosis `pica_execute` gave in
  all 36 logged cases since July, where an agent sent e.g. `audio_file_id`
  to `pica_audio_update` and was told the client had dropped the field in
  transit.
- **Shared `WRAPPED_ARGS_MISSING` envelope builder**
  (`wrapped-args-envelope.ts`) — both guards that can reject a
  `pica_execute` call before dispatch (the plain `required`-list guard, and
  the `requiredOneOf` one-of-N guard covering `*_inspect` / `pica_search` /
  bookings / licensing / shows / royalties tools) now build their message
  and `structuredContent` through one function, naming
  `unrecognised_fields` and `aliases_applied` in **both** the text block and
  `structuredContent` instead of asserting "client-side transport loss" for
  a non-empty args object. Empty args still name transport loss — there's
  nothing else an empty resolved-args object could be.
- **`offset` on `pica_audio_query`, `pica_notes_query`, `pica_sessions_query`**
  (Task 10), each returning a `page` envelope built with
  `@withpica/mcp-utils`'s `pageEnvelope()`. Audio and sessions fall back to
  "page was full" (no total available); notes uses the route's real
  `COUNT` for an exact `has_more`. A new `query-offset-parity` registry
  guard test pins which remaining `_query` tools still lack `offset` so
  each can be fixed and measured in isolation.

### Changed

- **Duplicate-release recovery hints point at a fix, not a dead end.**
  `RELEASE_ALREADY_EXISTS` on `pica_releases_create` /
  `pica_releases_create_with_tracks` now names `pica_releases_update` /
  `pica_releases_attach_track` (the message already carries the existing
  release's id) instead of `pica_releases_query`; `pica_releases_attach_track`'s
  own `RELEASE_ALREADY_EXISTS` (a different release already holds the UPC)
  points at `pica_releases_inspect`.
- **Deprecated alias removal deferred to 2.98.0** — see `metadata.ts`.

## [2.96.2] - 2026-09-02

The MPP pay rail (spec 2026-08-24 WS-B): a new tool that mints a pay link,
and `pica_subscription_status` learns to say what pica costs and what it is
holding. Depends on `@withpica/mcp-sdk` 3.13.0 (`BillingResource`) — publish
order matters, see `docs/releases/2026-08-26-mcp-release.md`. (This entry
was authored as 2.96.0, which npm deprecated after 2.96.1 shipped from
develop without `pica_billing_pay`; the branch re-releases on top of 2.96.1
as 2.96.2, and also carries the stdio relay change below.)

### Added

- **`pica_billing_pay`** — new tool, `scope: read:catalog`, `tier: read`
  (minting a link mints a capability, not a charge — nothing is billed until
  the link is paid). Wraps `POST /api/admin/billing/pay-link` via
  `PicaClient.billing.mintPayLink()`. Takes `offer` (`resident_month` | `unlock`
  | `settle`, required) and `entity_id` (required for `unlock`). Route
  refusals — billing disabled, nothing held on that work, offer no longer
  applies — come back as structured `isError` results with a `next_tool`
  pointer, not thrown errors. Added to the `subscription` tool category
  (renamed description: "what does pica cost, how do I pay, and what is pica
  holding — read your billing state and get a pay link"), the atlas
  (`"how do i pay pica?"` and its synonyms), and `RECOVERY_HINTS`.
- **`pica_subscription_status` carries `pricing` and `held` when the route
  measured them.** `SubscriptionStatusFlat` gains optional `pricing` and
  `held` fields (omitted, not `null`, when absent — a `null` price would read
  as "free"). The composed `summary` sentence appends one further clause
  naming the free-runway/resident-included song counts and, when
  `held.count > 0`, how many findings across how many works are held pending
  payment.
- **`pica_dashboard_briefing`'s `found` block description gains a `billing`
  clause**: when `billing.tab` is a string, state the tab total once and say
  that settling it clears what pica is holding; when `billing.pay` names a
  tool, offer to call it — mint a pay link, never charge unprompted.

### Changed

- **stdio relay forwards a narrowed result projection** — after a successful
  tool call the audit entry now carries `result_projection` (built by
  `narrowToolResultForRefs` from `@withpica/mcp-utils` ≥1.28.0), so the
  relay ingest can attribute import wins: `pica_import_streaming_link` /
  `pica_import_youtube_link` created-work ids live in the RESULT and their
  params carry no id, which is why stdio imports recorded `entity_refs:
null` and were invisible to the weekly roundup's wins beat. Only
  allow-listed uuid arrays travel; every other tool sends
  `result_projection: null` — nothing else on the wire changes. (The same
  entry field also rides `makeHttpAuditFn`'s POST to `/api/mcp/audit`,
  which destructures a fixed field list and ignores it.)
  ⚠️ Publish order: `@withpica/mcp-utils` 1.28.0 must be on npm before this
  release (2.96.2) — the dependency floor is `^1.28.0`.

## [2.96.1] - 2026-08-31

First npm release since 2.95.4 — carries everything below (the discover
changes have been live on the hosted connector since promote #2061; stdio
clients get them here). 2.96.0 was published from an unmerged branch and is
deprecated; do not use it.

### Fixed

- **`pica_import_validate` surfaces warnings on a VALID result.** A warning
  does not make a row invalid, so `valid: true` can carry warnings — e.g. a
  works CSV mapped to `release_year` (release-level data; a newly created
  work has no release) is receipted at row 0. The tool used to answer "All N
  rows are valid. Ready to import" and drop the warning, so the agent
  executed blind (2026-08-30 production finding, PR #2072 / promote #2074).
  Warnings now ride the valid branch in the text and as `warnings` in the
  structured data. The SDK's `ImportValidation.errors` rows carry the wire
  field `error` (with `message` kept for normalising callers).

### Changed

- **The ten tools slated for removal in 2.96.0 remain, `removal_target`
  extended to 2.97.0** (`pica_recording_credits_bulk_update`,
  `pica_statements_list`, `pica_documents_list`, `pica_documents_read`,
  `pica_projects_list`, `pica_releases_list`, `pica_releases_get`,
  `pica_releases_bulk_attach_tracks`,
  `pica_releases_attach_track_by_identifier`, `pica_notes_list`). This
  release exists to ship the validate-warnings fix; removing tools is its
  own change with its own review, not a rider on it.

- **`pica_discover({ query })` no longer scores the asker's own words as the entity.** Determiners, pronouns, prepositions and light verbs (`my`, `the`, `get`, `list`, `find` …) are dropped before ranking. "start my music catalogue and import my songs" led with three `pica_my_*` tools because "my" matched their domain segment; it now leads with the import tools. A query made only of such words matches nothing.
- **Query mode carries the fresh-org import line.** The ADR-294 hint ("on a fresh or empty org, run `pica_dashboard_briefing` first … paste a spotify album or playlist link") was only on the no-args category map; it is now on every query response, hit or miss.
- **Tools the caller cannot run on this connection rank last and are flagged.** Shortlist rows and `pica_tool_details` gain `callable_on_this_surface: false` plus a `surface_note` when the tool is on another surface (stdio / lobby) or needs a scope the connection does not hold — the judgement the HTTP dispatcher enforces at `pica_execute` time (installed only while the inner gate is in enforce mode). An existing scope or destructive-tier `callable_note` is kept beside it, and the `invoke` line on a flagged detail says the call will be refused instead of "pica_execute executes every tool returned here". Rows for callable tools are unchanged (no new keys). A capped shortlist keeps at least five tail rows for flagged tools and lets them fill what visible rows leave, so a gated capability reads as gated, not missing, and a narrow scope never shrinks the list. Query-mode `cache_until` is now one hour (the rows are per-connection); the full map keeps its day.

## [2.95.4] - 2026-08-25

An agent-readability release. Nothing about auth, tiers or tool behaviour changes; what changes is what a tool **tells** an agent about itself, and the shape of a refusal.

### Added

- **`pica_dashboard_briefing` reports what pica is holding.** The `found` block carries `held: { count, entities }` — findings pica produced but is HOLDING because the org is past its free runway or over its allowance (WS-A of the MPP spec, #2027; dormant unless billing enforcement is on).
- **Every tool now names the sibling it is most confused with.** 141 descriptions across every 3-or-more-member family gained a clause saying which same-family tool to prefer and when — derived from that sibling's own description and schema, not invented. Families with verb-first names (`pica_get_*`, `pica_update_*`) are deliberately exempt: those segments group unrelated tools by verb, so "name a sibling in your family" has no meaning for them. Where a description had to grow, over-long ones were paid for by dropping text the tool's own `inputSchema` or `nextSteps` already carried.
- **`did_you_mean` on a missed tool name.** An unknown inner tool via `pica_execute` now comes back with ranked candidates plus `next_tool: pica_discover`. Candidates never include a deprecated alias, and on the HTTP surface they are drawn only from tools the caller can actually see.

### Changed

- **⚠️ Wire change: an unknown tool now returns `TOOL_NOT_FOUND`, not `UNKNOWN_TOOL`.** The code read `UNKNOWN_TOOL` from 2026-07-03 until now. A client matching on the old literal will stop recognising this refusal — match on `isError` instead, which is set on every refusal. The `suggestions` key is kept alongside the new `did_you_mean` so existing readers keep working.
- **The HTTP dispatcher's bare `Not found` is now the same typed envelope.** An unregistered inner tool name was refused at the gate-shadow step before the registry's typed envelope could run. A registered-but-hidden name keeps its previous refusal, so the change leaks no information about tools the caller may not see.
- **Annotations are now derived for every tool on both surfaces.** `pica_tool_details` returned `annotations: null` for 275 of 332 tools because it passed the raw declaration through while `tools/list` derived hints from tier. Both now go through one derivation. Two consequences a client may notice: `openWorldHint` follows `createsExternalSideEffects`, so **24 tools flip `false` → `true` on the flat surface** (they already published that side-effect flag by another name — nothing about their behaviour changed); and a **declared** `idempotentHint` now wins over the derived value, which flips **4 tools**. Treat both as corrections to hints that were previously wrong or absent, not as new capability.
- **`pica_skill_get` puts the skill body in `structuredContent`.** The methodology travelled only in the text block, so clients that prefer `structuredContent` (claude.ai among them) received frontmatter and nothing to follow.
- **No live surface recommends a deprecated alias any more.** Server instructions, 7 tool descriptions, 17 recovery-hint `next_tool` values, 2 skills and 2 resources pointed agents at alias names. All now name the canonical tool. The alias names still resolve — the only remaining mentions are the aliases' own registration rows.
- **Refusals are typed envelopes with a `SCREAMING_SNAKE` code.** The notifications and telegram tools returned _success_ envelopes carrying an `error` key with no `isError`, so an agent read a failure as a success; credits and import returned bare prose. All four now share one error envelope.
- **The agent guide computes its counts at serve time** from the caller-visible tool set, instead of reciting numbers written down by hand and since drifted. It also drops a `mode:` parameter that never existed on the schema and a `prs_work_id` removed under ADR-282.
- **`pica_discover` ranking singularises tokens properly.** The old `-es` rule turned `notes` into `not` (a false hit on every query containing "not") and `releases` into `releas` (never matching "release"). Domain segments are now weighted, so a query naming a domain reaches that domain's tools first.
- Server instructions state what the registry actually enforces for `sections` / `limit` (3,110 / 3,200) instead of a softer claim.
- **`pica_import_execute` now imports one slice per call and tells you where to continue.** A call runs synchronously under the route's ~2 minute budget, so the server imports at most `row_limit` rows (default 2,000, max 10,000) from `row_offset` (default 0) and returns `rowRange.nextOffset` — call again with `row_offset: nextOffset` until it is null. The old "max 10,000 rows per import" claim was not honourable inside the budget: a call the budget ended committed its batches and reported nothing. For files over ~2,000 rows, upload once with `pica_upload_file` → `pica_upload_complete` and pass `file_id` so the CSV is not re-sent per call. (ADR-314)

- **`pica_import_execute` no longer loses a whole batch to one duplicate, and no longer reports a partial commit as a failure.** `conflict_strategy` now defaults to `"skip"` (a row that trips a unique constraint becomes a warning; every other row in its batch still lands) instead of `"error"` (one 23505 discarded the batch). A live run that wrote rows and also carries errors is answered `Import PARTIALLY complete: N created …` with `partial: true` in the payload and an explicit "the created rows are saved, do not re-send the whole file" instruction, because the backing route now returns 200 for a partial commit (400 is reserved for runs that wrote nothing). Observed 2026-08-17: a 2,239-row catalogue import lost a 500-row batch to a single repeated title, was told it had failed while 1,742 rows were already saved, and re-sent the file three times. Pass `conflict_strategy: "error"` explicitly to keep all-or-nothing batches.
- **`pica_upload_file` / `pica_upload_complete` work on a `write:files` credential again.** The platform's bearer write-scope floor had `/api/admin/storage/**` (the two routes these tools call) floored to `admin`, so every connector — none holds `admin` — was refused "insufficient scope: requires admin". Fixed on the platform side (no package change required); noted here because the tools' declared `write:files` scope was correct all along.
- **`pica_notifications_recent` / the directory server count.** The `.well-known` directory listing and `pica_discover` count 11 directory tools (was 9 by drift), and the one destructive directory tool no longer wears a write tier — pinned by a parity tripwire (#2024).

### Fixed

- **The daily briefing counted one work three times.** `entityIds` were not deduplicated, so a single work touched by three events reported as "3 works". Counts are deduplicated and a `canonical_total` now travels with them.
- Internal vocabulary left agent-facing copy in bulk: table names, CHECK-constraint names, HTTP status codes, `jsonb`, repository paths, "1 credit each" and internal constant names were removed from descriptions and required-schema copy. An agent handed a repository path cannot open it, and repeats it to the user as if it were vocabulary they should already know.

> Patch, not minor, on purpose: `2.96.0` stays reserved for the WS5 deprecated-alias removals, which `src/tools/deprecations.ts` enforces as its removal target. No alias is removed in this release.

## [2.95.3] - 2026-08-18

### Fixed

- **`tools/list` no longer publishes this package's own authoring metadata.** `ToolDefinition` is two types wearing one name — the MCP `Tool` a client consumes, and the compile-time declarations our lint reads — and `listTools()` spread the whole object, so every field of the second kind crossed the wire. `vernacular_kind`, `vernacular_reason` and `annotationOverrides` are now stripped at the wire boundary. **Not a breaking change:** none of the three is part of the MCP `Tool` schema, no documented behaviour read them, and everything a client acts on (`name`, `description`, `inputSchema`, `annotations`, `tier`, `scope`, `audiences`, `workflows`) is untouched. The registry's own view is untouched too, so tooling that reads definitions in-process still sees them.
- **The handshake and the skill bodies no longer cite internal decision records.** `vernacular_reason` on ~30 tools read "Specialist context; not yet covered by the Creator Question Atlas — pending telemetry-driven addition per ADR-226 Phase 4 substrate writes"; five `SKILL.md` bodies, two prompt bodies and the server instructions carried similar citations. An ADR number is a coordinate into a repository the reader cannot open, and an agent handed one repeats it to a user as if it were vocabulary they should already know — which is exactly what happened in a real connector session on 2026-08-18.
- **The handshake's URI-shape pointer now names something reachable.** It read `docs/mcp-source-uri-conventions.md` — a path in the app repository, handed to agents that have no checkout. It now names `pica_skill_get("enrich-from-peer-mcps")`, the skill that actually carries the scheme table, which an agent can follow in one call.

> Patch, not minor, on purpose: `2.96.0` stays reserved for the WS5 deprecated-alias removals, which `src/tools/deprecations.ts` enforces as its removal target. Bumping the minor here would bring that deadline forward by accident.

> Scope note for anyone tracing this: the stripped-metadata leak affected the **stdio** surface — this package's flat `tools/list`. The app's HTTP transport serves layered discovery, where `tools/list` returns only the meta tools and `pica_discover` / `pica_tool_details` build responses by picking fields explicitly, so a connector was never reading it. The handshake and skill-body fixes reach both surfaces.

## [2.95.2] - 2026-07-28

### Added

- **`pica_consent_readiness` (ADR-300)** — the read side of ADR-292's consent ledger. Returns an org-scoped readiness verdict: a `rungs` map giving cleared / partial / blocked / unset asset counts at each consent level, a `blockers` list naming credited people with no decision recorded or an explicit refusal (refusals first, then by catalogue reach), and `blockedAssets`. `truncated` reports honestly when a list was capped. An asset is cleared at a level only when EVERY credited contributor has granted at least that level; one live revocation blocks it at every level. Read tier, free, all three audiences. Deliberately read-only — consent WRITES stay human-session only, because a consent decision has to be made by the person whose rights it binds, not by an agent acting for them.

### Changed

- **`pica_export_ai_consent` description corrected.** It claimed to show "which works are human-made, AI-assisted, or AI-generated" — that is `works.ai_disclosure_level` (ADR-086), a different field the AI-consent service has never read. Agents were being pointed at the wrong feature. The description now says what the tool does (produces the consent FILE) and routes intent for the ANSWER to `pica_consent_readiness`.
- The consent file's per-asset `use_cases_allowed` now reflects the consent LEVEL, and `assets.csv` gains a `cleared_level` column. This narrows what the export claims: a work whose contributors granted only `tracking_only`, or where any contributor has not answered, no longer reports training/generation/commercial as permitted. See the app repo's FIX_LOG 2026-07-27 entry — a previously downloaded ZIP will disagree with the next one, and the next one is right.

> Patch, not minor, on purpose: `2.96.0` stays reserved for the WS5 deprecated-alias removals.

## [2.95.1] - 2026-07-27

### Added

- `pica_works_bulk_move_to_performances` now carries the `http` audience, matching its sibling `pica_works_bulk_delete` — the tool becomes reachable on the hosted connector surface at the next release. No auth change: the backing route already gated on `write:catalog` + `destructive:*`, identical to bulk-delete, and `risk: "destructive"` keeps it off unauthenticated public discovery. (Lands the branch parked 2026-07-13 to fold the connector-surface change into a batch; the WS5 batch shipped in 2.95.0, expiring the parking condition.)

### Fixed

- **Bulk reads over the 100KB cap now return complete records plus a real
  resume cursor, never a mid-record cut** (ops 41ff656c / 893e3e29). The
  dispatcher routes every result through `guardToolResult`
  (`@withpica/mcp-utils@1.27.0`): oversized LIST responses are trimmed to
  whole items and stamped with `truncated`, `items_dropped`, `next_offset`
  (offset-paging tools) and `recovery_hint` — in both the text view (still
  valid JSON) and `structuredContent`, which previously escaped the cap
  entirely. Non-list shapes keep the legacy text truncation.

## [2.95.0] - 2026-07-24

### Added

- **`pica_audio_bulk_assign_suggested`** — attach ALL orphan audio whose
  suggested match is high-confidence (ISRC-verified, non-ambiguous) in one
  call, instead of looping `pica_audio_assign` per file (agent feedback
  20f592ff: an agent hand-looped over a 74-file queue). Pairings are
  recomputed server-side at execution time — stale suggestions can't apply,
  dismissed pairs stay excluded, title-only matches are never bulk-attached.
  Supports `dry_run: true` preview (same server-side classification, nothing
  persisted — ADR-180 Rule 12).
- **`pica_audio_dismiss_match`** — reject a suggested audio → recording
  pairing so it never resurfaces in suggestions, the review queue, or the
  bulk attach. The file stays attachable elsewhere; repeat dismissals are
  no-op successes.

### Changed

- **`pica_recordings_query`** now surfaces the server's zero-result
  `recovery_hint` on empty searches (shipped in sdk 3.8.0 — the hint was
  built server-side but dropped on the wire; ops_issue 689a8e9a).
- **`pica_audio_assign`** names a mis-slotted id on failure: when the value
  passed as `work_id` is actually a recording id (or vice versa), the error
  says so and names the right parameter — instead of an opaque FK failure.

## [2.94.0] - 2026-07-16

### Added

- **`register-from-your-daw` MCP skill (ADR-293 agent lane).** Guides an agent
  through capturing the open DAW session via the user's own DAW MCP server
  (AbletonMCP et al.) and registering the work in PICA — session-read
  patterns, the Log.txt/.als fallback when the bridge is down, title-suffix
  hygiene, and read-merge-write for `works.metadata`. Field-proven on two
  real registrations the day it shipped. Served as a `skill://` resource on
  BOTH transports; four older skills' resources were backfilled onto the
  hosted transport in the same change.

### Fixed

- **The whole `pica_users_*` tool family works again** (get_my_profile,
  find_by_handle, set_handle, set_bio, set_avatar_url,
  import_bio_from_person) — every call crashed with `UNKNOWN_ERROR: Cannot
read properties of undefined` due to an envelope double-unwrap inside
  `@withpica/mcp-sdk`'s UsersResource. Requires `@withpica/mcp-sdk@^3.7.2`
  (dependency bumped). Same SDK release also revives `prefix_intel` on
  works/recordings inspect and the `releases` section of
  `pica_works_inspect`. No input-schema changes — no OpenAI connector
  resubmission.
- **Audio-files list envelope normalised** so `probable_match` on unassigned
  audio surfaces cleanly through `pica_audio_query`.

## [2.93.1] - 2026-07-14

### Fixed

- **`pica_works_bulk_link_artist` dry-run no longer returns a false preview.**
  The preview now routes through the same endpoint (with `dryRun=true`) as the
  commit, so it shares the commit's person-resolution and validation — a person
  the commit cannot find is refused at preview time instead of reporting an
  optimistic "would link N". Requires `@withpica/mcp-sdk@^3.7.1`. The tool's
  input schema is unchanged, so this does not trigger an OpenAI connector
  resubmission.

## [2.93.0] - 2026-07-12

### Removed

- **WS5 removal sweep — 6 zero-traffic deprecated aliases DELETED** after a
  30-day prod-telemetry gate (`agent_events`): `pica_projects_get`,
  `pica_notes_get`, `pica_sessions_list`, `pica_sessions_get`,
  `pica_statements_get`, `pica_share_links_list`. Each had 0 calls in the last
  30 days in production. Their standalone `ToolDefinition`, `TOOL_METADATA`,
  cost-model, discovery, and required-schemas rows were all removed; the shared
  executors (getProject, getNote, listSessions, getSession, getStatement,
  listShareLinks) stay because they back the canonical `_query` / `_inspect`
  tools. The 10 deprecated aliases that still carry live traffic
  (`pica_releases_list/_get`, `pica_releases_attach_track_by_identifier`,
  `pica_releases_bulk_attach_tracks`, `pica_projects_list`, `pica_notes_list`,
  `pica_documents_list/_read`, `pica_statements_list`,
  `pica_recording_credits_bulk_update`) STAY callable — their `removal_target`
  is consciously extended from 2.93.0 to 2.95.0 so the "expire loudly" lint
  does not block this publish. Re-run the traffic gate before 2.95.0.

### Changed

- **Royalty / statement read tools re-scoped to finance visibility.** The
  `pica_royalties_*` read tools (`_list`, `_get`, `_summary`, `_unmatched`) and
  the `pica_statements_query` / `_inspect` / `_list` reads move from
  `read:catalog` to `read:finances`; `pica_royalties_attribute` moves from
  `write:catalog` to `write:finances`. This aligns the MCP tool declaration
  with the backing routes' `callerHasFinanceAccess` gate (#1623) and the proxy
  bearer-write floor — gated callers are now filtered from `tools/list` at
  discovery instead of getting a 403 after calling. `read:finances` /
  `write:finances` are admin-tier (not user-grantable); admin satisfies both.

### Added

- **`dossier` section on `pica_works_inspect`.** `sections: ["dossier"]`
  returns the full song dossier — everything PICA holds about a song
  (recordings, releases, composition credits, people, provenance, AI
  disclosure, consent) in one structured payload, via
  `WorksResource.dossier()` (SDK 3.7.0). Rendered as a compact one-line text
  summary plus the full JSON under `structuredContent.data.dossier`. Opt-in
  only (never part of the default all-sections response, as it is a heavy
  superset); a downloadable PDF of the same dossier is available from the web
  UI. Not a new tool — folded onto the existing inspect tool per the
  tool-surface-consolidation preference so it rides the batched OpenAI
  resubmission rather than forcing a new one.

## [2.92.0] - 2026-07-09

### Added

- **`pica_find_duplicates` (works) surfaces fuzzy near-title duplicate groups**
  (WS4 agentic fixes, 2026-07-10). The backing service adds a conservative
  Levenshtein pass (≥85% on version-stripped base titles ≥5 chars,
  exclusion-aware, only among works no exact key grouped); groups arrive
  labelled `match_kind: "fuzzy_title"` with a `similarity` score (exact-key
  groups are labelled `match_kind: "exact"`), and fuzzy groups get a
  confidence-honest decision question ("look very similar … are they the same
  song?") with the safe default unchanged at "keep them separate". No
  inputSchema change — the fields are additive on the existing response.
- **`pica_catalog_health_plan` / `pica_catalog_health_fix` can now carry two
  new action shapes** (WS4): `merge_pair` with `preview.grain: "release"`
  (duplicate releases by normalized UPC, survivor = oldest, applied via the
  same engine as `pica_merge_duplicates`) and `normalize_work_splits`
  (work-collaborator splits summing ≠ 100 proportionally rescaled to exactly
  100.00 — propose-only, per-collaborator before→after preview, live-row
  recompute + post-apply verification at apply time). Replaces the previously
  un-appliable work-grain `set_splits_to_template` emission. Tool descriptions
  updated; schemas unchanged.

- **`pica_people_create` / `pica_people_update` gain `affiliation_kind` on
  `society_affiliations` items and a new `origin_countries` field** (ADR-292).
  `affiliation_kind` distinguishes a society the person is a current _member_
  of from their home-country _origin_ society for that code (defaults to
  `member`). `origin_countries` is an array of ISO 3166-1 alpha-2 heritage
  countries — the creator's self-identified origin, stored on a real
  `public.people` column and sanitized server-side.

- **New tool `pica_integrity_waive`** (registration integrity floor, PR
  #1595). Waives a registration-integrity gap the user has confirmed is
  intentional or unresolvable (cover writer, deceased collaborator,
  unreleased demo) — requires explicit user confirmation, and waivers are
  reversible. Completes the integrity-assess surface the write-tool
  pressure hints and dashboard-briefing nag point at.

- **`pica_works_create` / `pica_recordings_create` gain a `create_twin`
  flag + twin completion hints** (auto-twin, commit 689298e3c). The
  backing create routes auto-mint the twin entity (work↔recording);
  agents can now opt out via `create_twin: false`, and when a twin WAS
  minted the executor appends a completion hint ("also created the
  linked <recording|work> <id> — pass create_twin: false to skip").
  Backed by the SDK's new `requestWithEnvelope()` so the routes'
  `twin`/`twin_error` sibling fields survive the response unwrap.

### Fixed

- **`pica_enrichment_proposals_list` failed 100% of real calls with
  `UNEXPECTED_RESPONSE_SHAPE`** (since 2026-06-10; ops_issues
  18f25b41/20c76911). The route returns `{success, data: {proposals,
count, filters}}` and the SDK narrows to `.data`, so the tool receives
  the OBJECT — the `Array.isArray` guard rejected every real response.
  The tool now unwraps `.proposals` (guard retained for genuinely bad
  shapes). The `pica_enrichment_proposals_bulk_reject` dry-run filter preview had
  the sibling bug (normalised via a `.data` key that never exists —
  always previewed 0 ids) and is fixed in the same stroke.
- **`pica_releases_detach_track` reported "preview — call again with
  confirm:true" AFTER the delete executed** (ops_issue 0137b1b8,
  user_feedback e6320d08). The tool checked `result.data.confirmed` on a
  response the SDK had already unwrapped — always undefined — so an
  executed destructive mutation rendered as a pending preview. Now checks
  the unwrapped `{confirmed, preview|removed}` shape; both paths render
  correctly.
- **`pica_notes_list` / `pica_notes_query` silently returned an empty
  list on every call** (same envelope-unwrap class, found in the sweep).
  `GET /admin/notes` nests `{data, count, pagination}` under `data`; the
  tool's bare-array check discarded the object. Now unwraps the inner
  `data` array.

### Changed

- Deprecation `removal_target` on the 16 ADR-149/tool-consolidation aliases
  in `TOOL_ALIASES` / `TOOL_METADATA` extended from `2.92.0` to `2.93.0` —
  this release reaches `2.92.0`, so the prior target would have tripped the
  ADR-180 deprecation-expiry lint. No removal decision made; purely
  housekeeping to keep the aliases alive one more minor.

## [2.91.2] - 2026-07-09

### Added

- **WS3 "Confident Recall" — ambiguity signalling on `pica_search_all`.** When the top fuzzy matches
  are too close to call, the response carries an additive `ambiguity` block
  (`ambiguous: true`, the close candidates with confidences, and guidance)
  and the text content leads with an "⚠ AMBIGUOUS MATCH" banner — the agent
  is told to ask the user which entity they mean instead of acting on a
  guess. Absent on clear wins; identifier-exact lookups (ISRC/ISWC/IPI)
  never signal.
- **`pica_people_inspect` gains a `connections` section** — the one-call
  org-catalog neighbourhood of a person: works, recordings, releases, and
  agreements as `{kind, id, name, via}` with per-kind counts and a capped
  flag. Answers "everything connected to X" / "what have I done with X"
  without stitching sections. Creator Question Atlas backing added;
  `pica_graph_traverse` (global graph, identifier-only) cross-references it.
- **Accent parity, last mile.** The releases free-text query and the
  collaborator/contributor searches now match accent-insensitively
  ("fez" finds "Féz"), completing the parity the catalog entity searches
  gained on 2026-07-06.

## [2.91.1] - 2026-07-08

### Added

- **`pica_tool_details` now returns an `invoke` pointer on every tool detail**
  (single-tool and category paths). On the layered 3-tool surface the wrapped
  tool is not in the caller's function list; models that fetched a schema and
  found no matching callable concluded the tool was unavailable and gave up
  (observed on ChatGPT). The `invoke` field spells out the execution path:
  run via `pica_execute` with `{"tool": "<name>", "args": {...}}`.
- **`pica_resolve_person` / `pica_resolve_work` are now natural-language
  creator entries** ("find more information about this person / this song",
  "enrich this…", "look this song up in the MLC"), with Creator Question
  Atlas backing. Flipped from specialist after founder telemetry showed the
  ask arriving in exactly those words and neither resolver surfacing.

## [2.91.0] - 2026-07-07

### Added

- **The Oracle (Workstream 2) — count / rank / aggregate.** `pica_catalog_stats`
  now accepts `entity` (works / recordings / releases / people) and `group_by`,
  answering "how many have I produced", "who's my top collaborator", "genre
  breakdown", "releases per year" in one call. No args returns the flat
  multi-entity overview exactly as before (backward-compatible); `entity` alone
  returns that entity's total (now including releases); `entity` + `group_by`
  returns ranked buckets. Backed by org-scoped `GROUP BY` and a corrected
  distinct-work collaborator ranking. No new tool — the existing counts tool is
  enriched, and it is now surfaced as a natural-language creator-entry.

## [2.90.0] - 2026-07-07

### Changed

- **Tool-surface consolidation (Workstream 5).** Overlapping tools now sit behind
  one canonical front door each, via a silent-alias engine: deprecated names stay
  callable (they route to the canonical tool and carry a `_meta.deprecation` hint)
  but are hidden from `pica_discover` and `tools/list`. **No breaking changes** —
  every old name keeps working; the agent-facing surface simply shrinks.
- Attach cluster folded 4→2: `pica_releases_attach_track` is now a superset
  (single track by `recording_id`/`work_id`, by external `identifier_type`+`value`,
  or a batch via `items[]` with `dry_run`); `pica_releases_attach_track_by_identifier`
  and `pica_releases_bulk_attach_tracks` are deprecated aliases of it.
- Read tools aligned to the `query`/`inspect` pattern for releases, projects, notes,
  sessions, documents, statements, and share_links; legacy `list`/`get`/`read` names
  are deprecated aliases.
- `pica_recording_credits_update` accepts a `credits[]` batch; `pica_recording_credits_bulk_update`
  is a deprecated alias. The dead `audio_share` mode was removed from `pica_send_message`
  (use `pica_share_send` for asset sharing).

### Deprecated

- 16 tool names are now silent aliases (hidden from discovery, still callable). They
  will be removed in a future minor once traffic drains; callers should migrate to the
  canonical names surfaced by `pica_discover`.

## [2.89.0] - 2026-07-06

### Changed

- Picks up `@withpica/mcp-sdk` 3.4.0: `pica_recordings_query` free-text search now
  resolves through the server-side unified matcher, so recordings are findable by
  `artist_name`/ISRC, not just title (and without the 200-row client-filter
  ceiling). No tool-surface changes.

## [2.88.2] - 2026-07-05

### Changed

- `pica_search_all` now accepts `q` as an alias for `query`; `pica_recordings_inspect`
  accepts `isrc` and `pica_works_inspect` accepts `iswc` as entry points (each
  resolved to the entity server-side by exact match on the normalized code,
  `NOT_FOUND` when nothing carries it). All three are enforced via
  `requiredOneOf`, so a `{q:"..."}` / `{isrc:"..."}` / `{iswc:"..."}` call is no
  longer hard-rejected by the `pica_execute` dispatcher with
  `WRAPPED_ARGS_MISSING`. Fixes the in-app assistant roadblock of 2026-07-05,
  where the model repeatedly called search with `q` and inspect with `isrc`; the
  rejections cascaded into hallucinated-UUID `NOT_FOUND`s because it never
  resolved the real ids. Resolvers query the search backend with the normalized
  (dash/dot-free) code so a formatted `UK-JUX-17-00002` / `T-034.524.680-1` still
  matches the dash-free stored value.

## [2.88.1] - 2026-07-04

### Added

- `TEMPLATE_RENDER_FAILED` recovery hint for `pica_agreements_create_from_template`
  — the backing route now returns a typed 400 (bad `template_id` / missing
  required variables) instead of a generic 500, so the tool surfaces an
  actionable next step (query templates via `pica_agreement_types_query`).

### Removed

- `expires_in_days` on `pica_agreements_create_from_template` (it was a
  documented no-op). A draft has no signing window; set the expiry at
  `pica_agreements_send_for_signature`, which keeps its own `expires_in_days`.

## [2.88.0] - 2026-07-04

### Added

- `pica_agreements_create_from_template` — the missing entry point of the
  e-signature flow. `pica_agreements_send_for_signature` (ADR-289 Wave A)
  could email a draft agreement's required signers, but no tool could
  create that draft WITH `required_signers` set — the backing route,
  `POST /admin/agreements/create-from-template`, was the only API path that
  accepts it, and had no MCP tool at all. Renders an agreement template
  against real variables and creates the resulting agreement in `draft`
  status; success message names the agreement id, points at
  `/inspect/agreements/<id>`, and recommends
  `pica_agreements_send_for_signature` as the next step.

### Changed

- MCP discoverability wave (ADR-289 follow-up): reworked `pica_discover`
  ranking so the right tool wins each query class — importing a
  Spotify/ISRC link now routes to `pica_import_streaming_link`, and money
  questions route to the tool that actually answers them. Added a
  `pica_discover` query filter and surfaced gate visibility in
  `pica_tool_details`; rewrote the 15 worst tool descriptions with jargon
  glosses; swept stale copy (real tool count, free-runway language); purged
  phantom tool-name references and added a lint that blocks them permanently.

## [2.87.0] - 2026-07-04

### Added

- ADR-289 port wave: `pica_agreements_send_for_signature` (human-approval gated),
  `pica_bookings_query`, `pica_bookings_update`, `pica_projects_participants`,
  `pica_projects_multimedia`, `pica_artist_projects`, `pica_people_triangulate`,
  `pica_memory_update`, `pica_telegram_config`, `pica_enrichment_clear`,
  `pica_send_cancel`.
- Extensions: `pica_works_inspect` licensing section; `pica_agreements_inspect`
  signatures section; `pica_agreements_query` awaiting_signature filter;
  `pica_agreements_link_work` detach; counter-offer fields on
  `pica_update_license_enquiry_status`; `pica_import_youtube_link` refresh;
  `pica_projects_attach_works` detach; org inbound address in
  `pica_statements_pending`; file-request fields on `pica_send_message`.

### Changed

- Tool scopes aligned to their route floors so the in-app assistant's grant can
  call them (`pica_bookings_*`, `pica_update_license_enquiry_status`,
  `pica_send_message`, `pica_send_resend`, memory tool family).

## [2.86.3] - 2026-07-03

### Fixed

- **`pica_enrichment_candidates`** — the tool failed on every call with
  MCP -32602 "Invalid tools/call result: expected record, received array":
  the completeness endpoint returns a bare list and `formatAsText` emitted
  it as an array `structuredContent`, which the MCP spec forbids. Fixed at
  the shared formatter (`@withpica/mcp-utils`): list payloads are wrapped
  in the standard `{ items, count }` envelope.

## [2.86.2] - 2026-07-03

### Fixed

- **`pica_people_query`** — the `offset` parameter is now honoured on
  unfiltered (no query/filter) calls: paginated requests route through the
  search path, which forwards offset and returns a real `total`. Previously
  the plain-list path silently dropped offset and every page request
  re-served page one, making catalogs larger than one page unreachable.
- **`pica_audio_identify`** — a `NOT_FOUND` now carries a recovery hint
  naming the id class (identify takes an AUDIO FILE id, not a recording or
  work id) and steering to `pica_audio_query`, instead of leaving agents
  retrying stale ids blind.

## [2.86.1] - 2026-07-03

### Fixed

- **`pica_execute`** — an unknown wrapped tool name is now refused
  pre-dispatch with `UNKNOWN_TOOL`, the three closest registered tool
  names as `suggestions`, and a pointer to `pica_tool_details` — replacing
  the bare `EXECUTION_ERROR "Tool not found"` that left agents retrying
  guessed name variations blind. The `EXECUTION_ERROR` pass-through
  envelope also mirrors its code/message into `structuredContent`.
- **Audit telemetry** — dispatcher and executor error envelopes returned as
  content-text JSON (without `structuredContent`) now have their
  `error_code`/`error_message` lifted into the mcp audit event, so
  error-grouping pipelines see a code instead of an opaque error row
  (26 code-less ChatGPT dispatcher failures in 14 days were invisible to
  ops triage).

## [2.86.0] - 2026-07-02

### Changed

- **`pica_merge_duplicates`** — now `approvalMode:"human"` (ADR-288): a
  delegated-agent caller (OAuth connector / `pica_grant_*`) no longer gets
  refused — the call freezes into a pending human approval, the org owner
  approves out-of-band, and PICA replays the frozen merge server-side.
  Blocked merges (custody provenance, distinct-ISRC losers, tunecode
  collision) fail the approval cleanly with the blocking reason and zero
  partial mutation. Resource scope corrected `admin` → `write:catalog`
  (matching the backing route) so catalog-scoped delegated agents can
  propose; first-party callers still need the destructive tier as before.

## [2.85.1] - 2026-07-01

### Changed

- **`pica_releases_create_with_tracks`** — accept `parent_label` on the nested
  `release` object, completing the `parent_label` write path (create + update
  landed in 2.85.0). The RPC and service already persisted it; the tool schema
  omitted it, so the atomic-create path silently dropped the field (agent
  feedback `eee77119`).

## [2.85.0] - 2026-07-01

### Added

- ADR-286 Phase 0 royalty-reconciliation tools: `pica_royalties_unmatched`, `pica_royalties_attribute`.
- `pica_documents_delete` (clear a failed-to-parse / superseded document + its storage object).

### Changed

- **`pica_releases_create` / `pica_releases_update`** — accept `parent_label`
  (free-text parent/umbrella label name). It is a real `releases` column but was
  absent from the write schema, so agent writes were silently dropped while the
  call reported success (agent feedback `eee77119`).

## [2.84.0] - 2026-06-30

### Added

- **`pica_audio_update`** (#1379) — set `file_type` / `stem_label` /
  `version_label` / `title` on an audio file after upload, wrapping the
  org-validated `PATCH /audio-files/[id]` route.

### Changed

- **`pica_audio_query`** (#1378) — unassigned audio can now be filtered by
  filename/title, making discover-and-assign workflows precise on large
  upload sets.

## [2.83.2] - 2026-06-27

### Added

- **`pica_statement_ingest_document`** (`write:finances`, `approvalMode:"human"`) —
  agent-driven royalty/streaming statement ingestion for non-CSV statements
  (PDF/image/xlsx). The agent reads the document, extracts the line items, and
  calls this tool; for a delegated (OAuth/grant) caller it does NOT write — it
  raises a human-approval request, and only the org owner's approval replays it
  into `royalty_payments`, reusing the existing CSV parser downstream (identity
  resolution, currency normalisation, fingerprint dedup). CSV statements stay on
  the deterministic importer (PICA #1332).
- **`pica_statements_pending`** (`read:finances`) — lists uploaded
  `royalty_statement` documents the user hasn't imported yet (lean summaries, no
  financial figures), so the agent can discover them; routes to
  `pica_statement_ingest_document` (PICA #1332).
- **`pica_audio_assign`** (`write:files`) — attach an uploaded audio file to a
  work and/or recording after the fact; pairs with the new
  `pica_audio_query unassigned:true` filter so an agent can discover orphan
  audio and give it a home. Wraps the existing org-validated
  `PATCH /api/admin/audio-files/[id]`; no approval gate (low-risk, reversible)
  (PICA #1337).

### Changed

- **Society-agnostic reads (ADR-278).** Bumped `@withpica/mcp-sdk` to `^2.0.0`,
  whose read types replace the `prs`/`ppl`/`mlc` registration scalars with a
  single `society_registrations[]` array, and updated the `pica_works_query`
  tool descriptions so they no longer privilege PRS/PPL over other collection
  societies. No tool was added or removed and no handler logic changed; this is
  a republish so external stdio consumers receive the BREAKING SDK read shapes
  already live on the hosted `/api/mcp`. Consumers reading `prs`/`ppl`/`mlc`
  fields must migrate to `society_registrations[]`.
- **`pica_audio_query`** — new `unassigned` boolean filter returns audio with no
  work and no recording (PICA #1337).
- **`pica_documents_read`** — description now tells the agent to STOP retrying
  when `parsing_status` is `failed` and read the new `parse_error` field for the
  reason (e.g. a legacy `.xls` that must be re-saved as `.xlsx`), instead of
  polling a permanently-failing parse. Pairs with the server-side fix that makes
  the read return an honest `failed` + `parse_error` rather than looping in
  `processing` forever (ops_issue 8e197fe5). No tool/schema/SDK change.
- **`pica_documents_read`** — also points the agent at
  `pica_statement_ingest_document` when the document is a royalty/streaming
  statement (PICA #1332).

## [2.83.0] - 2026-06-25

### Added

- **`pica_resolve_release`** — release-grain Spotify resolver (scope
  `write:catalog`). Gap-fills `upc`, `release_date`, `artwork_url`, `title`,
  and `parent_label` from Spotify when `spotify_album_id` is set; routes every
  write through the authority gate so human / higher-authority / unprovenanced
  values are never overwritten. Idempotent (second call → `applied=[]`).
  Spotify failure lands in `errors[]`, never throws. No `spotify_album_id` →
  no-op + `recovery_hints`. v1 Spotify-only; additional sources in future ADRs.
  New: route `POST /api/admin/releases/[id]/resolve`, SDK `resolveRelease`.
- **`pica_catalog_health`** — agent-first no-arg whole-catalog **completeness +
  cleanliness** verdict (score + ranked punch-list across works / recordings /
  releases / people). Honest-zero: a check that throws is reported
  `notAssessed`, never a silent pass. Org-scoped, bounded sample ids. (ADR-272)
- **`pica_catalog_health_plan`** — derives an actionable fix-plan from the
  health verdict: concrete fix-actions tagged safe/needs-scope plus a residual
  list of what can't be auto-fixed, returned with a `planToken`. (ADR-273)
- **`pica_catalog_health_fix`** — applies accepted plan actions (a list of
  `actionIds`, or `all_safe_auto`) against a `planToken`; reports honest
  `applied` / `skipped` and never reports a partial apply as success. (ADR-273)
- **`pica_documents_list`** — lists uploaded catalog documents for the org
  (parsed status + lean projection). ADR-274 document bridge.
- **`pica_documents_read`** — scope-gated bounded read of a parsed document's
  extracted content (`offset` / `max_chars`). ADR-274 document bridge.
- **`pica_documents_link`** — stamps a parsed document's provenance link to a
  `work` / `person` / `recording`. ADR-274 document bridge.

### Changed

- **`pica_recordings_update`** now accepts a free-text `notes` field (master-grain
  annotation, parity with `works.notes`).

## [2.82.0] - 2026-06-22

### Added

- **`pica_releases_delete`** — delete a release (scope `write:catalog`, tier
  `destructive`). Refuses with the structured blocking list when the release
  still has tracks attached or active share links (releases have no soft-delete);
  detach the tracks and revoke the links, then retry.
- **`pica_audio_delete`** — delete an audio file (scope `write:catalog`, tier
  `destructive`). Removes the file record AND its underlying storage object — no
  orphan is left behind — along with its analysis/transcription satellites. Does
  not touch the parent work or recording.

### Fixed

- **`pica_merge_duplicates` (works)** now refuses a merge that would collapse
  distinct recordings: when the winner and a loser each link a recording with a
  different ISRC, the merge is blocked with `MERGE_BLOCKED_DISTINCT_RECORDINGS`
  (and a recovery hint) instead of destroying the loser work. Prevents the
  title-only-duplicate data-loss class.
- **`pica_people_update`** now accepts `is_group` (boolean) — previously
  rejected as an unknown field, forcing the `group` tag and boolean to drift.

## [2.81.0] - 2026-06-19

### Added

- **ADR-270 §3.4 leverage signals on `pica_works_query` + `pica_recordings_query`**: `streaming_activity` filter / sort / on-row (rank by traction — "what's worth my time"), and `missing_field_count` filter + sort on `pica_recordings_query` (the `pica_works_query` equivalent shipped in 2.80.0). Income stays agent-composed, never a field.

## [2.80.0] - 2026-06-17

### Added

- **`pica_register_set`** — one-call DAW set registration (ADR-259). Creates a
  work, a linked master recording, and a 100% org master ownership split in a
  single call via `POST /api/admin/register-set`, replacing the five sequential
  calls (`pica_works_create` + `pica_recordings_create` +
  `pica_recording_splits_create`, plus the dup pre-check and splits list). Used
  by the Ableton extension's register flow to collapse five serial round-trips
  into one. On a title+artist duplicate it fails with the existing work id so the
  caller can add a version or complete the existing work.
- SDK `register.set()` (`@withpica/mcp-sdk` 1.45.0).

## [2.79.0] - 2026-06-15

### Fixed

- **`pica_execute` annotations** now advertise `readOnlyHint: false` and
  `openWorldHint: true`, matching the dispatcher's real behaviour — it can route
  to write/destructive/external tools, so claiming read-only / closed-world was
  wrong (and was flagged in OpenAI app review). The scope `tier` stays `"read"`
  so a read-scoped key can still dispatch read tools through it; real scope and
  destructive confirmation remain enforced on the **wrapped** tool, not the
  wrapper. Implemented via a new advisory-only `annotationOverrides` field on the
  tool definition (completes the ADR-230 tier→hint coupling for the one tool
  whose client-facing hint must differ from its scope tier). No enforcement
  change.

## [2.78.0] - 2026-06-15

Carries the uniform deletion contract MCP rendering (originally 2.76.0) forward
on top of 2.77.0's `pica_import_from_spotify` removal. 2.76.0 (deletion contract)
and 2.77.0 (import removal) were published from parallel branches; 2.77.0 was
built before this branch merged, so it did not include the deletion rendering —
this release re-establishes it on top of the removal.

### Changed

- **`pica_works_delete` / `pica_recordings_delete`** render the uniform delete
  outcome via `formatDeletion` (status `deleted`/`archived`/refused + what was
  unlinked + how to fully delete); descriptions state the contract. Re-applied
  here after 2.77.0 (parallel branch) shipped without them.

## [2.76.0] - 2026-06-15

Uniform deletion contract surfaced to agents. `pica_works_delete` and
`pica_recordings_delete` now report whether the entity was deleted or archived,
what was auto-unlinked, and — when refused — exactly what to remove first.

### Changed

- **`pica_works_delete`** description states the contract (auto-unlinks from
  releases; archives instead of destroying when it has identifiers/dependents);
  result renders via `formatDeletion` (status + detached).
- **`pica_recordings_delete`** description states the contract (credits/splits
  removed with it; refused with a blocking list if it is a release track / has
  payments / is in a sync placement); result renders via `formatDeletion`.
- **`pica_works_bulk_delete`** description notes each work follows the same rule.
- **`pica_releases_list`** gains `query` (title substring), `upc` (exact), and
  `spotify_album_id` (exact) lookup filters — locate a release by
  title/UPC/album instead of paging the whole catalogue (feedback 9a7a50f7).

## [2.77.0] - 2026-06-15

### Removed

- **`pica_import_from_spotify`** (BREAKING) — removed after a multi-release
  deprecation window. It was superseded by **`pica_import_streaming_link`**,
  which offers the same two-step preview/confirm flow and output and also
  supports Deezer. Callers passing a Spotify URL should switch to
  `pica_import_streaming_link` with the `url` argument (the only rename is
  `spotify_url` → `url`). Shipped as its own deliberate breaking PR rather than
  bundled into an unrelated release, per the standing removal plan recorded in
  `TOOL_METADATA`. (2.76.0 = the uniform deletion contract, shipped separately.)

## [2.75.0] - 2026-06-15

### Added

- **`pica_work_writers_add`** — add composition writers to a work by name (ADR-259 DAW writer capture). Each name is resolved to a person (exact-match an existing person, else auto-create a tagged writer person) and **additively** added as a `role='writer'` collaborator — existing collaborators are never replaced. Percentages are not set (writer shares reconcile via attestation); ambiguous / multi-name entries are skipped and reported. Runs under `write:catalog`.

## [2.74.0] - 2026-06-14

### Added

- **`pica_recordings_bulk_reassign_artist`** — bulk-repoint recordings'
  `primary_artist_id` in one atomic call (by explicit `recording_ids` or by
  `from_person_id` to drain a junk "magnet" person; `to_person_id` null
  clears). Runs under `write:catalog` — no destructive scope needed, because
  it only re-points the structured link (the `artist_name` label is
  untouched). Returns `source_now_empty` so a drained junk person can be
  retired. The fix for the "unknown" artist magnet class.
- **`clean-up-artists` skill** — guided junk-artist cleanup: find magnets,
  build a plain-language repoint plan from each recording's own `artist_name`,
  preview, execute via the bulk tool, retire emptied rows.
- **`primary_artist_id` on `pica_recordings_update`** — repoint a recording
  to the correct in-org person (the service already validated it; the tool
  param was missing).

### Changed

- Route validation errors now surface their real code + message to agents
  instead of an opaque `UNKNOWN_ERROR` (via `@withpica/mcp-utils` 1.23.1).

## [2.73.0] - 2026-06-12

ADR-264 WS2 verify-before-assert + the WS4 front-door prerequisite (bulk
recording credits) + ADR-268 WS1 party claims / WS2 identifier-based party
dedup / WS5 ownership transfer types + the ADR-267 calibrated-question voice
contract. Also removes the never-functional `pica_team_*` tools and the
long-deprecated `pica_dashboard_pulse`.

### Added

- **`pica_party_claim_invite` + `pica_party_claim_status`** — ADR-268 WS1
  party org claim spine. `pica_party_claim_invite` sends an artist-anchored
  claim invite for an unclaimed label/distributor/publisher organisation
  (the user-supplied recipient email is the consent + identity anchor; the
  single-use link redeems on the public claim landing — redemption makes the
  claimant the org's owner, flips verification pending→verified and billing
  hibernated→trial, and mints an active org-to-org connection with the
  inviting catalog, never membership). Concurrent claims freeze both sides
  as `disputed` for founder adjudication, and the tool says so instead of
  pretending the email went out. `pica_party_claim_status` lists the claims
  the calling org has sent with their lifecycle status. Registered in the
  `labels` discovery category; tool count tripwire 282 → 284.
- **`pica_recording_credits_bulk_update`** — ADR-264 WS4 prerequisite. Apply
  one credit set across many recordings in a single call: target a release
  (`release_id` — every recording on its tracklist) or an explicit
  `recording_ids` list, plus a `credits[]` batch (`credited_name` + `role`
  per entry, optional `person_id` / `split_percentage` / `split_type` /
  `notes`). One album's production credits stops being 30 sequential
  per-recording calls. Existing (recording, credited name, role) rows are
  skipped, never overwritten — re-running is safe; `dry_run: true` previews
  the exact per-row `would_create` / `would_skip_existing` decision set the
  execute path uses. Validation is all-or-nothing with per-problem detail
  (invalid input applies NOTHING); application returns per-row
  `created` / `skipped_existing` results. Caps: 100 recordings, 50 credits,
  500 total rows per call. Backed by the new
  `POST /api/admin/recording-credits/bulk` route (org-scoped at every step:
  release, tracklist, recordings, and person_ids must all belong to the
  caller's organisation).
- **`pica_works_verify_registrations`** — ADR-264 WS2 truthful diagnosis. Verify
  the registration status of up to 25 works. `mode: "check"` looks each work up
  at the MLC (ISWC → title + writer IPI → title + primary-artist name) and
  records what the lookup PROVED: a found registration is written as a positive
  society fact (and any stale negative assertion is cleared); a clean not-found
  becomes a `verified_absent` assertion with a lookup receipt. Lookup errors and
  works with no usable identifiers write NOTHING (`lookup_failed` /
  `insufficient_identifiers`). `mode: "confirm_unregistered"` records the user's
  own confirmation (optional `society_code`; omitted = "not registered with any
  society"). `works_without_registrations` rows on `pica_dashboard_attention`
  now carry an `evidence` block (`unverified_absent` / `verified_absent` /
  `user_confirmed_absent` with `verified_via` + `checked_at`) reflecting these
  assertions.
- **`unconnected_collaborators`** attention class (taxonomy gap, f0266f09
  gap #6): people credited on the org's works with no linked PICA account now
  appear on `pica_dashboard_attention` / briefing totals / `pica_count_explain`
  — one shared compute with `briefing.unconnected_collaborators` so the
  surfaces structurally cannot drift. drill_down_skill null until a skill
  covers collaborator connection.
- **Decision blocks (calibrated questions)** — `pica_find_duplicates` groups,
  the `pica_merge_duplicates` MERGE_BLOCKED_PROVENANCE error, and
  `pica_works_verify_registrations` mode:"check" (when works couldn't be
  verified) now carry a `decision` field: a question with 2-3 options,
  plain-language consequences, and a safe default — no UUIDs in any decision
  text (a UUID-shaped title degrades by omitting the block, never failing the
  call). Always additive: the structured payload carries everything the block
  says. `pica_dashboard_briefing` now carries `user_message` — one
  ready-to-say lowercase sentence in the curiosity register (no loss framing
  on unverified counts).

### Changed

- **`pica_recording_custody_assign`** — ownership transfer types are live
  (ADR-268 WS5, the ADR-262 fast-follow): `transfer_type` now accepts
  `assignment` (title transfers) and `full_transfer` (ownership AND control)
  alongside the v1 control/possession types. An ownership leg also moves the
  recording's master ownership splits append-only in the same transaction —
  the transferor's active era closes (`end_date`), the transferee's opens —
  and the credit layer never moves. Honest-gap refusals (400, typed codes):
  a partial percentage with more than one active owner
  (`PARTIAL_TRANSFER_AMBIGUOUS`), a transfer exceeding the transferor's
  share (`INSUFFICIENT_TRANSFEROR_SHARE`), or a percentage outside (0,100]
  (`INVALID_PERCENTAGE`) — nothing is ever silently lost.
- **`pica_labels_create`** accepts an optional `identifiers` object
  (`musicbrainz_label_id` / `isni` / `label_code` / `discogs_label_id` /
  `wikidata_id` / `deezer_label_id`) — ADR-268 WS2. Dedup is now
  identifier-first: supplied identifiers resolve to the global label identity
  and an org already bridged to that identity is returned
  `already_existed: true` EVEN when the supplied name differs;
  case-insensitive name matching (ADR-265 behaviour) stays as the fallback,
  and a name-matched org missing the bridge is backfilled when an identity
  was resolved. Param addition only — no new tool.
- **`pica_recording_credits_update`** accepts an optional `instrument`
  param (ADR-259 Stage 2) — the played instrument(s) for this credit, e.g.
  `'drums'` or `'drums, bass'` (one credit per person; multiple instruments
  list on the same row). Stored on `recording_credits.instrument`, rendered
  in `/inspect`'s recording contributors table, and written by the Ableton
  extension's attribution checklist. Server-side ADR-255 resolve-on-write
  now also fires for ANY recording credit arriving without a `person_id`:
  an exact org-scoped name match links it, anything else stays a free-text
  draft (never auto-creates a person). Param addition only — no new tool.
- **`first-session` skill** now carries the calibrated-question library (ADR-267
  Step 1): open-over-closed decision questions, diagnosis labels, no-oriented
  phrasing for destructive moves, options-with-consequences, evidence-gated
  stakes, and a no-UUIDs-in-user-facing-text contract. Specialist skills adopt
  the same grammar incrementally; the WS4 front-door skill embeds it at birth.
- **`pica_works_create` / `pica_works_update` `mood` param description** now
  states the value is stored on the work's primary recording's audio traits
  (recording-grain, ADR-266 Stage 2c) — caller contract unchanged.

### Fixed

- **Review-link contract** — server instructions told every agent to share
  `withpica.com/workspace?type=<entity>&id=<id>` after writes; now the
  schema-direct `/inspect/<feature>/<id>` shape (both byte-parity copies).
  uploads/audio/exports tool descriptions point users at `/inspect`.

### Deprecated

- **`pica_import_from_spotify`** — use `pica_import_streaming_link` (same
  two-step flow, also supports Deezer). Structured
  `{deprecated, removal_target: "2.74.0"}` in TOOL_METADATA; the new
  lint-mcp-tools deprecation-expiry check BLOCKS any publish at or past the
  removal target, so this cannot overstay like pulse did.
- **`briefing.unverified_writers`** count source — `briefing.unenriched_writers`
  is the canonical name (matches the actionable-issues taxonomy class); the
  alias shares one definition and is accepted until 2.74.

### Removed

- **`pica_team_*` (all 5: `pica_team_list` / `pica_team_get` / `pica_team_invite`
  / `pica_team_update_role` / `pica_team_remove`)** — founder decision
  2026-06-11. The backing routes (`/api/admin/team/*`) are session-only BY
  DESIGN (ADR-248: team membership is human-only), so every bearer/MCP caller
  has always received a hard 401 — the tools never worked for any agent and
  never could. Removing them is honesty, not a regression: agents stop being
  advertised a capability that structurally cannot succeed. Team membership
  stays managed by humans in `/settings`. Also removed: the `team` discovery
  category, the `pica_team_remove` destructive-confirmation case, recovery
  hints, cost-model entries, and the `set-up-workspace` skill's Block 5 now
  points users at `/settings` instead of the tools. Tool count tripwire
  287 → 282; destructive tripwire 20 → 19.
- **`pica_dashboard_pulse`** — deprecated since ADR-247 with a removal window
  that passed (prod telemetry: 3 calls all-time, exploratory).
  `pica_dashboard_briefing` is the canonical attention surface; pulse's
  revenue-at-risk/deadline predicates retired with it (they disagreed with
  the briefing coherence contract). Tool count tripwire 288 → 287.
- **`suggested_skill`** alias dropped from `pica_dashboard_attention` rows
  (deprecated 2026-05-22; soak window long passed; zero non-test consumers).
  `drill_down_skill` is the canonical field.

## [2.72.0] - 2026-06-10

ADR-264 WS1.3 — recordings/releases dedup + merge (last trust-floor gap).

### Changed

- **`pica_find_duplicates`** — `entity_type` now covers `releases` and
  `recordings` (added in 2.72.0); description explicitly names the UPC and
  ISRC grouping keys so callers understand the detection basis.
- **`pica_merge_duplicates`** — extended to cover `recordings` and `releases`
  (+ singular aliases `recording` / `release` for backwards compatibility).
  - `recordings`: merges via `merge_recording_atomic` RPC — credits, splits,
    and release-track links are deduped by their natural keys; winner's NULL
    fields (ISRC, duration, streaming IDs, artist name, artwork) are back-filled
    from the first loser that has a value.
  - `releases`: merges via `merge_release_atomic` RPC — tracks are rehomed
    (dedup by recording_id within the winner's tracklist); UPC, release date,
    artwork, and label/distributor org links are gap-filled from losers.
  - **PROVENANCE BLOCK**: a recording loser with `recording_custodians` rows
    (ADR-262 append-only custody chain) blocks the merge. The tool returns a
    structured `MERGE_BLOCKED_PROVENANCE` error with `blocked_ids`, the `remedy`
    ("custody chains are append-only; transfer custody first or keep both
    recordings"), and `next_tool: "pica_recording_custody_assign"` so the AI can
    route directly to the safe resolution path.

### Recovery hints

- `MERGE_BLOCKED_PROVENANCE` added to the `pica_merge_duplicates` hint registry
  → `pica_recording_custody_assign`.

## [2.71.0] - 2026-06-10

ADR-265 release parties and invites — label/distributor org creation + recording/release-grain invite targets. Requires `@withpica/mcp-sdk@^1.39.0`.

### Added

- **`pica_labels_create`** — create an unclaimed label or distributor organisation.
  Accepts `name`, `org_type` (`label` | `distributor`), and optional `country` /
  `website` / `contact_email`. Server-side deduplication is catalog-wide across
  both party types: a case-insensitive name match returns the existing record
  with `already_existed: true` rather than creating a duplicate. The returned
  `id` can be assigned to `releases.label_organization_id` (via
  `pica_releases_update`) or `releases.distributor_organization_id` to formally
  link a release's label or distributor.
- **`recording_id` / `release_id` targets on `pica_collaborators_invite`** — in
  addition to the existing `work_id`, invite calls now accept exactly one of three
  target ids (`work_id` | `recording_id` | `release_id`). Recording-grain invites
  surface a claim scoped to the named recording's credits; release-grain invites
  surface a claim that verifies all the invitee's credits across every recording on
  the release. Exactly-one-of validation is enforced server-side (400 if zero or
  multiple targets are supplied).

### Fixed

- **`pica_labels_query` always returned "Found 0 labels"** — an SDK envelope
  double-unwrap (`labels.list` read `.data` off the already-unwrapped array)
  meant the tool had returned empty results regardless of catalog state since
  it shipped (2026-04-14). Fixed in `@withpica/mcp-sdk@1.39.0`.
- **`pica_labels_create` dedup messaging now sees `already_existed`** — the
  same envelope unwrap stripped the `already_existed` sibling flag, so every
  successful create tripped the no-data guard and the "already exists — link
  it instead" guidance never fired. The SDK now returns the label object
  directly with `already_existed` inside it.

## [2.70.0] - 2026-06-10

ADR-264 WS1 trust floor — conservative-by-default enrichment, truthful tool
outputs, and the reviewable enrichment queue. Requires `@withpica/mcp-sdk@^1.38.0`.

### Added

- `pica_enrichment_proposals_bulk_reject` (mutating, `dry_run` preview):
  reject up to 100 pending enrichment proposals by explicit ids OR a filter
  (`rule_id` / `source` / `max_confidence`). Per-proposal effects identical to
  `pica_enrichment_proposal_reject` (permanent content-hash suppression).
  `dry_run` resolves the real affected set — the filter path queries the live
  list, no fake previews.
- `pica_enrichment_proposals_list` gains `min_confidence`, `max_confidence`,
  and `created_after` filters, so a caller can isolate the batch they just
  triggered from accumulated stale low-confidence noise.
- `pica_releases_list` gains `offset` pagination with an honest envelope
  (`count`, `offset`, `has_more`, `next_offset`) — every release is reachable;
  no more mid-array ~100KB truncation dead-end.

### Changed

- Enrichment behaviour (backend, surfaced through proposals): official
  YouTube " - Topic" channel uploads now classify as `master` and the
  `artist_title_to_youtube` rule defaults to enriching the existing master
  recording (UPDATE proposal attaching `youtube_video_id`/`youtube_url`)
  instead of minting a phantom `alternate` recording; creating a new
  recording now requires affirmative low-sameness evidence (version markers
  or >3s duration delta). Proposals carry a `sameness_evidence` signal.
- `pica_import_from_spotify` preview wording: duplicates are reported as
  "already in catalog (will be skipped, not modified)" — the preview no
  longer promises an enrichment the executor does not perform.
- `pica_execute` hardening: the `WRAPPED_ARGS_MISSING` transport error now
  fires when ANY required field of the wrapped tool is missing (partial
  client-side argument strips included), naming `missing_required_fields`.
  The nested `args` object is the canonical call shape.

### Fixed

- `pica_enrichment_proposals_list` no longer renders the literal
  `"? pending proposal(s)"` when the backend returns an unexpected shape —
  that case is now a structured `UNEXPECTED_RESPONSE_SHAPE` error (with
  `structuredContent`), not a fake count.

### Removed

- Dropped the unused `mppx` runtime dependency. No `src/` consumer has existed
  since the MPP unit tests were deleted; the earlier cleanup left it in place
  "to avoid lockfile churn" and asked the next publisher to drop it — done.

## [2.69.0] - 2026-06-08

### Added

- Recording custody tools (ADR-262 — master-side control chain):
  `pica_recording_custody_assign` (transfer custody of a recording; admin/owner,
  control/possession transfer types only) and `pica_recording_provenance` (read
  the ordered custody legs + immutable origin credits). Thin wrappers over the
  live `/api/admin/recording-custody/*` routes via the new SDK
  `recordingCustody` resource (requires `@withpica/mcp-sdk@^1.37.0`).
- `pica_releases_create` / `pica_releases_update` accept an `availability`
  write param (`available` | `withdrawn`) to mark a release live or taken down.

## [2.68.0] - 2026-06-07

Adds a structured-metadata escape hatch to work creation. No SDK bump required (`@withpica/mcp-sdk` ^1.36.0).

### Added

- **`metadata` (jsonb) on `pica_works_create`** — callers can attach a free-form JSONB snapshot when creating a work (e.g. a DAW session capture: tracks, devices, samples, tempo). Stored verbatim on `works.metadata`; not individually queried.

## [2.67.0] - 2026-06-01

Enrichment field-level source ranking surfaces on the resolve tools. Requires `@withpica/mcp-sdk` ^1.36.0.

### Added

- **`force` on `pica_resolve_work` + `pica_resolve_recording`** — force a re-enrich past the per-source idempotence stamps. Safe by construction: the field-level authority gate never clobbers a human-entered, higher-authority, or pre-provenance (unprovenanced) value, so a forced re-run only fills gaps and upgrades lower-ranked automated values.
- **Overwrite receipts on both resolve tools** — the structured result now carries `overwrote[]` (a higher-authority source replaced a lower-ranked value, with old/new source) and `skipped_lower_authority[]` (a source was refused because the existing value out-ranked it, equalled it, or was a human/verified/unprovenanced value). The summary line reports the overwrote/kept counts.

### Changed

- **Resolve tool descriptions** now explain the authority gate (higher-authority sources win, human-entered and pre-provenance values are locked) and the two receipt arrays.

## [2.66.0] - 2026-06-01

ADR-250 human-in-the-loop approval goes live, plus the close-out batch carried by this release.

### Added

- **`pica_approval_status`** — read-only poll for an out-of-band human approval. When a delegated-agent destructive write is deferred, the dispatcher returns a `human_approval_required` envelope carrying an `approval_reference`; pass that as `approval_id` to check whether the grant owner has decided (`pending` / `approved` / `denied` / `expired` / `executed` / `failed`) and when/how. Ownership is enforced server-side — a foreign approval comes back as `not_found`, never a leak. ADR-250 PR-c.

### Changed

- **ADR-250 human-in-the-loop approval gate is active.** `pica_people_bulk_delete` now carries `approvalMode: "human"`: a delegated-agent caller proposes the deletion, the grant owner approves out-of-band, and PICA replays the frozen params server-side. Session and first-party-key callers fall through to the existing `dry_run`/destructive path unchanged. The tool stays connector-reachable rather than blanket-excluded.

## [2.65.0] - 2026-05-30

### Added

- **`pica_agreement_source_splits`** — reconcile the per-party splits you extracted from an uploaded agreement against a work's composition credits and source them if they match (±0.5% tolerance, 0.8 confidence gate). Outcomes: `sourced`, `needs_confirmation` (re-call with `confirm:true`), `discrepancy` (a finding, surfaced on `/inspect`). Split-source provenance phase 2. Requires `@withpica/mcp-sdk` ^1.35.0.

## [2.64.0] - 2026-05-30

ADR-252 WS-A surface alignment + the ADR-247 agent-hygiene crash fix. Requires `@withpica/mcp-sdk@^1.34.0`.

### Fixed

- **Agent-visible descriptions no longer name the dropped `work_credits` table.** ADR-252 WS-A dropped `work_credits`; composition credits now live on `work_collaborators` and master credits on `recording_credits`, but several tool/resource descriptions still described the old table (agents read these). Corrected across `pica_credit_add` hint (custody-hints), `pica_claim_credit` (now a status-only transition on `discovered_credits` — no credit row is written), `pica_claim_artist` (writes `recording_credits`), `pica_dashboard_attention` class glossary, `pica_recording_credits_update`, `pica_collaborators_accept`, the `pica_explain_count` glossary, the `pica_physical_assets_unlink_work` link-row label (now `recording_production_assets`), and the `work-credits-required` required-fields resource. Underlying write paths were already repointed in WS-A — this is description/accuracy only.
- **Dead tool pointer.** A custody gap-hint pointed agents at the non-existent `pica_work_credits_add`; corrected to the real `pica_credit_add`.
- **`pica_introduce_self` / `pica_outcome_log` / `pica_outcomes_recent` crashed on the happy path.** The SDK's `request()` unwraps the `{ success, data }` envelope and resolves to the inner `data`, but these three executors read `result.data.<field>` against the already-unwrapped object → `UNKNOWN_ERROR: Cannot read properties of undefined`. It surfaced rarely only because Bearer-key callers are rare (cookie/grant sessions are exempt from `introduce_self`). Root-caused in `@withpica/mcp-sdk@1.34.0` (return types corrected to the inner payload; `request()` now throws on a 2xx `success:false` body); the executors now read fields directly and guard the empty case. Added `agent-hygiene.test.ts` mocking the **inner** payload — the shape `request()` actually resolves to — which the prior coverage never exercised.

### Changed

- **`schema-mirror.json` refreshed off staging for the post-ADR-252 schema.** Replaced the dropped `work_credits` entry (`credit_type` / `rights_type` CHECKs no longer exist) with `work_collaborators` (only `attestation_status` is a CHECK; composition roles are validated tool-side), and added `recording_credits.attestation_status`. `refresh-schema-mirror.ts` `TABLES_TO_MIRROR` updated to match. The `work-credits-required` resource now surfaces only the `attestation_status` enum; valid role values come from the `role` field of `pica_credits_update`.

## [2.63.1] - 2026-05-25

Stdio/HTTP instruction parity fix.

### Fixed

- **Stdio `SERVER_INSTRUCTIONS`** now carry the ADR-247 first-call identity paragraph (Bearer API-key callers must call `pica_introduce_self` first). The paragraph had been added to the HTTP `/api/mcp` transport copy in 2.62.0 but not the published stdio binary, so stdio agents never saw the introduction-gate guidance even though the gate is enforced server-side. Restores parity between the two transports (enforced by the `mcp-server-instructions-parity` test).

## [2.63.0] - 2026-05-25

ADR-245 (composition surface) recording-session metadata + a work-export correctness fix.

### Added

- **`pica_recordings_*` recording-session metadata** — `pica_recordings_create` / `_update` accept, and `pica_recordings_inspect` / `_query` return, `recorded_at` (ISO date) and `studio_name` (text). ADR-245 §3.3. Engineer remains a `recording_credits` role; live remains `version_type: "live_performance"` — neither is promoted to a recordings column.

### Fixed

- **NRO/PPL export** no longer selects four columns that exist on no table (`ppl_artist_name`, `ppl_p_date`, `ppl_p_name`, `recording_date`) — they made the recording fetch return Postgres `42703` and broke the export for any work with recordings. Performer rows are now sourced from `works.recording_contributors` (their real home).

## [2.62.0] - 2026-05-24

ADR-247 (Agent Hygiene) sprint. After this version, every OAuth-Bearer / raw-API-key caller must declare itself via `pica_introduce_self` on first use, and every successful write-tier tool call is durably recorded in `workflow_outcomes` (the symmetric counterpart to `ops_issues` for errors).

### Added

- **`pica_introduce_self`** — first-call identity declaration. Required for OAuth-Bearer and raw-API-key callers; cookie + `pica_grant_*` callers are carved out at the router (their identity is on file at the auth boundary). `tier: "write"`. Inputs: `agent_name` (required), `agent_role`, `on_behalf_of`, `model_hint`. Declaration is valid 24h per credential; the router's hard introduction gate returns `412 IDENTITY_REQUIRED` until this lands. Backed by `AgentIntroductionsResource.declare` in `@withpica/mcp-sdk@1.33.0`.
- **`pica_outcome_log`** — agent-explicit workflow / milestone log. `tier: "write"`. Inputs: `kind` (required free-form — typical: 'workflow_completed' / 'milestone_hit'), optional `entity_refs[]` (UUID-validated server-side), `summary`, `agent_summary`. Throws on failure (unlike the auto-fire path which is fire-and-forget) so the agent gets useful feedback. Backed by `WorkflowOutcomesResource.log`.
- **`pica_outcomes_recent`** — read recent workflow outcomes for the current org. `tier: "read"`. Returns rows ordered by `last_seen_at DESC`, signature-deduped (repeat workflows collapse with `occurrence_count` incrementing). Captures both the auto-fire path (every write-tier MCP success) and the agent-explicit path (`pica_outcome_log` calls). Optional `kind` / `source` filters. Default limit 20, max 100.

### Changed

- **`pica_dashboard_briefing` response gains an `agent_velocity` block** when the org has workflow_outcomes activity in the trailing 7d. Shape: `{window_days: 7, count_7d, by_kind, top_tools: [{tool_name, count}]}`. Coherence contract: `count_7d == sum(by_kind values)`. Complements the existing `agent_activity` block (which is mcp_audit_log call counts by tier) — same window, different lens: `agent_activity` is "how many calls", `agent_velocity` is "what workflows actually completed / refused / nudged".
- **`pica_dashboard_pulse` deprecated.** Still callable (backwards-compat for N+2 releases) but `vernacular_kind` dropped from `creator-entry` to `specialist`. The Creator Question Atlas resolver for "give me the pulse" / "how's my catalog" now routes to `pica_dashboard_briefing` instead. Tool description prefixed with the deprecation notice + redirect. Deadlines/revenue/background-task signals pulse uniquely carried will land as `pica_dashboard_velocity` in a future ADR.

## [2.61.0] - 2026-05-23

### Changed

- **`pica_provenance_certificate` is now actually callable.** The tool's description has always been "Generate a provenance certificate", but the underlying SDK call did `GET /admin/works/[id]/certificate`, which 404s on any work without a pre-existing certificate. After bumping `@withpica/mcp-sdk@1.32.0`, the tool's executor POSTs to the same path and generates the certificate row. Closes ops_issues c3e64ee2 (NOT_FOUND, 2 events) and b2dddfee (AUTHENTICATION_ERROR, same caller, pre-OAuth-refresh flake). Description updated to disclose that each call writes a new row.

### Added

- **`pica_provenance_certificate_get`** — companion tool for the read path. `tier: "read"`, `risk: "safe"`, fetches an existing certificate row without regenerating. Returns 404 NOT_FOUND if no certificate has been generated yet; callers fall back to `pica_provenance_certificate` in that case. Registered alongside the existing generator in `analytics`, `metadata`, `recovery-hints`, `discovery`, and `public-filter`.

## [2.60.0] - 2026-05-23

Exports release. The four catalog export tools accept per-work scope, the catalog export tool's schema matches what the backend actually validates, and `pica_execute` is forgiving of the three call shapes agents emit in the wild.

### Added

- **`work_ids: string[]` on four export tools.** `pica_export_song_registration`, `pica_export_industry_ready`, `pica_export_ai_consent`, and `pica_export_catalog_asset_report` now accept an optional `work_ids` array. Omitted = whole catalog (existing behaviour). Provided = export limited to that set of works. Tool descriptions disclose the per-tool scoping semantics — `ai_consent` and `catalog_asset_report` apply the scope where it is structurally meaningful (recording rows on ai-consent, placements on CAR) and document the partial-scoping cases so agents don't pass `work_ids` expecting whole-row filtering.

- **`pica_execute` accepts FLAT and `arguments`-alias call shapes.** The dispatcher resolves wrapped args from the first non-empty of `args` / `arguments` / FLAT top-level keys. Canonical remains `{ tool, args: {…} }`; the other two shapes are tolerated because agents emit them in practice. `arguments` alias is for the small fraction that mirror the outer `tools/call` envelope shape; FLAT is what most agents naturally emit when they have a single inner tool to invoke.

- **`pica_execute` returns structured `WRAPPED_ARGS_MISSING` when the wrapped tool needs args.** When the dispatcher resolves no wrapped args AND the wrapped tool's schema declares `required` fields, dispatch is short-circuited with a structured error naming the missing fields and showing both canonical (`args: {…}`) and FLAT example call shapes. Previously the dispatcher silently invoked the wrapped tool with `{}`, surfacing as opaque downstream failures like `PG_INVALID_UUID` on `/admin/works/undefined`. Best-effort — wrapped tools unknown to the registry still dispatch through, letting their own validation speak.

- **`pica_works_inspect` degrades gracefully on missing entities.** Calls naming a non-existent work id no longer fail with an opaque `PG_INVALID_UUID` / 500. The tool returns a structured `{ ok: false, error: "WORK_NOT_FOUND", context: { work_id } }` shape and lists the closest candidates (by name fuzzy match) so the calling agent can recover without round-tripping the user.

### Fixed

- **`pica_export_catalog_csv` schema reconciled with backend.** The tool's `format` enum is now `pro` | `distributor` | `nro` | `sync-pdf` (was `csv` | `json`, both rejected by the backend) and `format` is now marked required. The schema also exposes `work_id` (string) and `work_ids` (string[]) — the backend requires one. Tool description rewritten to explain each format's destination (PRO/CMO, DSP delivery, neighbouring-rights body, sync one-sheet) plus the XOR work scope and the `sync-pdf` single-only constraint. Prior to this fix the tool was uncallable from its own schema: agents got three sequential 400s and could not recover from the surface.

## [2.59.0] - 2026-05-22

Catalog-hygiene release. The interpretation layer an agent had to rebuild from row prose every session — what kind of person record is this, is this work near-complete, why is this attention row at the top — is now exposed as typed columns, queryable filters, sortable fields, and component-scored objects. One breaking change to `pica_dashboard_attention` rows (urgency).

### Added

- **`pica_people_query` filters by record kind and outreach readiness.** New filter params `record_kind` (`individual` / `placeholder` / `artefact`) and `outreach_state` (`ready` / `needs_email` / `needs_disambiguation` / `not_a_target` / `do_not_contact` / `previously_declined`). The same fields ship on every row of the response. "Show me individuals ready to contact" is one call.

- **`pica_works_query` filters and sorts by completeness.** New filter params `completeness_score_lt`, `completeness_score_gt`, `has_statement_data`, and a `sort: completeness_score:asc | completeness_score:desc` option. Every row carries `completeness_score`, `completeness_components`, `completeness_model_version`, and `has_statement_data`. The works-most-worth-fixing query is a single call.

- **Component-scored urgency on `pica_dashboard_attention` rows.** New `urgency: { score, components: { streaming_activity, sync_inquiries, missing_credit_fields, days_to_release }, model_version }` shape replaces the opaque `urgency_score: number`. An agent can now explain why a row is at the top of its queue without re-querying.

- **`failure_mode` field on every attention row.** Stable short identifier (e.g. `missing_iswc`, `no_writer_split`, `placeholder_credit_present`) for client-side clustering. The full set is enumerated in the shared actionable-items source.

- **Four new attention classes**: `placeholder_credits`, `people_records_need_cleanup`, `concatenated_entries`, `publishers_in_people_table`. Surface through `pica_dashboard_attention`, `pica_count_explain`, and `pica_dashboard_briefing` with the same shape as existing classes.

- **Briefing unconnected-people breakdown.** `pica_dashboard_briefing`'s unconnected-people headline now carries `breakdown: { ready, needs_lookup, needs_cleanup, needs_lookup_by_record_kind: { individual, placeholder, artefact } }`. Values sum to the headline by construction.

- **Cached `works.completeness_score`, refreshed on write.** Three new cached columns on the `works` table (`completeness_score`, `completeness_components`, `completeness_model_version`) maintained by an application hook on any cache-invalidating write. The query path on `pica_works_query` reads from cache; the response shape is identical whether served from cache or computed live.

- **Composite indexes for the new query paths.** `works_org_statement_data_idx` on the `work_registrations` satellite (which owns `prs_work_id` / `ppl_recording_id`) and `works_org_completeness_score_idx` on `works(organisation_id, completeness_score DESC)`.

- **`RECIPIENT_DO_NOT_CONTACT` error on `pica_collaborators_invite`.** When the target person has `outreach_state = 'do_not_contact'`, the tool short-circuits before any row is inserted into `collaboration_invites` and returns the new error code with the recipient identifier in `context`.

### Changed

- **BREAKING — `urgency_score: number` removed from `pica_dashboard_attention` row shape.** Callers reading `row.urgency_score` will see `undefined`. Migration: read `row.urgency.score` for the aggregate, or `row.urgency.components.<name>` for the per-axis values.

- **Completeness scorer honours UPC overlay.** `scoreWork()` now resolves UPC through the master-metadata overlay. Works whose canonical UPC lives on an associated `releases` row are no longer scored as if UPC were missing.

- **Completeness scorer recognises non-UK PROs and the MLC.** Beyond the UK PRS/PPL pair, the scorer now counts `pro_ids` membership in ASCAP, BMI, GEMA, SACEM, SOCAN, JASRAC, APRA as registration evidence, and counts `mlc_song_code` presence as US mechanical-rights coverage. ISWC presence counts as registration evidence regardless of UK CMO flags.

- **Completeness response on `pica_works_inspect({ sections: ["completeness"] })` adopts `{ score, components, model_version }` envelope** wrapping the per-category breakdown. Existing readers of `breakdown` keep working.

### Deprecated

- **`suggested_skill` on `pica_dashboard_attention` rows.** Aliased to `drill_down_skill` for one release cycle (~2026-06-22 removal); `pica_count_explain` sources already use that name.

### Fixed

- **`pica_provenance_work` and `pica_provenance_certificate` authenticate correctly under MCP grant tokens.** Both routes (plus four sister provenance routes — certificate download, quality / provenance root / search / verify) now use the standard organisation-context boundary; previously returned 401 to all MCP traffic because they routed through the legacy session-only auth helper.

- **MCP `client_name` attribution lands on every audit row** under both HTTP and stdio transports. The UA-fingerprint regex was widened to recognise `Claude-User` (claude.ai consumer web) and `openai-mcp/X` (ChatGPT) alongside the existing client families, closing a stamping gap on the stateless HTTP transport.

### Database (staging applied; production applied)

- `20260522132950_adr244_record_kind_outreach_state.sql` — `record_kind` and `outreach_state` columns on `people`, with classifier-driven defaults.
- `20260522160011_adr244_c_works_completeness_cache.sql` — cached completeness columns on `works`, composite indexes (`works_org_statement_data_idx` on `work_registrations`, `works_org_completeness_score_idx` on `works`), and a one-shot backfill function.

Consumes `@withpica/mcp-sdk@1.30.0`.

## [2.58.0] - 2026-05-19

### Changed

- **`pica_report_issue` surfaces regression context at submit time.** When the upsert RPC flips a previously-resolved issue back to open (or lands on an already-regressed signature), the tool now narrates "this is a regression of a previously-resolved issue (fixed on `<date>`: `<note>`). Total occurrences now N." and exposes `is_regression` / `previously_resolved_at` / `previous_resolution_note` in `structuredContent.data`. Lets agents tell the user the bug they just hit has a known prior fix without a follow-up `pica_my_reported_issues` call. Backed by a migration that extends `upsert_ops_issue_explicit` and `upsert_ops_issue_from_audit` RETURNING JSONB (applied to staging + production 2026-05-19, backwards-compatible widening). Consumes `@withpica/mcp-sdk@1.29.0`.

## [2.57.0] - 2026-05-18

Closes the ADR-241 substrate sprint — agent-side orientation skill, dashboard coherence with SQL-side `NOT EXISTS`, and 4 sister bug fixes that the agents themselves filed via `pica_report_issue` over the preceding 72 hours.

### Added

- **`first-session` skill (ADR-241).** Agent-side counterpart of `set-up-workspace`. Five phases: identity recognition → reporting-discipline opening note → context load → priority ranking + routing → close-out sweep. Names the right specialist skill per priority class and hands the steering wheel over — does NOT invoke the specialist itself. Discoverable via `pica_skill_list` + the `skill://withpica/first-session/SKILL.md` Resource per the SEP-2640 bridge.
- **`SERVER_INSTRUCTIONS` coaching line.** Directs the agent to call `pica_skill_get("first-session")` on the first user message of a session unless the prompt is already task-specific. Char budget bumped 2300 → 2400 to fit the addition; final length 2337.
- **`pica_issue_agent_grant.scopes.items.enum`** — the 15 `VALID_SCOPES` are now declared inline in the JSON Schema, surfacing through `pica_tool_details` introspection. Closes ops_issue `3a65aa71` (agents trial-and-erroring on `write:works` / `write:credits` / `write:enrichment` which aren't valid; only `write:catalog` is).
- **`pica_dashboard_attention` gains a `class` filter** taking one of `works_without_credits | works_without_registrations | audio_pending | unenriched_writers | recordings_without_splits | disputed_credits`. Specialist skills now have a single-call entry to the top-of-their-own-queue (ADR-242).
- **`pica_count_explain` taxonomy mode** — omit `count_source` to get the full actionable-issue taxonomy as `sources[]` with `drill_down_skill` named per class. Machine-readable routing table that ADR-241's `first-session` skill reads to pick its next step.

### Changed

- **`pica_dashboard_briefing` / `_attention` / `pica_count_explain` are now coherent projections of a single shared source.** Implements ADR-242 § Decision: new `lib/services/dashboard-actionable.ts` (`getActionableIssues()` with a per-class registry); three projection helpers shape its output for the three tool handlers. `sum(count_explain.sources[].count) === briefing.critical_issues_total` for the same org/moment, by construction. Closes ops_issue `4042e806` (briefing 1495, count_explain 338, attention 0 — three tools, three numbers, no path through).
- **`pica_works_query health_filter='needs_attention'` no longer errors.** Hardens `getWorksHealth()` with per-work try/catch; restores `needs_attention` routing in the assembled-truth path. Closes ops_issue `4da6a869`.
- **`pica_split_sheet_generate` "No credits found" surfaces as 400, not 500.** Routes through the user-input-error classifier so agents see an actionable message instead of an opaque server error. Closes ops_issue `899a3d63`.
- **`pica_submit_feedback` dedups by content-hash, not session-time.** Distinct content within the same session now passes; identical resubmissions still dedup within the rate-limit window. SDK errors propagate to `mcp_audit_log` via the error envelope (was silent generic error). Closes ops_issues `4bab716a` + `808e5fa0`.
- **`pica_memory_save` accepts both kebab-case slugs and UUIDs.** Root cause: validator used `apiKey.keyId` (not a valid `user_id` FK) for the `user_id` field; row insert failed silently and surfaced as `"invalid id format"`. Closes ops_issue `315ef479`.

### Dependencies

- `@withpica/mcp-sdk` `^1.27.1` → `^1.28.0` (new `DashboardResource.actionableItems` + `CountExplainResource.taxonomy` methods).
- `@withpica/mcp-utils` `^1.21.1` → `^1.22.0` (workspace-version-alignment fix; develop's Vercel deploy was failing since 2026-05-17 because the `^1.21.1` range that 2.56.1 introduced was not satisfied by the local `1.21.0` workspace, forcing CI/Vercel to pull a duplicate copy from npm registry and breaking type identity).

### Database (staging applied; production applies on develop → main promote)

- Migration `20260517220000_adr242_actionable_issues_rpcs.sql` — 4 Postgres functions (`dashboard_actionable_works_without_credits`, `..._works_without_registrations`, `..._recordings_without_splits`, `..._unenriched_writers`) using SQL-side `NOT EXISTS`. PICA builds for scale; the fetch-all + filter-in-JS pattern was rejected by the founder. All four `LANGUAGE sql STABLE SECURITY INVOKER SET search_path = ''` per the 2026-05-11 function-hardening sweep.

## [2.56.1] - 2026-05-17

### Changed

- Brand consistency: "PICA" → "withPICA" in package metadata, keywords (added `"withpica"`, `"music-catalog"`, `"music-rights"`), README, JSDoc headers, and customer-visible tool descriptions: `pica_delete_my_account`, `pica_gdpr_export_my_data` (description + completion message), `pica_telegram_connect`, `pica_onboarding_complete` (description + `intent_notes` field), and the `new-catalog-setup` prompt.
- Registered `pica_works_attest` and `pica_credits_send_for_attestation` in `REQUIRED_SCHEMAS_SOURCE` companion_calls (both tools declared workflow membership but weren't referenced — pre-existing drift caught by `lint:required-schemas`).

### Fixed

- **`pica_submit_feedback` + `pica_physical_assets_create` no longer leak SDK errors to the user (PRs #453 + #457, 2026-05-15).**
  Both executors had naked SDK calls — when the upstream API errored, the
  framework's outer catch in `tools/index.ts` JSON-stringified the error
  body straight to the conversation (`{error: "UNKNOWN_ERROR", message: "..."}`).
  Wrap each SDK call in `try/catch`: on throw, fire-and-forget
  `pica.opsIssues.reportIssue({ kind: "tool_failure", ... })` with the
  underlying message + status + parameter shape in `context`, return
  neutral text (`"couldn't <verb> right now — the team has been alerted."`).
  Input-validation early-returns are preserved — those carry actionable
  text and aren't server failures. PR #457 added `tool_failure` to the
  `OpsIssueExplicitKind` taxonomy and retagged both wrappers (PR #453
  shipped as `kind: "data_issue"` because `tool_failure` didn't exist
  yet). 12 new unit tests across `feedback.test.ts` (new file) +
  `assets.test.ts`. **Note:** the auto-audit upsert path is bypassed
  because returning `isError: true` logs as `result_status: "success"`
  (`tools/index.ts:1859`) — the executor reports manually instead.

### Added

- **`pica_report_issue` accepts `kind: "tool_failure"` (PR #457, 2026-05-15).**
  Sixth value in the agent-explicit kinds enum. Lets agents file SDK /
  upstream-tool failures directly via the report-issue tool, mirroring
  the new fail-quiet wrappers.

## [2.56.0] - 2026-05-14

### Added

- **`conflict_strategy` arg on `pica_import_execute` (PR #427, promoted to main via PR #428 on 2026-05-13).**
  New optional input on the import executor MCP tool. Enum `["error", "skip"]`,
  defaults to `"error"` to preserve current bulk-batch behavior. `"skip"` falls
  back to per-row insert on Postgres 23505 unique-constraint collisions
  (duplicate `tunecode`/`iswc`), so a single duplicate no longer kills the
  entire batch — duplicates surface as warnings and the rest of the rows still
  land. Backward-compatible (existing callers without `conflict_strategy` get
  the default `"error"` behavior). Pairs with the underlying server-side
  satellite-write fix that also shipped in PR #427 (PRO-registration fields
  routed to `work_registrations` per ADR-125 WS5).

## [2.55.0] - 2026-05-13

### Added

- **`pica_release_notes` tool + `release-notes://withpica/mcp-server/latest` Resource (PR #412, merged to develop 2026-05-13, promoted to main via PR #417).**
  Agent-facing surface for platform release notes. Reads `CHANGELOG.md`
  embedded at build time via `scripts/build-changelog.ts`. Returns the
  last 3 versions by default with optional `since_version` and `sections`
  filters. Resource counterpart serves the full CHANGELOG for
  Resource-native clients (SEP-2640 bridge pattern, consistent with
  ADR-140 Phase 2b). Disambiguated from catalog "what's new" phrasing
  by tool name (`release_notes` ≠ "what's new") + NOT-FOR disclaimer
  in description pointing catalog phrasing at `pica_dashboard_briefing` /
  `pica_works_query`.

## [2.54.0] - 2026-05-13

> Skipped `2.53.0` — that version was claimed by a parallel publish on
> 2026-05-12 before this branch's publish chain ran. Sister of the
> sdk's `1.26.0 → 1.27.0` skip in the same chain. See the
> `feedback_npm_publish_chain_order` memory entry for the pattern.

### Added

- **`pica_import_analyze` / `pica_import_validate` / `pica_import_execute` accept `file_id` as an alternative to `file_content` (PR #415 → develop `2e74411bc`, PR #416 → main `3849c98e4`).**
  Closes the upload-then-import loop. After `pica_upload` →
  `pica_upload_complete`, agents can pass the resulting `documents.id`
  as `file_id` instead of pasting raw CSV content into a tool argument.
  Server resolves the bytes via a short-lived signed URL — org-scoped,
  CSV MIME-validated, 20 MB cap, UTF-8 validated. Requires
  `@withpica/mcp-sdk@^1.27.0` for the matching `ImportFileSource`
  type. Backwards-compatible: existing `file_content` callers
  unchanged.

### Fixed

- **Stale analyze-route assertions in `app/api/admin/import/__tests__/` flipped 500 → 400 (PR #396 era cleanup, bundled with the bridge).**
  Tests had been asserting the pre-PR-#396 500 status for parseCSV
  throws; route correctly returns 400 (caller-supplied bad input) since
  that fix. Loosened 4 `"fileContent is required"` exact-match
  assertions to `/fileContent or fileId/i` to match the new mutually-
  exclusive contract.

## [2.52.2] - 2026-05-13

### Changed

- **Server-instructions polish — workspace deeplink hint after writes + paragraph trims (commits `5a2bca4a1`, `7a3e2c226`).**
  Added a one-line coaching that agents share `https://withpica.com/workspace?type=<entity>&id=<id>` after every write tool so users get a clickable receipt back to the workspace — the cheapest version of the "chat ↔ workspace" tie-in (ADR-234), no per-tool sweep, agents construct the URL from the entity ID already in every write response. Two trims funded the addition: the `pica_execute` collision disclaimer compressed to one sentence (~120 chars saved) and the peer-MCP paragraph dropped its inline URI-scheme inventory (`gmail://` etc.) in favour of pointing at `docs/mcp-source-uri-conventions.md`, while gaining a load-bearing sentence that citations persist verbatim and propagate to applied entities (ADR-178 — `field_proposals.sources` jsonb + on-apply propagation to `recordings.field_sources`). Token budget restored 2400 → 2300, final length 2192/2300, headroom 108 chars.

## [2.52.1] - 2026-05-13

### Fixed

- **`pica_import_analyze` TypeError (`parsed.rows` → `parsed.preview`) (PR #408, shipped 2026-05-13).**
  Every successful analyze call returned `CSV analyze failed: Cannot read
properties of undefined (reading 'slice')`. Three-layer contract drift:
  the route returns `parsed: { headers, preview, totalRows, delimiter }`,
  the SDK type declared `parsed: { headers, rows, totalRows }` (wrong
  field, missing `delimiter`), and the MCP executor read `parsed.rows
.slice(0, 3)`. The SDK's `request<T>()` JSON.parse-cast with no runtime
  validation let TypeScript compile the lie. Aligned executor to read
  `parsed.preview`; SDK type aligned in `@withpica/mcp-sdk@1.25.1`.

## [2.52.0] - 2026-05-12

Closes out the accumulated Unreleased buffer (some entries below shipped in
2.49.0–2.51.0 but were never moved out of Unreleased; the `lint-changelog`
script that lands in the next bump prevents this drift going forward).

### Added

- **File-delivery tools `pica_file_deliver` + `pica_file_revoke_delivery` (PR #385, shipped 2026-05-12 in `@withpica/mcp-server@2.52.0`).**
  Agent-facing counterpart to the upload tools — symmetric with `pica_upload` /
  `pica_upload_file`. Wraps the first-class `/admin/files/deliver` surface
  (token + recipient_email + expiry + revocation) so agents can share files
  out of PICA with audit-trail accountability and a kill switch. Default
  expiry 48 hours; max 168 (one week). Registered in `discovery.uploads`
  (now bidirectional), `metadata.ts`, `recovery-hints.ts` (deliver →
  `rich_response`, revoke → `terminal`), and `lib/billing/tool-cost-model.data.ts`
  (deliver → export/EXPORT, revoke → admin/ADMIN). Backed by new
  `FilesResource` on `@withpica/mcp-sdk@1.25.0`.

- **ADR-214 follow-up: cross-transport resource parity lint (`scripts/lint-resource-coverage.ts`).**
  Asserts that `ResourceRegistry.listResources()` (stdio) and `UI_RESOURCE_DEFS` / the
  anonymous-lobby handler (HTTP, `app/api/mcp/route.ts`) expose the same resource URIs.
  Catches the W2 drift class — resources added to one transport only — before publish.
  Four assertions: stdio-lobby ⊆ HTTP-lobby, HTTP-lobby ⊆ stdio-lobby,
  stdio-authenticated \ ADR-152-carve-out ⊆ HTTP-authenticated, and the inverse
  HTTP-authenticated ⊆ stdio-authenticated. Wired into `prepublishOnly` (after
  `lint:agent-guide`) and `.github/workflows/mcp-tool-lint.yml`.

  **Pre-existing drift detected:** `pica://schemas/sync-placement-required` is present
  in `UI_RESOURCE_DEFS` (added in 2.47.0 per CHANGELOG) but absent from
  `ResourceRegistry.listResources()`. The lint correctly flags this; see the PR for
  the fix path.

### Changed

- **`metadata.test.ts` invariants tightened + counts re-baselined (PR #385).**
  Four long-standing failures resolved as part of the file-delivery sweep:
  (1) stale total-count assertion (175 → 242) reframed as a documented
  tripwire — drift means a tool was added without a `TOOL_METADATA` entry;
  (2) stale destructive-count assertion (12 → 17) — same shape, security-
  relevant because the destructive-tool dispatcher routes on `risk`;
  (3) `display_name` lowercase rule was over-strict — relaxed to "first
  character lowercase" so legitimate acronyms (S3, GDPR, ISNI, PICA, Azure,
  OAuth) pass per `.claude/rules/ui-components.md`'s proper-noun convention;
  (4) `pica_storage_disconnect` had `risk: "destructive"` + `retry_safe: true`
  in violation of the destructive-tool invariant — flipped `retry_safe` to
  `false`. Two-step confirmation token remains the actual safety mechanism;
  the tool's data-level idempotence claim stays true.

- **Tool registry catch path now forwards `error_message` to the audit log (PR #308).**
  When a tool throws, the registry calls `formatError()` and now passes the
  parsed `message` through to `logToolExecution` as `error_message`. Pre-fix,
  every error row in `mcp_audit_log` landed with `error_message=null` because
  the wire shape stripped the field — even when the wrapper held the
  underlying cause. Net effect for diagnostics: rows like the 116 silent
  `pica_merge_duplicates` failures on 2026-04-09 (which couldn't be
  attributed to any specific cause) will now carry the message going forward.
  Sister change in `@withpica/mcp-utils` adds the field to `McpAuditEntry`.

- **`pica_integrations_status` advertisement updated to current tools (PR #308).**
  ADR-179 (commit `dc1bfca72`, 2026-04-21) folded the six per-source
  `pica_enrich_*` tools into `pica_resolve_work` / `pica_resolve_person` /
  `pica_resolve_recording` with a `sources:[...]` parameter, then removed
  them. But `mcp-server/src/tools/integrations.ts` continued to advertise
  the removed names in the `platformReads` list, the tool description, and
  the summary prose. Agents reading the advertisement called names that no
  longer existed → UNKNOWN*ERROR loops on every catalog-enrichment ask
  routed through MLC/Spotify/MusicBrainz/YouTube/Discogs/ISNI. Now points
  at the surviving tools with the correct source value. Two regression
  tests in `integrations.test.ts` lock down that no removed
  `pica_enrich*\*`tool name appears in the advertised`use_via` strings.

## [2.48.0] - 2026-05-03

### Changed

- **Tool description scrub (PR #188).** Stripped internal-only ADR pointers and
  email-vendor names from user-visible tool descriptions across batch A
  (agreement-types, agreements, bulk, collaborators, credits), batch B
  (app-tools, feedback, projects, enrichment, recordings), and batch C.
  Replaced ADR-N references with the concept the ADR documented (e.g.
  "ADR-198 round-trip" → "verify the credit persisted") and removed
  Postmark from collaborator bulk-invite description and elicitation
  warning text. Tool descriptions surface to third-party agents and shouldn't
  reveal internal infrastructure choices.

- **recovery_hints expansion (PR #188).** Annotation fix + additional
  recovery hints for tools that fan out across sections.

- **`pica_releases_show` best-effort catch markers (ADR-224 convention).** Marked
  the two best-effort catches in `releases-rich.show()` (tracks + cover-art
  fallbacks) with `/* best-effort */` to match the ADR-224 Phase 4 lint
  convention. No behaviour change — the catches were already documented inline;
  this just makes the marker greppable so future scope extensions to `_show`
  tools recognise the intent. Surface audit confirmed all 9 `_inspect` tools
  addressed, 22 `_list` tools clean, no swallow patterns elsewhere.

## [2.47.0] - 2026-05-02

### Added

- **ADR-222 W3 — sync placements MCP tool surface (6 tools).**
  - `pica_sync_placements_query` — list placements with filters (work_id, status, verification_status, brand, production_company, person_id, recording_id, source_license_enquiry_id, min_fee, currency).
  - `pica_sync_placements_inspect` — fetch one placement with linked recordings, sources, agreement, and parties.
  - `pica_sync_placements_create` — atomic create (placement + draft sync_license agreement + ≥1 agreement_party + recordings + evidence sources in a single Postgres transaction). Empty `contacts[]` rejects with structured `MISSING_CONTACT` (`next_tool: "pica_people_query"`). New people auto-created inline via `person_name` instead of `person_id`.
  - `pica_sync_placements_update` — patch placement fields. Operator-only when `verification_status='operator_verified'|'disputed'` or `confidentiality_level` changes (throws structured `OPERATOR_REQUIRED`); illegal status transitions throw `INVALID_TRANSITION`.
  - `pica_sync_placements_delete` — operator-only hard delete (two-step confirmation token). Customer agents receive structured `OPERATOR_REQUIRED` with `next_tool: "pica_sync_placements_update"` pointing at the soft-delete path (`status: 'terminated'`).
  - `pica_sync_placements_cite` — attach an evidence source; auto-recomputes `verification_status` (any non-attestation kind → `evidence_attached`).

  All 4 mutating tools wrapped with `withBillingGate`. Stdio path delegates to `pica.syncPlacements.*` (new `@withpica/mcp-sdk` resource). HTTP transport mirrors via the shared `ToolRegistry`. Paired with W4's REST routes at `/api/admin/sync-placements`.

- **`pica://schemas/sync-placement-required` workflow schema resource (ADR-214).** Required + recommended fields for `pica_sync_placements_create` plus 4 companion call entries (inspect / update / cite / delete) and a worked example. Embeds the three `sync_placements` CHECK enums (`status` 5 values, `verification_status` 5 values, `confidentiality_level` 3 values) via the build-time generator + `schema-mirror.json` extension. Lives on both stdio (via the schema generator) and HTTP transport (`UI_RESOURCE_DEFS` in `app/api/mcp/route.ts`).

- **`WorkflowTag` union expanded 15 → 16 values** (added `sync-placement-required`). All 6 sync-placement tools tagged with this workflow; `lint-required-schemas` enforces forward + inverse coverage.

- **`schema-mirror.json` extended with `sync_placements` table** for status / verification_status / confidentiality_level CHECK enum embedding.

- **6 cost-model entries in `lib/billing/tool-cost-model.ts`** (READ for query/inspect, WRITE for create/update/delete/cite). Unblocks ADR-180 lint cost-model coverage check; ADR-215 shadow-metering will record `billable_events` rows on these tools from first call.

- **`sync_placements` discovery category** in `mcp-server/src/tools/discovery.ts` — layered-discovery agents (ChatGPT, Claude.ai connectors with `MCP_LAYERED_DISCOVERY=1`) can find the 6 tools via `pica_discover`.

- **MCP scope mapping** for the 6 tools under existing `read:agreements` (query + inspect) and `write:agreements` (create + update + delete + cite). Bundled under the agreements scope rather than minting a new public scope for one feature — every placement is backed by a sync_license agreement.

- **Four group-membership tools (ADR-223 Phase 5).** Customers can now
  manage group → member relationships on people directly via AI:
  - `pica_group_add_member` — add a person to a group person row.
    Server-side trigger enforces parent.is_group=true and same-org
    constraint; tool surfaces friendly errors at the route layer.
    Defaults: `role='member'`, `receive_notifications=true` (opt-out
    per membership). Wrapped in `withBillingGate`.
  - `pica_group_remove_member` — set `left_at` on the active row.
    Rows are never deleted; member history is preserved.
  - `pica_group_list_members` — active members by default;
    `include_inactive=true` for history.
  - `pica_person_list_groups` — inverse: every group a person is or
    was a member of.
    Role enum: `member`, `founder`, `lead`, `touring_member`,
    `session`, `guest`, `producer_in_residence`. Free-text `role_label`
    for variants outside the enum. Tools route through `mcp-sdk`
    `groups` resource → `/admin/groups/*` and
    `/admin/people/{id}/groups` HTTP routes.

### Changed

- **`@withpica/mcp-sdk` peer bumped to `^1.20.0`.** Required for the
  new `groups` and `syncPlacements` resources; older SDK versions lack
  `PicaClient.groups` and `PicaClient.syncPlacements`.

### Notes

- The 4 mutating sync-placement tools (`_create`, `_update`, `_delete`, `_cite`) need ADR-198 holdout contract specs in the sibling `Good-FM/pica-holdout-tests` repo. Coordinator follow-up — Path A from W3 Stage 1 audit §11.E.
- The brief's references to a `mcp-server/src/__tests__/contract-baseline.json` file and a `lint:contract-baseline` script are out of date; both were removed in ADR-180 Phase 3 (the lint chain is now fully strict by default — `scripts/lint-mcp-tools.ts` runs as part of `prepublishOnly`). W3 reinterpreted Stage 4 as ADR-180 metadata + cost-model + taxonomy entries (all added) plus ADR-198 holdout contracts (sibling repo, follow-up).

## [2.46.0] - 2026-05-02

### Changed

- **`pica_update_my_identity` now accepts `full_name`.** Customers can
  update the name on their PICA account via AI (e.g., fix typos from
  signup). Routes to `user_profiles.full_name`; rolls back via
  `revertMyIdentity` if the agent-action stamp insert fails. Tool
  description rewritten to lead with name capture. Closes the
  edit-symmetry gap surfaced during the SEV-1 sprint: previously the
  user could AI-rename their org but not their own name.

- **`pica_update_organisation_profile` now accepts `name`.** Customers can
  rename their organisation via AI (e.g., fix signup typos, replace the
  default "My Organization" placeholder). Updates `organisations.name`
  directly; non-empty TRIM validation matches the DB-layer SEV-1 guard
  shipped earlier on 2026-05-01. Tool description updated to mention the
  typo-fix use case. Closes the placeholder-name lock-in trap surfaced
  during the SEV-1 onboarding-stuck-state cleanup.

## [2.45.0] - 2026-05-01

### Added

- **ADR-214 Phase 3a — 3 new workflow schema resources.**
  - `pica://schemas/session-required` (studio session logging — `pica_sessions_create` primary; `pica_sessions_types` companion for session_type_id lookup; `pica_sessions_get` / `_list` for read).
  - `pica://schemas/split-sheet-required` (publishing royalty splits on works — `pica_split_sheet_generate` primary; `pica_split_sheet_send` / `_get` / `_list` companions).
  - `pica://schemas/recording-splits-required` (master ownership splits on recordings — `pica_recording_splits_create` primary with ADR-200 form-confirmation gate; `pica_recording_splits_verify` / `_list` companions).

  Each derives required + recommended fields from live tool input schemas at build time; `recording-splits-required` embeds the `split_type` and `role` CHECK enums (4 + 8 values respectively) from a hand-edited `schema-mirror.json` entry verified against prod project `fwgcmjhlwevdnxgqmkmh`.

- **WorkflowTag union expanded 12 → 15 values** (added `session-required`, `split-sheet-required`, `recording-splits-required`). 11 tools migrated from `"infrastructure"` to their P3a workflow tag (4 sessions + 4 split-sheet + 3 recording-splits).

- **Agent-guide expanded 6 → 14 sections** for authenticated mode:
  - 3 new write workflow sections (sessions, split-sheet, recording-splits) with canonical tool sequences and schema-URI footers.
  - 1 new identifier-assign narrative section explaining the inline-update pattern (`pica_*_update` accepts identifiers as inline fields including satellite-routed person identifiers — `isni`, `musicbrainz_id`, `wikidata_id`, `discogs_artist_id`, `deezer_artist_id` route to `person_enrichment` automatically; the surface is uniform).
  - 4 new read-workflow sections covering find-duplicates → merge, query → inspect → resolve gap-finding chain, lookup-person by industry identifier, and audit catalog completeness via dashboard tools.

  Authenticated guide now ~12,750 B (lobby variant unchanged ~855 B).

- **`schema-mirror.json` extended with `recording_splits` table** for `split_type` + `role` CHECK enum embedding. `sessions.status` and `split_sheets.status` documented inline in schema `summary` fields rather than `enum_tables` because they are not DB-CHECK-enforced.

- **2 new MCP prompts.**
  - `log-a-session` — guided studio session logging with optional `work_id` argument.
  - `set-recording-splits` — guided master ownership split assignment with ADR-200 propose-and-confirm + verify flow; takes optional `recording_id`.

- **`_meta.schemas` dispatcher emit on workflow-tagged tool results.** New `attachSchemaHints` private method on `ToolRegistry` mirrors the existing `attachSessionState` pattern verbatim — same merge semantics, null-safe handling, fail-open behavior. Every tool whose `workflows` field references one or more schema URIs auto-emits `_meta.schemas: ["pica://schemas/<workflow>-required", ...]` on successful results. 47 workflow-tagged tools across 13 files automatically tagged via the dispatcher hook (no per-executor edits). `ToolResult._meta` interface widened to `{ session_state?: SessionState; schemas?: string[]; [key: string]: unknown }`. Cross-transport survival verified — both HTTP (`app/api/mcp/route.ts`) and stdio (`mcp-server/src/server.ts`) preserve `_meta` end-to-end.

- **Cost-model entries for ADR-217 + ADR-218 tools** (`pica_notifications_recent` → READ, `pica_notification_acknowledge` → WRITE, `pica_storage_configure_start` + `pica_storage_disconnect` → ADMIN). Unblocks ADR-180 lint cost-model coverage check; ADR-215 shadow-metering now records `billable_events` rows for these tools.

### Changed

- **11 existing prompts updated to reference ADR-214 schema URIs** where the workflow shape is implicated: `analyze-catalog`, `find-duplicates`, `enrich-metadata`, `verify-works`, `assess-catalog-health`, `audit-credits`, `new-catalog-setup`, `close-the-loop`, `register-my-works`, `prepare-for-sync`, `workspace-autopilot`. Agents invoking a refreshed prompt pick up the relevant `pica://schemas/<workflow>-required` (and `pica://docs/agent-guide` where orientation helps) on first read.

- **`pica_merge_duplicates.entity_type` now accepts both forms.** Previously the enum was singular (`"work" | "person"`) while `pica_find_duplicates` was plural (`"works" | "people"`) — a real footgun documented in ADR-214 P3a Stage 1 R4. Tool now accepts all four (`works` / `people` / `work` / `person`); canonical plural form (`works` / `people`) matches the rest of the catalog tools. Internally coerces to plural for the success message and to singular for the SDK call. Backwards-compatible — no existing caller breaks. Success message no longer renders the broken `work(s)` / `person(s)` artifact when callers pass the plural form. The R4 confirm-branch (two-step ADR-200 confirmation flow) also uses canonical coercion with a try/catch fallback to "entities" so unknown entity_types don't break the confirm UX.

### Fixed

- **`pica_recordings_by_work` drift cleanup.** Removed tool was still referenced in:
  - `mcp-server/src/prompts/index.ts` `verify-works` prompt body — swapped to `pica_recordings_query` with `work_id` filter (single-line drift fix).
  - `mcp-server/src/tools/recovery-hints.ts` `next_tool` annotation — swapped to `pica_recordings_query`.
  - `mcp-server/README.md` Recordings Management section — section refreshed to current 5 tools (`_query`, `_inspect`, `_create`, `_update`, `_delete`); count corrected 6 → 5. Larger README drift in works/people/etc. sections flagged as separate cleanup.
  - `@withpica/mcp-utils` `recovery-hints.ts` DUPLICATE_ISRC `next_tool` — same swap (mcp-utils@1.15.1).

- **Source ↔ dist invariant restored** for `prompts/index.{js,d.ts,*.map}` (PR #173 source landed without committed dist regen — develop's dist had been missing `log-a-session` + `set-recording-splits`) and `tools/recovery-hints.{js,*.map}` (source was correct; dist was stale). No semantic change beyond what was already on develop; clears phantom local-tsc errors per `feedback_root_tsc_stale_mcp_dist`.

### Dependencies

- `@withpica/mcp-utils` `^1.14.0` → `^1.15.1` (recovery-hints `next_tool` fix).

## [2.44.0] - 2026-04-30

### Added

- **ADR-214 Phase 2 — 6 new workflow schema resources.**
  - `pica://schemas/audio-upload-required` (3-step chain: presigned →
    complete → analyze + poll)
  - `pica://schemas/enrichment-resolve-required` (resolve fan-out →
    proposals_list → proposal_apply / reject)
  - `pica://schemas/agreement-required`
  - `pica://schemas/multimedia-required`
  - `pica://schemas/export-required`
  - `pica://schemas/claim-required`

  Each derives required + recommended fields from the live tool input
  schemas at build time and embeds CHECK enums from `schema-mirror.json`.
  Agent-guide expanded with 6 workflow sections (4 net-new, 2 rewrites
  with schema-URI footers); intro bumps "five core" → "eleven core" and
  "~227 tools" → "~231 tools".

- **WorkflowTag union expanded to 12 values** (5 Phase 1 + 6 Phase 2 +
  `infrastructure`). 28 tools migrated from `"infrastructure"` to their
  Phase 2 workflow tag (4 audio-upload, 6 enrichment-resolve, 4
  agreement, 4 multimedia, 5 export, 5 claim).
- **`schema-mirror.json` extended with 6 new tables** for Phase 2
  CHECK-constraint enum resolution (agreements, discovered_artists,
  discovered_credits, discovered_custody, enrichment_proposals,
  multimedia_items). Verified live against prod project
  `fwgcmjhlwevdnxgqmkmh`.
- **HTTP transport coverage** — `app/api/mcp/route.ts` `UI_RESOURCE_DEFS`
  - `UI_RESOURCE_BODIES` extended with the 6 new schema URIs (matches
    the Phase 1 pattern from PR #152). Per
    `feedback_cross_transport_resource_coverage`.

## [2.42.0] - 2026-04-30

### Added

- **ADR-218 Primitive B — `pica_notifications_recent` tool.** New read-only
  MCP tool (`risk: "safe"`, `retry_safe: true`) returning the cross-transport
  `discovery_events` feed for the calling org, ordered by `created_at` DESC
  with optional `unread_only: boolean` filter (per-surface dedup against
  `seen_in`). Default limit 20, max 100. Distinct from `pica_notifications_list`
  which targets the person-scoped `pica.notifications` system; this reads the
  org-wide ledger that web bell + telegram dispatcher also share.
- **ADR-218 Primitive C — `pica_notification_acknowledge` tool.** New
  mutating tool (`risk: "mutating"`, `retry_safe: true`) that appends `'mcp'`
  to each event's `seen_in` array. Idempotent — re-acknowledging is a no-op.
  Org-scoped server-side: out-of-org ids are silently skipped. Empty
  `event_ids` returns a `VALIDATION_ERROR` without hitting the API.
- \*\*Both tools registered in the existing `notifications` taxonomy bucket
  - TOOL*METADATA (`category: "comms"`)\*\* + tagged `workflows: "infrastructure"`
    per ADR-214 W4. Ride alongside the legacy `pica_notifications*_`and`pica*notify*_` families — the agent disambiguates from the descriptions.

### Changed

- **ADR-218 Primitive D — telegram dispatch dedup.** `telegramNotificationService.notifyEvent`
  now consults `discovery_events` for an existing undismissed row matching
  `(organisation_id, event_type, entity_id)` where `'mcp' = ANY(seen_in)`.
  When found, suppresses the entire dispatch — closes the re-nag loop for
  `until_resolved`-strategy alerts. Fresh `one_shot` events with no prior
  row still fire normally. Lookup is fail-soft: any DB error logs a debug
  and proceeds with dispatch (better an extra ping than a silently-dropped
  alert).
- **ADR-218 Primitive A — `_meta.session_state` envelope gains optional
  `notifications` field.** `fetchSessionCounts` propagates the cross-transport
  unread summary from the catalog-stats response into the envelope. Missing
  field on older API servers leaves the envelope key off (no fabricated zero).

### Bumped

- `@withpica/mcp-sdk` peer to `^1.18.0` (`DiscoveriesResource.recent` +
  `.acknowledge` methods + `NotificationsSummary` type on `CatalogStats`).
- `@withpica/mcp-utils` peer to `^1.14.0` (optional `notifications` field on
  `SessionStateCounts` + exported `NotificationsSummary` type).

## [2.41.0] - 2026-04-30

### Added

- **ADR-210 P3 — `getAuthenticatedInstructions(slice)` + `getTrialEndingClause(slice)`
  exports in `src/server-instructions.ts`.** New helpers that optionally append
  a state-conditional trial-ending nudge to the authenticated handshake
  instructions. The clause fires only when `billing_state === 'trial'` AND
  `trial_days_remaining` is non-null AND `<= 3`; in all other cases the
  function returns the unchanged base const. Pass `null` when no slice is
  available — slice-less callers fail safe. Existing
  `SERVER_INSTRUCTIONS_AUTHENTICATED` const is unchanged (parity with
  `lib/mcp/server-instructions.ts` preserved).
- **New `TrialNudgeSlice` interface export.** Minimal shape consumed by
  the helper: `{ billing_state: string; trial_days_remaining: number | null }`.
  Structurally compatible with the platform's `OrgBillingSlice` type so
  callers can pass either.

### Note on stdio behaviour

The stdio binary in this release continues to call the equivalent of
`getAuthenticatedInstructions(null)` at boot (returns the unchanged const) —
trial state is not resolvable at boot without a `pica.catalogStats()` round
trip. Stdio agents still see live `trial_days_remaining` via
`_meta.session_state` on every write-tool result. A future phase may add
boot-time slice resolution; this release ships the helper itself so the
HTTP transport (`/api/mcp`) can use it immediately on a per-request basis.

## [2.40.0] - 2026-04-30

### Changed

- **ADR-214 W4 — `workflows` field is now required on `ToolDefinition`.**
  Flips the optional `workflows?: WorkflowTag | WorkflowTag[]` field
  introduced in W1 to a required `workflows: WorkflowTag | WorkflowTag[]`.
  TypeScript compilation now enforces that every new tool authored
  declares its workflow membership at the source site — no escape
  hatch except the explicit `"infrastructure"` opt-out. Drift
  prevention mechanism #1 from ADR-214 §"Drift prevention" is now
  fully load-bearing. `lint-required-schemas.ts` hardened: the
  parallel-window warning for untagged tools becomes a hard error
  (defence-in-depth in case a tool somehow reaches the registry
  without a `workflows` field at runtime).

### Added

- **ADR-214 W2 — agent-guide + required-fields schema resources.**
  Two new resource families on the catalog `mcp-server`:
  - `pica://docs/agent-guide` — workflow narrative for agents. Visible
    in both lobby and authenticated modes; content branches on
    `lobbyMode`. Lobby variant (~865 bytes) covers the signup → magic
    link flow + what unlocks. Authenticated variant (~4.3 KB) lists the
    five core workflows (register a work, add a recording, add audio,
    add recording credits, enrich metadata) with verbatim tool sequences
    and 8 common gotchas — verified against the live tool registry on
    2026-04-30.
  - `pica://schemas/work-required`, `pica://schemas/recording-required`,
    `pica://schemas/work-credits-required`,
    `pica://schemas/recording-credits-required`,
    `pica://schemas/person-required` — JSON contracts derived at build
    time from each tool's `inputSchema` (in `ToolRegistry`) joined with
    `schema-mirror.json` for live CHECK constraint enums. Authenticated
    mode only.
- **Build-time generator + schema mirror.**
  - `mcp-server/scripts/build-required-schemas.ts` — runs as part of
    `npm run prebuild` (chained after `bundle-apps.ts`); reads
    `required-schemas.source.ts` + `ToolRegistry` + `schema-mirror.json`
    and emits the deterministic `required-schemas.generated.ts`
    (committed to git so PR diffs surface drift, per
    `feedback_type_regen_cumulative_drift`).
  - `mcp-server/src/resources/schema-mirror.json` — committed snapshot
    of CHECK constraint enums for `works`, `recordings`, `work_credits`,
    `recording_credits`, `audio_files`. Refresh via
    `mcp-server/scripts/refresh-schema-mirror.ts` (humans only; CI never
    auto-regenerates).
- **Inverse-coverage CI lint (`scripts/lint-required-schemas.ts`).**
  Walks `ToolRegistry` and `REQUIRED_SCHEMAS_SOURCE` from both
  directions: every tool whose `workflows` field tags it for a workflow
  must appear in the source file; every source reference must resolve
  to a real tool. During the parallel-development window (W1 still in
  flight), tools missing the `workflows` field emit a single summary
  warning (per coordinator decision Stage 1 §R2). Once W4 makes the
  field required, the warning becomes an error.
- **Agent-guide drift lint (`scripts/lint-agent-guide.ts`).** Greps
  every `pica_*` token in `agent-guide.ts` against `ToolRegistry` (in
  both `discoveryMode` settings, so meta-tools like `pica_discover` are
  resolvable). Catches stale references after tool renames.
- **Meta-tests** for both lints (per `feedback_meta_test_load_bearing`):
  3 / 3 regressions caught and reverted before ship — see
  `docs/follow-ups/2026-04-30-adr214-w2-meta-test-outcomes.md`.

### Changed

- `mcp-server/package.json` — `prebuild` now chains
  `bundle-apps.ts && build-required-schemas.ts`. New scripts
  `lint:required-schemas` and `lint:agent-guide` added; both wired into
  `prepublishOnly` after the existing checks.
- `mcp-server/src/resources/index.ts` — registers 6 new URIs across
  `listResources()` (1 in the lobby branch, 5 in the authenticated
  branch) and `readResource()` (1 case for the agent-guide variant
  branch, 5 cases for the schema URIs).

### Dependencies

- `pg` + `@types/pg` added as devDependencies (used only by
  `refresh-schema-mirror.ts` for direct Postgres CHECK-constraint pulls
  against staging).

### Internal

- **ADR-214 W1 — `WorkflowTag` type + optional `workflows` field on tool
  definitions.** Adds a union type
  (`"work-required" | "recording-required" | "work-credits-required"
| "recording-credits-required" | "person-required" | "infrastructure"`)
  and a `workflows?: WorkflowTag | WorkflowTag[]` field to
  `ToolDefinition` at `mcp-server/src/tools/index.ts`. All 231 tools now
  carry the field — 8 catalog-core tools (`pica_works_create`,
  `pica_works_update`, `pica_recordings_create`,
  `pica_recordings_update`, `pica_people_create`, `pica_people_update`,
  `pica_credits_update`, `pica_recording_credits_update`) reference one
  or more Phase 1 workflows; the remaining 223 carry
  `"infrastructure"`. Field is optional in W1 to keep the rollout
  additive; W4 (post-merge of W1 + W2 resource scaffolding) flips it to
  required so every future tool is forced to declare its workflow at
  compile time. No runtime behaviour change in this release.

## [2.39.0] - 2026-04-30

### Added

- **ADR-211 Phase 1 — `pica_signup_start` unauthenticated tool.** New
  agent-discoverable signup verb available without a connection key on
  both stdio (lobby mode) and HTTP `/api/mcp` (anonymous mode). Mints
  nothing locally — every invocation calls
  `POST /api/public/onboarding/signup-start` so the
  `ONBOARDING_TOKEN_SECRET` signing secret stays on the backend, never
  in customer-distributed binaries. Returns a 15-minute JWT-bound
  `/onboard/<token>` URL via `resource_link` (universal floor) +
  capability-gated `elicitation/create url` (mode: "url"). Constructor
  takes `{apiUrl, transport, clientId?}` — stdio binary stamps
  `transport: "stdio"`, the HTTP carve-out stamps `transport: "http"`
  and threads through any DCR `client_id` for OAuth resume.

- **`SERVER_INSTRUCTIONS_UNAUTHENTICATED` lobby variant.** Short
  ~600-char handshake text pointing the agent at the 3 lobby tools
  (`pica_signup_start`, `pica_sign_in`, `pica_sign_out`) instead of the
  full 200+ catalog surface. Selected at boot when `lobbyMode === true`
  (stdio binary without `PICA_API_KEY`) and at handshake when
  `/api/mcp` is invoked without a Bearer token. Catalog-tool guidance
  omitted because no catalog tools are reachable in lobby mode.

- **`SERVER_INSTRUCTIONS_AUTHENTICATED` alias** of the legacy
  `SERVER_INSTRUCTIONS` export — the unaliased symbol stays exported
  unchanged for backward compatibility.

### Changed

- **Lobby surface is now THREE tools** (was two): `pica_signup_start`
  joins the existing `pica_sign_in` + `pica_sign_out` pair. Additive,
  not replacement — Phase 1 evaluates consolidation in Phase 2 once
  the `/onboard/<token>` page is live and the JWT-bound flow is the
  preferred path. ADR-211 § Primitive A editorial amendment 2026-04-30
  reflects this.

### Notes

- **`ONBOARDING_TOKEN_SECRET` env var required** on backend
  environments (preview + prod already provisioned 2026-04-30). The
  `lib/auth/scoped-jwt.ts` substrate gained an additive
  `options.secretEnvVar` parameter so each scoped-JWT consumer
  (upload-token, onboarding-token, future ADR-214 storage-config) can
  rotate its signing secret independently. SUPABASE_JWT_SECRET still
  serves as the local-dev fallback in both modes.

- **No new exports in `@withpica/mcp-utils` or `@withpica/mcp-sdk`** —
  Phase 1 consumes existing exports (`clientSupportsUrlElicitation`,
  `CallerContext`) only. Per
  `feedback_workspace_package_publish_discipline.md`: no bumps needed
  upstream of this release.

## [2.38.0] - 2026-04-30

### Added

- **ADR-208 Phase 3 Primitive D — `pica_release_show` rich-render tool.**
  The catalog's first MCP tool returning `image` + `text` content blocks
  in a single agent turn. Cover art ships as a base64 JPEG ≤512px;
  markdown summary covers title, year, primary artists, label name, UPC,
  catalog number, and tracklist with ISRCs and durations. Read-only
  (`readOnlyHint: true`). Registered in
  `mcp-server/src/tools/discovery.ts` `CATEGORIES.releases.tools` in
  the same commit as the `ToolRegistry` registration per the layered-
  discovery completeness rule.
- **`_meta.ui.resourceUri = "ui://pica/release"`** — MCP Apps clients
  (Claude Desktop, Cursor) render the iframe via the new resource at
  `mcp-server/src/apps/release.ts`. Phase 3 ships the resource stub
  (title + markdown + cover image binding); full interactive iframe
  with track-list expand and inline streaming embeds is queued under
  the ADR-200 Phase 1.x backlog.
- **`structuredContent`** envelope — `{ title, markdown, cover_base64,
cover_mime_type, resource_link_uri }`. Apps clients prefer this over
  re-decoding the image content block; non-Apps clients ignore it.

### Notes

- **Universal floor is `image + text` only in Phase 3.** A `resource_link`
  block is intentionally omitted because no release UI page exists in
  the codebase yet (verified — neither `/portal/releases/[id]` nor
  `/admin/releases/[id]`). Building one is queued under
  `docs/follow-ups/2026-04-30-adr208-phase3-5-release-detail-page.md`
  and ADR-208 § Primitive D was editorially amended on develop
  (`30ceefc9d`) to reflect the deferral.
- **"Key credits" deferred from the markdown body.** The existing
  `releasesService.listTracks()` join surfaces work + recording artist
  but not `work_credits`; adding the join widens Phase 3 scope. Will
  land alongside ADR-213 when credit-attribution surfaces consume the
  data cheaply.
- **Sharp lives in the main Next.js app, not in this stdio binary.**
  Cover-art bytes come from the new `/api/admin/releases/[id]/cover-art-thumbnail`
  endpoint that does the resize + cache server-side, so the
  customer-distributed `@withpica/mcp-server` install stays free of
  sharp's ~50MB platform-specific native binaries.

### Changed

- **Bumps `@withpica/mcp-sdk` peer to ^1.17.0** to consume the new
  `ReleasesResource.getCoverArtThumbnail()` method.
  `@withpica/mcp-utils` stays at ^1.12.0 (T2.4's bump absorbed via the rebase).

## [2.37.0] - 2026-04-30

### Added

- **ADR-210 Phase 2 — `pica_subscription_status` and `pica_subscription_manage`.**
  Two new tools shipping the explicit billing read + action surface
  that complements Phase 1's ambient `_meta.session_state.billing_slice`.
  - `pica_subscription_status` (`readOnlyHint: true`) wraps the
    existing `GET /api/admin/subscription` route and flattens it into
    the 9-field shape ADR-210 § Phase 2 specifies (`billing_state`,
    `trial_days_remaining`, `trial_ends_at`, `current_tier`,
    `capacity_used`, `capacity_limit`, `capacity_pct`,
    `recommended_tier`, `summary`). The `summary` line is a
    server-side template the agent uses verbatim — predictable
    wording, less variance across connectors.
  - `pica_subscription_manage` (`readOnlyHint: false`) is a single-verb
    dispatcher over the existing `POST /api/admin/subscription`
    (subscribe / upgrade) and `POST /api/admin/subscription/portal`
    (manage / cancel) routes. Returns `{url, expires_at, surface}`.
    Subscribe / upgrade without `tier` returns a recoverable error
    with `next_tool: pica_subscription_status` so the agent can
    chain to read `recommended_tier` first rather than auto-pick.
    Universal-floor `resource_link` content block ships on every
    success response per ADR-200 Phase 1; `elicitation/create url`
    fires only when `clientSupportsUrlElicitation(server)` (the
    capability gate is mandatory because `elicitInput` with
    `mode: "url"` throws synchronously against non-supporters per
    `feedback_mcp_sdk_capability_gates_elicitation.md`).
- **`subscription` category in `discovery.ts:CATEGORIES`** ("view your
  billing state, manage your subscription, and pick a tier"). Lint
  enforced by `scripts/lint-mcp-tools.ts` per
  `feedback_mcp_taxonomy_completeness.md`.

### Changed

- **Bumps `@withpica/mcp-sdk` peer to ^1.16.0** to consume the new
  `SubscriptionResource`. `@withpica/mcp-utils` stays at ^1.12.0
  (T2.4's bump absorbed via the rebase).

## [2.36.0] - 2026-04-30

### Added

- **ADR-213 Primitive A — `pica_recordings_create` post-create attribution hints.**
  Every successful create call now appends up to three Important-severity
  hints via the new `buildRecordingAttributionHints(recording)` helper
  (`mcp-server/src/tools/recording-attribution-hints.ts`). Mirror of
  `buildCustodyHints` for works:
  - `recording_ownership_unset` — fires when `ownership_percentage IS NULL`.
    Suggests `pica_recordings_update ownership_percentage:N`.
  - `recording_no_credits` — always fires (we never know credits from a
    single fresh row). Suggests the new `pica_recording_credits_update`.
  - `recording_master_unclaimed` — fires when `ownership_percentage IS NULL`.
    Alternative path for multi-party master claims via `pica_custody_claim`.
    NULL-only triggers: `0` is "explicit zero", no hint. Negative-path
    covered by jest at `recording-attribution-hints.test.ts` per
    `feedback_meta_test_load_bearing.md`.
- **ADR-213 Primitive B — `pica_recording_credits_update`.** First-party
  recording-side write tool; closes the work/recording credit-write
  asymmetry. Writes to `recording_credits` (NOT the trade-secret
  enrichment-cascade-fed `credits` table — see `.claude/rules/ip-protection.md`).
  Uses the 16-role `RECORDING_CREDIT_ROLES` enum (`MainArtist`,
  `FeaturedArtist`, `Producer`, `Mixer`, `Engineer`, ... `Other`).
  Songwriting credits (Writer, Composer, Lyricist, Arranger as a publishing
  role) continue to route through `pica_credits_update` on the parent work.
  Registered in `discovery.ts` `CATEGORIES.credits` in the same commit per
  `feedback_mcp_taxonomy_completeness.md`. Listed under `write:catalog` scope
  in `lib/services/mcp-scopes.ts`. `nextSteps` points at
  `pica_recordings_inspect sections:["recording_credits"]` for ADR-198
  round-trip verification.
- **ADR-213 Primitive A.1 — `pica_recordings_inspect recording_credits`
  section.** Additive (not replacement). The existing `credits` section
  (work-side credits via parent work) keeps its current behaviour. New
  `recording_credits` section reads first-party operator-authored credits
  directly from the `recording_credits` table via the new
  `pica.recordingCredits.list()` SDK method.
- **`TOOL_METADATA` entry for `pica_recording_credits_update`** in
  `mcp-server/src/tools/metadata.ts` (catalog/mutating/non-retry-safe).

### Changed

- **Bumps `@withpica/mcp-sdk` peer to ^1.15.0** and `@withpica/mcp-utils` to
  ^1.12.0 to consume the new `RecordingCreditsResource` and the metadata
  entry shipped alongside ADR-213.
- **Tool count moves from 224 → 225** with the addition of
  `pica_recording_credits_update`.

## [2.35.0] - 2026-04-29

### Added

- **ADR-208 Phase 2 — `pica_upload` returns a JWT-bound `/upload/<token>` URL.**
  The executor now calls `pica.uploads.createSession()` for server-side
  mint of a 600s-TTL HS256 JWT scoped to the caller's org / user /
  bucket-key / acceptFilter. Universal floor (`resource_link`) and
  capability-gated `elicitation/create url` shapes unchanged — only the
  URL flips from `${webBase}/workspace` to `${webBase}/upload/<jwt>`.
  Tool description updated; obsolete Phase 1.5 forward-reference removed.

### Changed

- **`session_state.pending_uploads` now reads from
  `organisations.pending_uploads_count`** (atomic +1 on JWT mint via
  `/api/admin/uploads/sessions`, atomic -1 on JWT-path completion via
  `/api/upload/[token]`). Phase 1's stub at `0` is gone. Atomic semantics
  via the `inc_pending_uploads` SECURITY DEFINER RPC; `GREATEST(0, ...)`
  clamp prevents negative drift. Sits alongside T1.2's billing fields on
  the same `_meta.session_state` envelope — no shared edit to
  `mcp-server-shared/mcp-utils/src/session-state.ts`.

### Notes

- Fail-soft: if `createSession()` throws (network blip, route 5xx), the
  executor falls back to the legacy workspace URL and surfaces the error
  in the `text_summary`. The card still works for MCP Apps clients via
  `_meta.ui.resourceUri` regardless.

### Bumped

- `@withpica/mcp-sdk` dependency range bumped to `^1.14.0`
  (`UploadsResource`, `CreateUploadSession*` types,
  `CatalogStats.pending_uploads_count` now consumed).
- `@withpica/mcp-utils` dependency range bumped to `^1.11.0`
  (T1.2's billing-fields extension on `SessionStateCounts`).
- `mcp-server/server.json` registry manifest version bumped to `2.35.0`
  in lockstep with the npm package.

## [2.34.0] - 2026-04-29

### Added

- **ADR-210 Phase 1 — billing slice on `_meta.session_state`.** The
  `fetchSessionCounts` private in `ToolRegistry` now resolves the four
  billing fields (`billing_state`, `trial_days_remaining`, `current_tier`,
  `capacity_pct`) from `pica.catalogStats().billing_slice` alongside the
  existing counts. `attachSessionState` flows them onto every successful
  write-tool result through the same `_meta.session_state` envelope (no
  new emission path — extends the existing one) so the agent sees ambient
  billing posture between mutations without a `pica_subscription_status`
  round-trip. Phase 2 will land that explicit read tool; Phase 1 ships
  the substrate the tool reads from.
- Same fail-soft posture as the existing fetcher: when `this.pica` is
  missing (HTTP-discovery without bearer scope), or when the SDK response
  is missing `billing_slice` (older server pre-Phase-1), the fetcher
  emits a conservative `{ billing_state: "trial", trial_days_remaining:
null, current_tier: null, capacity_pct: 0 }` rather than fabricating
  "active" — the agent never sees misleading "active" billing for an
  uninitialized context.

### Bumped

- `@withpica/mcp-sdk` dependency range bumped to `^1.13.0`
  (`CatalogStats.billing_slice` field now consumed).
- `@withpica/mcp-utils` dependency range bumped to `^1.11.0`
  (`SessionStateCounts` shape extended with `BillingSlice` fields).
- `mcp-server/server.json` registry manifest version bumped to `2.34.0`
  in lockstep with the npm package.

## [2.33.0] - 2026-04-29

### Added

- **ADR-208 Primitive B — `_meta.session_state` attached to write-tool results.**
  The central `ToolRegistry.executeTool` wrapper now composes the six-key
  session-state envelope (`works_count`, `recordings_count`,
  `completeness_pct`, `mutations_this_session`, `since_last_briefing`,
  `pending_uploads`) for every tool whose risk is `mutating` or
  `destructive` (`readOnlyHint: false`), and attaches it under
  `result._meta.session_state`. Read tools (`risk: safe`) skip this
  entirely so the high-volume read path stays cheap; failed mutations
  (`isError: true`) also skip because catalog state didn't change.
  `pica_dashboard_briefing` running successfully resets the
  `since_last_briefing` counter — handled in the wrapper, not the tool.
  Cache key is org-scoped via `config.picaApiKey` (stdio) or
  `callerContext.callerIdentity` (HTTP); two orgs sharing a worker
  process never share state. Fail-open: any error in session-state
  composition is swallowed and the result returns without `_meta.session_state`.
  See `@withpica/mcp-utils@1.10.0` for the underlying `buildSessionState`
  helper and the load-bearing cross-org isolation regression test.

### Changed

- Bump `@withpica/mcp-utils` dep range to `^1.10.0` to pick up the
  `buildSessionState` / `incrementSessionMutations` /
  `resetSinceLastBriefing` exports that compose Primitive B.

## [2.32.0] - 2026-04-28

### Changed

- Bump `@withpica/mcp-utils` dep range to `^1.9.0`. The previously-pinned `^1.8.0`
  resolved (via the npm registry) to a tarball that pre-dated the addition of
  `clientSupportsUrlElicitation` and `elicitBulkConfirmation` to the source — Vercel
  builds where the lockfile resolved from registry instead of the local workspace
  link failed with `TS2305`. 1.9.0 republishes the same source state with both
  exports present, so registry-pinned lockfiles now build cleanly without needing
  the workspace-link override.

## [2.31.0] - 2026-04-28

### Added

- **ADR-200 Phase 1 — `ToolExecutorContext` plumbed through the registry.**
  `ToolExecutor` now takes an optional second arg `(args, ctx?)` carrying the
  per-request `Server` reference. `server.ts` passes `{ server }` through to
  `executeTool`; the discovery dispatcher (`pica_execute`) forwards it to its
  delegate. Existing executors with the legacy `(args)` signature keep working
  unchanged — only tools that need `server.elicitInput()` opt into the second
  arg. Pre-existing destructive-confirmation gate (two-step token) is
  preserved.
- **`pica_upload` portable across non-MCP-Apps clients.** Response now ships
  with a `resource_link` content block (universal MCP floor) alongside the
  legacy text envelope and `_meta.ui.resourceUri` — Apps clients render the
  iframe unchanged, ChatGPT / Gemini / Claude Code / curl-style clients see a
  clickable link to the workspace upload page. Clients that advertised
  `capabilities.elicitation.url` additionally receive a server-issued
  `elicitation/create url` consent prompt; the call is gated on the new
  `clientSupportsUrlElicitation` helper from `@withpica/mcp-utils@1.8.0` so
  callers without the capability never see the SDK's synchronous
  "Client does not support url elicitation" throw. `elicitationId` is a
  per-call UUID. Universal floor remains usable when the user declines or the
  elicitation times out — the resource_link in the response is the fallback.
- **`pica_download` ships a `resource_link` for the signed file URL.** Same
  universal-floor improvement on the download surface — text envelope still
  carries the JSON shape ADR-152 cards expect, plus the new resource_link
  exposes the URL/filename/mime to clients that render link blocks.
- **ADR-200 Phase 1 — form-confirmation gate on the 4 customer-MCP Class D
  bulk tools** (`pica_works_bulk_update`, `pica_people_bulk_update_roles`,
  `pica_recording_splits_create`, `pica_collaborators_invite_bulk`). Each
  now calls `elicitBulkConfirmation` from `@withpica/mcp-utils@1.8.0`
  after the validation + dry_run paths but before persisting. The gate
  fires only when the connected client advertises
  `capabilities.elicitation`, and in that case the destructive write only
  proceeds if the form returns `accept` with `confirm: true`. Universal
  floor: clients without elicitation continue to execute as before
  (existing destructive-token gate, where applicable, remains
  authoritative). 11 new tests cover decline, accept-without-confirm,
  no-caps fallback, and dry_run-skips-elicitation across the four tools.
  `team_comms_send` (5th Class D tool) is gated in
  `@withpica/team-mcp-server@1.3.0`.
- **`ToolResult.content` widened to permit `resource_link` blocks.** Legacy
  `Array<{ type, text }>` shape kept compatible via a structural union; tools
  that only return text need no changes.

### Changed

- **Tool descriptions for `pica_upload` / `pica_download` updated** to point
  at the ADR-200 fallback ladder rather than the prose "tell the user to use
  the workspace dashboard" hint that ADR-152 originally shipped. Behaviour
  for MCP Apps clients is unchanged.

### Removed

- **9 nested `withCreditGate(...)` wrappers unwrapped** across `tools/audio-files.ts` (2), `tools/exports.ts` (3), and `tools/enrichment.ts` (4). Each always ran inside a `withBillingGate(...)`; the inner wrapper was a no-op shim retained since ADR-162 disabled credit gating. Unwrapping leaves the subscription check as the sole gate on these tools, which matches the billing model.
- **`tests/utils/credit-gate-mpp.test.ts` + `credit-gate-mpp-paths.test.ts` deleted.** Both suites exercised a disabled MPP + pay-per-credit path that no longer has any production call-sites. `tests/tools/metadata.test.ts` also drops its `credit-gated tools should have credits_required set` assertion — redundant now that `credits_required` is gone from the shared `@withpica/mcp-utils` type (companion entry in that package's CHANGELOG). `tests/tools/credits.test.ts` stale `describe("pica_credits_balance")` block (and associated `mockCreditsBalance` setup) removed — that tool was pulled from the surface 2026-04-22 but the tests were missed in that pass.
- **`src/__mocks__/mppx-server.ts` + `mppx-mcp-sdk-server.ts` deleted** and the matching `moduleNameMapper` entries removed from `jest.config.js`. Both mocks existed only to stub out `mppx` for the now-deleted MPP unit tests. The `testPathIgnorePatterns` entry pointing at `credit-gate-mpp-paths.test.ts` is also removed (ghost reference to the deleted file).
- **"credit-gated" prose stripped from tool descriptions** in `tools/app-tools.ts` (`pica_car_preview` description + carPreview reuse comment) and `tools/documents.ts` (`pica_documents_analyse` description). These strings were advertised in the tool description, telling agents a tool was "credit-gated (5 credits)" when it's actually subscription-gated at workspace level.
- **`mppx` runtime dep still listed in `package.json`** — left in place to avoid lockfile churn; no `src/` consumer remains after this commit. Next publisher should drop it and rerun `npm install` to clean the lockfile.

## [2.30.0] - 2026-04-27

### Added

- **`pica_audio_inspect` basic section surfaces 4 previously-invisible
  columns** — `recording_id`, `stem_label`, `version_label`, and
  `classification`. All four are real columns on `audio_files` written by
  `pica_audio_complete_upload`. The 5th audit-flagged field
  (`content_type`) is mapped to `audio_format` at write time and was
  already surfaced. Closes a Phase 2 gap from the 2026-04-27 read-write
  parity audit. SDK type `AudioFile` extended with the same three
  optional fields (classification was already there).
- **`pica_organisation_profile` includes `org_type`** — paired read tool's
  underlying `/admin/settings/organisation-profile` SELECT now returns
  the CHECK-enum value that `pica_update_organisation_profile` already
  accepted (publisher / label / pro / sync_library / client / venue /
  distributor / other). Same class as the settings/identity fan-out
  fixed in 2.29.0 — write side accepted, read side truncated.
- **`pica_physical_assets_inspect` adds a `links` section** — surfaces
  the typed-edge rows that `pica_physical_assets_link_work` and
  `pica_physical_assets_link_recording` write to `work_production_assets`
  and `recording_production_assets`. Pre-fix the link rows were
  write-only from the MCP surface even though both
  `/admin/assets/[id]/{work,recording}-links` GET endpoints already
  existed. SDK gains `assets.listWorkLinks(id)` and
  `assets.listRecordingLinks(id)`.
- **`dry_run` flag on three more Class D mutating tools** —
  `pica_recording_splits_create`, `pica_collaborators_invite_bulk`,
  and `team_comms_send`. Completes the Class D coverage started in the
  ADR-199 5a part 1 commit (`a10006e5b`). Each tool early-returns
  `{ dry_run: true, would_affect, sample / target_count }` after
  validation and before the mutating SDK call, so agents can preview
  bulk effects and external-comms dispatch before committing. For
  `team_comms_send`, the preview echoes recipient and length-only
  metadata; the message body is not surfaced to keep ops messaging
  off the dry-run trace.

### Fixed

- **`pica_recordings_inspect` no longer hallucinates into the void on 404.**
  ADR-198 verification 2026-04-26 found the inspect tool was returning 200
  with `recording_error: "API request failed: 404 ..."` for every missing
  entity / missing route handler. Agents read this as a successful empty
  result and proceeded to call downstream sections (credits, audio) that
  also 404'd silently — no Sentry alert, no audit row, no user-visible
  error. Fix: basic-section catch now re-throws `EntityNotFoundError`
  (new typed export from `@withpica/mcp-sdk` 1.12.0+), so 404 surfaces
  as a hard `isError: true` envelope. Non-404 errors on the basic fetch
  still get absorbed into `recording_error` (preserves the partial-result
  pattern for transient failures).

## [2.29.0] - 2026-04-25

### Fixed

- **94 previously-undiscoverable tools retrofitted into the layered-discovery
  taxonomy.** Same bug class that bit four times in three weeks
  (`pica_upload`/`pica_download` since ADR-152, `pica_submit_feedback` since
  ADR-184, `pica_report_issue`/`pica_my_reported_issues` from ADR-196 Phase 3):
  registered in `ToolRegistry` but absent from `CATEGORIES` in
  `mcp-server/src/tools/discovery.ts`, so layered-discovery agents (default
  for ChatGPT and Claude.ai connectors with `MCP_LAYERED_DISCOVERY=1`) could
  not find them via `pica_discover`. The 2026-04-25 hardening that added
  blocking `checkTaxonomyCompleteness` to `scripts/lint-mcp-tools.ts`
  surfaced 94 grandfathered violators in the baseline. This release retrofits
  every one. Fourteen new categories — `agreements`, `collaborators`,
  `custody`, `physical_assets`, `recording_splits`, `royalties_statements`,
  `share_links`, `split_sheets`, `licensing`, `notifications`, `messaging`,
  `agent_identity`, `telegram`, `labels` — and the tools fold into existing
  ones (`releases`, `analytics`, `enrichment`, `import`, `projects`,
  `profile`) where they belong. `scripts/mcp-tool-lint-baseline.json
→ missing_taxonomy` is now empty.
- **`pica_upload` and `pica_download` newly reachable via `pica_discover`** —
  the two generic harness cards (ADR-152) had been silently absent from the
  `CATEGORIES` taxonomy since they shipped. `pica_upload` added to the
  existing `uploads` category alongside the programmatic `pica_upload_file` +
  `pica_upload_complete` pair. `pica_download` added to the `export` category
  since it's the delivery side of any export-tool flow.

### Changed

- **`CATEGORIES` is now exported** from `mcp-server/src/tools/discovery.ts`
  so `scripts/lint-mcp-tools.ts` can read it. Compile-time only; no
  consumer change.

## [2.28.1] - 2026-04-25

### Fixed

- **`pica_report_issue`, `pica_my_reported_issues`, and `pica_submit_feedback`
  were unreachable via `pica_discover`** — the layered-discovery taxonomy
  in `mcp-server/src/tools/discovery.ts` did not list them in any of its
  26 categories, so agents using the layered flow (default for ChatGPT
  and Claude.ai connectors that need to keep their context window
  manageable) literally couldn't find these tools. Real-world failure
  caught 2026-04-25: a Claude Desktop session dogfooding the issue-
  reporting flow returned "no feedback or support tool exposed in the
  PICA MCP" and suggested `pica_notes_create` as the closest match —
  the exact failure mode `pica_report_issue` was designed to catch
  (which it couldn't, because it wasn't discoverable). New `feedback`
  category added to the discovery taxonomy listing all three tools.
  `pica_submit_feedback` had been silently invisible since ADR-184
  shipped — same bug class, same fix.

### Changed

- **`pica_report_issue` description tightened** to instruct the agent
  on WHEN to call: PROACTIVELY without being asked when the agent itself
  hits a workflow gap, AND on-demand when the user says "this is broken"
  / "this didn't work". Previously the description was descriptive
  ("file a structured issue when…") which left agents reading it as a
  passive option rather than a recommended habit. Also adds a
  cross-reference to `pica_my_reported_issues` for the resolution-poll
  side of the closed loop.

## [2.28.0] - 2026-04-24

### Added

- **ADR-196 Phase 4a — `pica_my_reported_issues` tool.** Org-scoped
  customer-side polling tool that closes the loop on `pica_report_issue`.
  Lets the agent answer "did anyone fix that thing I reported?" without
  going through the operator surface. Returns the issue, current status,
  resolution_note, and occurrence_count for each issue the calling org
  has reported or been impacted by. Default sort most-recently-touched
  first; optional `status` filter (open / triaged / investigating /
  resolved / wont_fix). Tool metadata: `category=comms`, `risk=safe`,
  `retry_safe=true`. Pairs with `@withpica/mcp-sdk@1.12.0` for the new
  `OpsIssuesResource.listMine()` method.

### Changed

- **`mcp-server/src/config.ts` VERSION constant is now derived from
  `package.json`** via `createRequire(import.meta.url)("../package.json")`,
  not a hardcoded literal. Drift-proof — future bumps no longer require
  manual sync between the two files. The version-sync lint in
  `scripts/lint-mcp-tools.ts` now accepts both forms (literal still
  blocks on drift; derived is always valid). Motivated by two
  consecutive publishes (2.27.0 + 2.28.0) being blocked by the stale
  literal — the trap is now permanently sealed.

### Fixed

- **`mcp-server/src/tools/discovery.ts` no longer references
  `metadata.credits_required`** — the property was removed from
  `ToolMetadata` in the chore/drop-mcp-credit-gating cleanup (per
  ADR-162: PICA is subscription-only) but two stale references in
  `discovery.ts` slipped through, blocking the publish until fixed.
  No tool carries the field anymore so the conditional spread was
  dead code; replaced with a comment recording the history. The
  comment itself is removed in chore/drop-mcp-credit-gating
  (this PR) — credit gating is gone for good.

### Changed

- **ADR-199 Week 1 — `withBillingGate` extended to every write-tool
  surface.** Previously the gate only covered `audio-files.ts`,
  `exports.ts`, and `enrichment.ts` (8 tools). Now wraps 38 write
  executors across 9 tool families: `agreements` (4), `agreement-types`
  (5 — `renderTemplate` deliberately ungated as it's a pure read),
  `custody` (4), `people` (3), `works` (5), `recordings` (3), `credits`
  (1), `assets` (7 — `assetExport` deliberately ungated to match
  `pica_export_catalog_csv`), and `dashboard` claim/review tools (4).
  Read tools (query / list / inspect / history / search / stats /
  pulse / briefing) intentionally not wrapped. Behaviour change for
  agents on hibernated workspaces: write tools that previously executed
  now return the lobby error. Operation labels in the lobby message
  use domain terms ("agreement creation", "physical asset link",
  "custody claim", "discovery review", etc.). The gate itself reads
  from `organisations.billing_state` via the new effective-state route
  (see `[Unreleased]` in `@withpica/mcp-utils`); `is_internal=true`
  organisations bypass the gate. Rollback path is the
  `ADR199_READ_BILLING_FROM_ORG` env var on the Vercel app — flipping
  to `false` reverts the read source to `user_profiles.billing_state`
  without an MCP redeploy.
- **ADR-199 Week 1 language sweep — agent-facing copy moves to domain
  terms.** "Your organisation" / "this organisation" / "your org"
  swept to "your catalog" (where the noun is the music data) or "your
  account" (where no domain term fits) across `audit`, `collaborators`,
  `custody`, `dashboard`, `directory`, `memory`, `metadata`,
  `my-reported-issues`, `releases`, and `settings` tool descriptions.
  Internal identifiers (`pica_organisation_profile`,
  `pica_update_organisation_profile`, `org_type` parameter, the
  `"organisation"` memory-visibility enum value) deliberately
  preserved — those are wire-protocol or schema names, not copy.
  Industry term "Performing rights organisation" preserved (it's a
  PRO, not a withPICA org). Display names in `metadata.ts`
  (`view organisation profile` / `update your organisation profile`)
  swept to `view account profile` / `update your account profile`.

## [2.27.0] - 2026-04-24

### Added

- **ADR-196 Phase 3 — `pica_report_issue` tool.** Lets an agent file a
  structured issue when a workflow breaks in a way raw tool failures can't
  capture (`workflow_gap` / `inconsistent_result` / `missing_step` /
  `data_issue` / `ux_confusion`). Auto-dedupes via the v2 signature
  (sha256 of `"v2|{kind}|{canonicalised_summary}"`), so two reports of
  the same problem with different wording collapse into one
  `ops_issues` row. Operators triage from the same record. Raw tool
  errors continue to auto-populate `ops_issues` via the `mcp-audit.ts`
  upsert path — agents should NOT call `pica_report_issue` for those
  (it would create a duplicate). Tool metadata: `category=comms`,
  `risk=mutating`, `retry_safe=true`. Pairs with
  `@withpica/mcp-sdk@1.11.0` and `@withpica/mcp-utils@1.7.0` published
  in the same cycle.

### Fixed

- **`config.ts` VERSION constant bumped to 2.27.0** to match the
  `package.json` version. The `version-sync` lint in
  `scripts/lint-mcp-tools.ts` blocked the initial 2.27.0 publish until
  this was corrected — caught a pre-existing trap where the banner
  version is hardcoded in source rather than derived from `package.json`
  at build time. Future bumps must update both files; consider
  auto-deriving in a follow-up.
- **`pica_report_issue` tool — drop double-unwrap of SDK response.**
  `OpsIssuesResource.reportIssue()` now returns the unwrapped payload
  (mcp-sdk@1.11.0 tightened the return type), so the tool's previous
  `result?.data || result` fallback was dead code that didn't
  typecheck. Now `const data = result;` directly.

## [2.26.0] - 2026-04-22

Version leapfrogs 2.25.0 (which was published to npm without a matching commit / changelog entry in this repo, discovered during this publish cycle). 2.26.0 ships from a clean main at commit `a3c8ca870` with the full bundle below. Pairs with `@withpica/mcp-sdk@1.10.0` and `@withpica/mcp-utils@1.6.0`, both published the same day.

### Removed

- **Billing-credit tools removed from the MCP surface:** `pica_credits_balance` (credits.ts), `pica_credits_history` (settings.ts), `pica_credits_purchase` (purchases.ts tombstoned to an empty class). See `docs/MCP_REFINEMENT_LOG.md` entry B-001. withPICA is subscription-only at £9.99/mo; surfacing a credit-balance + credit-history + credit-purchase tool trio contradicted the billing model and misled agents (caught 2026-04-22 when an agent transcript opened with "998,894 credits — plenty. Running the export now."). The underlying SDK methods (`creditsBalance.*`, `settings.creditsHistory`) and `lib/services/credits/**` infra remain intact — only the MCP tool surface shrinks. `pica_credits_list` and `pica_credits_update` are **kept**: they're work_credits (songwriter roles + splits on a work), not billing credits.
- **`CREDIT_INSUFFICIENT` recovery hints pointing at `pica_credits_balance`** removed from `recovery-hints.ts` for `pica_resolve_work`, `pica_export_song_registration`, `pica_export_catalog_asset_report`, `pica_audio_analyze`. Paired tests under `__tests__/tools/recovery-hints.test.ts` + `settings.test.ts` updated to assert the new shape.

### Changed

- **Credit-gating language stripped from tool descriptions** — `pica_audio_identify` ("Costs 3 pica credits on match…"), `pica_export_song_registration` ("Costs 1 pica credit."), `pica_export_industry_ready` (same), `pica_export_catalog_asset_report` ("Costs 5 pica credits…"), and the 3 `pica_resolve_*` tools in `enrichment.ts` ("Costs 1 pica credit per call."). The `withCreditGate` / `withBillingGate` runtime wrappers are **left in place** — only the agent-facing prompt-gating copy goes. Same B-001 refinement entry.
- **Server instructions** (`src/server-instructions.ts`, mirrored in `lib/mcp/server-instructions.ts` via the parity test) open with a one-line disambiguation from `@picahq/mcp` (Pica AI / picaos.com) — a different product whose short name collides with ours. Helps agents that have both connectors installed (or users who pasted their withPICA key into the wrong config) recognise which server they're actually talking to. See the picaos name-collision memory rule.
- **`pica_catalog_diligence` now returns structured JSON** instead of failing with a JSON-parse error on binary ZIP bytes. The fix lives in `@withpica/mcp-sdk`'s `AnalyticsResource.catalogDiligence()` — the SDK now requests `?format=json`. See the mcp-sdk CHANGELOG for detail. MCP refinement log: "Singleton — `pica_catalog_diligence` returns ZIP parsed as JSON".

### Fixed

- **ADR-174 Phase 2c — inverse phantom in `pica_works_update` (works slice).** Three jsonb compliance fields (`ai_disclosure`, `provenance_attestation`, `training_rights`) are satellite-routed to `work_licensing` via `WORK_SATELLITE_FIELDS` and accepted by both the HTTP route allow-list and the service layer, but were missing from `WORK_WRITE_PROPERTIES`. Agents following the `pica_works_update with ai_disclosure: ...` completion hint at works.ts:577 were hitting schema-level silent-drop (or strict-validator 400s). Added all three properties to the write schema with shapes copy-pasted from the parallel fields on `pica_recordings_update` (recordings.ts:193). Tool description updated to distinguish audio-trait satellite fields (still not exposed) from the three compliance satellite fields (now exposed). Full audit of the other 22 customer-MCP write tools yielded zero further inverse phantoms — see `docs/audits/2026-04-21-mcp-inverse-phantom-audit.md`.

### Added

- **ADR-189 Phase 4 slice 3: `pica_claim_artist` MCP tool.** INSTANT path for artist-claim on discovery-backed evidence. Wraps `artistClaimingService.processClaimDecision` to reuse the existing `work_claims.artist_claim_status` / `claimed_by_person_id` / `claim_confirmed_at` update path + `performer` work_credits insert. Refuses (409 `ADMIN_REVIEW_IN_PROGRESS`) when an open admin `artist_claims` row exists for the same (work, person) — returns `admin_review_id` in the body so the agent surface can route the user to resolve it. Rate-limited to 20/hour per user via the shared `claim_write` bucket. Wrapped in `withAgentActionStamp` when grant-authed — rollback captures pre-write `work_claims` row + tracks whether a new performer credit was inserted (deleted on compensation) or pre-existed (left alone). **Schema drift carryover:** the plan proposed capturing rollback state from 4 `works` columns, but live staging (`rtxcxazevrgcjnudvggf`) keeps the artist-claim lifecycle columns (`artist_claim_status`, `claimed_by_person_id`, `claim_confirmed_at`) on the `work_claims` satellite — only `primary_artist_id` lives on `works`. Service captures + restores from `work_claims` accordingly. The live `artist_claims.claim_status` CHECK constraint has no `under_review` state (only `pending | approved | rejected | withdrawn`), so the open-review gate checks `claim_status = 'pending'` only.

### Removed

- **ADR-154 F13: seven deprecated per-source enrich tools removed.** `pica_enrich_work_{mlc,musicbrainz,discogs,spotify,youtube}` and `pica_people_enrich_{isni,musicbrainz}` (tombstoned since ADR-179 Phase 1/2, 2026-04-17) are no longer registered — every call has returned a `-32070 TOOL_DEPRECATED` error for a full minor cycle, ending the one-minor deprecation window per ADR-179 Decision 2. Callers must use `pica_resolve_work(sources: [...])` or `pica_resolve_person(sources: [...])` instead; the `tools/list`, `metadata`, `recovery-hints`, and category-`discovery` surfaces shed the seven stale entries in this release. Downstream impact: clients that hardcoded the tool names now get `tools/list`-level absence rather than a deprecation envelope — fail-fast instead of fail-with-suggestion.

### Changed

- **ADR-154 F14: completion-hint sweep for the seven removed tools.** Updated every `→ then:` and hint-string reference that pointed at the F13 tombstones across `enrichment.ts`, `people.ts` (3 sites), `collaborators.ts`, `works.ts` (2 sites), `search.ts`, `server-instructions.ts`, `prompts/index.ts` (3 sites), and `resources/llms-primer.ts`. Agents discovering tools via descriptions will no longer be trained toward dead tool names. The ADR-179 "Replaces the five/two per-source tools" phrasing is gone from the resolver descriptions too — resolvers stand on their own now.
- **ADR-180 Phase 3: full TOOL_METADATA coverage.** 29 tools retrofitted in `mcp-server/src/tools/metadata.ts` across eight family-scoped commits — resolve (3), cascade (3), enrichment proposals (4), collaborators (4), notify-user scheduling (3), physical-assets links (4), misc singletons (5), and the ADR-185 Part 1 agent-identity drift (3). `scripts/mcp-tool-lint-baseline.json` is deleted; MCP Tool Lint now runs in fully-strict mode. No behaviour change — tool discovery, audit categorisation, and risk-tier gating now classify the same 29 tools they couldn't classify before. `credits_required` deliberately omitted from new entries (credit gating is a no-op pass-through since ADR-162). Retry-safety follows ADR-180's `risk × retry_safe` table — e.g. `pica_enrichment_proposal_apply` is `retry_safe: false` (writes to entity columns), `_reject` is `retry_safe: true` (content-hash suppression is set-once). ADR-180 flipped from `Proposed` to `Accepted`.
- **ADR-193 Surfaces 1+3: peer-connector coaching + URI source conventions (server-instructions addendum).** Handshake copy coaches agents to check peer MCPs (email / drive / notes / calendar / DAW) before asking users to paste or type data, and to cite findings via `pica_enrichment_propose` `sources[].url` using URI schemes — `gmail://` `gdrive://` `notion://` `file://` `telegram://` `calendar://` `daw://` `user://` `web://`. See `docs/mcp-source-uri-conventions.md` in the main repo for shapes. First concrete build for ADR-190 Principles 2 + 3 (Agent-Mediated Source Discovery). A latent HTTP-transport gap was fixed in the same develop commit (`63eef17f8`) — `/api/mcp/route.ts` was not wiring `SERVER_INSTRUCTIONS` into its Server constructor, so remote agentic clients (ChatGPT Apps, Claude Desktop remote, Cursor) saw zero coaching. The stdio transport in this package was always serving instructions correctly; only the remote transport was affected.

### Added

- **ADR-193 Surface 2: `gap_hints` envelope on 4 read tools.** `pica_works_inspect`, `pica_agreements_inspect`, `pica_people_inspect`, and `pica_enrichment_proposals_list` now emit an optional `gap_hints[]` field on `structuredContent` when a real gap is present. Each hint is capability-named (ADR-190 Principle 2 — `email_search` / `drive_search` / `filesystem_search` / `notes_search` / `calendar_search` / `web_search`, never vendor names), carries a concrete `query_shape`, and pre-fills the `then_call: "pica_enrichment_propose"` with the exact `then_args_shape` the agent should emit back (including URI-citation scaffolding from Surface 3). Gated by `MCP_GAP_HINTS_ENABLED=1` — off by default in prod. Empty array / omitted envelope when there's no real gap. Flat additive — the existing `CompletionHint` pipeline on `works_inspect` is untouched. Bundled: `enrich-metadata` prompt references the peer-connector pattern and points the agent at `gap_hints`.
- **ADR-193 Surface 4: `pica_enrichment_propose` accepts `proposal_action: "create"`.** Closes the reserved-but-unwired create path from ADR-178 Phase 1. The tool schema now accepts `entity_type: "recording" | "agreement" | "person"` paired with `parent_entity_type` + `parent_entity_id` for create proposals. Apply-path branches on `rule_id === "agent_research"` and routes to `recordingsService.create` (parent required; `work_id` linkage), `agreementsService.create` (optional work parent, linked via `agreementWorksService.addWork` after create), or `peopleAdminService.create` (standalone — people have no parent FK). Recording creates seed `field_sources` verbatim from `proposal.sources[].url`, preserving URI schemes (gmail://, gdrive://, notion://, file://, telegram://, user://, web://) per ADR-193 AC 5. Agreements + people don't carry `field_sources` on the main table today; provenance URIs are captured in `resolution_note` until a follow-up migration adds the column. Work + publisher creation remain explicitly out of scope (dedup-critical — deferred to future ADR). Migration `20260422_01_adr193_proposal_parent_fields` adds nullable `parent_entity_type` + `parent_entity_id` columns with a CHECK pair + partial index for the review-queue scan. Legacy `artist_title_to_youtube` create rule is untouched (it continues to overload `entity_id` as the parent work id). 4 new service-layer tests green; total proposal-service suite 42/42.
- **ADR-185 Part 1: three new session-auth-only tools.** `pica_create_agent_identity`, `pica_issue_agent_grant`, `pica_list_my_agent_grants`. Back a user-issued agent-identity + scoped-grant primitive — the substrate for Parts 2 (settings UI) and 3 (per-write provenance stamping). All three require `write:agent_identity` scope AND session-auth (raw API key / OAuth); the HTTP MCP dispatcher refuses `pica_grant_` callers with a structured `reason: "session_auth_required"` error before execution per AC 12. Subject is always the authenticated user — caller-supplied `user_id` is dropped per ADR-184 rule 2 / ADR-185 rule 1. Grant tokens use the format `pica_grant_<64 hex>` and are hashed at rest with HMAC-SHA256 + `API_KEY_HASH_PEPPER` (same scheme as api_keys per ADR-185 line 193 + ADR-167 D-5).
- **ADR-185 Part 2: two new session-auth-only tools.** `pica_revoke_agent_grant` (DELETE `/admin/agent-grants/:id`, idempotent, 404 masks ownership failures) and `pica_get_agent_activity` (GET `/admin/agent-activity`, paginated, optionally filtered by identity or grant). Both appended to `SESSION_AUTH_ONLY_TOOLS` in the HTTP MCP dispatcher and to the `write:agent_identity` scope bucket. Together with the Part 1 trio this completes the five-tool agent-identity surface the ADR Scope requires. Revocation takes effect on the next request (AC: within 60s — `resolveGrantToken` checks `revoked_at` on every call, no cache layer).
- **ADR-185 Part 3 passes 1-5: agent-action provenance stamping wraps 27 write-tool families.** Part 3 layers two complementary wrappers over existing write tools so every per-grant write records a row in `mcp_agent_actions` for audit + revoke-replay. `withAgentActionStamp` (compensation-after): handler runs, stamps; on stamp failure, compensation undoes the write via captured prior state — used for creates and updates (soft-delete families get meaningful delete compensation — flip `is_deleted` back). `withAgentActionStampFirst` (stamp-first, pass 4): stamp commits BEFORE the destructive op; stamp-ok + handler-fail logs a "ghost stamp" at error level — used for hard-delete families where compensation is impossible. Both wrappers are no-ops when the caller is not an agent grant (session-auth / raw API key writes are unaffected). Families covered across the five passes: works/recordings/people/agreements/releases/notes creates; notes_delete; sessions_create; share_links_create; projects_create; multimedia_create; recording_splits_create; works_update; recordings_update; people_update; agreements_update; projects_update; notes_update; releases_update; people_delete; publishers_create; the six hard-delete families (works/recordings/agreements/projects/sessions/releases) via the stamp-first variant; and pass 5's `pica_projects_attach_works` (project_works insert wrapped, GET + POST migrated from `requireAuth` + `getUserProfile` to `withOrgContext` for consistency). `pica_sessions_update` remains deferred — action-branching route (start/complete/cancel + participant add/remove + regular update) needs a dedicated design slice before wrapping.
- **New tool** `pica_submit_feedback`: agent-native feedback submission. Routes bug_report→devops, feature_request→ceo, question→comms, other→category-mapped. Requires `write:feedback` scope. Subject is always the authenticated user per ADR-184 rule 2. (ADR-184 slice 8 — landed on develop 2026-04-19 post-2.23.0 publish.)
- **ADR-189 Phase 3: two new identity-capture tools.** `pica_update_my_identity` (stage_name / IPI / ISNI / IPN / PRO) and `pica_update_organisation_profile` (org_type / display_name / tagline / ipi_number). Both admin-scoped, both `retry_safe: true` (idempotent upserts — re-applying the same IPI leaves the row in the same state). Subject derived from auth (no `user_id` / `organisation_id` parameters — ADR-184 rule 2). After the identity write, `pica_update_my_identity` re-runs cross-org discovery and returns the count of newly-visible credits / custody / artist-link rows so the agent can surface "found N more — claim them?" in the same conversation turn. Rate-limited at 20 writes/hour/user via `checkActionRateLimit` (R1 mitigation). Admin routes (`POST /api/admin/my-identity`, `POST /api/admin/organisation-profile`) wrap in `withAgentActionStamp` when the caller presents an agent grant (ADR-185 Part 3) — UPDATE compensation captures pre-write state via read-before-write and PATCHes the row back on stamp failure, or deletes a lazy-created person row.
- **ADR-189 Phase 4 Task 1: `pica_claim_credit`.** New activation-loop tool that drains a `discovered_credits` row into a `work_credits` row in the calling org, giving the user `view` access to the linked work. Check-and-set on `discovered_credits.status` prevents double-claims in concurrent sessions. Rate-limited to 20/hour per user via `checkActionRateLimit`. Returns `is_first_claim: true` on first-ever claim by the user (derived from `agent_action_log` stamp count). Wraps in `withAgentActionStamp` for grant callers — compensating transaction calls `revertClaimCredit` (flips discovery back to `pending` + deletes the `work_credits` row) if the stamp INSERT fails. Error taxonomy: `DISCOVERY_NOT_FOUND` (404), `DISCOVERY_ALREADY_RESOLVED` (409), `EVIDENCE_REVOKED` (410), `RATE_LIMITED` (429 + `Retry-After`). `pica_claim_credit` added to `ADMIN_ONLY_TOOLS` in `mcp-scopes.ts`; metadata entry `category: "discovery", risk: "mutating", retry_safe: false`. Route: `POST /api/admin/discoveries/[id]/claim-credit`. SDK method: `pica.discoveries.claimCredit(id)`.
- **ADR-189 Phase 4 Task 2: `pica_claim_custody` + +72h auto-approve worker.** Drains a `discovered_custody` row into a pending `custody_claims` row (status=`pending`, `chain`=custody_type, `custody_source`=`claimed`, provenance in `evidence.source`=`"auto-discovered-via-email"`). Schedules a silent-consent auto-approve by inserting a `scheduled_notifications` row (`source=pica_claim_custody`, `deliver_at`=+72h, message=`custody_auto_approve:<uuid>`). Does NOT mutate `works.{composition,master}_custodian_org_id` immediately — the 1-minute cron fires `scheduledNotificationsService.drainDue()` which now dispatches `source=pica_claim_custody` rows to a new `handleCustodyAutoApprove` branch that runs the **Delta 1 stale-promotion guard**: UPDATE `works` custodian columns ONLY IF the snapshot captured at claim time (`expected_previous_custodian_org_id` + `expected_custody_source` + `custody_side` — migration `20260421_01_custody_claims_expected_previous`) still matches; otherwise flip the claim to `superseded` (new status) and leave the catalog untouched. Error taxonomy identical to `pica_claim_credit`. `pica_claim_custody` was already in `ADMIN_ONLY_TOOLS` since Phase 4 Task 0; this ships the actual wiring. Route: `POST /api/admin/discoveries/[id]/claim-custody`. SDK method: `pica.discoveries.claimCustody(id)`. Stamp compensation via `revertClaimCustody` flips discovery back to pending, cancels the scheduled_notification, deletes the custody_claims row.

## [2.24.0] - 2026-04-21

### Fixed

- **`pica_integrations_status` no longer tells users to "connect Spotify".** Two real-user failures on Claude Desktop (2026-04-21) traced back to the tool lumping platform-level Spotify/YouTube (PICA has its own credentials; no per-user OAuth exists or has ever existed) into the same "Available to connect" list as genuine user-OAuth peer connectors (Google/Notion/Airtable/Telegram). Failure modes observed: (1) user pasted a Spotify track URL → agent replied "head to your Pica dashboard and add Spotify as an integration"; (2) user asked "what songs do I have in pica" with a catalog holding 1 work → agent replied "you currently have no connections set up in Pica — so there are no songs or any other data accessible." Reshape: the tool now returns `peerConnectors` (the 4 actual OAuth services) separately from `platformReads` (Spotify, YouTube, MusicBrainz, MLC, ISNI, Discogs, with the tool name for each). Summary leads with "PICA catalog is ALWAYS accessible via pica_works_query, pica_people_query, pica_dashboard_briefing, pica_search_all — no setup required." The dead `connectedProviders.includes("spotify")` check that could never evaluate true was removed.
- **Server instructions: catalog-is-always-accessible guidance.** Handshake now explicitly states (a) the catalog is always accessible via the read tools with zero setup, (b) `pica_integrations_status` showing "0 connected" refers to optional peer connectors, NOT the catalog — call `pica_works_query` to check, (c) Spotify/YouTube URLs go through `pica_import_streaming_link` / `pica_import_youtube_link` directly; no per-user Spotify/YouTube OAuth exists to set up, do not tell the user to "connect Spotify in your dashboard". Replaces earlier copy that left the agent free to hallucinate a dashboard-integration flow. `lib/mcp/server-instructions.ts` parity copy updated in lockstep; the HTTP transport at `/api/mcp` picks up the new text on next handshake without requiring a redeploy of this package. Under the 2000-char token budget at 1833 chars.

## [2.23.0] - 2026-04-19

### Added

- **ADR-181 Phase 1: `pica_audit_list` ported from `@withpica/mcp-server-settings`
  (ADR-154 F6).** Closes the one trailing-gap tool from the source-merge audit —
  every other tool the three sub-servers exposed was already registered by
  `mcp-server`'s superset. Registered in `lib/services/mcp-scopes.ts`
  `ADMIN_ONLY_TOOLS` alongside the team / org-profile admin operations; the
  `mcp_audit_log` read is org-scoped and matches the existing admin posture
  for `pica_organisation_profile` / `pica_team_list`. Landed in `54ae935d9`
  via PR #50.
- **ADR-181 Phase 1: bundle-size CI gate per Decision 2.** New
  `scripts/check-mcp-bundle.ts` walks `mcp-server/dist/`, sums byte size
  (excluding source maps), and fails if total > 210 MB (70% × Vercel's
  300 MB serverless-function cap). Runs on every push / PR that touches
  `mcp-server/**`, `mcp-server-shared/**`, the script, or the workflow
  itself (`.github/workflows/mcp-bundle-size.yml`). `package.json`
  `prepublishOnly` invokes it locally so a bloated bundle can't ship out
  of a developer machine. Current measurement on this branch:
  0.79 MB / 210 MB limit (0.38% of cap). Landed in `558315069` via PR #50.

## [2.22.0] - 2026-04-18

### Added

- **ADR-174 Phase 2 item 7 follow-up: `pica_projects_attach_works` (stdio).**
  Closes the `work_ids` FIELD_NOT_WIRED gap on `pica_projects_create` /
  `_update`. Pre-slice the MCP tools advertised `work_ids` but the admin
  route silently dropped it — the route redirect message already pointed
  agents at `POST /api/admin/projects/[id]/works`, but no MCP tool
  exposed that path. The new tool accepts
  `{ project_id, works: [{ work_id, project_day?, notes? }...] }` and
  attaches each item via `ProjectsResource.attachWork` with per-item
  result reporting (`attached[]`, `skipped[]` for already-linked,
  `failed[]` for other errors). Registered in `lib/services/mcp-scopes.ts`
  under `write:catalog`.

### Changed

- **ADR-174 Phase 2 item 22: `pica_publishers_create` reshaped.** Pre-slice
  the schema exposed only `{ name, ipi }` (2 fields) and the SDK's
  `PublishersResource.create` hit a 404 — the admin route did not exist.
  The companion slice 1 commit added `POST /api/admin/publishers`;
  this slice reshapes the tool to match. All 8 audit-row-22
  Add-candidates surfaced (`legal_name`, `country`, `isni`,
  `publisher_type`, `wikidata_id`, `parent_publisher_id`,
  `founded_date`, `headquarters`) plus the explicit `ipi → ipi_number`
  rename. `parent_publisher_id` description steers agents at
  `pica_publishers_query` to resolve the parent UUID. Required fields:
  `name`, `ipi_number`. The route is idempotent on `ipi_number` — a
  duplicate IPI returns the existing row with `already_existed: true`
  instead of failing.

- **ADR-174 Phase 2 items 14+15: `pica_agreements_create` / `_update`
  reshaped to consume `AGREEMENTS_WRITE_PROPERTIES` from
  `@withpica/mcp-utils`.** Tool schemas now advertise all 13
  Add-candidate columns from audit rows 14+15. Tool descriptions steer
  agents toward `counterparty_org_id` (ADR-176 first-class-party) over
  free-text `other_party_name` — use `pica_search_all` to resolve an
  org name to an ID. `_update` description calls out that
  counterparty_org_id changes require the linked-org check at the
  route layer (orgLinkingService.areLinked). No tool signature change
  otherwise; existing fields preserved.

### Route-side companion (non-package, same slice)

- `app/api/admin/agreements/route.ts` POST and
  `app/api/admin/agreements/[id]/route.ts` PATCH — phantom/unknown key
  rejection via `AGREEMENT_POST_ALLOWED_FIELDS` /
  `AGREEMENT_PATCH_ALLOWED_FIELDS` /
  `AGREEMENT_WRITE_PHANTOM_REDIRECTS` in
  `lib/services/agreements-write-constants.ts`. Pre-slice both routes
  spread `body` straight into the Supabase insert/update — effectively
  a wide-open write surface. Also surfaces and fixes pre-existing dark
  code in the PATCH test file (3 tests were sending `name` and `type`
  — phantom field names that silently dropped against `title` and
  `agreement_type`).

- `app/api/admin/publishers/route.ts` POST (NEW) — publishers had no
  admin route at all pre-slice, so `pica_publishers_create` and the
  SDK's `PublishersResource.create` 404'd at runtime. Slice 1
  (0ba4d400d) adds the route with phantom/unknown-field rejection via
  `PUBLISHERS_POST_ALLOWED_FIELDS` / `PUBLISHERS_WRITE_PHANTOM_REDIRECTS`
  in `lib/services/publishers-write-constants.ts`, idempotent
  deduplication by `ipi_number`, FK pre-validation on
  `parent_publisher_id`, and server-stamped provenance.

- **ADR-174 Phase 2 item 21: `pica_multimedia_create` reshaped.** Added
  15 real-column fields that were invisible to agents pre-ADR-174:
  `credits`, `duration_seconds`, `collection_id`, `display_order`,
  `is_featured`, `is_published`, `venue`, `event_name`,
  `performance_date`, `setlist_position`, `sync_side`, and the external
  identifier set (`spotify_url`, `spotify_track_uri`, `spotify_track_id`,
  `youtube_url`, `youtube_video_id`, `soundcloud_url`, `thumbnail_url`).
  Description now points agents at `POST /api/admin/multimedia/upload`
  for file uploads and `pica_multimedia_link_youtube` for the YouTube
  enrichment flow — the generic create surface is for external-URL
  linking and performance-context metadata.

### Route-side companion (non-package, same slice)

- `app/api/admin/multimedia/route.ts` POST — phantom/unknown key guard
  via `MULTIMEDIA_POST_ALLOWED_FIELDS` /
  `MULTIMEDIA_POST_PHANTOM_REDIRECTS` in
  `lib/services/multimedia-write-constants.ts`. Redirects upload-flow
  columns (s3*key, file_name, …) to `/api/admin/multimedia/upload`,
  enrichment columns (youtube_view_count, spotify_popularity) to their
  respective cascades, and scanner columns (virus_scan*\*) to the
  scanner flow. Also closes a latent privilege-escalation path: pre-slice
  the route overwrote `body.user_id` with the authenticated session's
  id, but a caller could still _attempt_ to pass `user_id` (silently
  overwritten). The guard now 400s on `user_id` / `organisation_id`
  injection attempts with a clear PHANTOM_FIELD message.

- **ADR-174 Phase 2 item 13: `pica_sessions_create` reshaped.** Dropped
  `additionalProperties: true` per Decision 2. Added 6 previously-missing
  real columns from audit row 13 as explicit Zod properties:
  `description`, `timezone`, `is_all_day`, `location_url`, `recording_id`,
  `status` (enum: scheduled / in_progress / completed / cancelled), plus
  the `metadata` jsonb bag and the `participants` relational array
  (already handled by the service but undocumented in the tool surface).
  `recurrence_rule` deferred — the service's `CreateSessionParams` type
  needs to accept it first (tracked in the route-guard PHANTOM_REDIRECT
  entry).

### Route-side companion (non-package, same slice)

- `app/api/admin/sessions/route.ts` POST now reads `body.status` and
  `body.metadata` (pre-slice silently dropped) and 400s on phantom /
  unknown keys via `SESSION_POST_ALLOWED_FIELDS` /
  `SESSION_POST_PHANTOM_REDIRECTS` in
  `lib/services/sessions-write-constants.ts`.

- **ADR-174 Phase 2 item 20: `pica_share_links_create` reshaped to
  consume `SHARE_LINKS_WRITE_PROPERTIES` from `@withpica/mcp-utils`.**
  Tool schema now advertises all 13 add-candidate columns from audit
  row 20 that were previously invisible to agents (access_type,
  allowed_person_ids, allow_comments, allow_view_collaborators,
  allow_view_financials, notify_on_access, watermark_audio,
  sent_to_emails, playlist_name, allow_streaming, include_all_files,
  included_file_ids, description). Executor widened to pass every
  caller-provided property through to the SDK / admin route — the
  route-layer allow-list (slice 1) rejects anything outside the
  contract. `password` remains the canonical plain-text input; the
  route hashes to `password_hash`.

- **ADR-174 Phase 2 items 16+17: `pica_agreement_types_create` / `_update`
  reshaped to consume `AGREEMENT_TYPES_WRITE_PROPERTIES` from
  `@withpica/mcp-utils`.** Dropped the inlined property map in favour of
  the shared module that documents per-branch applicability. Phantom
  fields `title`, `description`, `points_percentage`, `recoupment_terms`,
  `rights_assigned` removed from the schema (agents sending them now fail
  fast at the admin route guard with a `PHANTOM_FIELD` redirect pointing
  at the canonical column or resolution path). Tool descriptions updated
  to truthfully list the required fields per branch (template needs
  `name` + `category` + `templateText`; producer needs `producer_id` +
  `royalty_points` + `deliverables` + `work_id`|`recording_id`;
  work_for_hire needs `contractor_id` + `contractor_role` + `fee_amount`
  - `services_description` + `work_id`|`recording_id`). Tool description
    for `_update` now calls out that PATCH on producer_agreements and
    work_for_hire_agreements is action-only — the generic update path has
    no live field-update surface on those two branches until a follow-up
    slice promotes the action-based PATCH handlers to accept field
    updates.

### Added

- **ADR-179 Phase 1: `pica_resolve_work` (stdio).** Outcome-shaped
  resolver that fans a work out across every eligible enrichment source
  in one call. Replaces the five `pica_enrich_work_*` tools' role at
  the agent layer. Returns `applied[]` / `proposals[]` / `errors[]` /
  `recovery_hints[]`. `isError` is set only when every source that ran
  errored — partial failure is a non-error receipt. Credit + billing
  gates wire through the `external_resolver_call` action key.
- **ADR-179 Phase 2: `pica_resolve_person` (stdio).** Person-side
  resolver mirroring `pica_resolve_work`. Sources are narrower —
  `isni` | `musicbrainz` — covering the identity-graph cascade rules
  (ISNI→Wikidata and MBID→Wikidata crosswalks plus their downstream
  awards / biography enrichment). Same output shape, same isError
  semantics, same credit-gate action key as the work resolver.
  Registered in `lib/services/mcp-scopes.ts` under `write:people` and
  listed in the layered-discovery `enrichment` category so
  `pica_discover` surfaces it above the deprecated
  `pica_people_enrich_*` pair.

### Deprecated

- **`pica_enrich_work_mlc`, `pica_enrich_work_musicbrainz`,
  `pica_enrich_work_discogs`, `pica_enrich_work_spotify`,
  `pica_enrich_work_youtube`.** All five now return
  `TOOL_DEPRECATED` (jsonrpc code -32070) with a suggested-call
  payload pointing at `pica_resolve_work` with the correct `sources`
  narrowing. Tool definitions stay registered so `tools/list` and
  `pica_tool_details` can still surface them with a migration hint for
  one minor version — removal scheduled for the next minor after
  ADR-179 Phase 4's publish.
- **`pica_people_enrich_isni`, `pica_people_enrich_musicbrainz`.**
  Both now return `TOOL_DEPRECATED` (jsonrpc code -32070) with a
  suggested-call payload pointing at
  `pica_resolve_person(sources: ['isni' | 'musicbrainz'])`. Tool
  definitions stay registered until the next minor; same one-minor-
  version deprecation window as the work-side five.

### Changed

- `pica_tool_details` responses for the five deprecated work tools
  now carry a `TOOL_DEPRECATED` entry in `recovery_hints` as the first
  item. Existing `NO_MATCH` / `MISSING_INPUT` hints are kept behind
  it — they are dead code today but stay in the registry until tool
  removal so downstream consumers don't see a mid-version
  disappearance.
- Same treatment applied to the two deprecated person tools:
  `pica_people_enrich_isni` and `pica_people_enrich_musicbrainz` each
  get a `TOOL_DEPRECATED` recovery hint prepended to their existing
  `NO_MATCH` entry.

## [2.22.0] — 2026-04-16

### Changed

- **ADR-174 Phase 2 item 10 (recording_splits): reshape
  `pica_recording_splits_create`.** Adopts the shared
  `RECORDING_SPLITS_WRITE_PROPERTIES` from `@withpica/mcp-utils@1.4.0`,
  dropping `additionalProperties: true` per Decision 2. All 9 audit-
  Keep fields now enumerated as explicit Zod properties — no phantom
  additions, no renames. Description-level hint on `role` replaced
  with a proper `enum` array matching the route's validRoles allow-
  list. Schema is now byte-identical to `@withpica/mcp-server-business`
  (both consume the shared module) — pre-slice they were independent
  byte-divergent-risk copies.

### Route-side companion (non-package, same slice)

- `app/api/admin/recordings/[id]/splits/route.ts` POST — UNKNOWN_FIELD
  guard runs before the required-field check so misspelled keys fail
  fast with the field name. See the slice commit.

## [2.21.0] — 2026-04-16

### Fixed

- **ADR-174 Phase 2 item 24 (team): `pica_team_update_role` now actually
  updates role.** Pre-ADR-174 the tool was named `_update_role` but its
  Zod schema only accepted `permissions` — the `role` field was missing
  entirely. The admin PATCH route at `/api/admin/team/[id]` hard-
  destructured `{ permissions }` and silently dropped every other
  field, so even if an agent hand-crafted a call with `role` the DB
  write would not happen. Audit row 24 flagged this as "Add `role`, or
  rename the tool." Fixed by adding `role: string` to the MCP Zod
  schema, widening the route to build the update object from whichever
  of `role` / `permissions` are provided, and making the old
  "permissions required" check accept "at least one of role or
  permissions." `id` stays required; `role` and `permissions` are now
  both optional but at least one must be set.

### Route-side companion (non-package, same slice)

- `app/api/admin/team/[id]/route.ts` PATCH — phantom/unknown key
  rejection via `TEAM_MEMBERSHIP_PATCH_ALLOWED_FIELDS` (allow-list of
  `permissions` + `role`), error message updated from "permissions
  required" to "at least one of permissions or role is required", and
  "failed to update permissions" to "failed to update team membership"
  (now-generic — it updates role too).

## [2.20.0] — 2026-04-16

### Changed

- **ADR-174 Phase 2 item 7 (projects): reshape `pica_projects_create` /
  `pica_projects_update` Zod schemas.** Exposed 9 Add-candidate fields
  from audit row 18 that were already plumbed end-to-end through the
  route and service but invisible to agents via tool discovery:
  `start_date`, `end_date`, `location_name`, `location_address`,
  `location_url`, `virtual_url`, `is_open`, `max_participants`, `status`
  (enum: draft / scheduled / active / completed / cancelled). Also
  surfaced the route-level `generate_invite_code` flag and the
  freeform `metadata` jsonb object. Dropped `work_ids` from both
  schemas — the pre-ADR-174 tool advertised `work_ids` but the admin
  POST / PATCH routes never routed it to the `project_works` junction
  (see commit `edf890a24` for the route-side FIELD_NOT_WIRED rejection
  and redirect message pointing at
  `POST /api/admin/projects/[id]/works`). The `project_type`
  description replaces the pre-ADR-174 "album, ep, compilation, single,
  mixtape" hint (none of which are valid backend values) with the
  actual convention: writing_camp, recording_session, production,
  mixing, mastering, retreat, other.

### Route-side companion (non-package, same slice)

- `app/api/admin/projects/route.ts` POST and
  `app/api/admin/projects/[id]/route.ts` PATCH — phantom/unknown key
  rejection with `FIELD_NOT_WIRED` redirect for `work_ids`. See commit
  `edf890a24`.

## [2.19.0] — 2026-04-16

### Added

- **ADR-178: `pica_enrichment_propose` stdio tool.** New agentic write
  tool that files enrichment proposals sourced from open-web research
  (Audiomack, Songdata, MusicBrainz page, label portals, artist bios,
  etc.) rather than from PICA's structured cascade. Every proposed
  field must cite at least one source URL — `sources[].fields` arrays
  form the trust moat, and the server rejects uncited claims with
  `code: MISSING_SOURCE`. Source type routes the proposal as
  `source = 'agent_research'`, `rule_id = 'agent_research'` on the
  existing `enrichment_proposals` table (no new queue, no new review
  tools — reuses `pica_enrichment_proposals_list` / `_apply` /
  `_reject` from `2.10.0`).

  Tool description carries ADR-178 Decision 3 language verbatim: when
  the user is in-session, the agent asks for approval directly and
  calls `pica_enrichment_proposal_apply` after confirmation — the
  dashboard card is the receipt, not the workflow. This shapes agent
  behaviour in-context without needing a new code path for "in-
  session approval".

  Wrapped in `withBillingGate` and (via
  `@withpica/mcp-utils@1.3.0`) `withCreditGate` under the new
  `enrichment_propose` action key. Credit gating is telemetric only
  while ADR-162 keeps the subscription gate on; the wrap is already
  in place so re-enabling billing doesn't need a tool-surface change.

### Fixed

- **ADR-178 slice 1b regression caught in smoke test:** agent-research
  proposals whose `proposed_fields` cite columns that don't exist on
  the target table (tempo_bpm, musical_key, producers — the Heinzmann
  transcript's fields, not columns on `works`) used to 500 with a
  Postgres "column does not exist" error because the service built a
  column-scoped `SELECT` from the proposed keys. Fix snapshots the
  full live row and builds `current_fields` from the intersection of
  proposed keys and actual columns. Unknown keys now land as pending
  proposals with an empty baseline instead of crashing the request.
  Apply path for unknown-column fields is a separate open question on
  the ADR (see Open Question 5).

## [2.18.0] — 2026-04-16

### Changed

- **ADR-174 Phase 2 item 6: reshape `pica_credits_update` Zod schema.**
  Dropped phantom `publisher_name` and `publisher_ipi` properties —
  `work_credits` has no columns for either. Agents now resolve a
  publisher name or IPI to a UUID via `pica_publishers_query` (or
  create one via `pica_publishers_create`) and pass the result as
  `publisher_id`. Added three audit "Add candidate" columns to the
  per-credit-object shape: `is_primary` (boolean), `notes` (string),
  `attestation_status` (enum: pending / attested / disputed /
  declined). `attested_at` / `attested_by` remain service-managed.
  Description refresh calls out `credit_type` as the canonical column
  name stored behind the `role` alias and `writer_split_percentage`
  behind `splits`. No required-field change — `person_id`, `role`,
  `splits` stay required.

### Route-side companion (non-package, same slice)

- `app/api/admin/works/[id]/credits/route.ts` POST — phantom/unknown
  key rejection on each `credits[i]` element with field name + index
  in the error payload, plus a mirror-fill step that normalises
  `role` ↔ `credit_type` and `splits` ↔ `writer_split_percentage` so
  callers sending either spelling flow through. See commit
  `dbfb098ae` for the server-side guard.

### Not covered by this slice

- `work_collaborators` (publishing-side POST at
  `app/api/admin/works/[id]/collaborators/route.ts`) was not in the
  ADR-174 audit — the MCP tool's `updateCollaborators` SDK hop hits
  that route for writer/composer/lyricist/arranger roles today and
  inherits a separate column set. Tracked as a follow-up slice.

## [2.17.0] — 2026-04-16

### Changed

- **ADR-174 Phase 2 item 5: reshape `pica_physical_assets_create` and
  `pica_physical_assets_update` Zod schemas.** Adopted the shared
  `PHYSICAL_ASSETS_WRITE_PROPERTIES` from `@withpica/mcp-utils@1.2.0`,
  dropped `additionalProperties: true` on both tools, and moved 11
  phantom top-level fields (`current_valuation`, `valuation_currency`,
  `valuation_source`, `valuation_date`, `valuation_notes`,
  `reverb_listing_url`, `ebay_item_url`, `insured`,
  `insurance_provider`, `insurance_policy_number`, `insurance_coverage`)
  off the top-level surface. Agents set these via
  `external_references: { current_valuation: 1500, insurer: "ACME", ... }`
  per the ADR-174 Open Decision 1 (2026-04-14) resolution. Two fields
  were renamed at the jsonb layer: `insurance_provider → insurer`,
  `insurance_coverage → insured_value`. `asset_type`, `condition`,
  `acquisition_method`, `license_type`, and `status` gained proper
  `enum` arrays matching the underlying `physical_assets_*_check` CHECK
  constraints — pre-reshape they were loose strings with the enum
  hinted in the description (so callers could pass `studio_equipment`
  at the MCP layer and get a 500 from the CHECK constraint at the DB
  layer).
- **No description lie.** Both tool descriptions now explicitly name
  `external_references` as the home for valuation/insurance/DSP URL
  details rather than the pre-ADR-174 "tracks … valuation, photos,
  insurance, and status" claim that the schema couldn't honour.

### Route-side companion (non-package, same slice)

- `app/api/admin/assets/route.ts` POST and
  `app/api/admin/assets/[id]/route.ts` PATCH — phantom/unknown key
  rejection with redirect messages (400 + `PHANTOM_FIELD` /
  `UNKNOWN_FIELD` error codes). See commit `ced46b889` for the
  server-side guard.

## [2.16.0] — 2026-04-15

### Added

- **ADR-173: five release-track tools.** Closes the gap flagged by two
  independent external MCP reviewers on 2026-04-14 ("release layer looks
  shallow"). New tools on the stdio transport:
  - `pica_releases_attach_track` — attach a recording and/or work at a
    specific `(disc_number, track_number)`. Idempotent on position.
    At least one of `recording_id` / `work_id` is required.
  - `pica_releases_list_tracks` — list tracks in `(disc, track)` order
    with inlined title/artist/ISRC/duration from the linked recording
    and work.
  - `pica_releases_detach_track` — soft-confirmation detach by position
    (preview without `confirm:true`, delete with).
  - `pica_releases_reorder_tracks` — transactional positional reorder
    via the `reorder_release_tracks_atomic` Postgres RPC. Rejects
    duplicate positions in the payload before any DB write.
  - `pica_releases_attach_recording_with_work` — compound workflow that
    auto-pulls the recording's linked work onto the new track row.

### Changed

- **`pica_works_inspect` and `pica_recordings_inspect` gain a `releases`
  section.** Lazy (only fetched when requested). Returns every release
  the entity appears on with its track position. Answers "which albums
  is this master on?" in one call — previously unanswerable via MCP.

### Fixed

- **`pica_releases_list_tracks` 500 on every call.** Pre-merge E2E
  caught two latent bugs in the `listTracks` service query: a missing
  FK `release_tracks.recording_id → recordings(id)` (PostgREST could
  not resolve the `recording:recordings(...)` embed) and a
  `duration_seconds` column reference where the live column is
  `duration_ms`. Fixed in migration
  `20260415_01_adr173_fix_release_tracks_recording_fk.sql` +
  a rename in `lib/services/releases.ts`. The migration also NULLs 85
  orphan `recording_id` values on production left behind by the
  2026-04-04 soundslikefez duplicate-works cleanup.

### Note on version number

This release would have been `2.15.0` per the original ADR-173
implementation record, but `2.15.0` was taken on `develop` by the
ADR-174 Phase 2 item 4 slice while the ADR-173 fix commits were in
flight. Bumped to `2.16.0` to keep the published package timeline
linear.

## [2.15.0] — 2026-04-15

### Changed

- **ADR-174 Phase 2 item 4: `pica_people_create` / `pica_people_update`
  schema reconciliation.** Introduced `PEOPLE_WRITE_PROPERTIES` as the
  canonical source of truth for the people write surface — every
  property maps to a real column on either `public.people` or
  `public.person_enrichment`. `additionalProperties: true` is off;
  unknown keys 400 at the HTTP layer (see develop commit
  `fbea9e2a3`).
- **Satellite-routing made explicit.** Eleven fields remain in the
  schema but their descriptions now name `person_enrichment` as the
  write target: external identifiers (`isni`, `musicbrainz_id`,
  `wikidata_id`, `discogs_artist_id`, `deezer_artist_id`) and
  biographical data (`date_of_birth`, `gender`, `nationality`,
  `birth_place`, `instruments`, `career_start_year`). The service
  fans these out through `syncPersonEnrichmentSatellite`. Correction
  to the original audit (row 7) landed in ADR-174's Corrections
  block: six of these fields were classified as "Remove — no column
  on people", which missed the satellite hop. They are not phantom
  writes, and subsequent slices must audit satellite write paths
  before classifying any field as phantom.
- **Twelve add-candidates surfaced** (audit row 7 "Missing writable
  columns"): `middle_name`, `person_type`, `company`, `position`,
  `territory`, `social_media`, `emails`, `pro_name`,
  `pro_member_number`, `mcps_member_number`, `relationship_strength`,
  `email_preferences`. All verified against `public.people` columns
  2026-04-15 (project `fwgcmjhlwevdnxgqmkmh`).
- **Deprecated duplicates rejected.** `type` (deprecated dup of
  `person_type`) and `role` (deprecated singular of `roles`) are
  now 400 PHANTOM_FIELD with a redirect message naming the canonical
  column. Both DB columns still exist as unused dead space —
  removing them is out of scope and scheduled for ADR-165-style
  cleanup.
- **Description lies removed.** The audit (row 8) flagged
  `pica_people_update` description as "Additional fields are also
  accepted" with no enumeration. That sentence is gone from both
  `create` and `update` descriptions.

### Breaking

- Callers that were passing the deprecated `type` / `role`
  duplicates now get a 400 with a redirect hint — previously the
  keys were silently dropped by the ORM. Same failure-mode calculus
  as the works / recordings slices — the writes were already
  failing, now they fail visibly.
- Callers that were passing arbitrary unlisted keys now get a 400 —
  previously `additionalProperties: true` let them through and the
  ORM dropped them silently. Zero rows observed in `people.metadata`
  jsonb for any of the pre-existing phantom keys (verified during
  ADR-174 Phase 1 2026-04-14).

## [2.14.0] — 2026-04-14

### Changed

- **ADR-174 Phase 2: `pica_recordings_create` / `pica_recordings_update`
  schema reconciliation.** Introduced `RECORDING_WRITE_PROPERTIES` as
  the canonical source of truth for the recordings write surface —
  every property maps to a real column on `public.recordings`. Added
  thirteen fields the DB exposes that the tool was hiding:
  `duration_ms`, `preview_url`, `album_art_url`, `spotify_url`,
  `spotify_track_uri`, `spotify_track_id`, `youtube_video_id`,
  `youtube_url`, `apple_music_url`, `deezer_track_id`,
  `mlc_recording_id`, `mlc_song_code`, `isrc_prefix`. The
  `version_type` field is now enum-restricted to the ADR-166 values
  (`master`, `alternate_master`, `music_video`, `lyric_video`,
  `live_performance`, `acoustic`, `remix`, `cover`, `alternate`),
  matching the `recordings_version_type_check` DB constraint.
- **`pica_recordings_create` default `version_type` changed from
  `"original"` to `"master"`.** Pre-fix the executor defaulted to
  `"original"`, which is not a valid version_type — every call without
  an explicit type tripped the recording-type CHECK constraint and
  surfaced as a 500 from the service. `"master"` is the DB-level
  default and the canonical ADR-166 value.
- **Title rename made explicit.** `recording_title` is the canonical
  column on `public.recordings`. The tool keeps `title` as an input
  alias for ergonomics; the schema now declares both fields with the
  rename called out in their descriptions, instead of relying on
  `additionalProperties: true` to hide the translation.
- Tool descriptions now steer album metadata (`album_name`,
  `album_release_date`, `upc`, `track_number`) to
  `pica_releases_create/update`, ISWCs to `pica_works_create/update`,
  and label metadata to `pica_releases_*.label_organization_id` via
  `pica_labels_query`. `duration_seconds` is explicitly named as the
  works convention; `recordings` carries `duration_ms`.

### Removed

- **ADR-174 Phase 2: seven phantom fields rejected on
  `pica_recordings_create` / `pica_recordings_update`.** The Zod schema
  no longer accepts them and `additionalProperties: true` is gone:
  `album_name`, `album_release_date`, `upc`, `track_number`, `label`,
  `duration_seconds`, `iswc` — none of these corresponded to columns on
  `recordings`. The HTTP route at `POST/PATCH /api/admin/recordings[/id]`
  now 400s on any of these keys with a redirect message naming the
  correct tool (commit `cffa6f947`).

## [2.13.1] — 2026-04-14

### Changed

- **ADR-174 Phase 2: `pica_works_bulk_update.updates` schema gated on
  `WORK_WRITE_PROPERTIES`.** The bulk tool was the last write-path
  surface that still accepted any key — `updates: { type: "object" }`
  with no property list (audit row 25). Now validates against the same
  canonical schema as `pica_works_update`: phantom keys (label, upc,
  album_name, …) and arbitrary unlisted keys fail fast with a
  descriptive `VALIDATION_ERROR` before the SDK is called. One bad key
  invalidates the whole batch — no partial writes.

## [2.13.0] — 2026-04-14

### Changed

- **ADR-174 Phase 2: `pica_works_create` / `pica_works_update` schema
  reconciliation.** Introduced `WORK_WRITE_PROPERTIES` as the canonical
  source of truth for the works write surface — every property maps to
  a real column on `public.works`. Added five fields the DB exposes
  that the tool was hiding: `tempo`, `key`, `display_artist`,
  `published`, `tunecode`. Tool descriptions now steer album/release
  metadata (`album_name`, `album_art_url`, `track_number`, `upc`,
  `spotify_url`) to `pica_releases_create/update` and label metadata to
  `pica_releases_*.label_organization_id` via `pica_labels_query`.

### Removed

- **ADR-174 Phase 2: seven phantom fields removed from
  `pica_works_create` / `pica_works_update`.** `label`, `spotify_url`,
  `album_name`, `album_release_date`, `album_art_url`, `track_number`,
  `upc` — none of these corresponded to columns on `works`. The HTTP
  route at `POST/PATCH /api/admin/works[/id]` now 400s on any of these
  keys with a redirect message naming the correct tool (commit
  `9d68b16b1`). Previously they routed silently into the
  `work_album_metadata` / `work_spotify_data` satellites that ADR-165
  marked as dead — a write succeeded but no reader could find the
  value.
- **ADR-174 Phase 2: `additionalProperties: true` removed from both
  works write schemas.** The tool no longer silently accepts arbitrary
  keys; unknown keys surface as validation errors. Description lies
  about `available_for_licensing`, `vocal_type`, `energy`,
  `danceability` also removed — those fields live on satellite tables
  and are not exposed through MCP.

## [2.12.0] — 2026-04-14

### Added

- **ADR-174 Phase 2: `pica_labels_query` tool** — read-only resolution of
  label names to `organisations.id`. Returns label organisations
  (`org_type='label'`) whose name or display_name matches the substring
  query, for use as `releases.label_organization_id` via
  `pica_releases_create` / `pica_releases_update`. `query` and `limit`
  both optional; limit defaults to 50, max 100. Category: catalog.
  Annotations: `readOnlyHint: true`.

### Changed

- **ADR-174 Phase 2: `pica_releases_create` / `pica_releases_update`
  schema reconciliation.** Input schemas now expose the nine ADR-174
  Decision 3 fields that `releases` actually stores:
  `label_organization_id`, `distributor_organization_id`,
  `pre_release_date`, `artwork_url`, `spotify_album_uri`, `spotify_url`,
  `apple_music_url`, `other_urls`, `notes`. Tool descriptions now
  direct agents to call `pica_labels_query` (new) or `pica_search_all`
  to resolve a label/distributor name to an organisation ID before
  writing.

### Removed

- **ADR-174 Phase 2: freetext `label` removed from
  `pica_releases_create` / `pica_releases_update`.** The field never
  corresponded to a real column on `releases` — the DB models labels
  via `label_organization_id` FK. Agents must now resolve a label name
  to an organisation ID (via `pica_labels_query`) instead of passing a
  string. Pre-fix behaviour: tool schema accepted `label`, service
  propagated it to the DB, the DB rejected it as `42703 column does not
exist`, every call 500'd. No working integrations could have relied
  on this — the change is strictly a fix, not a break.

## [2.11.0] — 2026-04-13

### Changed

- **ADR-171: Prompts replaced with workflow-level skills.**
  11 prompts → 10 skills, each with decision trees, domain knowledge,
  enrichment source priority, and the shared preamble (tone, prerequisite
  check, summary pattern). Deployed as upgraded prompts; will convert to
  MCP skills format when the spec extension ships.

  New skills: `workspace-autopilot`, `establish-identity`,
  `catalog-your-music`, `upload-and-analyse`, `close-the-loop`,
  `manage-collaborators`, `establish-ownership`, `production-assets`,
  `stay-connected`, `export-and-report`.

  Retired prompts: `analyze-catalog`, `find-duplicates`,
  `enrich-metadata`, `verify-works`, `assess-catalog-health`,
  `audit-credits`, `new-catalog-setup`, `register-my-works`,
  `prepare-for-sync`. Functionality folded into new skills —
  see design spec for mapping.
  - `workspace-autopilot` now routes through the full journey sequence
    (identity → catalog → audio → gaps → collaborators → ownership →
    notifications → exports) instead of the old 5-condition router.
  - `close-the-loop` absorbs 7 former prompts and adds the enrichment
    cascade decision tree (MLC first if IPI, MusicBrainz second, Spotify
    third) and inline duplicate detection.
  - `export-and-report` absorbs `analyze-catalog`, `register-my-works`,
    and `prepare-for-sync` into a single export/report skill with CAR,
    CWR, CSV, provenance, sync readiness, and carbon footprint paths.

## [2.10.0] — 2026-04-11

### Added

- **ADR-163: Tier B enrichment proposal review tools.**
  - `pica_enrichment_proposals_list` — list pending proposals
    awaiting review. Filters: `entity_type`, `entity_id`,
    `rule_id`, `source`, `limit` (1..100, default 20), `offset`.
    Only `status='pending'` rows are returned. Backed by
    `GET /api/admin/enrichment-proposals`.
  - `pica_enrichment_proposal_apply` — apply a pending proposal.
    For update proposals, runs drift detection first: if the
    entity has changed since the proposal was created, returns
    `{ status: 'drift_detected', conflicts }` without writing.
    Agents surface conflicts to the user before retrying with
    `force=true`. For create proposals (`artist_title_to_youtube`),
    a uniqueness check runs instead and cannot be forced. Backed
    by `POST /api/admin/enrichment-proposals/:id/apply`.
  - `pica_enrichment_proposal_reject` — reject a pending proposal
    with an optional `resolution_note`. Content-hash suppression
    (ADR-163) permanently blocks re-proposal of identical content
    — no cooldown, no timer. Backed by
    `POST /api/admin/enrichment-proposals/:id/reject`.

- **ADR-166: YouTube import as a recording-version source.**
  - `pica_import_youtube_link` MCP tool wraps the Phase 3 orchestrator.
    Takes a YouTube URL, returns a preview (items + classification +
    work match), then `confirm: true` executes the import as a new
    recording on an existing or newly-created work. Supports
    `selectedVideoIds`, `targetWorkId`, and `overrideVersionType`
    for fine-grained agent control. Landed in `78da58344`
    (feat(adr-166): phase 4 — pica_import_youtube_link MCP tool + SDK
    methods).

### Changed

- `pica_cascade_health` outcome bucket list extended to include
  the ADR-163 Phase 1 `proposed` and `suppressed` outcomes. Tool
  description now reads `wrote / no_match / already_enriched /
error / proposed / suppressed`. Counters populate automatically
  from `cascadeOutcomeCounter` — no schema change required for
  existing `/api/admin/cascade-health` or
  `/api/internal/cascade-health` consumers.

- Several mcp-server source files got minor reformatting from ADR-165's
  flattener strip (removing `work_spotify_data` + `work_album_metadata`
  fields from the Work shape) and ADR-166's cascade rule rewrites.
  These are consumer-invisible at the MCP tool surface — the tools
  still accept the same inputs and return the same outputs — but the
  underlying `PicaClient.works` reads + writes now route through the
  ADR-165 overlay. See the ADR-165 and ADR-166 implementation records
  for details.

### Fixed

- `pica_run_person_cascade` tool description had a dangling
  `→ then: pica_people_inspect` chain with no parenthetical reason hint,
  which the `composability-chains.test.ts` linter caught as a content
  bug. Description updated to `→ then: pica_people_inspect (see what
the cascade wrote)`. Fixed in `3a2843636` (test(mcp-server): fix 10
  pre-existing test failures across 8 suites).

### Notes

- Backwards-compatible minor bump. Three new tools, one widened
  description, no removals or signature changes.
- Depends on `@withpica/mcp-sdk@^1.4.0` for the new
  `EnrichmentResource.listEnrichmentProposals` /
  `applyEnrichmentProposal` / `rejectEnrichmentProposal` methods.
- Source additions landed across ADR-163 Phase 3d (`ff9cf265a`) and
  Phase 6 (`ff97faa56`).
- Registry: https://www.npmjs.com/package/@withpica/mcp-server/v/2.10.0
- Published alongside `@withpica/mcp-sdk@1.4.0` and
  `@withpica/mcp-server-business@1.3.0` in ADR-163 Phase 6.

## [2.9.0] — 2026-04-11

### Added

- **ADR-164 audit follow-up: three new cascade MCP tools**
  - `pica_run_work_cascade(work_id)` — manually re-evaluate a work
    against every ADR-164 cascade rule and fire those whose
    preconditions are satisfied. Idempotent. Fire-and-forget. Backed by
    `POST /api/admin/works/:id/cascade-run`.
  - `pica_run_person_cascade(person_id)` — same for people. Fires
    `ipi_to_mlc_works` (MLC writer/publisher splits),
    `mbid_to_wikidata_crosswalk`, `isni_to_wikidata_crosswalk`,
    `wikidata_to_awards`, `wikidata_to_biography`, and
    `google_contact_suggest`. Backed by
    `POST /api/admin/people/:id/cascade-run`.
  - `pica_cascade_health` — snapshot of in-process cascade counters
    (ADR-164 Decision 5). Org-auth twin of the operator-only
    `/api/internal/cascade-health` endpoint. Returns per-rule failure
    counts bucketed by reason + outcome totals. Process-global, not
    persistent across serverless cold starts — treat as a spot-check.

### Notes

- Source additions landed in `38901f281` (ADR-164 audit follow-ups).
  Version bump + rebuilt dist + publish tagged in `edfbcb07b`.
- Minor bump per semver — new tools, no removed or renamed surface.
- Registry URL:
  https://www.npmjs.com/package/@withpica/mcp-server/v/2.9.0
- Tarball verified post-publish by pulling
  `withpica-mcp-server-2.9.0.tgz` from the registry and grepping for
  all three new tool names in `package/dist/tools/enrichment.js`.

## [2.8.0] — 2026-04-10

### Changed

- **ADR-162: subscription-based billing, credit gating disabled.**
  `withCreditGate` is now a pass-through — all tools are free for
  active subscribers. The MPP (Micropayment Protocol) machinery is
  retained in `@withpica/mcp-utils` for a future pay-per-credit
  re-enable but is dormant. Version bump carried in `7c4f5d24a`
  (`chore(adr-162): version bumps for npm publish`).

## [2.7.0] — 2026-04-08

### Added

- **ADR-158c custody tool release.** Six `pica_custody_*` tools go
  live: claim, accept, decline, dispute, incoming list, history.
  Version bump in `91dd1c275` (`chore(adr-158c): version bumps for
custody tool release (Phase 6b)`).

## [2.6.2] — pre-2026-04-08

### Fixed

- Stale version constant in the boot banner — the banner was still
  reporting an older version after an earlier bump. Fixed in
  `980dabc02` (`fix(mcp-server): correct stale version constant in
boot banner (2.6.2)`).

## [2.6.1] — pre-2026-04-08

### Fixed

- MCP server now emits `tools/list_changed` + `resources/list_changed`
  notifications after sign-in and sign-out, so clients refresh the
  tool list without a reconnect. Fixed in `f1ba2a211`.

## [2.6.0] — 2026-04-07

### Changed

- **ADR-155 launch — publish 6 packages.** Catalog server rebaselined
  from the broken `3.0.0` publish (deprecated + unpublished from npm
  because `file:` deps had been baked into the tarball, leaving every
  fresh installer dead) to `2.6.0`. Five new sibling packages
  (`@withpica/mcp-sdk`, `@withpica/mcp-utils`,
  `@withpica/mcp-server-business`, `-enrichment`, `-settings`)
  published at `1.0.0` alongside with real semver deps so `npm
install` actually resolves on user machines.
- `connect.withpica.com` leads with the no-api-key lobby-mode flow.
  Landed in `e235aed40` (`feat(mcp): ADR-155 launch — publish 6
packages, lobby-mode connect copy`).

## Pre-2.6.0 — pre-changelog era

Earlier versions (`2.0.0` through `2.5.x`) covered the pre-ADR-155
monolithic MCP server. Version history for that era is reconstructable
via `git log --follow -- mcp-server/package.json`. Notable milestones:

- `2.5.x` — MCP Apps prebuild script + ext-apps devDependency added
- `2.4.x` — build/publish polish, `.vercel/` excluded from tarball
- `~2.0.0` — ADR-149 agentic hardening (write tiers, ProposedAction,
  confirmation, audit, annotations)

[Unreleased]: https://github.com/withpica/pica/compare/edfbcb07b...HEAD
[2.9.0]: https://github.com/withpica/pica/compare/7c4f5d24a...edfbcb07b
[2.8.0]: https://github.com/withpica/pica/compare/91dd1c275...7c4f5d24a
[2.7.0]: https://github.com/withpica/pica/compare/980dabc02...91dd1c275
[2.6.2]: https://github.com/withpica/pica/compare/f1ba2a211...980dabc02
[2.6.1]: https://github.com/withpica/pica/compare/e235aed40...f1ba2a211
[2.6.0]: https://github.com/withpica/pica/commit/e235aed40