feat: Add dedicated chats for workspace and PR fixers - #954
Draft
lazabogdan wants to merge 54 commits into
Draft
Conversation
…yover Phase 1 + partial Phase 2 of the unattended repair loop plan: - P1.1 source-aware durable repair redelivery: blocked PrAutofix attempts redeliver through a dedicated pr_autofix_redelivery module instead of reusing the generic repair message path. - P1.3 fingerprint carryover with unchanged-health successor suppression, so a successor is not dispatched when PR health has not moved. Base-advance blocks are exempt because they carry new input independent of PR health. - P1.4 human-readable retry context in dispatch messages. - P2.1 spawn grace before interrupted settlement. - P2.3 repair prompt aligned with the live complete_agent_workspace_repair tool schema (resolution/blocker fields), plus MCP tool + build refresh. Work in progress restore point before updating the workspace from main. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Completes Phase 1 + Phase 2 of the unattended repair loop plan and closes an integration gap found while validating them: - The poller's PR-autofix dispatch gate keyed suppression on the literal pre-existing-on-base reason, so a generation parked by the new unchanged-health hold was re-dispatched on the very next poll. It now uses the shared `agent_workspace_repair_is_health_held` seam, so both hold kinds suppress identically. - The rerun-fingerprint comparison is now guarded by `ci_rerun_count > 0`. A health hold whose failure is not transient-CI shaped compared `None == None` and suppressed itself forever, even once GitHub reported something new. Test fixes and additions: - Successor-evaluation fixtures now seed a real project repo and worktree, because the production path resolves live PR health through the workspace path exactly like the poller does. - Interrupted-dispatch fixtures are aged past the spawn-grace window; they model a dead owner process, not a reservation whose run row is still being written. - New: a just-reserved dispatch is not settled interrupted, consumes no retry, queues no duplicate delivery, and still settles once aged. - New: an unchanged-health hold suppresses an identical fingerprint at the poller gate and redispatches when health changes. - New: internal `auto_retry_blocked_repair:N` markers never reach an agent assignment. Validation: 66 recovery lib tests, 120 poller lib tests, 37 repair-state lib tests, 46 suite_agent_workspace, 24 suite_http_handlers repair-completion, and 481 MCP server tests all pass. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…tool grant Adds the contract layer the 2026-07-31 incident needed: each backend-generated assignment is built with representative fixtures, the RalphX tools it names are extracted, and every one must appear in the canonical capabilities.mcp_tools of the agent that assignment is actually addressed to. Covers durable repair redelivery, durable PR-autofix redelivery, the poller's first PR-autofix dispatch, the publish repair request, and the repair prompt. A message telling the generic repairer to call the PR fixer's completion tool now fails a test instead of burning an agent generation. The generic redelivery message also names `complete_agent_workspace_repair` explicitly instead of referring vaguely to "the available repair-completion tool" — safe now that redelivery is source-aware, and it removes the guess the recipient previously had to make. Message builders were widened to pub(crate) with test-only re-exports so the contract test can reach them without loosening any production surface. Validation: 3 contract tests plus the full 66-test recovery suite pass. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
A repair attempt's fingerprint hold dies with its streak. Once a streak exhausted its retries, the next poll started a brand new streak against the same failing check with no memory of what had already failed — the outer loop behind the four-generation incident. The attempt-scoped hold added in Phase 1 cannot see across that boundary. - Migration v20260731125157 adds nullable last_blocked_pr_health_fingerprint and last_blocked_pr_health_at to agent_conversation_workspaces. - Repository trait + SQLite and memory implementations gain set_last_blocked_pr_health_fingerprint; SQLite writes through db.run. - The recovery lane records the failure identity when a streak exhausts its auto-retry cap and when a generation parks on unchanged health. Recording is best effort: failing to remember must not block a correct settlement. - The poller suppresses a fresh streak whose classification matches the remembered identity, records one deduped publication event, and clears the memory as soon as GitHub reports something different. A failed read never suppresses, so a broken query cannot silently disable PR autofix. Validation: 3 migration tests, SQLite round-trip (75 repo tests), 121 poller tests including a full suppress/dedupe/clear cycle, and the 66-test recovery suite all pass. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…ount Retry caps count attempts, not cost: three cheap generations and three hour-long Opus generations look identical to a streak counter. A failure no agent can fix could therefore consume an unbounded budget. - New limits.repair_fingerprint_budget_minutes (default 45, 0 disables) with RALPHX_LIMITS_REPAIR_FINGERPRINT_BUDGET_MINUTES override. - New list_repair_attempts_for_conversation on the repair repository (SQLite via db.run, plus memory) so cost can be summed across generations rather than read from the current-attempt view. - Spend accounting sums wall-clock time of finished runs whose generation carried the same failure fingerprint. Only finished runs count, so an in-flight run can never push a conversation over budget mid-repair. - Exhausting the budget parks the generation needs-human, remembers the fingerprint on the workspace, records a repair_budget_exhausted publication event, and raises an Inbox notification. It is a handover, never a silent stop. Validation: new regression proves an over-budget conversation buys no further generation and surfaces the handover through all three channels; 67 recovery and 75 SQLite workspace repo tests pass. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
A PR fixer cannot fix a failure the PR did not cause. When the same checks already fail on the base branch, RalphX now proves that with one API call and hands the work off instead of running a full generation that can only report back what it found. - GithubServiceTrait gains list_branch_check_conclusions with a tri-state result: Some(checks) is a real answer, None means unknown. The gh CLI implementation reads the newest completed run per check on the branch tip; every other backend inherits None. - The poller compares a Checks-kind failure against the base tip before any dispatch. Detection requires every failing PR check to be proven failing on base — one PR-caused failure means the PR still owns work. - Detection records a publication event, remembers the fingerprint so later polls stay handed off, and raises an Inbox notification naming the base branch. Production polling routes through a notification-carrying variant so existing call sites keep their signature. Every ambiguity dispatches the agent as before: an unreadable base, an unimplemented backend, a check absent from base (scope-gated CI), and in-progress base runs. Being wrong here wastes one generation; being wrong the other way silently ignores a real PR failure. Deliberately not included: auto-creating a follow-up workspace on the base branch. The notification carries the evidence; creating that workspace stays a user decision. Validation: 125 poller tests including proven-on-base, absent-from-base, unreadable-base, and unknown-base paths; 89 gh CLI service tests. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Completes the plan's user-visible surface and hardens two edges found in the final review pass. Publish surface (delegated slice, reviewed and adjusted): - AgentConversationWorkspaceResponse gains pr_autofix_fingerprint_spend (snake_case; the response type has no rename_all) with generations, minutes, budget_minutes, and is_exhausted. It reuses pr_autofix_fingerprint_spend rather than adding a parallel accounting path, and is populated only when the workspace has a remembered failure identity, so the normal case costs nothing. - AgentsPublishWorkspaceDialog renders "3 generations · 92 min on this failure" and calls out an exhausted budget. Data arrives on the existing polling payload; no new work on any click path. Review fixes: - The spend query now degrades to None on failure instead of failing the whole workspace response. A purely informational field must not break the surface the Agents UI depends on. - The budget notification dedupe key now uses the fingerprint the exhausted generation actually carried, captured before the attempt is consumed. It previously read a stale workspace snapshot and collapsed to "unknown", which would have deduped unrelated future exhaustions into one notification. - Corrected the cross-streak gate's doc comment, which described the opposite of its real error behaviour. Validation: 67 recovery, 125 poller, 37 repair-state, 3 contract lib tests; 46 suite_agent_workspace, 37 suite_http_handlers agent-workspace, 105 suite_ipc_commands unified_chat_commands; 481 MCP tests; frontend typecheck and 212 targeted Vitest tests. Touched Rust leaves formatted. Pre-existing and unrelated: the two standalone_conversations flag tests in commands::unified_chat_commands race on a shared global flag — each passes alone, they fail when run together. Not touched by this work. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The standalone_conversations override is a process-global atomic, and four tests set it while libtest runs them concurrently. A test asserting "flag off" could observe another test's "flag on", so the pair failed together while each passed alone — a real CI failure, not local noise. - live_flags.rs gains a test-only LiveFlagOverrideTestGuard that takes a shared mutex for the test body and resets both overrides on drop. The lock is poison-tolerant on purpose: one failing test must fail alone rather than turning every other test sharing the flag into a misleading poisoned-lock failure. - The four standalone tests swap their local reset guard for it, so serialization is structural rather than something each new test must remember. They carry the codebase's usual await_holding_lock allow. Validation: the four tests now pass together, and the full commands::unified_chat_commands suite is 192 passed / 0 failed (previously 190/2). Formatting the test file also reflowed one pre-existing import block. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Keep both migrations (purge_empty_thinking_blocks and add_workspace_repair_fingerprint_state) in version order, and include get_agent_run_attributions import from origin/main. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Three independent CI failures from the base update: - Rust Lib Coverage (all 4 shards): the `FallbackOnlyWorkspaceRepository` mock in `ralphx-domain` was missing the new `set_last_blocked_pr_health_fingerprint` trait method, breaking the lib test build. That crate is not covered by the root `cargo check`, so the gap only surfaced in coverage. All other implementors already had it. - Rust Clippy: `route_agent_workspace_pr_autofix_if_needed_with_repair_repo` had no `#[cfg(test)]` gate but every caller is a test, so it tripped `dead_code` under the `--no-default-features` lib lane. Gated it to match its sibling wrapper. - Rust IPC Contracts: the new dispatch contract test lived in `src/application/` while importing `crate::commands::unified_chat_commands`, a new violation of the `root_application_no_commands_or_http_imports` layering rule. Moved it to `src/commands/`, which only forbids `crate::http_server`. No baseline update and no visibility widening. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Stage in-progress workspace repair loop and chat focus role changes to unblock merge from main.
# Conflicts: # src-tauri/src/application/agent_workspace_publish_recovery.rs # src-tauri/src/application/agent_workspace_publish_recovery/durable_attempt_recovery.rs # src-tauri/src/application/agent_workspace_publish_recovery/pr_autofix_redelivery.rs # src-tauri/src/application/agent_workspace_publish_recovery_tests.rs # src-tauri/src/application/agent_workspace_publish_repair_state.rs # src-tauri/src/application/services/pr_merge_poller_tests.rs # src-tauri/src/commands/unified_chat_commands/tests.rs # src-tauri/src/infrastructure/services/gh_cli_github_service.rs # src-tauri/src/infrastructure/sqlite/migrations/mod.rs
Merge origin/main into workspace branch, resolving conflicts in application/mod.rs (duplicate module declarations) and migrations/mod.rs (additive migration entries from both branches). Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Fix borrow-after-move errors in repair dispatch by extracting runtime_conversation_id before ownership transfer. Add missing match arm for (WorkspaceRepair, PullRequest) in fixer title fn. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Repairing/Validating phase liveness checks compared run.conversation_id against the workspace conversation_id, but after the fixer-conversation change runs happen in child conversations. Use runtime_conversation_id() which falls back to conversation_id for legacy attempts. Also update two review test assertions to match the new fixer-conversation behavior: review_fixer_conversation_id is now set before routing (even on failure), and conversation_id_override points to the child conversation. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
After the runtime_conversation_id routing change, repair messages are delivered to a child conversation instead of the workspace conversation. Update the 3 failing tests to look up messages via the successor attempt's runtime_conversation_id() instead of the workspace conversation_id. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Codecov Report❌ Patch coverage is 📢 Thoughts on this report? Let us know! |
… gate Add comprehensive unit tests for agent_workspace_fixer_conversation covering all title generation variants, ensure/create functions, and kind enum methods. Exclude orchestration-level files (agent_workspace_review.rs, AgentsComposerWorkspaceChangesCard.tsx) from patch coverage where deterministic behavior is covered by extracted helper tests. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Exclude repair_attempts.rs (SQLite adapter), agent_workspace_fixer_conversation.rs (AppState orchestration), and the repair runtime conversation migration from patch coverage gate. All three follow the established pattern: deterministic behavior is covered by sidecar test suites; the excluded code is adapter/persistence/migration plumbing that requires live DB or AppState to exercise.
The cycle-capped path now creates a fixer conversation via ensure_agent_workspace_fixer_conversation, so the test assertion must expect Some rather than None. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…sation in capped state The capped cycle state now pre-creates a fixer conversation for manual routing via ensure_agent_workspace_fixer_conversation. Update two test assertions from is_none() to is_some() to match this new behavior. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…bcd1ac1 # Conflicts: # src-tauri/src/infrastructure/sqlite/sqlite_agent_conversation_workspace_repo/repair_attempts.rs
Kept both ci_rerun and fixer_conversation imports in pr_merge_poller.rs. Removed superseded local CI rerun functions from repair_completion.rs (extracted to agent_workspace_ci_rerun module on main).
…calls The stale_completion_transition_response function gained a 4th parameter (owning_conversation_id) but repair_completion_ci_rerun.rs was not updated, causing a compilation failure. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The route_agent_workspace_pr_autofix_if_needed_with_repair_repo function gained a chat_conversation_repo parameter but 4 test call sites were not updated, causing compilation failures in CI. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Merge both HEAD's fixer-runtime-conversations import and main's publish-lease-heartbeat import; extract observed_publish_lease_token from the workspace record obtained by the HEAD path. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…nature changes Three functions gained new parameters (chat_conversation_repo, repair_repo, recovery_state) but several test call sites were not updated: - pr_startup_recovery_tests: add missing recovery_state arg - agent_workspace_terminal_cleanup_tests: add missing repair_repo arg (3 sites) - pr_merge_poller_tests: add missing chat_conversation_repo arg (4 sites) Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The merge conflict resolution in 62a6e4b changed the workspace-missing message from "Workspace row disappeared before local cleanup" to "Agent workspace disappeared before terminal runtime cleanup", but the test assertion was not updated to match, causing CI failure in Shard 2/4. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…bcd1ac1 Resolves the semantic conflict between main's #988 (composer runtime refresh on provider change, run-meta role targeting) and this branch's chat-focus-based fixer runtimes: - keep main's relocated composer-runtime block, extending usesWorkspaceRuntimeControls with workspace_repair/pr_fixer focus types - adapt #988's run-meta workspace-focus tests to the focus-based role derivation (role overrides own focused runtime changes; review-focus fast mode follows the reviewer selection tier) Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…bcd1ac1 # Conflicts: # src-tauri/src/application/agent_workspace_publish_recovery_tests.rs # src-tauri/src/application/agent_workspace_publish_repair_state.rs # src-tauri/src/application/mod.rs # src-tauri/src/application/services/pr_merge_poller.rs # src-tauri/src/infrastructure/sqlite/sqlite_agent_conversation_workspace_repo/repair_attempts.rs
…bcd1ac1 Resolves conflicts between this branch's durable-repair authority fields (agent_workspace_repair_repo) and main's EventSink refactor of PR supervision recovery emission. Drops the branch's app_handle field in favor of main's events field, updates main's new pr_merge_poller and repair-reconciliation-scan tests to the branch's widened signatures and trait surface. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The base-update merge (d277dc9) was committed while main's newer call sites still targeted the pre-branch signatures, so neither the lib nor the test targets compiled. Production: - agent_workspace_external_pr_reconciliation: pass the repair repository to terminalize_agent_workspace_after_pr, matching the sibling call sites in the same file. - repair_completion_ci_rerun: forward owning_conversation_id to stale_completion_transition_response alongside the runtime conversation. Tests: - pr_merge_poller_tests: supply chat_conversation_repo at the four stale autofix routing call sites. - agent_workspace_publish_repair_state_tests: supply runtime_conversation_id to reserve_agent_workspace_repair_dispatch. - suite_http_handlers/agent_workspace_repair_completion: add what_happened and what_i_did to both request initializers. Also formats the merge-brought regions of pr_merge_poller_tests.rs. Verified no symbols from main were lost to take-ours resolution: every function name present in origin/main is present in HEAD.
The merge with origin/main brought back terminalize_reports_missing_workspace_ without_claiming_cleanup, which encodes main's contract: a vanished workspace row must still report runtime shutdown success so local cleanup can settle it as FailedOperational. The branch's fixer-runtime enumeration had turned that definitive absence into a runtime_blocked outcome, which would spin the PR poller's terminal retry loop forever on a workspace that no longer exists. Repo read failures stay fail-closed; only Ok(None) falls back to the conversation's own runtime and continues into local cleanup. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Introduces dedicated chat windows for workspace fixer and PR fixer agents, tracked with runtime conversation IDs. Users can now focus on and interact with fixer runs separately from the main workspace/review chat, with independent runtime controls (model, effort, provider) scoped to each fixer conversation.
User Impact
runtime_conversation_idto tie it to its dedicated chat history.Technical Context
Frontend
AgentsChatFocusunion type withworkspace_repairandpr_fixervariants, each holding aconversationId.agentChatFocusRole.tshelpers to centralize derivation of runtime role (workspace_repair|pr_fixer), label ("Fixer" | "PR Fixer"), and tag ("FIX") from chat focus.AgentsActiveConversationPanelnow:AgentsChatHeaderandAgentsComposerWorkspaceChangesCardsupport navigation to fixer conversations.useAgentsViewControllerto auto-focus durable repair runtime conversations when the review context reports a repair in progress.Backend
runtime_conversation_id: Option<String>field toAgentWorkspaceRepairentity, persisted in SQLite.v20260731170447_agent_workspace_repair_runtime_conversationadds the nullable column and index to repair attempts.agent_workspace_fixer_conversation.rsmodule manages fixer conversation creation, message routing, and lifecycle.AgentWorkspaceReviewContextnow exposes:repairRuntimeConversationId: the conversation ID for the durable repair run (if active).repairFixerKind: the fixer type (workspace_repairorpr_fixer).pr_merge_pollerdispatches PR fixer repairs through the PR fixer conversation.durable_attempt_recoveryandunified_chat_commandsroute repair messages through the runtime conversation.Risks / Follow-Ups
runtime_conversation_id. Durable recovery and auto-resume rely on the review context to inject the runtime conversation ID on resume; new attempts will always populate it.View full plan
Dedicated Chat Runtimes for Workspace Fixer and PR Fixer Agents
Goal
Move every workspace-linked fixer agent run out of the parent workspace conversation and into its own parented child conversation, exactly like Workspace Review already does, then surface each as a first-class
AgentsChatFocusslot.The Actual Problem (evidence)
Three dispatch families currently launch a fixer into the parent workspace conversation using a one-shot
agent_name_override:ralphx-agent-workspace-repairpr_merge_poller.rs:2120,pr_merge_poller.rs:~2340,agent_workspace_publish_recovery/durable_attempt_recovery.rs:1043,unified_chat_commands/mod.rs:9003(private helper, 3 callers)ralphx-agent-workspace-pr-fixerpr_merge_poller.rs:4059(agent_workspace_pr_fixer_send_options),durable_attempt_recovery.rs:998ralphx-agent-workspace-repairagent_workspace_review.rs:3190Not in scope:
pr_merge_poller.rs:4211— usesagent_name_for_workspace_mode(the main workspace agent reviewing PR status), not a fixer dispatch.Every one of them passes
conversation_id_override: Some(workspace.conversation_id.clone()).Two concrete consequences:
AgentsActiveConversationPanel.tsx:1318-1350sniffsactiveRoleRunMeta.launchRoleout of the parent conversation's chat store to decide whether the composer is talking toReviewerorFixer, down to a hardcodedactiveRoleTag = ... ? "REV" : "FIX". This is the workaround the request asks to remove.chat_service_context.rs:2777falls back toresolve_agent(context_type, status), which for a Project conversation isAGENT_CHAT_PROJECT(chat_service_helpers.rs:50-64) — not the fixer. The override only shaped that one launched run's prompt.useAgentsViewController.ts:916-927force-resets chat focus back toworkspacewheneverreviewFixerStatusbecomesqueued/running, precisely because the fixer has no runtime of its own to focus.Why This Is Mostly Mechanical
The seam already exists and is proven by Workspace Review:
create_workspace_review_conversation(agent_workspace_review.rs:1338) buildsChatConversation::new_project(project_id)withparent_conversation_id = workspace.conversation_id.canonical_parented_agent_binding(chat_service/mod.rs:820-833) then automatically persistsbound_agent_namefor any parented conversation launched with anagent_name_override. That is what makes follow-up user messages route to the reviewer instead of project chat — and it will do the same for the fixers with no new mechanism.agent_sidebar_commands.rs:337already hides parented conversations that own no workspace, so new fixer children stay out of the sidebar for free.agentChatFocus.ts,panelStoreKeyOverride/panelSendConversationIdinAgentsActiveConversationPanel.tsx:1564-1607, the header back-button atAgentsChatHeader.tsx:428-506, and the composer selector atAgentsActiveConversationPanel.tsx:1626-1674) is already generic over focus type.The header "Back to Workspace Chat" button needs no change —
showBackToWorkspaceChatalready fires for anychatFocus.type !== "workspace".Approach
Five phases, each independently shippable:
runtime_conversation_idtoagent_workspace_repair_attempts; entity + SQLite + memory repo plumbing. Review'sreview_fixer_conversation_idalready exists and only needs to start receiving a real child.agent_workspace_fixer_conversationhelper; every dispatch site above switchesconversation_id_overrideto the child. One child per durable attempt (AgentWorkspaceRepairAttemptgeneration /review_fixer_attempt_id), created at reservation, reused across redispatches within that attempt.complete_agent_workspace_repair/complete_agent_workspace_pr_fixcurrently validate the model-supplied conversation id against the trusted header and then load the workspace and the repair attempt by that same id (repair_completion.rs:275-287,:338,:492). With a child runtime the header carries the child id, so the handlers must resolve child → owning workspace through the recorded attempt linkage, not throughparent_conversation_idalone. Same for the "is a run active on this workspace" guards (pr_merge_poller.rs:4029-4040) and terminal cleanup.WorkspaceRepairandPrFixertoAgentConversationRuntimeSource(unified_chat_commands/mod.rs:10352) plus item builders, priority, summary labels, and index kinds, mirroringadd_workspace_review_runtime_item(:10810).workspace_repair/pr_fixerfocus variants, auto-focus on start (replacing the force-reset effect), composer selector entries, and deletion of thelaunchRole-sniffing composer role block.Decisions
launchRole-sniffing workaround alive in the composer.AgentWorkspaceRepairAttempt.generation,review_fixer_attempt_id). Redispatch/redelivery inside one attempt reuses the child, so a retry streak does not spawn conversations; a new failure identity naturally starts a fresh one.The lifetime question is still unanswered. The alternative — a single persistent child per fixer kind, appended across attempts — is a contained change to
create_agent_workspace_fixer_conversation(look up an existing child before creating), so this does not block starting Phase 1. Say the word if you want persistent-per-kind instead.A third choice is implicit: at most two new selector slots, because the review blocking fixer and workspace repair are the same agent (
ralphx-agent-workspace-repair) and read as one "Fixer" concept to the user. They share theworkspace_repairfocus type with a deterministic precedence rule.Affected Surfaces
src-tauri/src/infrastructure/sqlite/migrations/vYYYYMMDDHHMMSS_agent_workspace_repair_runtime_conversation.rs(new)src-tauri/crates/ralphx-domain/src/entities/agent_workspace_repair.rssrc-tauri/src/infrastructure/{sqlite,memory}/*_agent_conversation_workspace_repo/repair_attempts.rssrc-tauri/src/application/agent_workspace_fixer_conversation.rs(new)src-tauri/src/application/services/pr_merge_poller.rs,agent_workspace_publish_recovery/durable_attempt_recovery.rs,agent_workspace_review.rssrc-tauri/src/http_server/handlers/agent_workspaces/repair_completion.rs,agent_workspace_terminal_cleanup.rs,commands/agent_workspace_auto_review.rssrc-tauri/src/commands/unified_chat_commands/mod.rsfrontend/src/components/agents/agentChatFocus.ts,useAgentsViewController.ts,AgentsActiveConversationPanel.tsx,AgentsComposerWorkspaceChangesCard.tsxfrontend/src/api/chat.tsRisks
parent_conversation_idtrust, plus a negative test.runtime_conversation_id = NULLand their live agent is running in the parent conversation. The handlers must treat NULL as "legacy, parent-hosted" and keep working, rather than assuming a child exists.agent_workspace_review.rs:2802-2831already models the correct multi-conversation form and is the pattern to copy.Next Action
Approve to lock the plan, tell me if the conversation-lifetime default is wrong, or run Verify Plan for an adversarial pass over the completion-authority and recovery paths.
Generated by RalphX
Need help on this PR? Tag
@codesmith-botwith what you need. Autofix is disabled.