2.1.3 Issue-key assignment on create (PROJ-123)
Estimate: 16m · Depends on: 2.1.2, 1.3.1
Wire issue creation in issuesService.createIssue(...) to assign a human-readable key ({PROJECT_KEY}-{n}, e.g. PROD-123) using Story 1.3's per-project key counter, atomically with the issue insert. The counter increment + issue insert happen in one $transaction so two concurrent creates can't collide on the same number.
Why atomic: the key counter is contended — every create in a project reads-then-increments it. Without a transaction + row lock (SELECT … FOR UPDATE on the project counter row, per CLAUDE.md's transaction rules), two simultaneous creates could both read n and both mint PROD-n. Lock the counter row, increment, insert, commit.
Acceptance criteria
createIssueassigns{projectKey}-{n}using 1.3's counter, atomic with the insert, inside one transaction with a FOR UPDATE lock on the counter row.- Validates parent type via 2.1.2 before writing.
- Keys are unique within a project and monotonically increasing; deleting an issue does not recycle its key.
- Vitest concurrency test: N parallel creates yield N distinct sequential keys (no duplicates, no gaps from the lock path).
Context refs
- Story 1.3's identifier generator / key counter
workspacesService.createWorkspace— the existing atomic-multi-write + collision pattern to mirrormotir-core/CLAUDE.md— transaction + FOR UPDATE rules