Skip to content

feat(signals): Signals list API changes for Code inbox#51740

Open
Twixes wants to merge 5 commits intomasterfrom
twixes/signal-report-priority-api
Open

feat(signals): Signals list API changes for Code inbox#51740
Twixes wants to merge 5 commits intomasterfrom
twixes/signal-report-priority-api

Conversation

@Twixes
Copy link
Member

@Twixes Twixes commented Mar 20, 2026

Problem

We're currently saving priority (P0-P4) on the actionability judgement artifact, and PostHog Code needs it on signal reports in list responses without fetching /artefacts/ per report. Code also needs to fetch the reports not just by creation, but by their status in pipeline.

Changes

SignalReportSerializer gets read-only priority field from the latest ACTIONABILITY_JUDGMENT artefact + gets more robust ordering, now by status.
To avoid N+1 we're doing artefact prefetching in the list endpoint.

Also, decreasing the buffer wait time from 60s to 5s, as discussed with Olly on Slack.

How did you test this code?

Some new API tests.

Publish to changelog?

do not publish to changelog

Copy link
Member Author

Twixes commented Mar 20, 2026

This stack of pull requests is managed by Graphite. Learn more about stacking.

@github-actions
Copy link
Contributor

github-actions bot commented Mar 20, 2026

Size Change: 0 B

Total Size: 110 MB

ℹ️ View Unchanged
Filename Size
frontend/dist/368Hedgehogs 5.27 kB
frontend/dist/abap 14.2 kB
frontend/dist/Action 20.5 kB
frontend/dist/Actions 1.03 kB
frontend/dist/AdvancedActivityLogsScene 34 kB
frontend/dist/AgenticAuthorize 5.27 kB
frontend/dist/apex 3.96 kB
frontend/dist/ApprovalDetail 16.2 kB
frontend/dist/array.full.es5.js 330 kB
frontend/dist/array.full.js 429 kB
frontend/dist/array.js 183 kB
frontend/dist/AsyncMigrations 13.2 kB
frontend/dist/AuthorizationStatus 717 B
frontend/dist/azcli 852 B
frontend/dist/bat 1.85 kB
frontend/dist/BatchExportScene 50.4 kB
frontend/dist/bicep 2.56 kB
frontend/dist/Billing 493 B
frontend/dist/BillingSection 20.7 kB
frontend/dist/BoxPlot 5.01 kB
frontend/dist/browserAll-0QZMN1W2 37.4 kB
frontend/dist/ButtonPrimitives 562 B
frontend/dist/CalendarHeatMap 4.82 kB
frontend/dist/cameligo 2.2 kB
frontend/dist/changeRequestsLogic 544 B
frontend/dist/CLIAuthorize 11.3 kB
frontend/dist/CLILive 3.99 kB
frontend/dist/clojure 9.65 kB
frontend/dist/coffee 3.6 kB
frontend/dist/Cohort 23.3 kB
frontend/dist/CohortCalculationHistory 6.24 kB
frontend/dist/Cohorts 9.41 kB
frontend/dist/ConfirmOrganization 4.5 kB
frontend/dist/conversations.js 64.5 kB
frontend/dist/Coupons 731 B
frontend/dist/cpp 5.31 kB
frontend/dist/Create 666 B
frontend/dist/crisp-chat-integration.js 2.11 kB
frontend/dist/csharp 4.53 kB
frontend/dist/csp 1.43 kB
frontend/dist/css 4.51 kB
frontend/dist/cssMode 4.18 kB
frontend/dist/CustomCssScene 3.56 kB
frontend/dist/CustomerAnalyticsConfigurationScene 2 kB
frontend/dist/CustomerAnalyticsScene 26.2 kB
frontend/dist/CustomerJourneyBuilderScene 1.59 kB
frontend/dist/CustomerJourneyTemplatesScene 7.2 kB
frontend/dist/customizations.full.js 18.7 kB
frontend/dist/CyclotronJobInputAssignee 1.33 kB
frontend/dist/CyclotronJobInputTicketTags 717 B
frontend/dist/cypher 3.4 kB
frontend/dist/dart 4.26 kB
frontend/dist/Dashboard 1 kB
frontend/dist/Dashboards 14.7 kB
frontend/dist/DataManagementScene 646 B
frontend/dist/DataPipelinesNewScene 2.33 kB
frontend/dist/DataWarehouseScene 1.17 kB
frontend/dist/DataWarehouseSourceScene 634 B
frontend/dist/Deactivated 1.14 kB
frontend/dist/dead-clicks-autocapture.js 13.1 kB
frontend/dist/DeadLetterQueue 5.41 kB
frontend/dist/DebugScene 20.1 kB
frontend/dist/decompressionWorker 2.85 kB
frontend/dist/decompressionWorker.js 2.85 kB
frontend/dist/DefinitionEdit 7.13 kB
frontend/dist/DefinitionView 22.8 kB
frontend/dist/DestinationsScene 2.72 kB
frontend/dist/dist 575 B
frontend/dist/dockerfile 1.88 kB
frontend/dist/EarlyAccessFeature 685 B
frontend/dist/EarlyAccessFeatures 2.85 kB
frontend/dist/ecl 5.35 kB
frontend/dist/EditorScene 839 B
frontend/dist/elixir 10.3 kB
frontend/dist/EmailMFAVerify 2.99 kB
frontend/dist/EndpointScene 37.2 kB
frontend/dist/EndpointsScene 21 kB
frontend/dist/ErrorTrackingConfigurationScene 2.2 kB
frontend/dist/ErrorTrackingIssueFingerprintsScene 6.99 kB
frontend/dist/ErrorTrackingIssueScene 85.9 kB
frontend/dist/ErrorTrackingScene 13 kB
frontend/dist/EvaluationTemplates 575 B
frontend/dist/EventsScene 2.45 kB
frontend/dist/exception-autocapture.js 12.1 kB
frontend/dist/Experiment 259 kB
frontend/dist/Experiments 17.1 kB
frontend/dist/exporter 21 MB
frontend/dist/exporter.js 21 MB
frontend/dist/ExportsScene 3.88 kB
frontend/dist/FeatureFlag 102 kB
frontend/dist/FeatureFlags 572 B
frontend/dist/FeatureFlagTemplatesScene 7.05 kB
frontend/dist/FlappyHog 5.8 kB
frontend/dist/flow9 1.81 kB
frontend/dist/freemarker2 16.7 kB
frontend/dist/fsharp 2.99 kB
frontend/dist/GAPromotionDialogContent 867 B
frontend/dist/go 2.66 kB
frontend/dist/graphql 2.27 kB
frontend/dist/Group 14.5 kB
frontend/dist/Groups 3.94 kB
frontend/dist/GroupsNew 7.36 kB
frontend/dist/handlebars 7.37 kB
frontend/dist/hcl 3.6 kB
frontend/dist/HealthScene 11.9 kB
frontend/dist/HeatmapNewScene 4.18 kB
frontend/dist/HeatmapRecordingScene 3.94 kB
frontend/dist/HeatmapScene 6.05 kB
frontend/dist/HeatmapsScene 3.89 kB
frontend/dist/HogFunctionScene 58.8 kB
frontend/dist/HogRepl 7.38 kB
frontend/dist/html 5.6 kB
frontend/dist/htmlMode 4.64 kB
frontend/dist/image-blob-reduce.esm 49.4 kB
frontend/dist/InboxScene 54.6 kB
frontend/dist/index 260 kB
frontend/dist/index.js 260 kB
frontend/dist/ini 1.11 kB
frontend/dist/InsightOptions 4.81 kB
frontend/dist/InsightScene 27.1 kB
frontend/dist/IntegrationsRedirect 744 B
frontend/dist/intercom-integration.js 2.16 kB
frontend/dist/InviteSignup 13.3 kB
frontend/dist/java 3.23 kB
frontend/dist/javascript 996 B
frontend/dist/jsonMode 13.9 kB
frontend/dist/julia 7.24 kB
frontend/dist/kotlin 3.41 kB
frontend/dist/lazy 153 kB
frontend/dist/LegacyPluginScene 21.2 kB
frontend/dist/LemonDialog 482 B
frontend/dist/less 3.9 kB
frontend/dist/lexon 2.45 kB
frontend/dist/lib 2.23 kB
frontend/dist/Link 468 B
frontend/dist/LinkScene 24.9 kB
frontend/dist/LinksScene 4.21 kB
frontend/dist/liquid 4.54 kB
frontend/dist/LiveDebugger 19.2 kB
frontend/dist/LiveEventsTable 4.46 kB
frontend/dist/LLMAnalyticsClusterScene 15.7 kB
frontend/dist/LLMAnalyticsClustersScene 43.1 kB
frontend/dist/LLMAnalyticsDatasetScene 19.7 kB
frontend/dist/LLMAnalyticsDatasetsScene 3.29 kB
frontend/dist/LLMAnalyticsEvaluation 40.5 kB
frontend/dist/LLMAnalyticsEvaluationsScene 29.3 kB
frontend/dist/LLMAnalyticsPlaygroundScene 36.4 kB
frontend/dist/LLMAnalyticsScene 111 kB
frontend/dist/LLMAnalyticsSessionScene 13.4 kB
frontend/dist/LLMAnalyticsTraceScene 127 kB
frontend/dist/LLMAnalyticsUsers 526 B
frontend/dist/LLMASessionFeedbackDisplay 4.85 kB
frontend/dist/LLMPromptScene 16.9 kB
frontend/dist/LLMPromptsScene 3.54 kB
frontend/dist/Login 8.37 kB
frontend/dist/Login2FA 4.22 kB
frontend/dist/logs.js 39.1 kB
frontend/dist/LogsScene 13.4 kB
frontend/dist/lua 2.13 kB
frontend/dist/m3 2.82 kB
frontend/dist/ManagedMigration 14 kB
frontend/dist/markdown 3.79 kB
frontend/dist/MarketingAnalyticsScene 24.4 kB
frontend/dist/MaterializedColumns 10.2 kB
frontend/dist/Max 767 B
frontend/dist/mdx 5.38 kB
frontend/dist/MessageTemplate 16.3 kB
frontend/dist/MetricsScene 844 B
frontend/dist/mips 2.59 kB
frontend/dist/ModelsScene 13.7 kB
frontend/dist/MonacoDiffEditor 403 B
frontend/dist/MoveToPostHogCloud 4.46 kB
frontend/dist/msdax 4.92 kB
frontend/dist/mysql 11.3 kB
frontend/dist/NavTabChat 4.55 kB
frontend/dist/NewSourceWizard 758 B
frontend/dist/NewTabScene 647 B
frontend/dist/NodeDetailScene 15.5 kB
frontend/dist/NotebookCanvasScene 3.04 kB
frontend/dist/NotebookPanel 5.07 kB
frontend/dist/NotebookScene 8.06 kB
frontend/dist/NotebooksScene 7.61 kB
frontend/dist/OAuthAuthorize 573 B
frontend/dist/objective-c 2.42 kB
frontend/dist/Onboarding 669 kB
frontend/dist/OnboardingCouponRedemption 1.2 kB
frontend/dist/pascal 3 kB
frontend/dist/pascaligo 2.01 kB
frontend/dist/passkeyLogic 484 B
frontend/dist/PasswordReset 4.33 kB
frontend/dist/PasswordResetComplete 2.95 kB
frontend/dist/perl 8.26 kB
frontend/dist/PersonScene 16 kB
frontend/dist/PersonsScene 4.75 kB
frontend/dist/pgsql 13.5 kB
frontend/dist/php 8.03 kB
frontend/dist/PipelineStatusScene 6.24 kB
frontend/dist/pla 1.69 kB
frontend/dist/posthog 253 kB
frontend/dist/postiats 7.86 kB
frontend/dist/powerquery 17 kB
frontend/dist/powershell 3.28 kB
frontend/dist/PreflightCheck 5.54 kB
frontend/dist/product-tours.js 118 kB
frontend/dist/ProductTour 274 kB
frontend/dist/ProductTours 4.72 kB
frontend/dist/ProjectHomepage 21.9 kB
frontend/dist/protobuf 9.05 kB
frontend/dist/pug 4.83 kB
frontend/dist/python 4.79 kB
frontend/dist/qsharp 3.2 kB
frontend/dist/r 3.14 kB
frontend/dist/razor 9.36 kB
frontend/dist/react 146 B
frontend/dist/recorder-v2.js 113 kB
frontend/dist/recorder.js 113 kB
frontend/dist/redis 3.56 kB
frontend/dist/redshift 11.8 kB
frontend/dist/RegionMap 29.5 kB
frontend/dist/render-query 20.7 MB
frontend/dist/render-query.js 20.7 MB
frontend/dist/ResourceTransfer 9.16 kB
frontend/dist/restructuredtext 3.91 kB
frontend/dist/RevenueAnalyticsScene 25.6 kB
frontend/dist/ruby 8.51 kB
frontend/dist/rust 4.17 kB
frontend/dist/SavedInsights 664 B
frontend/dist/sb 1.83 kB
frontend/dist/scala 7.33 kB
frontend/dist/scheme 1.77 kB
frontend/dist/scss 6.41 kB
frontend/dist/SdkDoctorScene 11 kB
frontend/dist/SessionAttributionExplorerScene 6.62 kB
frontend/dist/SessionGroupSummariesTable 4.64 kB
frontend/dist/SessionGroupSummaryScene 17.1 kB
frontend/dist/SessionProfileScene 15.9 kB
frontend/dist/SessionRecordingDetail 1.74 kB
frontend/dist/SessionRecordingFilePlaybackScene 4.48 kB
frontend/dist/SessionRecordings 742 B
frontend/dist/SessionRecordingsKiosk 8.87 kB
frontend/dist/SessionRecordingsPlaylistScene 4.08 kB
frontend/dist/SessionRecordingsSettingsScene 1.91 kB
frontend/dist/SessionsScene 3.88 kB
frontend/dist/SettingsScene 3 kB
frontend/dist/SharedMetric 15.7 kB
frontend/dist/SharedMetrics 515 B
frontend/dist/shell 3.08 kB
frontend/dist/SignupContainer 22.8 kB
frontend/dist/Site 1.2 kB
frontend/dist/solidity 18.6 kB
frontend/dist/sophia 2.77 kB
frontend/dist/SourcesScene 5.98 kB
frontend/dist/sourceWizardLogic 662 B
frontend/dist/sparql 2.56 kB
frontend/dist/sql 10.3 kB
frontend/dist/SqlVariableEditScene 7.26 kB
frontend/dist/st 7.41 kB
frontend/dist/StartupProgram 21.2 kB
frontend/dist/SupportSettingsScene 35.9 kB
frontend/dist/SupportTicketScene 22.8 kB
frontend/dist/SupportTicketsScene 733 B
frontend/dist/Survey 746 B
frontend/dist/SurveyFormBuilder 1.55 kB
frontend/dist/Surveys 18.2 kB
frontend/dist/surveys.js 90.2 kB
frontend/dist/SurveyWizard 56.2 kB
frontend/dist/swift 5.28 kB
frontend/dist/SystemStatus 16.9 kB
frontend/dist/systemverilog 7.62 kB
frontend/dist/TaskDetailScene 19.1 kB
frontend/dist/TaskTracker 16.4 kB
frontend/dist/tcl 3.57 kB
frontend/dist/toolbar 9.75 MB
frontend/dist/toolbar.js 9.75 MB
frontend/dist/ToolbarLaunch 2.53 kB
frontend/dist/tracing-headers.js 1.93 kB
frontend/dist/TracingScene 14.4 kB
frontend/dist/TransformationsScene 1.97 kB
frontend/dist/tsMode 24 kB
frontend/dist/twig 5.98 kB
frontend/dist/TwoFactorReset 4 kB
frontend/dist/typescript 240 B
frontend/dist/typespec 2.83 kB
frontend/dist/Unsubscribe 1.63 kB
frontend/dist/UserInterview 4.55 kB
frontend/dist/UserInterviews 2.03 kB
frontend/dist/vb 5.8 kB
frontend/dist/VercelConnect 4.03 kB
frontend/dist/VercelLinkError 1.92 kB
frontend/dist/VerifyEmail 4.49 kB
frontend/dist/vimMode 211 kB
frontend/dist/VisualReviewRunScene 18.7 kB
frontend/dist/VisualReviewRunsScene 6.14 kB
frontend/dist/VisualReviewSettingsScene 9.97 kB
frontend/dist/web-vitals.js 6.6 kB
frontend/dist/WebAnalyticsScene 5.72 kB
frontend/dist/WebGLRenderer-DYjOwNoG 60.3 kB
frontend/dist/WebGPURenderer-B_wkl_Ja 36.3 kB
frontend/dist/WebScriptsScene 2.58 kB
frontend/dist/webworkerAll-puPV1rBA 330 B
frontend/dist/wgsl 7.35 kB
frontend/dist/Wizard 4.47 kB
frontend/dist/WorkflowScene 101 kB
frontend/dist/WorkflowsScene 47.5 kB
frontend/dist/WorldMap 4.75 kB
frontend/dist/xml 2.98 kB
frontend/dist/yaml 4.61 kB

compressed-size-action

@Twixes Twixes changed the title feat(signals): expose signal report priority on list API feat(signals): Expose signal report priority on list API Mar 20, 2026
@Twixes Twixes marked this pull request as ready for review March 20, 2026 13:15
@Twixes Twixes requested a review from a team March 20, 2026 13:15
@greptile-apps
Copy link
Contributor

greptile-apps bot commented Mar 20, 2026

Prompt To Fix All With AI
This is a comment left during a code review.
Path: products/signals/backend/serializers.py
Line: 147-152

Comment:
**`AttributeError` on valid-JSON non-dict content**

`data.get("priority")` is called *outside* the `try/except` block. If `art.content` is valid JSON but not a dict (e.g. `'null'`, `'[]'`, or `'"some string"'`) then `json.loads()` succeeds, the except is not triggered, and `data.get(...)` raises `AttributeError` — resulting in a 500 for the list/retrieve endpoint.

```suggestion
        try:
            data = json.loads(art.content)
        except (json.JSONDecodeError, TypeError, ValueError):
            return None
        if not isinstance(data, dict):
            return None
        p = data.get("priority")
        return p if isinstance(p, str) else None
```

How can I resolve this? If you propose a fix, please make it concise.

---

This is a comment left during a code review.
Path: products/signals/backend/test/test_signal_report_api.py
Line: 150-176

Comment:
**Prefer parameterised tests; missing non-dict JSON case**

Per the project convention, these three standalone "priority is null" tests (`invalid_json`, `non_string_priority`, and the no-artefact test above) are good candidates for a single `@parameterized.expand`. A parameterised approach also makes it easy to add the currently-untested case of *valid* JSON that isn't a dict (e.g. `content='null'`), which would expose the `AttributeError` described in the sibling comment.

Example sketch (artefact-content cases only):

```python
@parameterized.expand(
    [
        ("invalid_json", "not-json{"),
        ("json_null", "null"),
        ("json_array", "[]"),
        ("non_string_priority", json.dumps({"priority": 2})),
        ("missing_priority_key", json.dumps({"choice": "immediately_actionable"})),
    ]
)
def test_list_priority_null_for_bad_content(self, _name, content):
    report = self._create_report()
    SignalReportArtefact.objects.create(
        team=self.team,
        report=report,
        type=SignalReportArtefact.ArtefactType.ACTIONABILITY_JUDGMENT,
        content=content,
    )
    response = self.client.get(self._list_url())
    assert response.status_code == status.HTTP_200_OK
    row = next(r for r in response.json()["results"] if r["id"] == str(report.id))
    assert row["priority"] is None
```

How can I resolve this? If you propose a fix, please make it concise.

Last reviewed commit: "test(signals): add A..."

Comment on lines +150 to +176
def test_list_priority_null_when_artefact_json_invalid(self):
report = self._create_report()
SignalReportArtefact.objects.create(
team=self.team,
report=report,
type=SignalReportArtefact.ArtefactType.ACTIONABILITY_JUDGMENT,
content="not-json{",
)

response = self.client.get(self._list_url())
assert response.status_code == status.HTTP_200_OK
row = next(r for r in response.json()["results"] if r["id"] == str(report.id))
assert row["priority"] is None

def test_list_priority_null_when_priority_not_a_string(self):
report = self._create_report()
SignalReportArtefact.objects.create(
team=self.team,
report=report,
type=SignalReportArtefact.ArtefactType.ACTIONABILITY_JUDGMENT,
content=json.dumps({"priority": 2}),
)

response = self.client.get(self._list_url())
assert response.status_code == status.HTTP_200_OK
row = next(r for r in response.json()["results"] if r["id"] == str(report.id))
assert row["priority"] is None
Copy link
Contributor

Choose a reason for hiding this comment

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

P2 Prefer parameterised tests; missing non-dict JSON case

Per the project convention, these three standalone "priority is null" tests (invalid_json, non_string_priority, and the no-artefact test above) are good candidates for a single @parameterized.expand. A parameterised approach also makes it easy to add the currently-untested case of valid JSON that isn't a dict (e.g. content='null'), which would expose the AttributeError described in the sibling comment.

Example sketch (artefact-content cases only):

@parameterized.expand(
    [
        ("invalid_json", "not-json{"),
        ("json_null", "null"),
        ("json_array", "[]"),
        ("non_string_priority", json.dumps({"priority": 2})),
        ("missing_priority_key", json.dumps({"choice": "immediately_actionable"})),
    ]
)
def test_list_priority_null_for_bad_content(self, _name, content):
    report = self._create_report()
    SignalReportArtefact.objects.create(
        team=self.team,
        report=report,
        type=SignalReportArtefact.ArtefactType.ACTIONABILITY_JUDGMENT,
        content=content,
    )
    response = self.client.get(self._list_url())
    assert response.status_code == status.HTTP_200_OK
    row = next(r for r in response.json()["results"] if r["id"] == str(report.id))
    assert row["priority"] is None
Prompt To Fix With AI
This is a comment left during a code review.
Path: products/signals/backend/test/test_signal_report_api.py
Line: 150-176

Comment:
**Prefer parameterised tests; missing non-dict JSON case**

Per the project convention, these three standalone "priority is null" tests (`invalid_json`, `non_string_priority`, and the no-artefact test above) are good candidates for a single `@parameterized.expand`. A parameterised approach also makes it easy to add the currently-untested case of *valid* JSON that isn't a dict (e.g. `content='null'`), which would expose the `AttributeError` described in the sibling comment.

Example sketch (artefact-content cases only):

```python
@parameterized.expand(
    [
        ("invalid_json", "not-json{"),
        ("json_null", "null"),
        ("json_array", "[]"),
        ("non_string_priority", json.dumps({"priority": 2})),
        ("missing_priority_key", json.dumps({"choice": "immediately_actionable"})),
    ]
)
def test_list_priority_null_for_bad_content(self, _name, content):
    report = self._create_report()
    SignalReportArtefact.objects.create(
        team=self.team,
        report=report,
        type=SignalReportArtefact.ArtefactType.ACTIONABILITY_JUDGMENT,
        content=content,
    )
    response = self.client.get(self._list_url())
    assert response.status_code == status.HTTP_200_OK
    row = next(r for r in response.json()["results"] if r["id"] == str(report.id))
    assert row["priority"] is None
```

How can I resolve this? If you propose a fix, please make it concise.

Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!

@Twixes Twixes changed the title feat(signals): Expose signal report priority on list API feat(signals): Signals list API changes for Code inbox Mar 20, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant