Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 5 additions & 1 deletion docs/.vitepress/rfd-summaries.json
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@
"summary": "Abandoned RFD split into RFD 048 and RFD 049; preserved for historical context on non-interactive mode design."
},
"020-parallel-conversations.md": {
"hash": "81206c64fcf58682afb91178c17bfc945489ea89ecc380428798a5fa1a197724",
"hash": "75ceb2a99600c700dc1fd304961cc5fdd5cdbe3391017b515a1496bab49caf9e",
"summary": "Replace global active conversation with per-session tracking and add conversation locks for parallel terminal work."
},
"021-printer-live-redirection.md": {
Expand Down Expand Up @@ -206,5 +206,9 @@
"052-workspace-data-store-sanitization.md": {
"hash": "199bf135a50791b21bf4c5ef6028012334cef5f8be9acb9cea14c54f1f461621",
"summary": "Workspace sanitization validates and repairs corrupted conversations, trashing broken data with recovery explanations."
},
"053-auto-refresh-conversation-titles.md": {
"hash": "42b4f764ddcb1a2f2177a2b9d61d56177b723ce40bba39038a0a19370f6c0dfd",
"summary": "Auto-refresh stale conversation titles periodically by re-running LLM generation when accumulated turns exceed a configured threshold."
}
}
27 changes: 18 additions & 9 deletions docs/rfd/020-parallel-conversations.md
Original file line number Diff line number Diff line change
Expand Up @@ -302,8 +302,8 @@ pub struct ConversationLock {
obtain a `ConversationLock` is through this method. The lock is acquired once at
the start of `jp query` and lives in a long-lived scope (e.g., `Ctx`).

Mutable access to a conversation's event stream requires a reference to the
lock:
Mutable access to a conversation's event stream or metadata requires a reference
to the lock:

```rust
impl Workspace {
Expand All @@ -312,26 +312,35 @@ impl Workspace {
id: &ConversationId,
_lock: &ConversationLock,
) -> Option<&mut ConversationStream> { /* ... */ }

pub fn get_conversation_mut(
&mut self,
id: &ConversationId,
_lock: &ConversationLock,
) -> Option<Mut<'_, ConversationId, Conversation>> { /* ... */ }
}
```

All existing mutation methods stay on `ConversationStream`. The only change is
that `get_events_mut` (and `try_get_events_mut`) take a `&ConversationLock`
parameter. Call sites add one argument:
All existing mutation methods stay on `ConversationStream` and `Conversation`.
The change is that `get_events_mut`, `try_get_events_mut`,
`get_conversation_mut`, and `try_get_conversation_mut` take a
`&ConversationLock` parameter. Call sites add one argument:

```rust
// before
workspace.try_get_events_mut(&cid)?.add_config_delta(delta);
workspace.try_get_conversation_mut(&cid)?.title = Some(title);

// after
workspace.try_get_events_mut(&cid, &lock)?.add_config_delta(delta);
workspace.try_get_conversation_mut(&cid, &lock)?.title = Some(title);
```

This enforces the lock-before-mutate invariant at the API boundary. You cannot
call `get_events_mut` without proof that the process holds the lock.
`ConversationLock` is held for the entire `jp query` run, so any `&mut
ConversationStream` obtained through it is guaranteed to be protected by the
lock for its entire lifetime.
call `get_events_mut` or `get_conversation_mut` without proof that the process
holds the lock. `ConversationLock` is held for the entire `jp query` run, so any
mutable reference obtained through it is guaranteed to be protected by the lock
for its entire lifetime.

The lock file is deleted in the `Drop` implementation of `ConversationLock`.

Expand Down
Loading
Loading