BUG: chat_create can return and mutate an existing context when new_context collides #181

Open
opened 2026-09-09 15:05:27 +00:00 by nsaspy · 0 comments
Owner

Finding

api/chat_create.py accepts an optional caller-provided new_context value:

new_ctxid = input.get("new_context", guids.generate_id())
new_context = self.use_context(new_ctxid)

helpers.context_utils.use_context() first calls AgentContext.use(ctxid) and returns that existing context when the ID is already present. It only creates a context when lookup fails.

chat_create then treats the returned object as new and may copy project metadata and chat_model_override from current_context into it before returning ok: true / that same ID.

So a create request with a colliding valid context ID is not a conflict/idempotent no-op; it can mutate an unrelated existing conversation's project/model state while claiming a chat was created.

This should be fixed alongside the context-ID validation/containment work in #43, but it is a separate lifecycle invariant.

Direction

Separate get/use existing context from create context with requested id APIs. Creation must be atomic under the context registry lock and fail deterministically on collision unless a specific endpoint documents idempotent create semantics.

The normal WebUI should usually generate the ID server-side; caller-supplied IDs should exist only for an explicit import/restore/test contract if still needed.

Acceptance

  • chat_create never returns an existing context as a newly-created chat.
  • A requested ID collision returns a deterministic conflict/error and leaves the existing context byte-for-byte/logically unchanged.
  • Concurrent create calls for the same ID cannot both succeed.
  • Project metadata, profile reconciliation and model override inheritance run only on the newly-created object.
  • Normal create uses a server-generated validated ID by default.
  • Import/restore workflows that need caller-controlled identity use a separate explicit path or documented collision policy.
  • Regression test proves a collision cannot alter an existing chat's project/profile/model settings.

Mirrored from lost-rob0t/a0-symbolics#44 via tracker sync.

## Finding `api/chat_create.py` accepts an optional caller-provided `new_context` value: ```python new_ctxid = input.get("new_context", guids.generate_id()) new_context = self.use_context(new_ctxid) ``` `helpers.context_utils.use_context()` first calls `AgentContext.use(ctxid)` and returns that existing context when the ID is already present. It only creates a context when lookup fails. `chat_create` then treats the returned object as new and may copy project metadata and `chat_model_override` from `current_context` into it before returning `ok: true` / that same ID. So a create request with a colliding valid context ID is not a conflict/idempotent no-op; it can mutate an unrelated existing conversation's project/model state while claiming a chat was created. This should be fixed alongside the context-ID validation/containment work in #43, but it is a separate lifecycle invariant. ## Direction Separate `get/use existing context` from `create context with requested id` APIs. Creation must be atomic under the context registry lock and fail deterministically on collision unless a specific endpoint documents idempotent create semantics. The normal WebUI should usually generate the ID server-side; caller-supplied IDs should exist only for an explicit import/restore/test contract if still needed. ## Acceptance - [ ] `chat_create` never returns an existing context as a newly-created chat. - [ ] A requested ID collision returns a deterministic conflict/error and leaves the existing context byte-for-byte/logically unchanged. - [ ] Concurrent create calls for the same ID cannot both succeed. - [ ] Project metadata, profile reconciliation and model override inheritance run only on the newly-created object. - [ ] Normal create uses a server-generated validated ID by default. - [ ] Import/restore workflows that need caller-controlled identity use a separate explicit path or documented collision policy. - [ ] Regression test proves a collision cannot alter an existing chat's project/profile/model settings. --- *Mirrored from [`lost-rob0t/a0-symbolics#44`](https://github.com/lost-rob0t/a0-symbolics/issues/44)* via tracker sync.
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
nsaspy/a0-symbolics#181
No description provided.