Repository navigation
fix(api): prevent repeated idea adoption from duplicating nodes - #310
Conversation
Reject another adoption while the idea has nodes in the graph, using the existing project lock. Keep original request receipt replay and allow adoption again after undo removes the path. Document the conflict and cover real isolated API transactions, including concurrent requests without a shared receipt. Fixes mikamikasuki#113 Assisted-by: OpenAI Codex
There was a problem hiding this comment.
🟡 Changes recommended
Although I found no code defects, the author explicitly did not run the PostgreSQL concurrency, full backend suite, or frontend paths, so final human validation of the concurrency guarantee is prudent before approval.
No open issues 🎉
What changed in this PR
This PR makes the "adopt idea" API endpoint resistant to accidental duplication. Previously, re-clicking "Adopt direction" (or retrying after an uncertain response) on an already-adopted idea without a shared request_id would silently create a second identical hypothesis/implementation/experiment node chain in the project graph (issue #113). The fix adds a guard in services/api/resources.py::adopt that rejects a fresh adoption with IDEA_ALREADY_ADOPTED (HTTP 409) whenever nodes tagged with that idea's adopted_idea_id already exist in the graph. The guard runs after the existing request_id replay short-circuit (so legitimate retries with the original receipt still return the current graph) and under the existing _lock_project transaction lock (so concurrent unkeyed requests are serialized). Undoing the adoption removes the nodes, which re-enables creating a new path.
Changes:
- Reject repeated unkeyed adoption of an idea that still has nodes in the graph with
IDEA_ALREADY_ADOPTED(409), placed after therequest_idreplay check and inside the project lock. - Add regression tests covering repeat adoption (no/different receipt), re-adoption after undo, and five concurrent unkeyed adoptions.
- Update the endpoint description and 409 error list in the hand-written contract, generated OpenAPI document, and API reference docs.
| File | Description |
|---|---|
services/api/resources.py |
Adds the IDEA_ALREADY_ADOPTED guard checking existing nodes' adopted_idea_id. |
tests/test_idea_adoption.py |
Adds three case_* regression tests for repeat, post-undo, and concurrent adoption. |
services/api/contracts.py |
Updates adopt endpoint description and 409 error enumeration. |
packages/contracts/openapi.json |
Regenerated OpenAPI description/409 text matching the contract. |
docs/API_REFERENCE.md |
Regenerated reference description/409 text matching the contract. |
Review effort: Balanced
Summary
Adopting the same idea again without a shared request receipt creates another copy of its research path. Reject a new adoption with
IDEA_ALREADY_ADOPTED(HTTP 409) while nodes belonging to that idea remain in the graph. The check runs under the existing project transaction lock.Replaying the original
request_idstill returns the current graph. Undoing the adoption allows a new path to be created. The API description and generated reference/OpenAPI document describe the conflict.Fixes #113.
Validation
01df33a5f5eca13b350efd90de4fdd6a02464e70.python -m pytest tests/test_idea_adoption.py -q→ 2 failed, 5 passed. Repeated and concurrent requests create duplicate paths.python -m pytest tests/test_idea_adoption.py tests/test_graph.py tests/test_api_contracts.py tests/test_contract_export.py -q→ 63 passed.python scripts/export_api_contract.py --check→ 185 operations across 142 API paths checked.The tests use actual FastAPI handlers, transactions and temporary SQLite databases on Linux/Python 3.12.3. They cover retries without a receipt, retries with another receipt, five concurrent unkeyed requests, replay with the original receipt, undo and adoption, and no automatic experiment runs. No provider or paid model calls were made.
Limits and AI assistance
PostgreSQL concurrency, browser interaction, the complete backend suite and frontend build were not run. The locking mechanism is unchanged; the executed concurrency regression uses SQLite.
Implementation, tests, investigation and this description were prepared with OpenAI Codex on Dcastroro's instructions. No human code review is claimed. No credentials, authentication bypasses or disabled security checks are introduced.