# Changelog

Behaviour a caller could notice. The tool surface is machine-read, so an agent
that cached a schema has no other way to learn that it moved.

## 2026-09-04

- Closed value sets are declared as enums, not described in prose: `verdict`, `tier`, `model` and `primary` on `get_buyer_quality`, `anchor` on its cohort, and `direction` on `skill_demand`'s trend. An agent can branch on these deterministically now.
- Every nullable result field says what its null MEANS. `buyer_payment_verified` null is "we do not know" and is not the same as false; `max_rate` null is "no hourly contracts at all" and is not a low rate.
- `get_activity` returns `succeeded`, `refused_or_failed` and `scope`. All three were being computed and then dropped, because they were not declared in the output schema.
- Nine cross-tool guarantees are stated in the `initialize` instructions, so a client can rely on them instead of cross-checking.
- When a client's coverage is thin, the share is repeated inside each model's `basis`, so quoting the number alone still carries its denominator.
- Every refusal message now contains its own fix.

## 2026-09-03

- `search_jobs` PAGES. It returns `next_cursor` while more match; its absence means the set is exhausted. Previously capped at 50 with no way to see the rest.
- `search_jobs` results carry `matched_in`, and the description snippet is centred on the matching term rather than the start of the text.
- Webhook deliveries are SIGNED: `X-Moxlade-Signature: t=<unix>,v1=<hex>`. `save_search` returns the secret once. Searches saved earlier carry no secret and arrive unsigned until re-saved.
- `skill_demand` buckets carry `days_covered`, `partial` and `per_day`, and a trend computed from complete weeks only. Raw bucket counts were never comparable across the first and last week.
- `get_buyer_quality`: `company_known` is replaced by `buyer_record_known` and `identity_known`. The old field conflated "a buyer record exists" with "we know who they are" and could contradict `get_buyer`.
- `get_buyer_quality` coverage: `share_of_their_history` now divides by the client's public HIRES, not their postings, and `thin` is null rather than false when there is no basis to judge.
- `get_job_score` returns one authoritative score; the ingest-time value appears as `score_at_ingest` only when it differs.
- `get_plan` returns `usage` — today's calls and distinct corpus questions against their caps — and reports `trial_ends_at` only while the state is trial.
- Every tool declares annotations, a title, and an output schema; `search_jobs` and `get_plan` publish full result field schemas.
- Arguments are validated and REFUSED rather than silently repaired. `limit: 51` no longer returns 50, `limit: 0` no longer returns the default, and an unknown argument name is rejected instead of ignored.
- An empty `skill` is refused. It previously matched the entire census or the entire corpus.
- `check_prefilter` returns `warnings` when a clause is valid and cannot match, and its matched count now reflects the filter — it previously returned the whole-window count for every filter.
- `get_activity` is account-scoped, and so are the feed and saved searches: re-minting a token no longer loses your reach or your saved searches.
- An unsupported `MCP-Protocol-Version` answers `-32022` with the supported list instead of a complaint about `params._meta`.
