actions: materializeIdeaPlanAction + relinkIdeaPlanAction (M12 T-498)
actions/ideas.ts:
- materializeIdeaPlanAction(id):
- guard: status===PLAN_READY, plan_md present, product linked, demo-403
- parsePlanMd → 422 with line-info on fail
- Prisma.\$transaction:
- SELECT max(code) for PBI/Story/Task within product
- INSERT PBI with sort_order = lastPbi+1 within priority
- per story: INSERT (sequential ST-NNN), per task: INSERT (T-N)
- UPDATE idea SET pbi_id, status=PLANNED
- INSERT IdeaLog{PLAN_RESULT, metadata}
- returns 409 on P2002 (concurrent-materialize race)
- relinkIdeaPlanAction(id):
- guard: status===PLANNED && pbi_id===null (PBI manually deleted via SetNull FK)
- reverts to PLAN_READY + IdeaLog{NOTE}
Tests: 39 cases total (8 new for materialize + relink): happy creates entities,
status-mismatch-422, parse-fail-422 with details, demo-403, P2002→409,
relink happy + invalid-precondition guards.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>