DELETE /tool-applications/:applicationId pre-checked for connections and
then deleted in a later statement. Because tool_connections.application_id
was ON DELETE CASCADE, a connection created in that gap was silently
cascade-deleted instead of forcing the promised 409.
Switch the FK to ON DELETE NO ACTION so the database fails closed: an
application with connections can never be deleted, and a concurrently
inserted connection (which takes a FOR KEY SHARE lock on the parent row)
forces the delete to raise a foreign_key_violation rather than cascade.
NO ACTION (end-of-statement check) rather than RESTRICT (immediate) keeps
company teardown cascading cleanly.
The service still pre-checks for a friendly 409 with connectionCount, and
now also translates a 23503 violation on the connection FK into the same
409 so the race surfaces the conflict instead of a 500.
Regression tests: the raw delete is rejected and both rows survive (the
old cascade would have silently dropped the connection), and a company
delete still cascades application + connection away.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
DELETE /tool-applications/:applicationId with board-only auth + company
check mirroring the PATCH route. Guards with 409 when the application
still has connections (no force-cascade in v1) and writes a
tool_application.deleted activity log entry. Adds deleteApplication to
the UI client and a destructive confirm dialog in the row actions menu
that surfaces the connection-guard guidance inline on 409.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>