Repository navigation
fix(leads): refuse create collisions without overwriting contacts - #29
Merged
debpalash merged 1 commit intoOct 2, 2026
Merged
Conversation
Signed-off-by: Rudy Celekli <47457359+rudycelekli@users.noreply.github.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What & why
Closes #15. POST /api/lead now refuses an existing company/city row with HTTP409 and its existing lead_id, instead of silently replacing its contact and clearing omitted enrichment fields. The existing data model has one lead per workspace/company/city. Explicit PUT remains the supported update/clear operation; collection callers retain the default upsert behavior. Both backend implementations map a concurrent unique-key collision to the same nonmutating error. The web API client propagates that failure; the Add Lead dialog shows the actionable message, preserves input and releases its loading state without reporting success.
The database-backed HTTP regression preserves exact existing row on rejected collision then verifies explicit PUT replacement/clearing. Six simultaneous creates have exactly one winner, five409replies and no contact replacement on both backends.
Type
Validation
before.log:2failed, real HTTP API returned200/sameID instead of refusing collision on rawSQLite and actual ORM store running SQLite.
after-final.log:19passed, four new parametercases plus existing store/job/utility suites; final docstrings added afterward, behavioral source unchanged.
Actual SQLite raw+SQLAlchemy stores and HTTP router tested; no native PostgreSQL/RLS claim. Schema/uniqueness unchanged. Create POST collisions intentionally now409; PUT explicit replacements/empty clears remain supported.
The native Bun API regression fails on unchanged main because an HTTP409 is returned as a successful creation. After the fix, both error and successful-creation cases pass (
bun test apps/web/tests/api.lead-create.test.ts, Bun1.4.2). The dialog captures its form before the async call, clears/reset only on success, and retains inputs on refusal. The actual changed web app also passesbun run build(TypeScript build and Vite production build) andbun run lintwith the locked shared dependencies. The complete backend suite also passed; the documentation-workspace build was not run.Checklist
Security-sensitive?
Same-workspace data correctness. Existing authentication, workspace/RLS selectors, outbound fetch guards, secrets and LLM handling are unchanged. No exploitable vulnerability claim.
Final validation
uv run --frozen pytest -q -m "not live and not postgres": 1845 passed, 131 skipped, 12 warnings in 64.13s (0:01:04).Prepared with AI assistance; reproduced locally and independently reviewed.
Frontend: web ESLint and TypeScript/Vite build passed on this patch. Documentation-workspace build was not run.