# STATE 17: PERSIST_ON_TOUCH

**PRECONDITIONS:**
- STATE 16 POSTCONDITIONS met (unit tests complete for CRITICAL modules)

**ACTIONS:**

Follow ON-TOUCH pattern per `patterns/on-touch-check.md`.

**Persist ON-TOUCH list**: Write the ON-TOUCH module list from `.runs/bootstrap-scan.json` to `experiment/on-touch.yaml`:
```yaml
# Auto-generated by /bootstrap. Modules to test when next modified.
# /change reads this file to trigger TDD prerequisites.
on_touch:
  - path: src/app/api/invoices/route.ts
    reason: "Read-only GET, no mutations"
    source: bootstrap
```

Each entry includes `source: bootstrap` to distinguish from future on-touch entries.

- **Write trace artifact** (`.runs/bootstrap-on-touch-trace.json`):
  ```bash
  PAYLOAD=$(python3 -c "
  import json
  trace = {
      'on_touch_count': 0,
      'file': 'experiment/on-touch.yaml'
  }
  print(json.dumps(trace))
  ")
  bash .claude/scripts/lib/write-gate-artifact.sh \
    --path .runs/bootstrap-on-touch-trace.json \
    --payload "$PAYLOAD" \
    --skill bootstrap
  ```

Check off in `.runs/current-plan.md`: `- [x] ON-TOUCH persisted (state 17)` (#1118)

**POSTCONDITIONS:**
- `experiment/on-touch.yaml` exists with ON-TOUCH module list
- `.runs/bootstrap-on-touch-trace.json` exists

**VERIFY:**
```bash
test -f experiment/on-touch.yaml && test -s experiment/on-touch.yaml && python3 -c "import json; d=json.load(open('.runs/bootstrap-on-touch-trace.json')); assert isinstance(d.get('on_touch_count'), int) and d['on_touch_count']>=0, 'on_touch_count not valid int'; assert d.get('file')=='experiment/on-touch.yaml', 'file field mismatch'"
```

**STATE TRACKING:** After postconditions pass, mark this state complete:
```bash
bash .claude/scripts/advance-state.sh bootstrap 17
```

**NEXT:** Read [state-18-commit-and-push.md](state-18-commit-and-push.md) to continue.
