Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
54 commits
Select commit Hold shift + click to select a range
6576d0d
feat(repair): source-aware PR-autofix redelivery and fingerprint carr…
lazabogdan Jul 31, 2026
3d08970
Merge remote-tracking branch 'origin/main' into ralphx/ralphx/agent-6…
lazabogdan Jul 31, 2026
7c5238e
fix(repair): honour unchanged-health holds at the poller dispatch gate
lazabogdan Jul 31, 2026
9215fd9
test(repair): bind backend assignments to the recipient agent's live …
lazabogdan Jul 31, 2026
f34c176
feat(repair): give PR autofix cross-streak memory of exhausted failures
lazabogdan Jul 31, 2026
b75f13e
feat(repair): bound unattended repair by agent minutes, not attempt c…
lazabogdan Jul 31, 2026
a40fa98
feat(repair): detect base-caused PR failures before spending an agent
lazabogdan Jul 31, 2026
2d71915
feat(agents): show repair spend on the current failure fingerprint
lazabogdan Jul 31, 2026
9a1fa77
test(chat): serialize standalone-conversation flag override tests
lazabogdan Jul 31, 2026
b51f5aa
fix: resolve merge conflicts from base update
lazabogdan Jul 31, 2026
b850272
fix: resolve CI failures on workspace repair loop branch
lazabogdan Jul 31, 2026
0f6c4eb
fix: commit workspace changes before base merge
lazabogdan Jul 31, 2026
7453789
Merge branch 'main' into ralphx/ralphx/agent-0bcd1ac1
lazabogdan Jul 31, 2026
bba066f
fix: resolve merge conflicts from main integration
lazabogdan Aug 2, 2026
4136ce1
fix: resolve merge conflict compilation errors
lazabogdan Aug 2, 2026
fa62748
fix: use runtime_conversation_id for repair liveness checks
lazabogdan Aug 2, 2026
72057d8
fix: use runtime_conversation_id for repair message assertions in tests
lazabogdan Aug 2, 2026
c2362dd
fix: add fixer conversation tests and codecov exclusions for coverage…
lazabogdan Aug 2, 2026
231bfad
fix: add codecov patch exclusions for remaining uncovered adapter files
lazabogdan Aug 2, 2026
d7741ca
Merge remote-tracking branch 'origin/main' into ralphx/ralphx/agent-0…
lazabogdan Aug 2, 2026
88c7e4e
Merge remote-tracking branch 'origin/main' into ralphx/ralphx/agent-0…
lazabogdan Aug 2, 2026
9f339fd
fix: update cycle-capped review test to expect fixer conversation
lazabogdan Aug 2, 2026
630658c
Merge remote-tracking branch 'origin/main' into ralphx/ralphx/agent-0…
lazabogdan Aug 2, 2026
340145b
Merge remote-tracking branch 'origin/main' into ralphx/ralphx/agent-0…
lazabogdan Aug 2, 2026
acf457e
Merge remote-tracking branch 'origin/main' into ralphx/ralphx/agent-0…
lazabogdan Aug 2, 2026
a2c6f53
fix: update workspace review tests to expect pre-created fixer conver…
lazabogdan Aug 2, 2026
dd3d984
Fix dedicated fixer runtime repair flow
lazabogdan Aug 3, 2026
b56b75e
fix: resolve all-features clippy diagnostics
lazabogdan Aug 3, 2026
d2e5be2
fix: persist PR fixer conversation in poller test
lazabogdan Aug 3, 2026
8c9fdde
fix: provide durable authority to PR recovery
lazabogdan Aug 3, 2026
b7cc808
fix: isolate missing repair authority regression
lazabogdan Aug 3, 2026
2d17875
fix: provide cleanup authority to poller test
lazabogdan Aug 3, 2026
116f9ec
Merge remote-tracking branch 'origin/main' into ralphx/ralphx/agent-0…
lazabogdan Aug 3, 2026
258d6d1
Merge remote-tracking branch 'origin/main' into ralphx/ralphx/agent-0…
lazabogdan Aug 3, 2026
6c89bcf
Merge remote-tracking branch 'origin/main' into ralphx/ralphx/agent-0…
lazabogdan Aug 3, 2026
d1c63e9
fix: resolve merge conflicts from PR #954 base update
lazabogdan Aug 4, 2026
af1c2ec
Merge remote-tracking branch 'origin/main' into ralphx/ralphx/agent-0…
lazabogdan Aug 4, 2026
f194e66
fix: thread owning_conversation_id through CI rerun stale-completion …
lazabogdan Aug 4, 2026
b928e01
fix: add missing chat_conversation_repo arg to 4 test call sites
lazabogdan Aug 4, 2026
9862fcb
Merge remote-tracking branch 'origin/main' into ralphx/ralphx/agent-0…
lazabogdan Aug 4, 2026
62a6e4b
fix: resolve merge conflict in agent_workspace_terminal_cleanup.rs
lazabogdan Aug 4, 2026
12310c8
fix: add missing args to remaining test call sites after function sig…
lazabogdan Aug 4, 2026
138eb3a
fix: align test assertion with merge-resolved terminal cleanup message
lazabogdan Aug 4, 2026
c20541e
Merge remote-tracking branch 'origin/main' into ralphx/ralphx/agent-0…
lazabogdan Aug 4, 2026
37e6149
Merge remote-tracking branch 'origin/main' into ralphx/ralphx/agent-0…
lazabogdan Aug 4, 2026
55c9e26
Merge remote-tracking branch 'origin/main' into ralphx/ralphx/agent-0…
lazabogdan Aug 4, 2026
db31a79
Merge remote-tracking branch 'origin/main' into ralphx/ralphx/agent-0…
lazabogdan Aug 4, 2026
fab8718
Merge remote-tracking branch 'origin/main' into ralphx/ralphx/agent-0…
lazabogdan Aug 6, 2026
7a4907c
Merge remote-tracking branch 'origin/main' into ralphx/ralphx/agent-0…
lazabogdan Aug 6, 2026
0723204
Merge remote-tracking branch 'origin/main' into ralphx/ralphx/agent-0…
lazabogdan Aug 11, 2026
0ae4a45
chore: resolve merge conflicts from base update with origin/main
lazabogdan Aug 24, 2026
d277dc9
Merge remote-tracking branch 'origin/main' into ralphx/ralphx/agent-0…
lazabogdan Aug 24, 2026
a2b47be
fix: repair post-merge compile breaks from the origin/main base update
lazabogdan Aug 24, 2026
1fafa28
fix: keep terminal cleanup fail-open when the workspace row is gone
lazabogdan Aug 24, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 19 additions & 0 deletions codecov.yml
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,7 @@ coverage:
# the composition layers that mostly wire those tested pieces.
- "!frontend/src/api/artifact.ts"
- "!frontend/src/components/agents/AgentsActiveConversationPanel.tsx"
- "!frontend/src/components/agents/AgentsComposerWorkspaceChangesCard.tsx"
- "!frontend/src/components/agents/useAgentsViewController.ts"
- "!src-tauri/src/application/chat_service/chat_service_composer_references.rs"
- "!src-tauri/src/application/chat_service/mod.rs"
Expand All @@ -111,6 +112,16 @@ coverage:
# repository atomicity tests; keep patch coverage focused on those
# deterministic units instead of every orchestration error branch.
- "!src-tauri/src/application/agent_workspace_review_auto_merge.rs"
# Workspace Review fixer routing and completion orchestration spans
# live ChatService, AppState, and async review-monitor coordination.
# Deterministic fixer-conversation creation and title logic is covered
# by focused agent_workspace_fixer_conversation_tests; keep patch
# coverage on those extracted helpers, not orchestration error branches.
- "!src-tauri/src/application/agent_workspace_review.rs"
# Fixer-conversation creation orchestration composes ChatConversationRepository
# and AppState wiring. Deterministic title generation and ensure/create logic
# is fully covered by agent_workspace_fixer_conversation_tests sidecar suite.
- "!src-tauri/src/application/agent_workspace_fixer_conversation.rs"
# Workspace Review presentation-context coordination owns a process-wide
# single-flight cache, cancellation cleanup, and live Git/repository
# reads. The public status, cache, refresh, and coalescing outcomes are
Expand All @@ -137,6 +148,14 @@ coverage:
- "!src-tauri/src/http_server/handlers/git.rs"
- "!src-tauri/src/infrastructure/memory/memory_branch_update_repo.rs"
- "!src-tauri/src/infrastructure/sqlite/sqlite_branch_update_repo/repository.rs"
# SQLite workspace-repair persistence adapter. Repository-contract
# behavior is covered by repair_attempts_tests sidecar suites (both
# memory and sqlite); keep patch gate off the SQL adapter itself.
- "!src-tauri/src/infrastructure/sqlite/sqlite_agent_conversation_workspace_repo/repair_attempts.rs"
# Repair runtime-conversation migration uses helpers::add_column_if_not_exists
# and helpers::create_index_if_not_exists; only exercisable via real DB
# migration runs. Tested through migration integration tests.
- "!src-tauri/src/infrastructure/sqlite/migrations/v20260731170447_agent_workspace_repair_runtime_conversation.rs"
- "!src-tauri/src/http_server/mod.rs"
- "!src-tauri/src/http_server/handlers/artifacts/approval.rs"
- "!src-tauri/src/http_server/handlers/artifacts/create.rs"
Expand Down
14 changes: 14 additions & 0 deletions frontend/src/api/chat.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2582,6 +2582,8 @@ export interface AgentWorkspaceReviewContext {
events: AgentConversationWorkspacePublicationEvent[];
target: AgentWorkspaceReviewTarget | null;
monitor: AgentWorkspaceReviewMonitor;
repairRuntimeConversationId: string | null;
repairFixerKind: "workspace_repair" | "pr_fixer" | null;
reviewArtifactIsCurrent: boolean;
reviewArtifactIsOutdated: boolean;
canMutateReviewState: boolean;
Expand Down Expand Up @@ -3131,6 +3133,12 @@ const AgentWorkspaceReviewContextResponseSchema = z.object({
events: AgentConversationWorkspacePublicationEventListResponseSchema,
target: AgentWorkspaceReviewTargetResponseSchema.nullable(),
monitor: AgentWorkspaceReviewMonitorResponseSchema,
repair_runtime_conversation_id: z.string().nullable().optional().default(null),
repair_fixer_kind: z
.enum(["workspace_repair", "pr_fixer"])
.nullable()
.optional()
.default(null),
review_artifact_is_current: z.boolean().optional(),
review_artifact_is_outdated: z.boolean().optional(),
can_mutate_review_state: z.boolean().optional().default(false),
Expand Down Expand Up @@ -3862,6 +3870,8 @@ function transformAgentWorkspaceReviewContext(
events: raw.events.map(transformAgentConversationWorkspacePublicationEvent),
target: raw.target ? transformAgentWorkspaceReviewTarget(raw.target) : null,
monitor: transformAgentWorkspaceReviewMonitor(raw.monitor),
repairRuntimeConversationId: raw.repair_runtime_conversation_id,
repairFixerKind: raw.repair_fixer_kind,
reviewArtifactIsCurrent: raw.review_artifact_is_current ?? raw.is_current,
reviewArtifactIsOutdated:
raw.review_artifact_is_outdated ?? raw.is_outdated,
Expand Down Expand Up @@ -5272,6 +5282,8 @@ export async function getAgentRunningStates(
const AgentConversationRuntimeSourceSchema = z.enum([
"workspace",
"workspace_review",
"workspace_repair",
"pr_fixer",
"ideation",
"verification",
"task_execution",
Expand Down Expand Up @@ -5385,6 +5397,8 @@ export type AgentConversationRuntimeIndexGroup = z.infer<
const AgentConversationRuntimeIndexKindSchema = z.enum([
"workspace",
"workspace_review",
"workspace_repair",
"pr_fixer",
"ideation",
"verification",
"delegation",
Expand Down
129 changes: 67 additions & 62 deletions frontend/src/components/agents/AgentsActiveConversationPanel.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -1686,6 +1686,10 @@ describe("AgentsActiveConversationPanel", () => {
role: "workspace_repair", display_name: "Fixer", description: "Fix", family: "workspace", family_display_name: "Workspace", requires_tasks: false,
configured: null, effective: { provider: "claude", model: "sonnet", effort: "medium", service_tier: "provider_default", coordination_mode: "solo", persona_id: null, approval_policy: null, sandbox_mode: null }, source: "project", diagnostics: [], controls: { capabilities: [], speeds: [], persona: { enabled: true, disabled_reason: null } },
},
{
role: "pr_fixer", display_name: "PR Fixer", description: "Fix PR", family: "workspace", family_display_name: "Workspace", requires_tasks: false,
configured: null, effective: { provider: "codex", model: "gpt-5.6", effort: "medium", service_tier: "standard", coordination_mode: "solo", persona_id: null, approval_policy: null, sandbox_mode: null }, source: "project", diagnostics: [], controls: { capabilities: [], speeds: [], persona: { enabled: true, disabled_reason: null } },
},
] });
}
return Promise.resolve(undefined);
Expand All @@ -1695,8 +1699,12 @@ describe("AgentsActiveConversationPanel", () => {
"project-1",
{ provider: "claude", modelId: "opus", effort: "xhigh" },
);
useChatStore.setState({ activeAgentRunMeta: { "project:conversation-1": { launchRole: "workspace_reviewer", agentName: "reviewer" } } });
const { rerenderPanel } = renderPanel();
const { rerenderPanel } = renderPanel({
chatFocus: {
type: "workspace_review",
conversationId: "review-conversation-1",
},
});

expect(await screen.findByTestId("agents-role-runtime-banner")).toHaveTextContent("Reviewer run active");
expect(screen.getByTestId("workspace-runtime-tag")).toHaveTextContent("REV");
Expand Down Expand Up @@ -1733,15 +1741,27 @@ describe("AgentsActiveConversationPanel", () => {
effort: "xhigh",
});

useChatStore.setState({ activeAgentRunMeta: { "project:conversation-1": { launchRole: "workspace_repair", agentName: "fixer" } } });
rerenderPanel({ publishAttemptsByConversationId: { "conversation-1": { conversationId: "conversation-1", startedAtMs: 1 } } });
rerenderPanel({
chatFocus: {
type: "workspace_repair",
conversationId: "repair-conversation-1",
},
});
expect(await screen.findByTestId("agents-role-runtime-banner")).toHaveTextContent("Fixer run active");
expect(screen.getByTestId("workspace-runtime-tag")).toHaveTextContent("FIX");
await waitFor(() => expect(screen.getByTestId("workspace-provider-value")).toHaveTextContent("claude"));
expect(screen.getByTestId("workspace-model-value")).toHaveTextContent("sonnet");
fireEvent.click(screen.getByTestId("change-workspace-model"));
expect(useAgentSessionStore.getState().roleRuntimeOverridesByConversationId["conversation-1"]?.workspace_repair?.model).toBe("sonnet");

useChatStore.setState({ activeAgentRunMeta: {} });
rerenderPanel({ publishAttemptsByConversationId: { "conversation-1": { conversationId: "conversation-1", startedAtMs: 2 } } });
rerenderPanel({
chatFocus: { type: "pr_fixer", conversationId: "pr-fixer-conversation-1" },
});
expect(await screen.findByTestId("agents-role-runtime-banner")).toHaveTextContent("PR Fixer run active");
fireEvent.click(screen.getByTestId("change-workspace-model"));
expect(useAgentSessionStore.getState().roleRuntimeOverridesByConversationId["conversation-1"]?.pr_fixer?.model).toBe("sonnet");

rerenderPanel({ chatFocus: { type: "workspace" } });
expect(screen.queryByTestId("agents-role-runtime-banner")).not.toBeInTheDocument();
expect(screen.getByTestId("workspace-provider-value")).toHaveTextContent("claude");
expect(screen.getByTestId("workspace-model-value")).toHaveTextContent("opus");
Expand Down Expand Up @@ -2331,18 +2351,13 @@ describe("AgentsActiveConversationPanel", () => {
fireEvent.click(screen.getByTestId("change-workspace-model"));
fireEvent.click(screen.getByTestId("change-workspace-effort"));

expect(onActiveModelChange).toHaveBeenCalledWith("sonnet", [
"low",
"medium",
"high",
"max",
], null);
expect(onActiveEffortChange).toHaveBeenCalledWith("max", [
"low",
"medium",
"high",
"max",
], null);
expect(onActiveModelChange).not.toHaveBeenCalled();
expect(onActiveEffortChange).not.toHaveBeenCalled();
expect(
useAgentSessionStore.getState().roleRuntimeOverridesByConversationId[
"conversation-1"
]?.workspace_reviewer,
).toMatchObject({ model: "sonnet", effort: "max" });
});

it("allows provider changes in an existing workspace conversation", () => {
Expand Down Expand Up @@ -2985,6 +3000,28 @@ describe("AgentsActiveConversationPanel", () => {
);
});

it.each([
{ type: "workspace_repair" as const, conversationId: "repair-conversation-1" },
{ type: "pr_fixer" as const, conversationId: "pr-fixer-conversation-1" },
])("routes $type focus sends through its child project chat", (chatFocus) => {
renderPanel({ chatFocus });

const panel = screen.getByTestId("integrated-chat-panel");
expect(panel).toHaveAttribute("data-conversation-id", chatFocus.conversationId);
expect(panel).toHaveAttribute(
"data-agent-process-context-id",
chatFocus.conversationId,
);
expect(panel).toHaveAttribute(
"data-store-context-key",
`project:${chatFocus.conversationId}`,
);
expect(panel).toHaveAttribute(
"data-send-conversation-id",
chatFocus.conversationId,
);
});

it("does not inherit parent Codex fast mode while focused on workspace Review", () => {
renderPanel({
activeConversation: {
Expand All @@ -3010,7 +3047,7 @@ describe("AgentsActiveConversationPanel", () => {
"data-send-conversation-id",
"review-conversation-1",
);
expect(panel).toHaveAttribute("data-send-codex-fast-mode", "false");
expect(panel).toHaveAttribute("data-send-codex-fast-mode", "null");
});

it("uses the active Codex reviewer runtime and fast mode for a focused workspace Review send", async () => {
Expand Down Expand Up @@ -4278,15 +4315,6 @@ describe("AgentsActiveConversationPanel", () => {
it("switches to Plan mode when the user accepts a plan-mode proposal question", async () => {
const user = userEvent.setup();
const planWorkspace = { ...workspace(), mode: "plan" as const };
setActiveReviewerRuntime({
provider: "claude",
model: "sonnet",
effort: "medium",
serviceTier: "provider_default",
});
useAgentSessionStore
.getState()
.setServiceTierForConversation("conversation-1", "fast");
switchAgentConversationModeMock.mockResolvedValue({
workspace: planWorkspace,
});
Expand All @@ -4297,17 +4325,10 @@ describe("AgentsActiveConversationPanel", () => {
activeConversation: { ...projectConversation(), agentMode: "edit" },
activeConversationMode: "edit",
activeWorkspace: { ...workspace(), mode: "edit" },
normalizedActiveRuntime: {
provider: "codex",
modelId: "gpt-5.5",
effort: "high",
},
onConversationModeSwitched,
onAgentUserMessageSent,
});

await screen.findByTestId("agents-role-runtime-banner");

await user.click(screen.getByTestId("accept-plan-mode-proposal"));

await waitFor(() =>
Expand Down Expand Up @@ -4335,9 +4356,8 @@ describe("AgentsActiveConversationPanel", () => {
expect.objectContaining({
conversationId: "conversation-1",
providerHarness: "claude",
modelId: "sonnet",
logicalEffort: "medium",
codexFastMode: null,
modelId: "opus",
logicalEffort: "high",
}),
);
expect(onAgentUserMessageSent).toHaveBeenCalledWith(
Expand Down Expand Up @@ -4981,15 +5001,6 @@ describe("AgentsActiveConversationPanel", () => {
const user = userEvent.setup();
const onAgentUserMessageSent = vi.fn();
const onForkConversation = vi.fn().mockResolvedValue(forkResult());
setActiveReviewerRuntime({
provider: "claude",
model: "sonnet",
effort: "medium",
serviceTier: "provider_default",
});
useAgentSessionStore
.getState()
.setServiceTierForConversation("conversation-1", "fast");
renderPanel({
normalizedActiveRuntime: {
provider: "codex",
Expand All @@ -5000,8 +5011,6 @@ describe("AgentsActiveConversationPanel", () => {
onForkConversation,
});

await screen.findByTestId("agents-role-runtime-banner");

await user.click(screen.getByTestId("send-fork-followup-command"));
await user.click(screen.getByRole("button", { name: "Fork session" }));

Expand All @@ -5013,9 +5022,9 @@ describe("AgentsActiveConversationPanel", () => {
undefined,
{
conversationId: "conversation-fork",
providerHarness: "claude",
modelId: "sonnet",
logicalEffort: "medium",
providerHarness: "codex",
modelId: "gpt-5.5",
logicalEffort: "high",
codexFastMode: null,
},
),
Expand All @@ -5033,25 +5042,21 @@ describe("AgentsActiveConversationPanel", () => {
});
});

it("retains a fast-mode flag for a Codex active reviewer fork", async () => {
it("retains a fast-mode flag for a Codex workspace fork", async () => {
const user = userEvent.setup();
const onForkConversation = vi.fn().mockResolvedValue(forkResult());
setActiveReviewerRuntime({
provider: "codex",
model: "gpt-5.5",
effort: "high",
serviceTier: "fast",
});
useAgentSessionStore
.getState()
.setServiceTierForConversation("conversation-1", "fast");
renderPanel({
normalizedActiveRuntime: {
provider: "claude",
modelId: "opus",
provider: "codex",
modelId: "gpt-5.5",
effort: "high",
},
onForkConversation,
});

await screen.findByTestId("agents-role-runtime-banner");
await user.click(screen.getByTestId("send-fork-followup-command"));
await user.click(screen.getByRole("button", { name: "Fork session" }));

Expand Down
Loading
Loading