feat(scouts): link scout findings to their inbox report#2686
Conversation
Surfaces the reverse of the report -> signals link on the Scout page: each finding now shows a chip linking to the inbox report (if any) its signal grouped into, replacing the "report assignment isn't traceable here yet" note. Backed by the new GET signals/scout/runs/<run_id>/emissions/reports endpoint (PostHog/posthog#63817). - api-client: ScoutEmissionReportLink / LinkedSignalReport types + listScoutEmissionReports. - useScoutEmissionReports: per-run reverse-lookup query, loaded alongside useScoutRunEmissions. Best effort — a failure is non-fatal, the cards just render without the chip. - ScoutLinkedReportChip: footer chip that opens the report in the inbox. - useOpenInboxReport: extracted from useInboxDeepLink (fetch by id, seed cache, reset filters, navigate to the right tab) and reused by the chip so both paths land on the correct surface.
|
React Doctor found no issues in the changed files. 🎉 Reviewed by React Doctor for commit |
Prompt To Fix All With AIFix the following 1 code review issue. Work through them one at a time, proposing concise fixes.
---
### Issue 1 of 1
packages/ui/src/features/scouts/components/ScoutSignalsSection.tsx:108-112
`reportBySourceId` is reconstructed on every render of `RunEmissions`. It only needs to change when `emissionReports` changes, so it should be wrapped in `useMemo`.
```suggestion
const reportBySourceId = useMemo(
() =>
new Map(
(emissionReports ?? [])
.filter((link) => link.report)
.map((link) => [link.source_id, link.report]),
),
[emissionReports],
);
```
Reviews (1): Last reviewed commit: "feat(scouts): link scout findings to the..." | Re-trigger Greptile |
Addresses bot review on #2686: - wrap reportBySourceId in useMemo so it only rebuilds when emissionReports changes (greptile). - build the map in a single loop instead of chained filter().map() (react-doctor js-combine-iterations), which also drops the null report values for a precise Map<string, LinkedSignalReport>.
There was a problem hiding this comment.
Clean additive feature — extracts shared logic into useOpenInboxReport, adds a non-fatal best-effort report chip to scout emission cards, and properly uses useMemo (addressing the resolved bot comment). No architecture violations, no breaking changes, no security concerns.
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 4f7ce5a9ce
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| if (report.implementation_pr_url) { | ||
| navigateToInboxPullRequestDetail(report.id); | ||
| } else { | ||
| navigateToInboxReportDetail(report.id); |
There was a problem hiding this comment.
Route run reports to the run detail
When the fetched report has a run-only status (potential, candidate, in_progress, pending_input, or failed), this branch still sends it to /code/inbox/reports/$reportId because only PRs are special-cased. Those reports are excluded from the Reports tab by isReportTabReport, and the run detail route is the one that renders the task log, so clicking a scout linked-report chip for an active/failed run lands on the wrong inbox surface. Add a run-status branch before the reports fallback.
Useful? React with 👍 / 👎.
| const data = await this.scoutGet< | ||
| { results: ScoutEmissionReportLink[] } | ScoutEmissionReportLink[] | ||
| >(projectId, `runs/${runId}/emissions/reports/`); |
There was a problem hiding this comment.
Swallow 404s for the optional scout lookup
When this client is deployed against a backend before the new emissions/reports/ action exists, scoutGet uses the shared fetcher, which throws on any non-2xx in packages/api-client/src/fetcher.ts, so this call rejects with a 404 instead of producing an empty link list. Since useScoutEmissionReports mounts one query per visible emitted run, opening a scout page on that backend will generate failing API requests rather than the intended quiet fallback; catch 404 here and return [] if the endpoint is optional.
Useful? React with 👍 / 👎.
Problem
When you open an inbox report you can see the findings that contributed to it. There was no way to do the reverse: looking at a finding on the Scout page, you couldn't tell which inbox report (if any) it ended up in. The emission card literally said "report assignment isn't traceable here yet."
What this does
Each scout finding now shows a chip linking to the inbox report its signal grouped into — the reverse of the report's evidence list. Clicking it opens the report in the Inbox.
It's best effort: the chip only appears when the reverse lookup resolves a report. Findings that haven't grouped yet, were de-duplicated away, or whose signal was deleted just render as before.
Changes
api-client—ScoutEmissionReportLink/LinkedSignalReporttypes +listScoutEmissionReports(projectId, runId).useScoutEmissionReports— per-run reverse-lookup query, loaded alongsideuseScoutRunEmissionsinRunEmissions. A failure here is non-fatal — the cards still render, just without the chip.ScoutLinkedReportChip— footer chip on the emission card; opens the linked report in the Inbox.useOpenInboxReport— extracted fromuseInboxDeepLink(fetch report by id, seed the detail cache, reset filters, navigate to the right tab — Pulls if it has an implementation PR, otherwise Reports) and reused by the chip, so the deep-link handler and the chip share one proven path.useInboxDeepLinknow just wires the tRPC subscription to it.ScoutEmissionCard— new optionallinkedReportprop; renders the chip and drops the "not traceable yet" note when a link exists.analytics-events— newopen_linked_reportscout action type +report_statusproperty.Design notes
source_id.navigate, so the chip lands on the correct tab and primes the cache the same way an externalposthog-code://inbox/<id>link does.Testing
pnpm typecheck(full repo) and Biome pass via pre-commit.source_id → reportmap (covered by typecheck) — consistent with the inbox guidance to keep tests on pure logic.