---
name: autosave-status
description: Design, implement, or review the save indicator of an autosaving form ("saving", "saved", "not saved"). Models autosave as versions instead of booleans and replays edit/save sequences to check what the interface should show. Use when building autosave, reviewing an autosave pull request, or debugging a "saved" label that lies.
---

# Autosave status

An autosave indicator is only honest when "saved" means: the server confirmed the version the user is looking at. A boolean `isSaving` flag cannot say that. If the user edits while a request is in flight, the request confirms an older version, and the flag still flips to "saved".

## Model

Track three versions and one flag. Read [the model reference](references/model.md) for the full rules.

- `draft`: the version on screen. Every edit adds one.
- `sending`: the version inside the save request in flight, or none.
- `confirmed`: the last version the server acknowledged.
- `failed`: whether the last request failed.

The status is derived, never stored: `saving` while a request is in flight; `saved` when `draft === confirmed`; `error` when the last request failed and changes are pending; `unsaved` otherwise.

## Workflow

1. Find where the code decides what the indicator shows. List every input it reads (flags, mutation state, timestamps).
2. Write the scenario you care about as a trace of `edit`, `save`, `ok`, `fail` events, with the status you expect after each step. Start from `examples/`.
3. Run `node <skill-directory>/scripts/replay.mjs <trace.json>`. Exit codes: 0 means every expectation matches the model, 1 means findings, 2 means an input error.
4. For each `expectation` finding, check whether the code would show the same wrong status. The usual cause is a flag that is cleared by the response of an older request.
5. Fix by storing versions: send the draft version with the request, and on success compare it with the current draft before showing "saved". One save at a time per form is the simplest way to keep order; with TanStack Query v5, mutations that share a `scope.id` run in series.
6. Keep the trace next to the tests so the scenario stays documented.

## Limits

The model covers one form in one tab, with one save in flight at a time. It does not model other tabs, other devices, other users, or the server's own write ordering; those need versioning or conflict handling in the API. Debounce timing is outside the model: it only decides when `save` happens. A passing replay means the expectations are consistent with the model, not that the code implements it. Do not claim the code was verified unless you also tested the code.
