# Subprocess: Session Touch Invocation
### Purpose
Document when `.usage touch::` triggers `refresh_account_token()` to activate idle quota windows, and the exact predicate and skip-reason trace system.
### Trigger Predicate (`apply_touch()`)
An account is submitted for touch when ALL of the following are true:
| `aq.result` is `Ok(data)` | Only valid-quota accounts are touched |
| `aq.is_owned == true` AND `aq.is_occupied_elsewhere == false` | G4 gate (BUG-302 fix) |
| NOT solo gate | `solo::1` skips non-current accounts |
| `touch_idle = false` cache flag absent (or cache absent) | Defense-in-depth: skip if subprocess already activated this account (AC-16; avoids API lag re-triggers) |
| At least one quota timer absent: | |
| `five_hour.resets_at == None` | 5h window not started |
| OR `seven_day` present AND `seven_day.resets_at == None` | 7d window not started |
| OR `seven_day_sonnet` present AND `seven_day_sonnet.resets_at == None` | 7d-Sonnet window not started |
| `five_hour_left(aq) > 15.0%` | Skip h-exhausted accounts |
| `seven_day_left(aq) > 0.0%` | Skip weekly-exhausted accounts |
Source: `src/usage/touch.rs`
### Skip-Reason Trace Codes
| `reason: Err` | `aq.result` is `Err` |
| `reason: touch_idle=false` | Cache flag indicates already activated |
| `reason: all_running` | All three timers have active windows |
| `reason: h-exhausted` | `five_hour_left ≤ 15.0%` |
| `reason: weekly-exhausted` | `seven_day_left ≤ 0.0%` |
| `reason: not owned` | `is_owned = false` |
| `reason: occupied elsewhere` | `is_occupied_elsewhere = true` |
| `reason: solo-skip` | `solo::1` and not current account |
### Invocation
Same `refresh_account_token()` call as token refresh (with `update_marker=false`). Internally: `run_isolated(["--print", "."], model, timeout_secs=35)`.
Model selection uses [algorithm/001](../algorithm/001_touch_model_selection.md) — Sonnet when `son_idle=true` or `son_available=true` (prevents BUG-289 infinite loop where Haiku cannot open the 7d-Sonnet window).
### Post-Touch Actions
After any `refresh_account_token()` call (regardless of `credentials` result):
- Update `expires_at_ms` if `credentials = Some(new_json)`
- Re-fetch quota unconditionally: `account_quota.result = fetch_oauth_usage(new_token)`
- Write quota cache (Fix BUG-309)
### Ordering with `refresh::`
When both `refresh::1` and `touch::1` are active, `apply_refresh()` runs first (retries auth errors), then `apply_touch()` runs on the post-refresh results.
### Cross-References
| [feature/024_session_touch.md](../feature/024_session_touch.md) | Full feature spec |
| [subprocess/001](001_run_isolated_contract.md) | `run_isolated()` contract |
| [subprocess/002](002_credential_writeback.md) | Credential write-back protocol |
| [subprocess/003](003_token_refresh_invocation.md) | Token refresh (same infrastructure) |
| [algorithm/001](../algorithm/001_touch_model_selection.md) | Model selection algorithm |