Skip to content
This repository was archived by the owner on Aug 6, 2026. It is now read-only.

Commit 83fe05e

Browse files
posthog[bot]PostHog Code
andauthored
fix(inbox): snooze & "Provide input for PR" for Needs input reports (#1806)
Co-authored-by: PostHog Code <code@posthog.com>
1 parent c3cf17f commit 83fe05e

3 files changed

Lines changed: 77 additions & 31 deletions

File tree

‎apps/code/src/renderer/features/inbox/components/detail/ReportDetailPane.tsx‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -203,9 +203,9 @@ export function ReportDetailPane({ report, onClose }: ReportDetailPaneProps) {
203203
const setSelectedRepo = useInboxCloudTaskStore((s) => s.setSelectedRepo);
204204
const runCloudTask = useInboxCloudTaskStore((s) => s.runCloudTask);
205205

206-
/** Matches server autostart rules: ready + immediately actionable + not already fixed. */
206+
/** Matches server autostart rules: ready (or awaiting user input) + immediately actionable + not already fixed. */
207207
const canCreateImplementationPr =
208-
report.status === "ready" &&
208+
(report.status === "ready" || report.status === "pending_input") &&
209209
report.actionability === "immediately_actionable" &&
210210
report.already_addressed !== true;
211211

‎apps/code/src/renderer/features/inbox/components/detail/ReportTaskLogs.tsx‎

Lines changed: 69 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -177,8 +177,8 @@ interface Bar {
177177
summary: BarSummary;
178178
/** Tooltip shown on hover (e.g. pipeline status explanation). */
179179
tooltip?: string;
180-
/** When true, render a "Create PR" control instead of the status label. */
181-
showRunAction?: boolean;
180+
/** When set, render a run-action button with this label instead of (or alongside) the status label. */
181+
runActionLabel?: string;
182182
}
183183

184184
interface ReportTaskLogsProps {
@@ -213,8 +213,10 @@ export function ReportTaskLogs({
213213
// Build the stacked bars we'll render. We always surface the research bar
214214
// (using a pending/unavailable placeholder if no research task exists yet).
215215
// For `ready` reports without an implementation task yet, we still show the
216-
// implementation row ("Not started"); the Create PR control only appears when
217-
// the parent passes `onRunInCloud` (ready + actionable).
216+
// implementation row ("Not started"); the run-action control only appears
217+
// when the parent passes `onRunInCloud`. For `pending_input` reports, we
218+
// surface the control as "Provide input for PR" so the user can supply the
219+
// additional context the agent is waiting on.
218220
const bars: Bar[] = [];
219221

220222
if (researchTask) {
@@ -236,13 +238,21 @@ export function ReportTaskLogs({
236238
});
237239
}
238240

241+
const isPendingInput = reportStatus === "pending_input";
242+
const runActionLabel = onRunInCloud
243+
? isPendingInput
244+
? "Provide input for PR"
245+
: "Create PR"
246+
: undefined;
247+
239248
if (implementationTask) {
240249
bars.push({
241250
relationship: "implementation",
242251
task: implementationTask,
243252
summary: getTaskStatusSummary(implementationTask),
253+
runActionLabel: isPendingInput ? runActionLabel : undefined,
244254
});
245-
} else if (reportStatus === "ready") {
255+
} else if (reportStatus === "ready" || isPendingInput) {
246256
bars.push({
247257
relationship: "implementation",
248258
task: null,
@@ -251,7 +261,7 @@ export function ReportTaskLogs({
251261
color: "var(--gray-9)",
252262
icon: <DotOutlineIcon size={14} />,
253263
},
254-
showRunAction: !!onRunInCloud,
264+
runActionLabel,
255265
});
256266
}
257267

@@ -262,7 +272,8 @@ export function ReportTaskLogs({
262272
tasks.length > 0 ||
263273
reportStatus === "candidate" ||
264274
reportStatus === "in_progress" ||
265-
reportStatus === "ready";
275+
reportStatus === "ready" ||
276+
isPendingInput;
266277

267278
if (!showBar) {
268279
return null;
@@ -318,30 +329,42 @@ export function ReportTaskLogs({
318329
{/* Stacked header bars — one per task relationship. */}
319330
<div className="shrink-0" style={{ pointerEvents: "auto" }}>
320331
{bars.map((bar, index) => {
321-
const { relationship, task, summary, tooltip, showRunAction } = bar;
332+
const { relationship, task, summary, tooltip, runActionLabel } =
333+
bar;
334+
const showRunAction = !!runActionLabel;
322335
const isExpanded = expanded === relationship;
323336
const isInteractive = !!task;
337+
// When we have both an expandable task and a run button, the outer
338+
// element becomes a <div role="button"> so we can legally nest the
339+
// run button inside. Without a task, there's nothing to expand; the
340+
// run button becomes the sole interactive control.
341+
const hideStatusLabel = showRunAction && !task;
324342

325343
const rowClassName = [
326344
"flex w-full items-center gap-2 bg-transparent px-3 py-2 text-left transition-colors",
327345
index > 0 ? "border-gray-5 border-t" : "",
328-
showRunAction
329-
? "cursor-default"
330-
: isInteractive
331-
? "cursor-pointer hover:bg-gray-2"
346+
isInteractive
347+
? "cursor-pointer hover:bg-gray-2"
348+
: showRunAction
349+
? "cursor-default"
332350
: "cursor-default opacity-70",
333351
isExpanded && isInteractive ? "bg-gray-2" : "",
334352
]
335353
.filter(Boolean)
336354
.join(" ");
337355

356+
const toggleExpand = () =>
357+
setExpanded((curr) =>
358+
curr === relationship ? null : relationship,
359+
);
360+
338361
const rowInner = (
339362
<>
340363
<span style={{ color: summary.color }}>{summary.icon}</span>
341364
<Text size="1" weight="medium" className="text-[12px]">
342365
{RELATIONSHIP_LABELS[relationship]}
343366
</Text>
344-
{showRunAction ? (
367+
{hideStatusLabel ? (
345368
<span className="flex-1" />
346369
) : (
347370
<Text
@@ -363,10 +386,10 @@ export function ReportTaskLogs({
363386
}}
364387
>
365388
<Cloud size={12} />
366-
Create PR
389+
{runActionLabel}
367390
</Button>
368391
)}
369-
{isInteractive && !showRunAction && (
392+
{isInteractive && (
370393
<span
371394
className="inline-flex text-gray-9"
372395
style={{
@@ -380,30 +403,48 @@ export function ReportTaskLogs({
380403
</>
381404
);
382405

383-
const row =
384-
isInteractive && !showRunAction ? (
385-
<button
406+
const row = isInteractive ? (
407+
showRunAction ? (
408+
// biome-ignore lint/a11y/useSemanticElements: a <button> can't contain the nested run-action <button>
409+
// biome-ignore lint/a11y/useKeyWithClickEvents: keyboard handled via onKeyDown
410+
// biome-ignore lint/a11y/noStaticElementInteractions: role="button" makes this interactive
411+
<div
386412
key={relationship}
387-
type="button"
388-
onClick={() =>
389-
setExpanded((curr) =>
390-
curr === relationship ? null : relationship,
391-
)
392-
}
413+
role="button"
414+
tabIndex={0}
415+
onClick={toggleExpand}
416+
onKeyDown={(e) => {
417+
if (e.target !== e.currentTarget) return;
418+
if (e.key === "Enter" || e.key === " ") {
419+
e.preventDefault();
420+
toggleExpand();
421+
}
422+
}}
393423
className={rowClassName}
394424
style={{ height: BAR_HEIGHT }}
395425
>
396426
{rowInner}
397-
</button>
427+
</div>
398428
) : (
399-
<div
429+
<button
400430
key={relationship}
431+
type="button"
432+
onClick={toggleExpand}
401433
className={rowClassName}
402434
style={{ height: BAR_HEIGHT }}
403435
>
404436
{rowInner}
405-
</div>
406-
);
437+
</button>
438+
)
439+
) : (
440+
<div
441+
key={relationship}
442+
className={rowClassName}
443+
style={{ height: BAR_HEIGHT }}
444+
>
445+
{rowInner}
446+
</div>
447+
);
407448

408449
return tooltip ? (
409450
<Tooltip key={relationship} content={tooltip}>

‎apps/code/src/renderer/features/inbox/hooks/useInboxBulkActions.ts‎

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,14 +27,19 @@ const suppressibleStatuses = new Set<SignalReport["status"]>([
2727
const snoozableStatuses = new Set<SignalReport["status"]>([
2828
"in_progress",
2929
"ready",
30+
"pending_input",
3031
]);
3132

3233
/** Clause after "Disabled because …" (see `@components/ui/Button`). */
3334
const DISABLED_NO_SELECTION = "you haven't selected a report";
3435

3536
/** Matches labels in the inbox list/filter (`inboxStatusLabel`). */
3637
const SNOOZE_ALLOWED_STATUS_PHRASE = (
37-
["in_progress", "ready"] as const satisfies readonly SignalReport["status"][]
38+
[
39+
"in_progress",
40+
"ready",
41+
"pending_input",
42+
] as const satisfies readonly SignalReport["status"][]
3843
)
3944
.map((status) => inboxStatusLabel(status))
4045
.join(" or ");

0 commit comments

Comments
 (0)