2.3 Issue create / edit (form + server actions)
The issue-mutation surface: a create-issue modal + a full edit form route, both following the
4-layer architecture (Server Action → service → repository → Prisma). Type + parent picker
that surfaces 2.1.2's assertValidParent rule inline as a filtered combobox
(illegal selections aren't constructible, not flagged after submit). A reusable
MarkdownEditor primitive on top of the descriptionMd field 1.4 already
ships, with paste/drop image upload via Vercel Blob. The edit form also resolves
finding #46 by routing every status change through 2.2.4's gated
updateStatus and removing the ungated status patch on
updateWorkItem. Story also includes the previously-relocated
delete-with-reassign flow for in-use statuses (2.3.1, with its own E2E in 2.3.2)
— kept here because its E2E spans both the workflow-settings surface AND the work-item mutation
pipeline, which is exactly this Story's seam.
Prerequisites: Story 1.4 already ships
workItemsService.createWorkItem (atomic key allocation via the project counter,
parentage gate via 2.1.2's assertValidParent, audit revisions) and
updateWorkItem (patch-style updates for non-status fields). Story 2.1 ships the
issue-type metadata (lib/issues/issueTypes.ts) + parent rules
(lib/issues/parentRules.ts) the type+parent picker reads from. Story 2.2 ships
transition validation on workItemsService.updateStatus (2.2.4) — the SINGLE gated
path every status change in this Story routes through — plus initial-status seeding on create
(also 2.2.4 + 2.2.2) and the workflow-management UI 2.3.1 extends. Issue-key assignment is
NOT re-built here — Story 2.1.3 already verified the atomic monotonic-counter path; this Story
just consumes createWorkItem as-is. All work follows
motir-core/CLAUDE.md's 4-layer architecture (Route/Server-Action → Service →
Repository → Prisma). Per finding #26, every new route + Server Action carries an explicit
workspaceId gate at the application layer — RLS is defense-in-depth, not the sole
gate (the dev/CI superuser bypasses RLS until the Epic-8 runtime cutover).
Verification
- Pull the Story branch,
pnpm install && pnpm prisma generate && pnpm prisma migrate devagainst a fresh local DB. pnpm test— Vitest covers: 2.3.1 delete-with-reassign matrix; 2.3.3 createIssue Server Action; 2.3.4 candidate-parents service + picker; 2.3.5 MarkdownEditor round-trip + render guard; 2.3.6 edit form + the finding #46 grep guard (nostatusinUpdateWorkItemInput) + stale-edit; 2.3.7 attachment service gates.pnpm test:e2e— both E2E suites: 2.3.2workflow-delete-reassign+ 2.3.8issue-create-edit-flow.- Manual UX check — create: sign in, project page, press "C", create a Story with title + Markdown description + a pasted image; assert the toast shows the PROD-N identifier, the linked detail/edit page renders the description with the image rendered inline.
- Manual UX check — type+parent inline validation: open create modal; for every type, open the parent picker and confirm the candidate list matches 2.1.2's matrix (Epic→none, Story→Epic, Task→Story/Epic, Bug→Story/Task/Epic, Subtask→Story/Task/Bug); changing type after picking a parent clears the parent with the documented notice.
- Manual UX check — edit + finding #46: open
/projects/[key]/issues/[key]/edit; change title + status in one form, save; both succeed; activity tab shows two revisions; assert that mutating the row externally between page load + save surfaces the stale-edit banner. - Manual UX check — delete-with-reassign (2.3.1): as project admin, create a custom status, change 2–3 issues into it, delete it from
/settings/project/[key]/workflow, confirm reassign modal + per-item migration + status row removal + per-item revision. - Cross-workspace check: sign in as a user with only workspace-A; visit a workspace-B issue URL → 404 (no leak of title/existence/key).
- Findings-log update: append
> Resolved: 2.3.6to finding #46 inprodect_plan/PRODECT_FINDINGS.md.