Skip to content

fix(api): prevent repeated idea adoption from duplicating nodes - #310

Merged
mikamikasuki merged 1 commit into
mikamikasuki:mainfrom
Dcastroro:fix/repeated-idea-adoption
Oct 21, 2026
Merged

mikamikasuki merged 1 commit into
mikamikasuki:mainfrom
Dcastroro:fix/repeated-idea-adoption

Conversation

@Dcastroro

Copy link
Copy Markdown
Contributor

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_id still 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

  • Tested base: 01df33a5f5eca13b350efd90de4fdd6a02464e70.
  • With the final regression tests and the original route: python -m pytest tests/test_idea_adoption.py -q → 2 failed, 5 passed. Repeated and concurrent requests create duplicate paths.
  • With the correction: 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.

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
Copilot AI balanced review requested due to automatic review settings October 21, 2026 12:54

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 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 the request_id replay 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

@mikamikasuki
mikamikasuki merged commit 5b5abda into mikamikasuki:main Oct 21, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Re-adopting an idea duplicates its node chain

3 participants