> ## Documentation Index
> Fetch the complete documentation index at: https://prism.ntecdev.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# Retro: Phase 2 — CRUD + Rename to Prism

> Phase 2 — CRUD + Rename to Prism

<Info>**Phase:** Phase 2 — CRUD + Rename to Prism · **Status:** `closed` · Filed 2026-04-16</Info>

## What Worked

* Single shared service module (phase2\_service.py) — five entities, one file
* Per-project monotonic number via (project\_id, number) UNIQUE
* Backward-compatible rename — zero external identifiers flipped
* Smoke-test-as-you-go caught MissingGreenlet PATCH bug in isolation
* One-line MCP verb dispatchers — verb logic stays in backend
* Frank stayed out of the loop the whole session — AI FIRST worked

## What Didn't

* MissingGreenlet on PATCH after onupdate=func.now() — needed await session.refresh(row)
* Stale uvicorn binding port 8010 — old server still bound, new one failed silently
* Edit tool failures on unread files — three wasted tool calls
* Wakeup timer fired stale — completed work before timer expired
* Initial smoke missed PATCH — POST/GET looked green but PATCH blew up
* ScheduleWakeup at 60s burned cache for nothing — should have polled /health

## Lessons

* **\[project]** L5 — All ORM models with onupdate=func.now() need await session.refresh(row) after any write
* **\[project]** L6 — Every CRUD smoke must include PATCH, not just POST/GET
* **\[project]** L7 — Rename without flipping external identifiers. ADR-017 pattern is the template
* **\[global]** G3 — In async SQLAlchemy, onupdate=func.now() is a footgun unless paired with explicit refresh()
* **\[global]** G4 — Before debugging 'my code isn't loading,' verify the running process is actually the one I started
