81e8349dd5
The feature is unreleased and this branch is local, so there is no rolling deploy in which an old instance still selects `chats.messages` — the usual reason to keep a replaced column for one more release does not apply, and after release the drop would cost two deploys. So it goes now. With no data to preserve there is no backfill, and with no backfill there is no such thing as a stored message without an id: `message_id` is `NOT NULL` and the writers read `message.id` with no fallback, so a malformed message fails the insert instead of being given an invented identity by untested code. Removing the column from the drizzle schema is also the enforcement that nothing writes it — a surviving TypeScript reference stops compiling. The scan for raw SQL naming the column found nothing to guard, so there is no test for it. The four idempotency invariants each get their own test against a real table: a repeated message id creates no row and keeps its position; eight genuinely concurrent appends take eight distinct positions, with a raw insert past the query layer proving it is `UNIQUE (chat_id, position)` doing the work; a controlled update changes one body and leaves identity, position and every other row alone; and a mid-turn append survives the turn's write, landing where it happened rather than at the end. `dashboardAgentTranscriptMerge.test.ts` becomes `dashboardAgentTranscriptStore.test.ts`: it no longer tests a merge, and the ordering it asserted has deliberately changed.