Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
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
10 changes: 10 additions & 0 deletions products/desktop/packages/api-client/src/posthog-client.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2669,6 +2669,16 @@ describe("PostHogAPIClient", () => {
task_id: "t1",
created_by: null,
},
{
id: "a15",
type: "autostart_skip",
content: {
skip_reason: "blocked_by_dependency",
linked_report_id: "8f7c9d2e-1b3a-4c5d-9e6f-0a1b2c3d4e5f",
detail: "Work starts when the dependency report closes.",
},
created_at: "2026-06-01T00:00:14Z",
},
];

it("normalizes every backend artefact type without dropping rows", async () => {
Expand Down
37 changes: 36 additions & 1 deletion products/desktop/packages/api-client/src/posthog-client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,8 @@ import {
} from "@posthog/shared";
import type {
ActionabilityJudgmentArtefact,
AutostartSkipArtefact,
AutostartSkipReason,
AvailableSuggestedReviewer,
AvailableSuggestedReviewersResponse,
ChannelFeedMessage,
Expand Down Expand Up @@ -78,6 +80,7 @@ import type {
TaskThreadMessage,
UserBasic,
} from "@posthog/shared/domain-types";
import { AUTOSTART_SKIP_REASON_LABELS } from "@posthog/shared/domain-types";
import { buildPosthogProjectHeaderRecord } from "@posthog/shared/posthog-property-headers";
import {
spaceSetupInputSchema,
Expand Down Expand Up @@ -1359,7 +1362,8 @@ type AnyArtefact =
| LineReferenceArtefact
| CommitArtefact
| TaskRunArtefact
| NoteArtefact;
| NoteArtefact
| AutostartSkipArtefact;

// Reasons valid on a dismissal artefact. Resolve reasons are included because the
// backend stores resolve feedback on the same artefact type (a resolve writes a
Expand Down Expand Up @@ -1701,6 +1705,32 @@ function normalizeNoteArtefact(
};
}

const AUTOSTART_SKIP_REASONS = new Set<string>(
Object.keys(AUTOSTART_SKIP_REASON_LABELS),
);

function normalizeAutostartSkipArtefact(
value: Record<string, unknown>,
): AutostartSkipArtefact | null {
const id = optionalString(value.id);
if (!id) return null;
const c = isObjectRecord(value.content) ? value.content : null;
if (!c) return null;
const skipReason = optionalString(c.skip_reason);
if (!skipReason || !AUTOSTART_SKIP_REASONS.has(skipReason)) return null;

return {
id,
type: "autostart_skip",
...artefactBase(value),
content: {
skip_reason: skipReason as AutostartSkipReason,
linked_report_id: optionalString(c.linked_report_id),
detail: optionalString(c.detail) ?? "",
},
};
}

/** Best human-readable one-liner from arbitrary artefact content. */
function contentPreview(content: unknown): string {
if (typeof content === "string") return content;
Expand Down Expand Up @@ -1801,6 +1831,11 @@ function normalizeSignalReportArtefact(value: unknown): AnyArtefact | null {
if (dispatchType === "note") {
return normalizeNoteArtefact(value) ?? normalizeFallbackArtefact(value);
}
if (dispatchType === "autostart_skip") {
return (
normalizeAutostartSkipArtefact(value) ?? normalizeFallbackArtefact(value)
);
}

const id = optionalString(value.id);
if (!id) {
Expand Down
32 changes: 31 additions & 1 deletion products/desktop/packages/shared/src/domain-types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1020,6 +1020,35 @@ export interface NoteContent {
author?: string | null;
}

/**
* Artefact with `type: "autostart_skip"` — automatic implementation was held back by
* a typed link to another report.
*/
export interface AutostartSkipArtefact extends SignalReportArtefactBase {
type: "autostart_skip";
content: AutostartSkipContent;
}

export type AutostartSkipReason =
| "duplicate_of"
| "blocked_by_dependency"
| "plan_parent";

export interface AutostartSkipContent {
skip_reason: AutostartSkipReason;
/** The report that held this one back, when one report decided it. */
linked_report_id: string | null;
/** One line that names what has to happen before work starts. */
detail: string;
}

export const AUTOSTART_SKIP_REASON_LABELS: Record<AutostartSkipReason, string> =
{
duplicate_of: "Duplicate",
blocked_by_dependency: "Waiting on a dependency",
plan_parent: "Tracked by other reports",
};

/** Response from the `commit` artefact diff endpoint — the commit rendered against its parent. */
export interface CommitDiffResponse {
/** Unified diff (patch) text introduced by the commit. */
Expand Down Expand Up @@ -1123,7 +1152,8 @@ export type AnySignalReportArtefact =
| LineReferenceArtefact
| CommitArtefact
| TaskRunArtefact
| NoteArtefact;
| NoteArtefact
| AutostartSkipArtefact;

export interface SignalReportArtefactsResponse {
results: AnySignalReportArtefact[];
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
import type {
AutostartSkipArtefact,
AutostartSkipContent,
} from "@posthog/shared/types";
import { fireEvent, render, screen } from "@testing-library/react";
import { beforeEach, describe, expect, it, vi } from "vitest";

const openReport = vi.fn();
vi.mock("@posthog/ui/features/inbox/hooks/useOpenInboxReport", () => ({
useOpenInboxReport: () => openReport,
}));

import { ArtefactLogList } from "./ArtefactLogList";

function autostartSkip(content: AutostartSkipContent): AutostartSkipArtefact {
return {
id: "skip-1",
type: "autostart_skip",
created_at: "2026-06-01T00:00:00Z",
content,
};
}

describe("ArtefactLogList", () => {
beforeEach(() => {
openReport.mockReset();
});

it.each([
["duplicate_of", "Duplicate"],
["blocked_by_dependency", "Waiting on a dependency"],
["plan_parent", "Tracked by other reports"],
] as const)(
"explains an autostart skip for %s",
(skipReason, reasonLabel) => {
render(
<ArtefactLogList
reportId="report-1"
artefacts={[
autostartSkip({
skip_reason: skipReason,
linked_report_id: null,
detail: "Work starts when the other report closes.",
}),
]}
/>,
);

expect(screen.getByText("Work not started")).toBeTruthy();
expect(screen.getByText(reasonLabel)).toBeTruthy();
expect(
screen.getByText("Work starts when the other report closes."),
).toBeTruthy();
expect(screen.queryByText("No preview available.")).toBeNull();
expect(screen.queryByText("Open related report")).toBeNull();
},
);

it("opens the linked report in the app", () => {
render(
<ArtefactLogList
reportId="report-1"
artefacts={[
autostartSkip({
skip_reason: "duplicate_of",
linked_report_id: "report-2",
detail: "This report duplicates another open report.",
}),
]}
/>,
);

fireEvent.click(screen.getByText("Open related report"));

expect(openReport).toHaveBeenCalledWith("report-2");
});
});
Original file line number Diff line number Diff line change
@@ -1,29 +1,33 @@
import {
ArrowRightIcon,
ArrowSquareOutIcon,
CaretDownIcon,
CaretRightIcon,
} from "@phosphor-icons/react";
import { attributionLabel } from "@posthog/core/inbox/activityLog";
import type {
ActionabilityJudgmentContent,
AnySignalReportArtefact,
CodeReferenceContent,
CommitContent,
DismissalContent,
LineReferenceContent,
NoteContent,
PriorityJudgmentContent,
SafetyJudgmentContent,
SignalFindingContent,
SignalReportArtefactContent,
SuggestedReviewer,
TaskRunArtefactContent,
import {
type ActionabilityJudgmentContent,
type AnySignalReportArtefact,
AUTOSTART_SKIP_REASON_LABELS,
type AutostartSkipContent,
type CodeReferenceContent,
type CommitContent,
type DismissalContent,
type LineReferenceContent,
type NoteContent,
type PriorityJudgmentContent,
type SafetyJudgmentContent,
type SignalFindingContent,
type SignalReportArtefactContent,
type SuggestedReviewer,
type TaskRunArtefactContent,
} from "@posthog/shared/types";
import { MarkdownRenderer } from "@posthog/ui/features/editor/components/MarkdownRenderer";
import { ArtefactCommit } from "@posthog/ui/features/inbox/components/detail/ArtefactCommit";
import { ArtefactTaskRun } from "@posthog/ui/features/inbox/components/detail/ArtefactTaskRun";
import { SignalReportActionabilityBadge } from "@posthog/ui/features/inbox/components/utils/SignalReportActionabilityBadge";
import { SignalReportPriorityBadge } from "@posthog/ui/features/inbox/components/utils/SignalReportPriorityBadge";
import { useOpenInboxReport } from "@posthog/ui/features/inbox/hooks/useOpenInboxReport";
import { CodeBlock } from "@posthog/ui/primitives/CodeBlock";
import { HighlightedCode } from "@posthog/ui/primitives/HighlightedCode";
import { RelativeTimestamp } from "@posthog/ui/primitives/RelativeTimestamp";
Expand Down Expand Up @@ -51,6 +55,7 @@ const TYPE_LABELS: Record<string, string> = {
suggested_reviewers: "Reviewers suggested",
repo_selection: "Repo selected",
dismissal: "Report dismissed",
autostart_skip: "Work not started",
video_segment: "Video segment",
};

Expand Down Expand Up @@ -236,6 +241,31 @@ function ReviewersBody({ reviewers }: { reviewers: SuggestedReviewer[] }) {
);
}

function AutostartSkipBody({ content }: { content: AutostartSkipContent }) {
const openReport = useOpenInboxReport();
const linkedReportId = content.linked_report_id;
return (
<Flex direction="column" gap="1">
<Flex align="center" gap="2" wrap="wrap">
<Badge color="gray" variant="soft">
Comment on lines +248 to +250

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📐 Maintainability & Code Quality | 🟠 Major | ⚡ Quick win

🔎 Supported by static analysis

🏁 Script executed:

rg -n 'Radix|UI Components|@posthog/quill' products/desktop/AGENTS.md products/desktop/packages/ui/AGENTS.md products/desktop/.storybook/AGENTS.md
sed -n '1,35p' products/desktop/packages/ui/src/features/inbox/components/detail/ArtefactLogList.tsx
sed -n '240,275p' products/desktop/packages/ui/src/features/inbox/components/detail/ArtefactLogList.tsx

Repository: PostHog/posthog

Length of output: 6619


Replace the new Radix components.

products/desktop/AGENTS.md bans Radix components in desktop code, not only in Storybook. Replace the new Flex and Badge usage in AutostartSkipBody with @posthog/quill components or div elements with Tailwind classes.

{AUTOSTART_SKIP_REASON_LABELS[content.skip_reason]}
</Badge>
{linkedReportId ? (
<button
type="button"
onClick={() => void openReport(linkedReportId)}
className="inline-flex items-center gap-0.5 text-[12px] text-gray-9 hover:text-gray-11"
>
Open related report
<ArrowRightIcon size={10} />
</button>
) : null}
</Flex>
<RelevanceNote note={content.detail} />
</Flex>
);
}

function ArtefactBody({
reportId,
artefact,
Expand Down Expand Up @@ -378,6 +408,10 @@ function ArtefactBody({
</Flex>
);
}
case "autostart_skip":
return (
<AutostartSkipBody content={artefact.content as AutostartSkipContent} />
);
default: {
const c = artefact.content as SignalReportArtefactContent | null;
const text = typeof c?.content === "string" ? c.content : "";
Expand Down
Loading