# Did my fix work?

> Declare which release fixes an issue, measure recovery for the people it affected, and read past recorded answers.

Canonical page: https://anectico.com/docs/investigate/fix-outcome/


A quiet issue does not prove a fix worked. The people who hit it may not have tried again.
Declare the issue, release and success event. Anectico then checks what happened to the original
subject, in the exact environment. **Declaring a fix does not close the issue.**

## Ask your agent

> Release 2.4.1 fixes the checkout failure in staging. The success event is checkout_completed.
> Declare that fix, then tell me who recovered, who failed again and who has not come back.
> Show the past recorded answers too.

The declaration is a write. The person approves the preview; a workspace owner supplies a key
with `errors:write`, `errors:read`, `analytics:read`, `persons:read` and `mcp:write`. Outcome and history reads need
`errors:read`, `analytics:read` and `mcp:read`. They work through OAuth for an existing declaration.
Without `persons:read`, outcome reads return counts and name that scope for withheld people.

The agent uses `declare_fix` through `execute_internal_action`. Its first call saves nothing and
returns a readable preview and confirmation token. The second confirms the same input. The
required fields are `project_id`, a new UUID `id`, `group_id`, `claimed_release`, `environment`
and the exact `success_event`. A short optional note is untrusted text, never a claim.
An explicit MCP subject can name at most eight people so its write receipt can be
erased safely for each of them; REST and the CLI allow up to 500.

The server supplies the declaration time and the verified person or agent credential. A request
cannot choose them or backdate the declaration. CLI:

```bash
anectico issues watches declare-fix --file declaration.json --yes
anectico issues watches outcome <watch-id>
anectico issues watches history <watch-id>
```

An existing declaration can be found with `list_recovery_watches` or
`anectico issues watches list --group-id <group-id>`. Its untrusted declaration JSON names the
release and watch id. This inventory also needs `persons:read`; a counts-only caller needs a supplied watch id. Read it; do not declare the same fix again just to answer a read question.

## The subject and denominator

By default the subject is the resolved people, identified or anonymous, this issue affected in the readable seven days
before declaration. `baseline_start` can choose a shorter readable window. Alternatively select
up to 500 canonical `person_ids`, or up to 20 exact `operation_ids`, never both. A capture that
exceeds its people, operation or scan bound is refused; it never freezes a sample as the total.

The original subject size is fixed. Each current read states `original_people`,
`readable_people`, `excluded_people` and reason counts. Person erasure removes the stored subject
and is counted in `erased_people`; it does not silently reduce the original denominator. Errors
without a resolved person are counted separately as unattributed records, never as people.
Any percentage must name which denominator it uses. Unknown counts are never zero.

The failure and success must carry the same `anectico.operation.id`. A person is recovered only
when each of their affected operations has a counted success. Missing operation identifiers,
unreadable baseline records, retention or identity changes prevent judging that person.

## Two rules, stated in every answer

A success after declaration counts by **release** if its `service.version` is the claimed release
or a later observed release. Release names are the application's own text: Anectico never orders
them alphabetically or guesses a version scheme. The order is earliest visible error or product
event time in this project and exact environment, within readable retained history. Equal first
observations are unordered. A registered deploy time is not an observation. An unseen claimed
release is `not_observed_yet` and release-bearing outcomes cannot yet be judged.

A success with **no release** counts by **time**, only after the server's declaration clock.
The result says how many in each outcome used each rule. Give both numbers for recovery. This
fallback does not verify the release. Mixed-operation recovery uses the time rule when any
required success needed it. The same release/time rules apply to matching repeated failures.

## Read the five outcomes

`get_fix_outcome` through `execute_read_action` returns the current answer and a conclusion with
its window, denominator, counts, certainty and missing parts. People and their operation evidence
are returned when `persons:read` permits it; notes and evidence JSON are marked as untrusted data.

| Outcome | Meaning |
| --- | --- |
| `recovered` | Each affected operation ends in a qualifying success, after any qualifying failure. |
| `failed_again` | The same issue failed in a matching operation on the fixed release or later, or by the stated no-release time fallback. Its last qualifying event is a failure; a later success recovers it. Failure wins a timestamp tie. |
| `not_seen` | No error or product activity after declaration. Silence is never recovery. |
| `seen_without_outcome` | Active, without a counted outcome in the affected operations on the fixed release. |
| `not_readable` | Retention, erasure, missing operation or an unreadable or changed baseline prevents judging the person. |

Say "three of six recovered; one failed again; one was active without an outcome; one has not
been seen. Two recoveries were judged by release and one by time." Do not call the silent person
recovered or say the issue is closed. Returned issue and person links open proof pages. There is
no release proof page.


The last qualifying event in each affected operation decides its current outcome. A person
recovers only when all affected operations end in success. A failure with no release can decide
the outcome by the weaker declaration-time rule; name that rule rather than hiding the failure.
In `failed_again`, `success_then_failure` counts people who **recovered, then failed again**.
The total `count` minus that number counts people **still failing**, without a prior success
in the deciding failed operations. Report both counts. A simultaneous success is not a prior
recovery; failure wins the tie. Plain CLI outcome/history JSON preserves these same counts.

## Keep a history

Ordinary reads save no observations. `record_fix_outcome` is a separate confirmed write with
`project_id` and `watch_id`. The CLI uses an explicit flag:

```bash
anectico issues watches outcome <watch-id> --record --yes
```

`list_fix_history` returns the series. Each entry stores counts, the release/time split, original,
readable and excluded denominator, the window and evaluation clock. It contains no people or
samples. Earlier entries are never edited. A new confirmed recording appends a new entry, so
reconcile history before retrying a recording without an idempotency key.

History keeps the most recent 256 observations for 90 days. Erasing any subject removes that
watch's past series; later reads count the removal. Deleting the project or workspace removes the
declaration, subjects and history. An empty series means no recorded answers remain.

## Correct or withdraw

A declaration stays fixed except for person-erasure note redaction. Correct it with confirmed `supersede_fix`, using `watch_id` and a
complete new declaration with a new `id`. It freezes a new subject at a new server clock; both
assertions stay readable. CLI: `anectico issues watches supersede <watch-id> --file replacement.json --yes`.

Confirmed `withdraw_fix` ends observation at withdrawal and retains the declaration and history.
CLI: `anectico issues watches withdraw <watch-id> --yes`. Neither action changes issue status.
The older watch-delete command refuses release fix declarations; use withdrawal.

## Limits

The observation runs for at most 30 days, defaulting to 30 days from declaration. Reads measure
readable errors and product events, with a bound of 200,000 returned records and a source scan
budget. `scan_limit` and `outcome_counts_known=false` mean outcome counts are unknown. Late
arrivals, identity changes, retention and erasure can change a live answer. Upstream capture
completeness is unknown; a measured success is evidence, not proof that the release caused it.

The older `customer_recovery` action and `anectico issues recovery` still compare explicit time
windows. They do not declare a release fix or keep this history.

Live fix measurement needs `agents:content:read` and a project policy that allows
the operation properties. Person erasure replaces matching notes in current and older declarations with
`[withheld by erasure]`. Outcome and history reads show `erasure_changed`. Redacting a
note does not change outcome counts. A later save that restores matched erased text is
refused. Shared notes stay out of a person's export.
Matching uses held names and identifiers with at least four letters or digits.
Nicknames, descriptions and unknown names may not match.
