Skip to content

feat: Add dedicated chats for workspace and PR fixers - #954

Draft
lazabogdan wants to merge 54 commits into
mainfrom
ralphx/ralphx/agent-0bcd1ac1
Draft

feat: Add dedicated chats for workspace and PR fixers#954
lazabogdan wants to merge 54 commits into
mainfrom
ralphx/ralphx/agent-0bcd1ac1

Conversation

@lazabogdan

@lazabogdan lazabogdan commented Aug 2, 2026

Copy link
Copy Markdown
Collaborator

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

  • New focus types: Workspace and PR repair conversations now appear in the chat focus switcher with "Fixer" and "PR Fixer" labels and warning tone.
  • Separate chat UI: When focused on a fixer repair, the composer and chat targeting switch to the dedicated repair conversation instead of the parent workspace.
  • Independent runtime controls: Fixer conversations have their own role-based runtime overrides (model, effort, provider), persisted per conversation.
  • Back navigation: Users can navigate back from fixer chat to the parent workspace/review conversation.
  • Repair tracking: Each repair attempt now carries a runtime_conversation_id to tie it to its dedicated chat history.

Technical Context

Frontend

  • Extended AgentsChatFocus union type with workspace_repair and pr_fixer variants, each holding a conversationId.
  • Added agentChatFocusRole.ts helpers to centralize derivation of runtime role (workspace_repair | pr_fixer), label ("Fixer" | "PR Fixer"), and tag ("FIX") from chat focus.
  • AgentsActiveConversationPanel now:
    • Routes fixer focus types to their child conversations for sends, receives, and context key isolation.
    • Applies workspace runtime controls (model, effort overrides) when a fixer is focused.
    • Renders fixer status in the runtime banner ("Fixer run active" vs. "PR Fixer run active").
  • AgentsChatHeader and AgentsComposerWorkspaceChangesCard support navigation to fixer conversations.
  • Updated useAgentsViewController to auto-focus durable repair runtime conversations when the review context reports a repair in progress.

Backend

  • Added runtime_conversation_id: Option<String> field to AgentWorkspaceRepair entity, persisted in SQLite.
  • New migration v20260731170447_agent_workspace_repair_runtime_conversation adds the nullable column and index to repair attempts.
  • New agent_workspace_fixer_conversation.rs module manages fixer conversation creation, message routing, and lifecycle.
  • AgentWorkspaceReviewContext now exposes:
    • repairRuntimeConversationId: the conversation ID for the durable repair run (if active).
    • repairFixerKind: the fixer type (workspace_repair or pr_fixer).
  • Repository methods now support querying and updating repair attempts by runtime conversation ID, with per-attempt fencing to prevent cross-attempt confusion.
  • pr_merge_poller dispatches PR fixer repairs through the PR fixer conversation.
  • durable_attempt_recovery and unified_chat_commands route repair messages through the runtime conversation.
  • Both memory and SQLite repo implementations updated with repair attempt tracking and fencing tests.

Risks / Follow-Ups

  • Migration: Existing repair attempts will have NULL 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.
  • Fixer lifecycle: Repair conversation creation must happen before the first message arrives; if creation fails or is delayed, messages may not route correctly. Startup recovery validates the tie-in at startup.
  • Focus routing complexity: The chat focus system now branches on two fixer types in multiple places; test coverage validates both paths.
  • Backward compat: Repair fingerprinting and cross-attempt memory do not depend on runtime conversation IDs, so they remain stable across agent restarts.
View full plan

Dedicated Chat Runtimes for Workspace Fixer and PR Fixer Agents

Goal

"we have a couple of agents that are started ... from different scenarios linked to a agent workspace such as agent > review > fix workspace or agent > pr monitor > fix PR … that are using the same chat surface as the main agent runtime UI. this causes complications with the chat composer ... would like to change these to their own dedicated chat runtimes just like agent workspace review is, switching to the separate chat UI context and getting a back to workspace chat button in the chat header ... and getting a slot into the chat selector in the chat composer so we can switch back and forth."

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 AgentsChatFocus slot.

The Actual Problem (evidence)

Three dispatch families currently launch a fixer into the parent workspace conversation using a one-shot agent_name_override:

Family Agent Dispatch site
Workspace repair (base-update / publish / PR-conflict) ralphx-agent-workspace-repair pr_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)
PR autofix ralphx-agent-workspace-pr-fixer pr_merge_poller.rs:4059 (agent_workspace_pr_fixer_send_options), durable_attempt_recovery.rs:998
Review blocking fixer ralphx-agent-workspace-repair agent_workspace_review.rs:3190

Not in scope: pr_merge_poller.rs:4211 — uses agent_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:

  1. Composer role guessing. AgentsActiveConversationPanel.tsx:1318-1350 sniffs activeRoleRunMeta.launchRole out of the parent conversation's chat store to decide whether the composer is talking to Reviewer or Fixer, down to a hardcoded activeRoleTag = ... ? "REV" : "FIX". This is the workaround the request asks to remove.
  2. Follow-up messages go to the wrong agent. A user reply typed while a fixer run is live resolves through the parent conversation's normal identity. chat_service_context.rs:2777 falls back to resolve_agent(context_type, status), which for a Project conversation is AGENT_CHAT_PROJECT (chat_service_helpers.rs:50-64) — not the fixer. The override only shaped that one launched run's prompt.
  3. The UI actively fights this today. useAgentsViewController.ts:916-927 force-resets chat focus back to workspace whenever reviewFixerStatus becomes queued/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) builds ChatConversation::new_project(project_id) with parent_conversation_id = workspace.conversation_id.
  • canonical_parented_agent_binding (chat_service/mod.rs:820-833) then automatically persists bound_agent_name for any parented conversation launched with an agent_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:337 already hides parented conversations that own no workspace, so new fixer children stay out of the sidebar for free.
  • The frontend focus machinery (agentChatFocus.ts, panelStoreKeyOverride/panelSendConversationId in AgentsActiveConversationPanel.tsx:1564-1607, the header back-button at AgentsChatHeader.tsx:428-506, and the composer selector at AgentsActiveConversationPanel.tsx:1626-1674) is already generic over focus type.

The header "Back to Workspace Chat" button needs no changeshowBackToWorkspaceChat already fires for any chatFocus.type !== "workspace".

Approach

Five phases, each independently shippable:

  1. Durable identity — migration adding runtime_conversation_id to agent_workspace_repair_attempts; entity + SQLite + memory repo plumbing. Review's review_fixer_conversation_id already exists and only needs to start receiving a real child.
  2. Child creation + dispatch rerouting — one shared agent_workspace_fixer_conversation helper; every dispatch site above switches conversation_id_override to the child. One child per durable attempt (AgentWorkspaceRepairAttempt generation / review_fixer_attempt_id), created at reservation, reused across redispatches within that attempt.
  3. Authority follows the child (highest risk)complete_agent_workspace_repair / complete_agent_workspace_pr_fix currently 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 through parent_conversation_id alone. Same for the "is a run active on this workspace" guards (pr_merge_poller.rs:4029-4040) and terminal cleanup.
  4. Runtime status surface — add WorkspaceRepair and PrFixer to AgentConversationRuntimeSource (unified_chat_commands/mod.rs:10352) plus item builders, priority, summary labels, and index kinds, mirroring add_workspace_review_runtime_item (:10810).
  5. Frontend focus slots — new workspace_repair / pr_fixer focus variants, auto-focus on start (replacing the force-reset effect), composer selector entries, and deletion of the launchRole-sniffing composer role block.

Decisions

Decision Status Value Rationale
Scope Confirmed by user All three families — workspace repair, PR autofix, and the review blocking fixer Leaving any one behind would keep the launchRole-sniffing workaround alive in the composer.
Conversation lifetime ⚠️ Assumed — open One child per durable attempt, not per dispatch Matches Review's per-run isolation while using the unit the backend already fences on (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 the workspace_repair focus type with a deterministic precedence rule.

Affected Surfaces

Area Paths
Migration src-tauri/src/infrastructure/sqlite/migrations/vYYYYMMDDHHMMSS_agent_workspace_repair_runtime_conversation.rs (new)
Domain src-tauri/crates/ralphx-domain/src/entities/agent_workspace_repair.rs
Persistence src-tauri/src/infrastructure/{sqlite,memory}/*_agent_conversation_workspace_repo/repair_attempts.rs
Child creation src-tauri/src/application/agent_workspace_fixer_conversation.rs (new)
Dispatch src-tauri/src/application/services/pr_merge_poller.rs, agent_workspace_publish_recovery/durable_attempt_recovery.rs, agent_workspace_review.rs
Authority src-tauri/src/http_server/handlers/agent_workspaces/repair_completion.rs, agent_workspace_terminal_cleanup.rs, commands/agent_workspace_auto_review.rs
Runtime status src-tauri/src/commands/unified_chat_commands/mod.rs
Frontend focus frontend/src/components/agents/agentChatFocus.ts, useAgentsViewController.ts, AgentsActiveConversationPanel.tsx, AgentsComposerWorkspaceChangesCard.tsx
API types frontend/src/api/chat.ts

Risks

  • Completion authority is the sharp edge. Getting child → workspace resolution wrong either breaks every repair completion (fails closed, loud) or lets an unrelated child settle another workspace's attempt (fails open, silent). The blueprint requires exact-linkage validation, never bare parent_conversation_id trust, plus a negative test.
  • In-flight attempts across the upgrade. Existing unsettled attempts have runtime_conversation_id = NULL and 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.
  • Guards that ask "is the workspace busy?" currently query only the parent conversation. Missing one lets a second fixer dispatch concurrently. agent_workspace_review.rs:2802-2831 already 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


View with [code]smith Autofix with [code]smith
Need help on this PR? Tag @codesmith-bot with what you need. Autofix is disabled.

lazabogdan and others added 17 commits July 31, 2026 14:03
…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>
lazabogdan and others added 12 commits August 2, 2026 09:30
… 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>
lazabogdan and others added 25 commits August 3, 2026 06:20
…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>
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.

1 participant