# Refactor UI lifecycle functions into AppLifecycle with persistent ownership

> Replaced free lifecycle functions with an AppLifecycle class in src/tunacode/ui/lifecycle.py.

- Skill: `tools-only/refactor-ui-lifecycle-functions-into-applifecycle-with` (Agent Skill, multi-file: 3 files)
- Install (CLI): `npx skillmds@latest add tools-only/refactor-ui-lifecycle-functions-into-applifecycle-with`
- Raw SKILL.md: https://api.skillmd.com/api/skills/tools-only/refactor-ui-lifecycle-functions-into-applifecycle-with/raw
- Safety review: pending (external: skill-scanner PASS, skillspector PASS)
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Coding & Dev Tools
- Author: tools-only (https://skillmd.com/u/tools-only)
- Updated: 2026-09-29
- Page: https://skillmd.com/skills/tools-only/refactor-ui-lifecycle-functions-into-applifecycle-with

---


# Refactor UI lifecycle functions into AppLifecycle with persistent ownership

## Summary

Replaced free lifecycle functions with an `AppLifecycle` class in `src/tunacode/ui/lifecycle.py`.

Updated `TextualReplApp` to persist a single lifecycle instance (`self._lifecycle`) and reuse that instance across `on_mount` and `on_unmount`.

This removes function-level indirection and makes REPL startup idempotent through `_repl_started` inside lifecycle state.

## Files changed

- `src/tunacode/ui/PLAN.md`
- `src/tunacode/ui/lifecycle.py`
- `src/tunacode/ui/app.py`

## Behavior notes

- Theme and session metadata initialization stay in mount flow.
- Setup dismissal still proceeds into REPL startup.
- Slopgotchi timer ownership remains on `TextualReplApp._slopgotchi_timer`, with lifecycle start/stop helpers managing it.
- `on_unmount` now fails loudly if lifecycle was never initialized.

## Validation

- `uv run ruff check src/tunacode/ui/app.py src/tunacode/ui/lifecycle.py` ✅
- `uv run pytest tests/unit/ui/test_supported_themes.py -q` ✅
- `uv run ruff check .` ✅

