Skip to content

Commit 523addd

Browse files
committed
feat(inbox): show signals report data in detail pane
- Add Priority and Actionability detail rows with collapsible "Why?" explanations from priority_judgment and actionability_judgment artefacts - Add 'already addressed' warning banner driven by report or judgment - Pass per-signal SignalFinding (verified flag, code paths, data queried) to SignalCard so the detail pane can surface research evidence - Add VerificationBadge to SignalCardHeader and CodePaths/DataQueried collapsibles inside each source-specific signal card variant - Add SignalReportActionabilityBadge alongside the existing priority/ status chips on the inbox list card - Restructure the evidence section in ReportDetailPane to surface loading and unavailable states (forbidden, not_found, invalid_payload, request_failed) so the user gets feedback when artefacts can't load - Add typed normalizers for priority_judgment, actionability_judgment and signal_finding artefacts in posthogClient - Drop the legacy JudgmentBadges component, which used an outdated Record<string, unknown> shape that no longer matches the typed artefact schema in shared/types Squashed-from: signals/new-report-data-in-inbox-ui-backup Originally PR #1428
1 parent e55079e commit 523addd

6 files changed

Lines changed: 676 additions & 203 deletions

File tree

apps/code/src/renderer/api/posthogClient.ts

Lines changed: 124 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
11
import type {
2+
ActionabilityJudgmentArtefact,
3+
PriorityJudgmentArtefact,
24
SandboxEnvironment,
35
SandboxEnvironmentInput,
6+
SignalFindingArtefact,
47
SignalReportArtefact,
58
SignalReportArtefactsResponse,
69
SignalReportSignalsResponse,
@@ -71,19 +74,138 @@ function optionalString(value: unknown): string | null {
7174
return typeof value === "string" ? value : null;
7275
}
7376

74-
type AnyArtefact = SignalReportArtefact | SuggestedReviewersArtefact;
77+
type AnyArtefact =
78+
| SignalReportArtefact
79+
| PriorityJudgmentArtefact
80+
| ActionabilityJudgmentArtefact
81+
| SignalFindingArtefact
82+
| SuggestedReviewersArtefact;
83+
84+
const PRIORITY_VALUES = new Set(["P0", "P1", "P2", "P3", "P4"]);
85+
86+
function normalizePriorityJudgmentArtefact(
87+
value: Record<string, unknown>,
88+
): PriorityJudgmentArtefact | null {
89+
const id = optionalString(value.id);
90+
if (!id) return null;
91+
92+
const contentValue = isObjectRecord(value.content) ? value.content : null;
93+
if (!contentValue) return null;
94+
95+
const priority = optionalString(contentValue.priority);
96+
if (!priority || !PRIORITY_VALUES.has(priority)) return null;
97+
98+
return {
99+
id,
100+
type: "priority_judgment",
101+
created_at: optionalString(value.created_at) ?? new Date(0).toISOString(),
102+
content: {
103+
explanation: optionalString(contentValue.explanation) ?? "",
104+
priority: priority as PriorityJudgmentArtefact["content"]["priority"],
105+
},
106+
};
107+
}
108+
109+
const ACTIONABILITY_VALUES = new Set([
110+
"immediately_actionable",
111+
"requires_human_input",
112+
"not_actionable",
113+
]);
114+
115+
function normalizeActionabilityJudgmentArtefact(
116+
value: Record<string, unknown>,
117+
): ActionabilityJudgmentArtefact | null {
118+
const id = optionalString(value.id);
119+
if (!id) return null;
120+
121+
const contentValue = isObjectRecord(value.content) ? value.content : null;
122+
if (!contentValue) return null;
123+
124+
// Support both agentic ("actionability") and legacy ("choice") field names
125+
const actionability =
126+
optionalString(contentValue.actionability) ??
127+
optionalString(contentValue.choice);
128+
if (!actionability || !ACTIONABILITY_VALUES.has(actionability)) return null;
129+
130+
return {
131+
id,
132+
type: "actionability_judgment",
133+
created_at: optionalString(value.created_at) ?? new Date(0).toISOString(),
134+
content: {
135+
explanation: optionalString(contentValue.explanation) ?? "",
136+
actionability:
137+
actionability as ActionabilityJudgmentArtefact["content"]["actionability"],
138+
already_addressed:
139+
typeof contentValue.already_addressed === "boolean"
140+
? contentValue.already_addressed
141+
: false,
142+
},
143+
};
144+
}
145+
146+
function normalizeSignalFindingArtefact(
147+
value: Record<string, unknown>,
148+
): SignalFindingArtefact | null {
149+
const id = optionalString(value.id);
150+
if (!id) return null;
151+
152+
const contentValue = isObjectRecord(value.content) ? value.content : null;
153+
if (!contentValue) return null;
154+
155+
const signalId = optionalString(contentValue.signal_id);
156+
if (!signalId) return null;
157+
158+
return {
159+
id,
160+
type: "signal_finding",
161+
created_at: optionalString(value.created_at) ?? new Date(0).toISOString(),
162+
content: {
163+
signal_id: signalId,
164+
relevant_code_paths: Array.isArray(contentValue.relevant_code_paths)
165+
? contentValue.relevant_code_paths.filter(
166+
(p: unknown): p is string => typeof p === "string",
167+
)
168+
: [],
169+
relevant_commit_hashes: isObjectRecord(
170+
contentValue.relevant_commit_hashes,
171+
)
172+
? Object.fromEntries(
173+
Object.entries(contentValue.relevant_commit_hashes).filter(
174+
(e): e is [string, string] => typeof e[1] === "string",
175+
),
176+
)
177+
: {},
178+
data_queried: optionalString(contentValue.data_queried) ?? "",
179+
verified:
180+
typeof contentValue.verified === "boolean"
181+
? contentValue.verified
182+
: false,
183+
},
184+
};
185+
}
75186

76187
function normalizeSignalReportArtefact(value: unknown): AnyArtefact | null {
77188
if (!isObjectRecord(value)) {
78189
return null;
79190
}
80191

192+
const dispatchType = optionalString(value.type);
193+
if (dispatchType === "signal_finding") {
194+
return normalizeSignalFindingArtefact(value);
195+
}
196+
if (dispatchType === "actionability_judgment") {
197+
return normalizeActionabilityJudgmentArtefact(value);
198+
}
199+
if (dispatchType === "priority_judgment") {
200+
return normalizePriorityJudgmentArtefact(value);
201+
}
202+
81203
const id = optionalString(value.id);
82204
if (!id) {
83205
return null;
84206
}
85207

86-
const type = optionalString(value.type) ?? "unknown";
208+
const type = dispatchType ?? "unknown";
87209
const created_at =
88210
optionalString(value.created_at) ?? new Date(0).toISOString();
89211

0 commit comments

Comments
 (0)