Introduce a desktop/web-only /handoff-review flow that generates a handoff from the active implementation session, creates or reuses a separate review session in the same directory, and links the pair through hidden OpenChamber session metadata. Add review flow orchestration, metadata helpers, magic prompts, localized command/action labels, session metadata create/update support, and assistant message transfer actions for sending reviewer feedback back to the implementer or implementation responses back to the reviewer. Review sessions are ordinary sessions, not child sessions. The flow avoids exposing session IDs or routing metadata to agents, hides review controls on mobile and VS Code, hides unrelated assistant actions inside review sessions, cleans up stale metadata where possible, and uses the optimistic send path so cross-session sends scroll like normal composer messages.
767 lines
26 KiB
Markdown
767 lines
26 KiB
Markdown
# Review Flow Implementation Plan
|
|
|
|
## Goal
|
|
|
|
Build an end-to-end OpenChamber review handoff flow that lets one session implement changes and another normal session review them, with OpenChamber metadata connecting the two sessions invisibly.
|
|
|
|
The agents must not see session IDs, metadata, linked-session wording, or routing details. They should only receive natural prompts:
|
|
|
|
- Review session initial prompt: a handoff plus an instruction to review it.
|
|
- Review-to-implementer prompt: another agent reviewed the changes and left feedback; resolve relevant issues.
|
|
- Implementer-to-review prompt: the agent implementing changes responded to previous feedback; review latest state again.
|
|
|
|
The review session is not a child/subsession. It is a normal session in the same directory as the original session.
|
|
|
|
## Proposed Command Name
|
|
|
|
Do not use `/review`, because that overlaps with OpenCode's default review command semantics.
|
|
|
|
Use `/handoff-review` unless we choose a shorter name before implementation.
|
|
|
|
Other acceptable names if `/handoff-review` feels too long:
|
|
|
|
- `/review-handoff`
|
|
- `/ask-review`
|
|
- `/start-review`
|
|
|
|
This plan assumes `/handoff-review`.
|
|
|
|
## Existing Code Paths To Reuse
|
|
|
|
### Slash Command Routing
|
|
|
|
Relevant files:
|
|
|
|
- `packages/ui/src/sync/session-ui-store.ts`
|
|
- `packages/ui/src/lib/opencode/client.ts`
|
|
- `packages/ui/src/lib/magicPrompts.ts`
|
|
|
|
Current behavior:
|
|
|
|
- `routeMessage(...)` detects messages starting with `/`.
|
|
- It checks command metadata from `getDirectoryState(requestDirectory)?.command` and `useCommandsStore.getState().commands`.
|
|
- If the command exists, it uses `optimisticSend(...)` and calls `opencodeClient.sendCommand(...)`.
|
|
- `opencodeClient.sendCommand(...)` calls SDK `client.session.command(...)` with `sessionID`, `command`, `arguments`, selected model, selected agent, variant, files, and client-generated `messageID`.
|
|
|
|
Reuse this for invoking the handoff-generation command. The review flow should not invent a separate command transport.
|
|
|
|
### Magic Prompt Registry
|
|
|
|
Relevant file:
|
|
|
|
- `packages/ui/src/lib/magicPrompts.ts`
|
|
|
|
Current examples:
|
|
|
|
- `session.summary.visible`
|
|
- `session.summary.instructions`
|
|
- `session.review.visible`
|
|
- `session.review.instructions`
|
|
- `git.commit.generate.visible`
|
|
- `git.commit.generate.instructions`
|
|
|
|
Add new prompt entries for this flow instead of hardcoding long prompts in components/actions.
|
|
|
|
Needed prompt entries:
|
|
|
|
- `session.reviewHandoff.visible`
|
|
- `session.reviewHandoff.instructions`
|
|
- `session.reviewSession.visible`
|
|
- `session.reviewSession.instructions` if we need hidden instructions for the review session starter prompt
|
|
- `session.reviewFeedbackToImplementer.visible`
|
|
- `session.implementationResponseToReviewer.visible`
|
|
|
|
The two cross-session prompts must match the agreed wording closely.
|
|
|
|
Review feedback sent back to the original session:
|
|
|
|
```md
|
|
Another agent reviewed your changes and left the feedback below.
|
|
|
|
Please review the feedback, resolve the relevant issues, and explain what you changed.
|
|
|
|
<review feedback>
|
|
```
|
|
|
|
Implementation response sent back to the review session:
|
|
|
|
```md
|
|
The agent implementing the changes has responded to the previous review feedback.
|
|
|
|
Please review the latest state again and report any remaining issues.
|
|
|
|
<implementation response / latest assistant message>
|
|
```
|
|
|
|
Initial review session prompt should be similar to:
|
|
|
|
```md
|
|
Please review the changes described in this handoff.
|
|
|
|
Focus on correctness, regressions, missing implementation, missing tests, and whether the implementation satisfies the stated intent. Provide concise, actionable feedback for the agent implementing the changes.
|
|
|
|
<handoff>
|
|
```
|
|
|
|
The handoff-generation prompt should be based on the summary command, but explicitly include the user's intent and enough implementation context for another agent to review.
|
|
|
|
### Handoff Generation Concept
|
|
|
|
Relevant existing prompt:
|
|
|
|
- `session.summary.instructions` in `packages/ui/src/lib/magicPrompts.ts`
|
|
|
|
Current summary instructions already include:
|
|
|
|
- completed work
|
|
- in-progress work
|
|
- modified files and why
|
|
- open questions and next steps
|
|
- user requests, constraints, preferences
|
|
- technical decisions and rationale
|
|
|
|
The new handoff prompt should keep those ideas and make intent explicit:
|
|
|
|
- What the user wanted and why
|
|
- What was implemented
|
|
- What files changed and why
|
|
- Important design choices
|
|
- Known limitations or uncertainty
|
|
- Validation/test status if known from the session
|
|
- Anything the reviewer should pay special attention to
|
|
|
|
This is an implementation detail, not a risk. The prompt should be specific enough that the review agent can judge intent and implementation without needing private OpenChamber routing context.
|
|
|
|
### Active Session Generation Concept
|
|
|
|
Relevant files:
|
|
|
|
- `packages/ui/src/lib/gitApi.ts`
|
|
- `packages/ui/src/components/views/GitView.tsx`
|
|
|
|
Current commit-message generation uses:
|
|
|
|
- `resolveSessionGenerationContext()` to find current session, model, agent, and variant.
|
|
- `runStructuredGenerationInActiveSession(...)` to send a visible prompt plus hidden synthetic instructions to the active session.
|
|
- `extractAssistantText(...)` and JSON parsing to get output from the assistant response.
|
|
|
|
Important difference for review flow:
|
|
|
|
- Commit generation uses `client.session.prompt(...)` and receives the response directly.
|
|
- Slash commands use `client.session.command(...)`, are effectively fire-and-forget from the UI path, and rely on SSE to populate messages/status.
|
|
|
|
For `/handoff-review`, prefer the visible slash command path so the original session contains the generated handoff. Then wait for the resulting assistant output through sync state. Reuse the commit-generation concepts for:
|
|
|
|
- selected model/agent/variant resolution
|
|
- extracting text from assistant message parts
|
|
- forcing chat scroll if useful
|
|
- timeout/error handling style
|
|
|
|
Create a small reusable helper for waiting for the next completed assistant text after a known user command message ID.
|
|
|
|
### Session Create/Update/Delete
|
|
|
|
Relevant files:
|
|
|
|
- `packages/ui/src/lib/opencode/client.ts`
|
|
- `packages/ui/src/sync/session-actions.ts`
|
|
- `packages/ui/src/sync/event-reducer.ts`
|
|
- `packages/ui/src/stores/useGlobalSessionsStore.ts`
|
|
|
|
Current behavior:
|
|
|
|
- `opencodeClient.createSession(...)` calls `client.session.create(...)` using the legacy OpenCode session API.
|
|
- OpenCode supports `metadata` on that API, but OpenChamber currently only forwards `parentID` and `title`.
|
|
- `opencodeClient.updateSession(...)` currently only forwards `title` and `time.archived`.
|
|
- OpenCode `metadata` update replaces the whole metadata object. It does not deep-merge.
|
|
- `deleteSession(...)` and `deleteSessionInDirectory(...)` optimistically remove the session, then call `opencodeClient.deleteSession(...)`, and restore snapshots on failure.
|
|
- `event-reducer.ts` replaces session objects from `session.created` and `session.updated` events.
|
|
|
|
Add metadata support here first. The review flow depends on it.
|
|
|
|
### Context Panel Session Tabs
|
|
|
|
Relevant files:
|
|
|
|
- `packages/ui/src/stores/useUIStore.ts`
|
|
- `packages/ui/src/components/session/sidebar/SessionNodeItem.tsx`
|
|
- `packages/ui/src/components/layout/ContextPanel.tsx`
|
|
- `packages/ui/src/components/chat/message/MessageBody.tsx`
|
|
- `packages/ui/src/components/chat/message/parts/ToolPart.tsx`
|
|
|
|
Current behavior:
|
|
|
|
- `useUIStore.openContextPanelTab(directory, tab)` opens or upserts context panel tabs.
|
|
- Chat tabs use `mode: 'chat'`.
|
|
- Existing dedupe key convention for session chat tabs is `session:<sessionID>`.
|
|
- Sidebar already opens a session in the side panel with:
|
|
|
|
```ts
|
|
openContextPanelTab(sessionDirectory, {
|
|
mode: 'chat',
|
|
dedupeKey: `session:${session.id}`,
|
|
label: sessionTitle,
|
|
})
|
|
```
|
|
|
|
Reuse the same convention for opening the review session in the context panel.
|
|
|
|
### Assistant Message Action Buttons
|
|
|
|
Relevant file:
|
|
|
|
- `packages/ui/src/components/chat/message/MessageBody.tsx`
|
|
|
|
Current behavior:
|
|
|
|
- `AssistantMessageActionButtons` renders icon-only buttons for copy, save image, and TTS.
|
|
- The buttons use shared `Button`, `Tooltip`, and `Icon` components.
|
|
- The shared icon sprite already contains `arrow-left-right`.
|
|
|
|
Extend this action area with an optional review-transfer action. Do not import icons directly from Remixicon.
|
|
|
|
## Metadata Contract
|
|
|
|
Use a namespaced metadata object so we do not collide with user or upstream metadata.
|
|
|
|
Original session metadata:
|
|
|
|
```ts
|
|
{
|
|
openchamber: {
|
|
reviewSessionID: string
|
|
}
|
|
}
|
|
```
|
|
|
|
Review session metadata:
|
|
|
|
```ts
|
|
{
|
|
openchamber: {
|
|
kind: 'review'
|
|
originalSessionID: string
|
|
}
|
|
}
|
|
```
|
|
|
|
Rules:
|
|
|
|
- Only one review session per original session.
|
|
- If original metadata already has `openchamber.reviewSessionID`, reuse that session instead of creating a new review session.
|
|
- The review session must not have `parentID` set to the original session.
|
|
- Both sessions must stay in the same directory.
|
|
- Metadata is internal routing state only. Never include it in prompts.
|
|
- Metadata updates must preserve unrelated metadata keys.
|
|
|
|
Recommended helpers:
|
|
|
|
```ts
|
|
type OpenChamberSessionMetadata = {
|
|
openchamber?: {
|
|
kind?: 'review'
|
|
originalSessionID?: string
|
|
reviewSessionID?: string
|
|
}
|
|
[key: string]: unknown
|
|
}
|
|
```
|
|
|
|
Helper functions should live in a focused module, for example:
|
|
|
|
- `packages/ui/src/lib/sessionReviewMetadata.ts`
|
|
|
|
Functions:
|
|
|
|
- `getOpenChamberMetadata(session)`
|
|
- `isReviewSession(session)`
|
|
- `getOriginalSessionID(session)`
|
|
- `getReviewSessionID(session)`
|
|
- `withReviewSessionLink(metadata, reviewSessionID)`
|
|
- `withReviewSessionMarker(metadata, originalSessionID)`
|
|
- `withoutReviewSessionLink(metadata, reviewSessionID)`
|
|
|
|
The helpers should clone only the metadata branch they change and preserve all unrelated metadata.
|
|
|
|
## Implementation Steps
|
|
|
|
### 1. Load Required Skills Before Editing
|
|
|
|
When implementing this plan, load these skills before changing code:
|
|
|
|
- `ui-api-decoupling` because the work changes SDK data access and session API wrapper behavior.
|
|
- `theme-system` because the work adds a UI button/icon.
|
|
- `locale-ui-patterns` because the work adds tooltips, aria labels, toasts, and command text.
|
|
|
|
If the final implementation touches Settings magic prompt UI, also load:
|
|
|
|
- `settings-ui-patterns`
|
|
|
|
### 2. Add Metadata Support To OpenCode Client Wrapper
|
|
|
|
File:
|
|
|
|
- `packages/ui/src/lib/opencode/client.ts`
|
|
|
|
Change `createSession` signature from:
|
|
|
|
```ts
|
|
async createSession(params?: { parentID?: string; title?: string }, directory?: string | null): Promise<Session>
|
|
```
|
|
|
|
to:
|
|
|
|
```ts
|
|
async createSession(
|
|
params?: {
|
|
parentID?: string
|
|
title?: string
|
|
metadata?: Record<string, unknown>
|
|
},
|
|
directory?: string | null,
|
|
): Promise<Session>
|
|
```
|
|
|
|
Forward `metadata: params?.metadata` only when it is defined.
|
|
|
|
Change `updateSession` patch type from:
|
|
|
|
```ts
|
|
patch: { title?: string; time?: { archived?: number | null } }
|
|
```
|
|
|
|
to:
|
|
|
|
```ts
|
|
patch: {
|
|
title?: string
|
|
metadata?: Record<string, unknown>
|
|
time?: { archived?: number | null }
|
|
}
|
|
```
|
|
|
|
Forward `metadata` when defined.
|
|
|
|
Important: this method should still replace metadata because the upstream API replaces metadata. Do not hide this with an implicit merge here. Add merge behavior in a separate helper so call sites are explicit.
|
|
|
|
### 3. Make Session Types Metadata-Aware In OpenChamber
|
|
|
|
OpenCode SDK response types should include metadata in the current v2 SDK legacy `Session`, but verify local imports and generated types used by OpenChamber.
|
|
|
|
Files to inspect/update:
|
|
|
|
- `packages/ui/src/stores/types/sessionTypes.ts`
|
|
- Any local `Session` wrapper/normalizer if present
|
|
- `packages/ui/src/sync/sanitize.ts`
|
|
- `packages/ui/src/sync/event-reducer.ts`
|
|
- `packages/ui/src/stores/useGlobalSessionsStore.ts`
|
|
|
|
Goal:
|
|
|
|
- `session.metadata` should survive list, get, create, update, SSE event replacement, global sessions, and reconnect recovery.
|
|
- Do not strip `metadata` in sanitation helpers.
|
|
- Do not create new broad store subscriptions. Use leaf selectors where UI only needs review metadata for one session.
|
|
|
|
### 4. Add Explicit Metadata Merge Helpers
|
|
|
|
Add a helper, likely in `packages/ui/src/sync/session-actions.ts` or a small module imported by it:
|
|
|
|
```ts
|
|
async function patchSessionMetadata(
|
|
sessionId: string,
|
|
directory: string | null | undefined,
|
|
updater: (metadata: Record<string, unknown>) => Record<string, unknown>,
|
|
): Promise<Session>
|
|
```
|
|
|
|
Behavior:
|
|
|
|
1. Read the current session with `opencodeClient.getSession(sessionId)` using the correct directory.
|
|
2. Read `current.metadata ?? {}`.
|
|
3. Apply updater.
|
|
4. Call `opencodeClient.updateSession(sessionId, { metadata: nextMetadata }, directory)`.
|
|
5. Upsert the returned session into `useGlobalSessionsStore` and the relevant child store if needed.
|
|
|
|
Do not swallow fetch/update errors. Callers need to know if metadata linkage failed.
|
|
|
|
### 5. Add Review Flow Magic Prompts
|
|
|
|
File:
|
|
|
|
- `packages/ui/src/lib/magicPrompts.ts`
|
|
|
|
Add these prompt records:
|
|
|
|
1. `session.reviewHandoff.visible`
|
|
|
|
Suggested template:
|
|
|
|
```txt
|
|
Prepare a handoff for another agent to review this work.
|
|
```
|
|
|
|
2. `session.reviewHandoff.instructions`
|
|
|
|
Suggested template:
|
|
|
|
```txt
|
|
Produce a review handoff for another agent. Do not compact or mutate session history. Your output is an assistant message that OpenChamber will send to a separate reviewer agent.
|
|
|
|
Include:
|
|
- The user's original intent and any later clarifications that changed the intent
|
|
- What was implemented and why
|
|
- Files changed, with brief purpose per file
|
|
- Important design decisions and tradeoffs
|
|
- Validation/tests run, if known
|
|
- Known gaps, uncertainty, or areas the reviewer should inspect closely
|
|
|
|
Formatting:
|
|
- Concise markdown with clear sections
|
|
- No preamble like "Here is a handoff"
|
|
- Do not mention OpenChamber metadata, linked sessions, session IDs, or routing
|
|
- Respond in the same language the user used most in the conversation
|
|
```
|
|
|
|
3. `session.reviewSession.visible`
|
|
|
|
Suggested template with `{{handoff}}` placeholder:
|
|
|
|
```txt
|
|
Please review the changes described in this handoff.
|
|
|
|
Focus on correctness, regressions, missing implementation, missing tests, and whether the implementation satisfies the stated intent. Provide concise, actionable feedback for the agent implementing the changes.
|
|
|
|
{{handoff}}
|
|
```
|
|
|
|
4. `session.reviewFeedbackToImplementer.visible`
|
|
|
|
Suggested template with `{{review_feedback}}` placeholder:
|
|
|
|
```txt
|
|
Another agent reviewed your changes and left the feedback below.
|
|
|
|
Please review the feedback, resolve the relevant issues, and explain what you changed.
|
|
|
|
{{review_feedback}}
|
|
```
|
|
|
|
5. `session.implementationResponseToReviewer.visible`
|
|
|
|
Suggested template with `{{implementation_response}}` placeholder:
|
|
|
|
```txt
|
|
The agent implementing the changes has responded to the previous review feedback.
|
|
|
|
Please review the latest state again and report any remaining issues.
|
|
|
|
{{implementation_response}}
|
|
```
|
|
|
|
### 6. Add Localized UI Strings
|
|
|
|
Files:
|
|
|
|
- `packages/ui/src/lib/i18n/messages/en.ts`
|
|
- Other locale files as required by the project pattern
|
|
|
|
Add strings for:
|
|
|
|
- Command autocomplete description for `/handoff-review`.
|
|
- Review flow button aria label on review session: “Send review feedback to implementing agent”.
|
|
- Review flow button aria label on original session: “Send implementation response to reviewing agent”.
|
|
- Tooltip text for both directions.
|
|
- Toasts for starting handoff generation, review session creation/reuse, transfer success, transfer failure, missing linked session, missing assistant text.
|
|
|
|
Follow locale-ui-patterns. Do not hardcode user-facing text inside components.
|
|
|
|
### 7. Register The New OpenChamber Slash Command
|
|
|
|
There are two possible implementation paths. Pick the one matching how OpenChamber-owned commands are currently registered.
|
|
|
|
Likely locations:
|
|
|
|
- `packages/ui/src/lib/magicPrompts.ts`
|
|
- command autocomplete/store code around `useCommandsStore`
|
|
- command rendering in `ChatInput` / command autocomplete components
|
|
|
|
The command should appear as `/handoff-review` in OpenChamber command autocomplete.
|
|
|
|
It should be treated as an OpenChamber flow command, not only a raw OpenCode command, because after the handoff assistant output completes OpenChamber must create/reuse/open/send to the review session.
|
|
|
|
Implementation options:
|
|
|
|
1. Intercept `/handoff-review` in `routeMessage(...)` before normal OpenCode command lookup.
|
|
2. Add it to the command store as an OpenChamber-owned command with a handler.
|
|
|
|
Prefer the smallest approach consistent with existing command architecture.
|
|
|
|
### 8. Implement Handoff Generation And Wait Helper
|
|
|
|
Add a helper that starts the handoff command in the original session and resolves with the assistant handoff text.
|
|
|
|
Possible module:
|
|
|
|
- `packages/ui/src/lib/reviewFlow.ts`
|
|
|
|
Inputs:
|
|
|
|
```ts
|
|
{
|
|
originalSessionID: string
|
|
directory: string
|
|
providerID: string
|
|
modelID: string
|
|
agent?: string
|
|
variant?: string
|
|
}
|
|
```
|
|
|
|
Flow:
|
|
|
|
1. Render `session.reviewHandoff.visible` and `session.reviewHandoff.instructions`.
|
|
2. Send a user message to the original session using `opencodeClient.sendMessage(...)` or the existing command route, depending on final command integration.
|
|
3. Include the visible handoff request as the visible user text.
|
|
4. Include hidden instructions as synthetic additional part if using `sendMessage(...)`.
|
|
5. Capture the generated user message ID.
|
|
6. Wait until a later assistant message for the same session is complete and has text.
|
|
7. Extract text with the same idea as `flattenAssistantTextParts(...)` / `extractAssistantText(...)`.
|
|
8. Timeout with a clear failure if no handoff arrives.
|
|
|
|
Waiting rules:
|
|
|
|
- Prefer sync store state over polling the server repeatedly.
|
|
- Use existing `getSyncMessages(sessionID)` and `getSyncParts(sessionID)` from `sync-refs` if they expose enough data.
|
|
- If a subscription-based wait is not easy, use a bounded interval that reads sync refs and stops on timeout or completion.
|
|
- Ensure it waits for assistant completion, not just first streaming text.
|
|
- Avoid broad store subscriptions in React components.
|
|
|
|
### 9. Create Or Reuse The Review Session
|
|
|
|
After handoff text is available:
|
|
|
|
1. Read original session with `opencodeClient.getSession(originalSessionID)`.
|
|
2. Read `original.metadata.openchamber.reviewSessionID`.
|
|
3. If it exists:
|
|
- Try to get that review session in the same directory.
|
|
- If it exists and has `metadata.openchamber.kind === 'review'`, reuse it.
|
|
- If it is missing/deleted, clear the stale link and create a new review session.
|
|
4. If it does not exist, create a new normal session in the same directory with metadata:
|
|
|
|
```ts
|
|
{
|
|
openchamber: {
|
|
kind: 'review',
|
|
originalSessionID,
|
|
}
|
|
}
|
|
```
|
|
|
|
5. Patch original session metadata with:
|
|
|
|
```ts
|
|
{
|
|
openchamber: {
|
|
reviewSessionID: reviewSession.id,
|
|
}
|
|
}
|
|
```
|
|
|
|
Preserve unrelated metadata on both sessions.
|
|
|
|
If metadata patching original fails after creating review session, report failure clearly. Do not silently proceed with an unlinked session.
|
|
|
|
### 10. Send Initial Prompt To Review Session
|
|
|
|
After create/reuse:
|
|
|
|
1. Render `session.reviewSession.visible` with `handoff`.
|
|
2. Send it to the review session as a normal user message.
|
|
3. Use the same provider/model/agent/variant policy as the current session unless product decision says otherwise.
|
|
4. Do not mention session IDs or linked sessions.
|
|
|
|
Important:
|
|
|
|
- If reusing an existing review session, still send the new handoff prompt into it.
|
|
- Reuse does not mean “do nothing”; it means continue the same review conversation.
|
|
|
|
### 11. Open Review Session In Context Panel
|
|
|
|
Use:
|
|
|
|
```ts
|
|
useUIStore.getState().openContextPanelTab(directory, {
|
|
mode: 'chat',
|
|
dedupeKey: `session:${reviewSession.id}`,
|
|
label: reviewSession.title,
|
|
})
|
|
```
|
|
|
|
This should happen after the review session exists and the initial prompt has been sent, or immediately after creation if sending happens asynchronously but errors are still surfaced.
|
|
|
|
### 12. Add Cross-Session Transfer Button On Assistant Messages
|
|
|
|
File:
|
|
|
|
- `packages/ui/src/components/chat/message/MessageBody.tsx`
|
|
|
|
Add optional props to `AssistantMessageActionButtons`:
|
|
|
|
```ts
|
|
reviewTransferAction?: {
|
|
ariaLabel: string
|
|
tooltip: string
|
|
disabled?: boolean
|
|
onClick: () => Promise<void> | void
|
|
}
|
|
```
|
|
|
|
Render an icon-only button with:
|
|
|
|
```tsx
|
|
<Icon name="arrow-left-right" ... />
|
|
```
|
|
|
|
Visibility rules:
|
|
|
|
- Only assistant messages.
|
|
- Only messages with copyable text.
|
|
- In a review session: show button to send review feedback to the original session.
|
|
- In an original session with `metadata.openchamber.reviewSessionID`: show button to send implementation response to the review session.
|
|
- Do not show in mini-chat if that surface should avoid extra controls; follow current action-button surface rules.
|
|
|
|
To avoid button spam:
|
|
|
|
- Preferred first implementation: show on assistant messages where normal assistant action buttons already show.
|
|
- Do not add the button to user messages.
|
|
- If this feels too noisy in testing, narrow to latest completed assistant message per session as a follow-up, but not required for initial end-to-end implementation.
|
|
|
|
### 13. Implement Review Feedback Transfer
|
|
|
|
When clicking the button in a review session:
|
|
|
|
1. Get current review session metadata.
|
|
2. Resolve `originalSessionID`.
|
|
3. Extract the clicked assistant message text.
|
|
4. Render `session.reviewFeedbackToImplementer.visible` with `review_feedback`.
|
|
5. Send it as a normal user message into the original session.
|
|
6. Use original session directory.
|
|
7. Optionally open/focus the original session or leave context panel as-is. The agreed behavior only requires sending.
|
|
8. Show success/failure toast.
|
|
|
|
Message sent to the agent must be exactly natural-language feedback, not routing data.
|
|
|
|
### 14. Implement Implementation Response Transfer
|
|
|
|
When clicking the button in the original session:
|
|
|
|
1. Get original session metadata.
|
|
2. Resolve `reviewSessionID`.
|
|
3. Extract the clicked assistant message text.
|
|
4. Render `session.implementationResponseToReviewer.visible` with `implementation_response`.
|
|
5. Send it as a normal user message into the review session.
|
|
6. Use same directory.
|
|
7. Open/focus the review session context panel tab, because review continuation happens there.
|
|
8. Show success/failure toast.
|
|
|
|
### 15. Cleanup Metadata When Deleting Review Session
|
|
|
|
Files:
|
|
|
|
- `packages/ui/src/sync/session-actions.ts`
|
|
- `packages/ui/src/lib/opencode/client.ts`
|
|
|
|
Before deleting a session:
|
|
|
|
1. Read the session being deleted.
|
|
2. If it is a review session and has `originalSessionID`, read the original session.
|
|
3. If original metadata has `reviewSessionID` equal to the deleted review session ID, patch original metadata to remove it.
|
|
4. Then delete the review session.
|
|
|
|
Failure behavior:
|
|
|
|
- If metadata cleanup fails, do not delete silently. Return failure and show the existing delete failure path/toast.
|
|
- If original session no longer exists, continue deleting the review session; there is nothing to clean.
|
|
- If delete fails after metadata cleanup succeeded, restore the original metadata link as part of rollback if possible. At minimum, log and surface the delete failure.
|
|
|
|
Also apply this to `deleteSessionInDirectory(...)`.
|
|
|
|
### 16. Cleanup Stale Link When Reusing Review Session
|
|
|
|
If original metadata points to a review session that no longer exists:
|
|
|
|
1. Patch original metadata to remove stale `reviewSessionID`.
|
|
2. Create a fresh review session.
|
|
3. Patch original metadata with the fresh review session ID.
|
|
|
|
This is not a background reconciler. It only happens when the user starts the review flow.
|
|
|
|
### 17. Tests
|
|
|
|
Add focused tests for helpers and flow boundaries.
|
|
|
|
Likely files:
|
|
|
|
- New `packages/ui/src/lib/sessionReviewMetadata.test.ts`
|
|
- Existing `packages/ui/src/sync/session-actions.test.ts`
|
|
- Component test around `MessageBody` only if nearby test patterns exist
|
|
|
|
Test cases:
|
|
|
|
1. Metadata helper marks review session without removing unrelated metadata.
|
|
2. Metadata helper links original session without removing unrelated metadata.
|
|
3. Metadata helper removes review link only when it matches the deleted review session ID.
|
|
4. `createSession` forwards metadata to SDK client.
|
|
5. `updateSession` forwards metadata to SDK client.
|
|
6. Review flow reuses existing review session ID instead of creating another.
|
|
7. Review flow clears stale review session ID when referenced review session is missing.
|
|
8. Delete review session cleans original metadata before deleting.
|
|
9. Transfer prompt for review-to-implementer contains no session ID / metadata / linked-session wording.
|
|
10. Transfer prompt for implementer-to-reviewer contains no session ID / metadata / linked-session wording.
|
|
|
|
### 18. Validation
|
|
|
|
Run:
|
|
|
|
```sh
|
|
bun run type-check
|
|
bun run lint
|
|
```
|
|
|
|
Manual validation checklist:
|
|
|
|
1. Start `/handoff-review` in a normal session.
|
|
2. Confirm a handoff assistant message appears in original session.
|
|
3. Confirm a normal review session is created in the same directory, not as a child.
|
|
4. Confirm original metadata has `openchamber.reviewSessionID`.
|
|
5. Confirm review metadata has `openchamber.kind === 'review'` and `openchamber.originalSessionID`.
|
|
6. Confirm review session opens in context panel.
|
|
7. Confirm review session receives the initial handoff review prompt.
|
|
8. Confirm arrow-left-right appears on review assistant message actions.
|
|
9. Click it and confirm original session receives the agreed review feedback prompt.
|
|
10. Confirm arrow-left-right appears on original assistant message actions when original has review metadata.
|
|
11. Click it and confirm review session receives the agreed implementation response prompt.
|
|
12. Run `/handoff-review` again on the same original session and confirm it reuses the existing review session.
|
|
13. Delete the review session and confirm original metadata link is removed.
|
|
14. Delete original session and confirm no review cleanup crash occurs.
|
|
|
|
## Non-Goals
|
|
|
|
- Do not add a review status state machine.
|
|
- Do not allow multiple review sessions for one original session in this first implementation.
|
|
- Do not expose metadata, linked sessions, or session IDs to agents.
|
|
- Do not use parent/child session relationships for this feature.
|
|
- Do not change OpenCode core or SDK unless OpenChamber cannot access metadata from the existing SDK types.
|
|
- Do not make a background metadata reconciler.
|
|
|
|
## Main Implementation Risks To Watch While Coding
|
|
|
|
These are coding concerns, not product blockers:
|
|
|
|
- Metadata replacement must not drop unrelated metadata.
|
|
- Event/store sanitation must not strip `metadata` from session records.
|
|
- The handoff wait helper must wait for completed assistant output, not first streaming text.
|
|
- Cross-session sends must use the correct directory dynamically, not cached closure values.
|
|
- Message action buttons must not subscribe broad chat rows to global session collections.
|
|
- Delete cleanup should not delete the review session if cleanup fails in a way that would leave confusing metadata behind.
|