# Retention proof matrix — pass 1

PR [#22208](https://git.cafe/pingdotgg/t3code/pull/22208). Audited head [2d67e12b06](https://github.com/pingdotgg/t3code/commit/2d67e12b06b7e33f89c37c8c748528a106611d8b), branch `cursor/retention-toggle-14743`, in the lead-owned `/home/ubuntu/wt/pr-22208`. Before is the computed merge-base [5d91ab1117](https://github.com/pingdotgg/t3code/commit/5d91ab1117ad51ef1288a94717d85c70e7af09fe), not the older round's before revision. Read-only source audit; no application, browser, test, server, or emulator launched. No edits to the checkout, pushes, or comments. Every capture is pending.

## Findings first

1. The required proof spans four callers, two pages, and different persistence destinations. Inactive and settled worktrees are on `/settings/worktrees`; browser artifacts and rotated logs are on `/settings/maintenance`. `/settings/storage` redirects. Project/checkout Custom scope exposes only the worktree controls. A capture confined to Maintenance cannot cover these paths.
2. A checked switch showing 8 does not establish that nothing was saved. Every interaction recording needs a timestamped request/response and authoritative settings-readback transcript. Activation must leave the persisted rule null and must not enable `Delete now` when all other rules are off. Enter on untouched 8 is a separate path from an edited value.
3. Failure coverage must exercise the real scoped-save hook and toast. The browser test mocks settings scope, `SettingsRow`, updates and toast, and renders only `StorageCleanupSections`; it is useful logic evidence but cannot establish project wiring, mixed selections, permission gating, real persistence or error presentation.
4. Config writes are **serial per environment**. Queueing later age edits while the first save is held is realistic; arbitrary same-environment response reordering is not. The stale-failure fixture must preserve the real scheduler and distinguish queued invocations from dispatched wire requests. A synthetic out-of-order component harness must be labelled as such and cannot replace the serial application recording.
5. Project Custom, mixed environments, and queued save identity are explicit mandatory rows M12/M17/M18. Earlier captures at a different head do not fill these cells. No runtime correctness or visual acceptance conclusion is made in pass 1.

## Source map and complete render-path inventory

- [RetentionControl](https://github.com/pingdotgg/t3code/blob/2d67e12b06b7e33f89c37c8c748528a106611d8b/apps/web/src/components/settings/StorageSettings.tsx#L103): props `label`, nullable numeric `value`, async `onChange`, `minimum` default 1. Four labels change accessible names; all use the same group/switch markup. Settled alone has minimum 0. Maximum 3650 and integer rounding apply to all. No provider branch, list, count, loading spinner, or pending-disable treatment in this component.
- Component states: null value + no enabling state -> Off and unchecked switch; numeric value -> number group and checked switch; null value + `draft` -> focused 8 and checked switch; `edited` -> edited/raw/empty input with switch still checked; pending `{days}` -> number group stays visible while first save waits. Saved-prop changes reset draft and enabling. Number input width uses locale-formatted draft, saved value, then fallback 8; cover one digit, two digits, grouped four digits, and cleared input. Focus ring, dark input background and responsive row stacking are the visual dimensions.
- Component transitions: toggle on; value change; valid commit; null commit; dedup of same pending age; existing-value commit; first success/failure with submission identity guard; pointer suppression on switch mousedown; draft off versus saved/pending off; blur within control versus leaving it; untouched and edited Enter; Escape only while enabling is a string; composition/keyCode 229 bypass. Base UI also commits through +/- and ArrowUp/ArrowDown/Home/End. Plain-text parse failure is distinct from an empty input.
- [Worktree callers and gates](https://github.com/pingdotgg/t3code/blob/2d67e12b06b7e33f89c37c8c748528a106611d8b/apps/web/src/components/settings/StorageSettings.tsx#L505): environment/all use `storageCleanup` patches; `resolveWorktreeCleanup` overlays global custom/off policies on legacy settings. Project and checkout require `projectWorktreeCleanup`; absent support produces an update notice. Every connected target must support `storageCleanup`. Project mode `inherit`, `off`, `custom`, or mixed changes description and visibility: only homogeneous Custom mounts the two retention controls. `ruleStatus` adds Mixed across selected machines when resolved rule values differ. Settled is under Cleanup's Advanced disclosure, requires at least one connected target and all connected targets supporting `settledWorktreeCleanup`, and appends an offline-machine warning when selected targets outnumber connected ones. Inactive is outside Advanced. Other worktree booleans, keep-when values and their descriptions do not change this control.
- [Artifacts/logs callers and gates](https://github.com/pingdotgg/t3code/blob/2d67e12b06b7e33f89c37c8c748528a106611d8b/apps/web/src/components/settings/StorageSettings.tsx#L746): project/checkout returns null; unsupported cleanup shows a scope/update notice; otherwise two retention rows, each with mixed-value status. Database notice and manual cleanup are siblings. The latter uses persisted rules to enable `Delete now`, including settled 0 and project custom rules; it must not react to an uncommitted draft.
- [Scoped save hook](https://github.com/pingdotgg/t3code/blob/2d67e12b06b7e33f89c37c8c748528a106611d8b/apps/web/src/components/settings/useScopedSettings.ts#L50): unavailable plan -> warning toast + false; no failed environments -> true; all destinations fail -> error `Setting not saved` + false; some succeed -> error `Setting saved on some environments`, destination-specific description + false. Authorization can fail at execution even after a writable control was rendered. Existing other hook callers ignore the new boolean; there is no newly introduced layout in those components. `SourceControlWritingSettings.browser.test.tsx` only adjusts its mock's return type/value.
- [Scope planning and persistence](https://github.com/pingdotgg/t3code/blob/2d67e12b06b7e33f89c37c8c748528a106611d8b/apps/web/src/components/settings/scopedSettings.ts#L226): all/environment targets connected environments; project/checkout writes `projectSettingsOverrides[projectId].worktreeCleanup`, completing partial custom rules per destination. Global policy can require both legacy `storageCleanup` and `worktreeCleanup` updates. A failed or unsupported/dropped write contributes a destination failure. A mixed-selection display uses the representative target; it is not a list of independently displayed controls.
- [SettingsRow](https://github.com/pingdotgg/t3code/blob/2d67e12b06b7e33f89c37c8c748528a106611d8b/apps/web/src/components/settings/settingsLayout.tsx#L297): server-scoped rows become inert/dim without permission or connected targets, with a focusable tooltip. Worktree rows in Custom project scope pass `serverScoped=false`; do not assume the same inert treatment there. Grid versus stacked layout switches at row-container width 32rem, not just the viewport breakpoint. Retention rows have no `settingKeys`, so do not invent inheritance/reset badges on those rows; the project mode row owns its own inheritance/reset UI.
- [Route scope boundary and remount](https://github.com/pingdotgg/t3code/blob/2d67e12b06b7e33f89c37c8c748528a106611d8b/apps/web/src/routes/_chat.settings.tsx#L44): invalid selection yields a message; selected offline single environment yields Reconnect; changed scope search keys remount content. Closing Settings and changing pages unmount the draft. [Advanced](https://github.com/pingdotgg/t3code/blob/2d67e12b06b7e33f89c37c8c748528a106611d8b/apps/web/src/components/settings/AdvancedSettings.tsx#L10) hides settled retention until opened, including opening through the worktree cleanup search target.
- [Config scheduler](https://github.com/pingdotgg/t3code/blob/2d67e12b06b7e33f89c37c8c748528a106611d8b/packages/client-runtime/src/state/server.ts#L756) serializes per environment; [settings command](https://github.com/pingdotgg/t3code/blob/2d67e12b06b7e33f89c37c8c748528a106611d8b/packages/client-runtime/src/state/server.ts#L1143) shares that scheduler. Cross-environment saves can finish independently.
- Web: local server and remote/hosted web use this same UI; include one remote connection in M17. Desktop wraps the same UI but native focus/key routing warrants M21. Native mobile exposes no retention settings: no native capture. Providers and agent adapters are not involved in these settings controls; no per-provider fixture.

## Capture contract and seed recipes

Status `PENDING` means derived from source and required but **not captured or runtime verified**. `SOURCE ONLY` is an unchanged gate deliberately excluded from the minimum. These IDs are new and do not inherit any old F-row completion.

Cells: **WL** = web 1440×1000 light; **WD** = web 1440×1000 dark; **NL** = responsive web 390×844 light; **ND** = responsive web 390×844 dark; **EL** = actual Electron client 1280×900 light. All sizes are CSS pixels. `GRID` = WL + WD + NL + ND. Native mobile is not implied by NL/ND. Use en-US, UTC, DPR 1, zoom 100%, same fonts and reduced-motion preference on both revisions. A real different locale need not multiply the matrix: M11 covers formatted width with en-US 3,650.

Each mandatory cell requires **before and after** at the two pinned revisions, with identical persisted seed and the same action script. Record actual baseline behavior; do not force baseline to mimic the new draft model. If baseline cannot perform the next action because the field is absent while its immediate activation save is held, record that divergence and release the prerequisite request before continuing; do not hide the missing field behind a locator timeout or fabricate an equivalent state. An after-only recording remains incomplete. Multiple rows may be named chapters of one recording; a single clip may satisfy several rows/cells only when its viewport, theme, destination and checkpoints match. There is no requirement to multiply every interaction across every theme or caller.

Keep page/scope identity, target row, switch, field and relevant status/toast visible. Scroll Worktrees to Cleanup and open its Advanced (not Creation's Advanced); preserve enough context to identify the page. For M01, stills are sufficient; for **every other action/time row use a recording**, optionally extracting stills for state comparisons. M02 recordings supply the transient focused-state stills. Attach matched state stills/diffs where useful; stills cannot substitute for the sequence.

**S0 — isolated real app seed.** Capturer uses lead-owned disposable dev homes, never the running user's database. Settings from `server.getSettings` and `server.updateSettings` with payload `{patch: ...}` are the authoritative seed/readback API, through the app's authenticated RPC client, separately for each environment. Do not invent an HTTP settings endpoint. Save transcripts without auth material. Prefer UI edits for setup when convenient; reset through RPC between chapters. Set all four age fields null, worktree booleans false, clear fixture project overrides, and set global `worktreeCleanup:null` so legacy age fields actually control the display. The complete storage object is `{worktreeKeepWhen:"uncommitted-changes",worktreeAfterDays:null,worktreeSettledAfterDays:null,worktreeOnMerge:false,worktreeOnDelete:false,worktreeUnchanged:false,browserArtifactsAfterDays:null,logsAfterDays:null}`. Use synthetic fixture projects with no real worktrees/captures/logs at risk. A save can trigger a cleanup sweep even without pressing Delete now.

**S1 — routes and selectors.** Use `/settings/maintenance?machine=<environmentId>#storage-artifacts` for artifacts/logs; `/settings/worktrees?machine=<environmentId>#storage-worktrees` for worktrees. Without `machine`, `project`, `checkout` the selection is All environments (clear retained query state explicitly). Accessible switch labels: `Delete inactive worktrees`, `Delete settled worktrees`, `Delete old browser artifacts`, `Delete old rotated logs`. Input is `<switch label> in days`; steppers are `Decrease <label>` and `Increase <label>`. Select-all then type; assert visible text before committing so Base UI selection behavior cannot silently append digits. An input can be a textbox rather than spinbutton.

**S2 — saved age.** Patch the selected `storageCleanup` field to 30 (or M11's boundary), get settings, then navigate/remount. For global `worktreeCleanup` custom/off variants seed that policy explicitly; its effective values override legacy worktree ages. Also verify one successful worktree age update when global policy is custom, so a legacy-only patch does not falsely appear saved.

**S3 — project/checkout.** Create a disposable project through the app, derive the actual sidebar `projectKey`, member `projectId` and `physicalProjectKey`; do not guess them from a directory or reuse a deleted project id. Scope with `?project=<projectKey>&machine=<environmentId>`; add `checkout=<physicalProjectKey>` for checkout. Preflight mode selection to Custom, a visible inactive switch and expanded settled row **before capture**. UI `Automatic worktree cleanup` -> Custom is preferred; equivalent RPC seed is `projectSettingsOverrides[projectId].worktreeCleanup={mode:"custom",rules:<complete S0 worktree rules>}`. Read back that exact override and a non-target project's unchanged state. Inherit clears the override, Off stores `{mode:"off"}`; only Custom shows retention inputs. Artifacts/logs must remain absent in project/checkout Maintenance.

**S4 — two actual disposable environments.** Attach two test servers to the same client; both advertise required cleanup capabilities and settings-write permission. Give them distinct public-safe labels (e.g. Build A and Test B) and divergent ages. Select All environments explicitly. Use a remote connection for the second when available; record the connection mode. Verify both get-settings values independently. A real project on both targets can seed mixed project modes; no invented global client state. If a second environment is unavailable, M17 stays pending; a mocked scope/provider version is a **labelled harness capture**, not application coverage.

**S5 — controlled faults/time.** Ordinary pending/error states cannot be selected from settings. Prefer a dev-only transport-boundary driver/proxy around the real app RPC that records, holds and releases `server.updateSettings` responses (and relevant config notifications), without mocking `RetentionControl`, `SettingsScopeContext`, the scoped-save hook, layout or toast. Record the exact interception point, whether the server actually applied the write, and which events were held. Use deterministic acknowledgements, not guessed sleeps. Preserve per-environment serial command order. To reject a first save without changing persisted settings, fail the request before it reaches the server or use a test server rejection. Transport-injected states are **labelled fault-harness recordings of the real application**. If that boundary cannot be driven, use a labelled component/service harness and retain the real-application gap. Failure/partial-save captures must retain the production `useRunScopedPlan` so the boolean and toast are real. Never claim a mocked `onChange(false)` proves the toast.

For each interaction attach: seeded settings; user actions; intercepted/dispatched patch payloads with environment identity; responses; persisted config/readback after completion; relevant cleanup report state. Mark setup writes separately. For no-save assertions show zero user-action update calls and null readback. For committed success reload **that exact chapter's state without reseeding**. For multi-environment state read every target. Do not enable `Delete now` by clicking it; its disabled/enabled state is sufficient side-effect evidence here.

## Minimum fixture matrix

| ID / named fixture | Diff affected / data and state | Real-app seed and action; required observation | Minimum cells / artifact | Pass-1 status |
|---|---|---|---|---|
| M01 — baseline Off and saved layouts | Shared render paths retained; draft rendering changes beside them. Four callers, short and long descriptions. | S0/S1: both pages, all four Off; worktree Advanced open. S2: saved inactive 30, settled 0, artifacts 8, logs 3650. Show Off/unchecked versus field/checked, zero valid, grouped-width 3,650, no clipping. Global custom and legacy policies with identical effective values need not duplicate layout stills. | **GRID**, both page sections, Off and saved; **stills**. | PENDING |
| M02 — turn on then accept untouched 8 | New `draft`, focus, fallback width, untouched Enter branch. All four labels/callback destinations. | Independently reset each caller to null. Switch on -> field 8 focused, checked, zero save; outside persisted state stays null. Then Enter -> exactly one age 8 write, blur, real saved state and reload. On Maintenance with S0, Delete now remains disabled until commit. Activate one caller at a time; do not blur it by activating a second. | **GRID**, both pages; cover four callers in WL; remaining cells one representative per page. **recording**. | PENDING |
| M03 — edited Enter including same shown age | Edited text parse/commit rather than untouched shortcut. | S0, artifacts: activate, select-all type 30, Enter -> one 30 write, no stale 8, blur, reload 30. Reset and explicitly retype 8 before Enter -> one 8 write. Include inactive custom-global success per S2 to cover policy routing. | **WL**, **recording**. | PENDING |
| M04 — edited blur and internal focus | Child commit precedes parent's leave check; moving focus within group must not discard. | S0: activate, type 30, click heading outside -> one 30 save; repeat with Tab. Separately move untouched 8 from input to switch with Tab -> draft remains checked and unsaved while focus stays inside; next external blur cancels. | **WL**, **recording**. | PENDING |
| M05 — stepper/keyboard commits | Value-change and commit can occur in one interaction, pending duplicate guard. | S0, artifacts: activation then + -> committed 9; reset activation then - -> 7. Include ArrowUp/Down, Home/End on a focused numeric field, logging actual normalized values, and saved-value stepper smoke. Do not count pointer press, release and ensuing blur as separate saves. | **WL**; one +/- chapter also **ND** with touch/coarse input. **recording**. | PENDING |
| M06 — Escape versus composing keys | String enabling cancellation; prevents Settings dismissal; composing/keyCode 229 early return. | From null show untouched and edited draft Escape -> Off, zero age save, Settings remains open. Separately dispatch controlled composing Enter/Escape and keyCode 229: no custom commit/cancel while composing; finish composition, then ordinary explicit commit/cancel works. Use a real IME if available; otherwise label the composition-event subchapter as an **event harness**, not real IME evidence. Saved or pending Escape follows normal dialog handling; include in M18. | **WL**, cancellation also **ND**. **recording**; composition subchapter labelled if synthetic. | PENDING |
| M07 — switch off an uncommitted draft | `onMouseDown` focus protection; draft cancellation without transient age write. | S0: activate, edit 30, pointer switch off -> Off, zero writes. Repeat untouched. Keyboard: focus input, Tab to switch (record any input blur commit), Space; distinguish already-committed 30 then null from untouched zero-save cancellation. Touch edited cancellation must log actual event order; do not assume mousedown suppression covers touch. | **WL** pointer/keyboard + **ND** actual touch/coarse events. **recording**. | PENDING |
| M08 — untouched leave and unmount | Outer blur cancellation and scope/page/Advanced unmount. | S0: activate 8, click outside -> Off no write; repeat external Tab. Reopen then leave via another settings page, Close/reopen Settings, and scope change -> no persisted 8 or revived draft. Settled: open Advanced, activate then collapse/reopen -> no revived draft. Record whether an action first blurs the field. | **WL**, **recording**, chapter per exit, including settled. | PENDING |
| M09 — empty or invalid visible text | `onValueCommitted(null)` reset versus parse failure; Enter must not save stale draft. | S0: activate, clear text, Enter/blur -> no age save and Off after leaving. Reset and type accepted partial invalid text such as `+`, assert visible string, Enter -> no stale 8. S2 saved 30: clear and blur -> restore 30 without null rule write; invalid saved input should not turn rule off or save stale text. Record actual Base UI normalization. | **WL**; cleared fallback-width still within recording. **recording**. | PENDING |
| M10 — existing saved rule edit and immediate off | `value !== null` commit and off branches; no draft-start behavior. | S2 saved 30: edit 45, Enter -> 45; re-enter saved age and blur -> expected commit without reopening an enabling draft. Reload exact state. Edit local unsaved text, click switch off -> null immediately, no intervening edited age write; reload Off. Also turn saved settled 0 off. | **WL**, **recording**. | PENDING |
| M11 — numeric bounds, zero and width | minimum 1 versus 0, integer rounding, max, Intl-format size. | Ordinary caller: type 0 -> commit 1; 30.6 -> 31; 9999 -> 3650 (3,650 display). Settled: explicit 0 -> saved 0, checked, reload still 0; lower/upper steppers clamp at min/max. Empty draft covered M09; do not infer persisted max from visible raw text alone. | **NL** ordinary bounds; **WD** settled 0 and 3650. **recording**. | PENDING |
| M12 — project and checkout Custom | Distinct destination, mode visibility, project-only layout; earlier project gap. | S3. Start Inherit -> Custom with both worktree ages null. Inactive activation -> no override age change, then explicit 30 -> project override only; settled explicit 0 -> project override. Cancel one new draft. Reset to Inherit and set Off -> retention rows absent. Include checkout-scoped save and project mode reset; global/non-target state unchanged. Maintenance in project scope has no artifact/log retention. Preflight scope IDs before recording. | **WL** project full sequence; **ND** checkout Custom with both ages visible and one commit. **recording**. | PENDING |
| M13 — first save pending, dedup and success | Object enabling keeps field mounted during blur; same pending age guarded; no spinner/disable added. | S5, S0: activate, submit 30, hold first request completion and config update. Blur -> still checked 30, persisted rule still null if request held before apply. Repeat commit of 30 -> no second same-age submission while pending. Release real success/config -> saved 30, exact reload. Keep request-applied versus response-only hold semantics explicit. | **WL**, **recording**, labelled fault harness. | PENDING |
| M14 — first save fails and retry | New false return, identity-specific rollback, real all-failed toast. | S5: from null submit untouched 8, reject before apply -> Off + error Setting not saved with destination; null readback. Retry -> focused fresh 8, edit 30 and allow success -> saved 30/reload. Use actual hook/toast, also test formatted error fits narrow surface. | **WL + ND**, **recording**, labelled fault harness. | PENDING |
| M15 — saved rule save failure | Existing-value commits ignore boolean for draft rollback; off depends on persisted value. | S2 saved 30; S5 reject edit 45 -> error toast, saved rule remains enabled, authoritative state 30. Record local display behavior rather than claiming a rollback the code does not implement. Reject switch-off -> error and saved rule remains enabled; no fresh-draft Off rollback. | **WL**, **recording**, labelled fault harness. | PENDING |
| M16 — external persisted-value replacement | `savedValue !== value` resets local draft/enabling while mounted. | S0: leave a focused uncommitted draft mounted, second authenticated RPC client saves 30 then null; UI follows saved props, no write from the original client. S2 saved 30 with local edit 45, external update 60 -> draft resets to 60. Check focus and number-group unmount/reappearance without route navigation. | **WL**, **recording**, real app + second RPC client. | PENDING |
| M17 — mixed values and partial destination failure | Mixed status, representative display, partial-save false/toast; earlier mixed gap. | S4 All: A null, B 30 for artifacts and inactive/settled; verify visible mixed statuses. Activate representative-null rule with no writes, then commit 45 -> both targets converge and mixed status clears. Reset both null: allow A, reject B, then reverse failing/successful representative in a second chapter. Show partial toast names, read each target and record whether representative success keeps field on or representative failure resets Off while mixed remains. Include one mixed project-mode Inherit/Custom state (controls hidden) if available through real projects. | **WL**, two connected targets; **ND** partial-toast/wrapping checkpoint in a recording. **recording**, fault-labelled only for induced partial failure. | PENDING |
| M18 — queued submission identity and off | Submission object equality guard; pending off sends null; queued failure cannot clear newer pending value just because age matches. | S5 preserve serial scheduler: hold first submitted 30; enter/commit 60, then 30 again while it waits (three UI submissions; only first dispatched initially). Fail first -> newer pending 30 remains, then release later requests and show real final state. Separate chapter: hold first 30; edit 1 and click Off -> queue null, no 1 write; release to final Off. Separate stale-failure chapter: old request fails after Off then a new unsaved 45 draft -> old failure cannot clear newer draft. Pending Escape can dismiss Settings; reopen and record authoritative completion, with no invented Escape cancellation. | **WL**, **recording**, labelled fault harness with queue/dispatch/event transcript. | PENDING |
| M19 — unavailable save plan and warning | Changed `Promise.resolve(false)` early return rather than all-failed RPC path. | A normal unavailable/offline single-environment route hides controls, so cannot seed this by simply opening that route. Use labelled context/service harness: mount a null retention draft, then make the scoped plan have no writable destinations before Enter; keep real hook/toast -> warning Setting not saved, false result, Off. A real-app mid-connection-loss capture may replace this only if it actually reaches the early-return branch rather than unmounting/inerting first. | **WL**, **recording**, **labelled harness** unless real race is demonstrated. | PENDING |
| M20 — hidden, unavailable and read-only gates | Context only; these conditions are not changed by the diff. | Inventory: project/checkout Inherit/Off/mixed hides worktree controls; unsupported storage/project cleanup shows notice; settled missing support or collapsed Advanced hides it; offline mixed selection adds warning; no connected targets leaves controls unavailable; invalid scope/offline single scope boundary shows message; environment/all without settings-write makes server-scoped retention inert/dim with tooltip. Real old server/limited-scope connection can seed; capability/context harness otherwise labelled. In project Custom, do not infer serverScoped inerting. M12/M17 already include the changed controls' mode contexts. | **No additional minimum capture**. Optional WL context still; any attempted gate interaction needs a recording. | SOURCE ONLY, deliberately excluded |
| M21 — Electron focus/key smoke | Shared web layout, native-shell focus/key routing. | Actual desktop client connected to isolated test server: artifacts Off -> focused 8 with no save -> Enter saves 8; reset, activate/edit/Escape -> Off and Settings remains open; saved rule Off -> null. Record actual shell/platform, no browser frame relabelled as desktop. | **EL**, **recording**. If Electron cannot run, retain PENDING with platform limitation; web is not replacement proof. | PENDING |

## Collapsed or excluded dimensions

- Manual cleanup result counts 0/1/many, empty entries, omitted entries, errors, loading, report timestamp, permission combinations and database compaction notice are unchanged siblings, not independent minimum rows. Use a settled/no-report deterministic state for capture. The persisted-rule dependency of `Delete now` is affected indirectly and explicitly covered by M02/M10/M11; no manual cleanup run is required. If real automatic saves produce changed reports, retain and explain them rather than fabricating matching state.
- No new list layout depends on long project/environment names. Existing scope labels and partial-save toast may wrap: ND in M12/M17 supplies the relevant narrow stress. Do not multiply arbitrary account/project/thread counts.
- All/every-environment with identical values is the same retention layout as one target; M17 adds the visually distinct mixed status and partial failure. Project and checkout use the same retention markup; M12 separates destination smoke, not every gesture permutation. `t3.json` inheritance does not introduce a retention-row badge because these rows do not pass settingKeys; mode-hidden states remain inventory, not another full interaction suite.
- Loading/auth/capability gates that prevent a control from mounting have no new rendering in this diff. They stay source inventory M20; fault/disabled UI must never be silently presented as successful interactive coverage.
- Compose-event harness evidence and synthetic fault evidence must be labelled in both index and clip chapters. Actual connected multi-environment and Custom-project recordings remain required; otherwise mark the exact row pending.

## Completion accounting for the capturer

20 required fixture rows are PENDING (M01–M19 and M21); M20 is an explicitly excluded source inventory. A required row becomes covered only when all its stated cells and subchapters have paired before/after artifacts and the needed transcript/readback. Shared videos may cover multiple rows; link exact chapters. Do not report a row complete if it only has an after clip, a seeded replacement for its reload, a mocked destination, or source reasoning alone. Coverage exceptions require a named missing cell and reason. No captured status is awarded by this pass.

FRICTION:
tool | Web fetch could not open the earlier ghdrop index, and Python urllib got HTTP 403 verifying the upload; curl succeeded for both. | Use curl for ghdrop text retrieval, or make responses compatible with these other clients.
