Skip to content

feat(signals): wrong-repo dismissals feed repo selection - #90022

Open
sortafreel wants to merge 25 commits into
masterfrom
posthog/wrong-repo-dismissal-feedback
Open

feat(signals): wrong-repo dismissals feed repo selection#90022
sortafreel wants to merge 25 commits into
masterfrom
posthog/wrong-repo-dismissal-feedback

Conversation

@sortafreel

Copy link
Copy Markdown
Contributor

Problem

When Self-driving targets the wrong repository, reviewers have no way to say so: the dismiss dialog has no wrong-repo reason, the feedback lands under "Something else", and the selection agent repeats the same wrong pick on the team's next report. Raised in #team-self-driving.

Refs #86091

Changes

  • The dismiss dialog (desktop and web inbox) offers a new reason, "Agent picked the wrong repository", with an optional searchable picker for the repository it should have been.
  • The repo selection agent's prompt now carries the team's recent wrong-repo corrections on every signals-side selection (report pipeline, custom agents, scout emits), so a corrected mistake is not repeated. One entry per report and per distinct selected-to-corrected pair, capped at 20 within 180 days.
  • The state API accepts corrected_repository (only with dismissal_reason='wrong_repo'); the dismissal artefact records the repository the pipeline had selected and the correction.
  • A correction naming a connected repository also becomes the report's newest repo_selection artefact with autostart_eligible=False, so restoring the report re-researches against the right repository without granting auto-start.
  • Scout runs are told wrong_repo is a targeting verdict, not a precision failure, so a scout does not stop filing a valid topic.
  • Mechanical: regenerated OpenAPI and MCP types, setState now uses the generated request type, and the suppress request body is built by one helper per platform.

Old wrong-repo dismissals filed under other/analysis_wrong are not backfilled; the loop starts from the structured code.

No screenshots: the sandbox cannot run the app. The visible change is one new radio option in the existing dismiss dialogs, plus a repository picker that appears when it is selected.

How did you test this code?

  • test_repo_corrections.py (new): catches the block including non-wrong-repo dismissals, a bulk dismissal flooding the block, prompt injection through note or repository fields, and window/malformed-row tolerance.
  • TestSignalReportSuppressionAPI (extended): catches a dropped correction, missing selected-repo denormalization, a correction written for an unconnected repository, an autostart-eligible correction, and corrected_repository validation (format, wrong-reason pairing).
  • test_repo_selection_prompt.py (new): catches the corrections section being dropped from the selection prompt.
  • Ran locally: the tests above, the dataclass and module-collision repo invariants, desktop vitest for dismissal-reasons, desktop package typechecks (shared, api-client, core, ui), the full web typescript:check, ruff, Biome, oxlint, and hogli ci:preflight --fix (clean).
  • Not run: repo-wide mypy (CI runs it) and DB-backed suites beyond the files above. No manual UI testing.

Automatic notifications

  • Publish to changelog?

Docs update

products/signals/ARCHITECTURE.md is updated in this PR (dismissal artefact shape, wrong-repo dismissals, past-corrections injection).

🤖 Agent context

Autonomy: Human-driven (agent-assisted)

  • The driver answered six scoping questions before implementation: optional correction picker; the report stays archived on correction; prompt injection over agent self-lookup; all signals-side selections; tenant guard out of scope; single PR. The driver's GitHub handle was not provided in-session, so no assignee is set.
  • Skills invoked: improving-drf-endpoints, writing-ui-components, writing-user-facing-copy, writing-code-comments, writing-tests, writing-dataclasses, adopting-generated-api-types, writing-pr-descriptions, reviewing-before-pr, code-review.
  • Local review: Greptile was unavailable in this headless sandbox (exit 78), so the harness fallback review ran (8 finder angles plus verification); the bot review stays enabled. Findings fixed in f62155e: corrected selections stamped autostart_eligible=False; per-lesson dedupe against bulk flooding; SQL prefilter, typed Dismissal parse, and repo-shape sanitization in the corrections feed; scout prompt taxonomy; a shared suppress-payload helper; the generated setState type; bulk help_text; docstring style. Findings rejected: widening the connected-repo gate beyond the pipeline's own integration (research clones through that integration, so a wider gate would write selections research cannot use); reusing the quill GitHubRepositoryCombobox inside the Lemon tree (quill is not for frontend/src; server-side search covers long lists); batching the per-report selected-repo lookup in bulk (bounded to wrong-repo dismissals, one indexed query inside per-report transactions); computing the corrections block lazily (one cheap prefiltered query per selection run); migrating the desktop dialog off Radix (the addition is quill/Tailwind-only; the file-wide migration is a separate refactor).
  • Follow-up worth filing against bug report: self-driving pull requests opened against an unrelated repo #86091: a PR-open-time guard that refuses a repository outside the source project's connected integrations, deliberately out of scope here.
  • Public artifact: the work drew on an internal Slack discussion; no thread content is quoted, and all test data is invented.

Created with PostHog Desktop

Adds a first-class wrong_repo dismissal reason with an optional
"which repository should it have been?" picker in the desktop and web
inbox dismiss dialogs. The correction is recorded on the dismissal
artefact (with the repo the pipeline had selected), becomes the
report's latest repo_selection artefact when connected, and recent
corrections are rendered into the repo selection agent's prompt on
every signals-side selection so the same mistake is not repeated.

Generated-By: PostHog Desktop
Task-Id: 70848b5e-fd66-4cf9-a68e-e1551e205f38
Local review findings applied: corrected repo selections carry
autostart_eligible=False; the corrections block dedupes per lesson so a
bulk dismissal cannot flood it, prefilters wrong_repo rows in SQL, parses
via the Dismissal schema, and drops repository values that are not shaped
like owner/repo; the scout prompt taxonomy covers wrong_repo; the web
suppress payload is built in one helper; setState uses the generated
request type.

Generated-By: PostHog Desktop
Task-Id: 70848b5e-fd66-4cf9-a68e-e1551e205f38
@trunk-io

trunk-io Bot commented Aug 27, 2026

Copy link
Copy Markdown

Merging to master in this repository is managed by Trunk.

  • To merge this pull request, check the box to the left or comment /trunk merge below.

After your PR is submitted to the merge queue, this comment will be automatically updated with its status. If the PR fails, failure details will also be posted here

@github-actions

Copy link
Copy Markdown
Contributor

Hey @sortafreel! 👋

It looks like your git author email on this PR isn't your @posthog.com address (woutut@gmail.com). Since you're on the PostHog team, it's worth pointing your local git author email at your @posthog.com address. Why it matters:

  • Consistent work identity in git history — internal tooling that attributes commits to team members keys off your @posthog.com address.
  • Keeps team contributions easy to tell apart from external community ones when scanning history.

You can fix it for this repo with:

git config user.email "you@posthog.com"

Or set it globally with git config --global user.email "you@posthog.com". No need to redo this PR — just a nudge for next time. 🙂

@github-actions

github-actions Bot commented Aug 27, 2026

Copy link
Copy Markdown
Contributor

🤖 CI report

⚠️ Trunk lane — backend Python lane

This PR is assigned to the backend Python lane. It runs backend Python tests and may merge in parallel with PRs in other lanes.

⚠️ Bundle size — 🔺 +132.3 KiB (+0.2%)

Uncompressed size of every built .js bundle, compared against the base branch.

Total: 68.17 MiB · 🔺 +132.3 KiB (+0.2%)

File Size Δ vs base
render-query/src/render-query/render-query.js 21.78 MiB 🔺 +37.0 KiB (+0.2%)
exporter/src/queries/schema.js 1.18 MiB 🔺 +25.0 KiB (+2.1%)
posthog-app/src/queries/schema.js 1.18 MiB 🔺 +25.0 KiB (+2.1%)
posthog-app/_parent/products/replay_vision/frontend/replay_scanners/ScannerEditorScene.js 47.9 KiB 🔺 +10.7 KiB (+28.7%)
posthog-app/_parent/products/error_tracking/frontend/scenes/ErrorTrackingIssueScene/ErrorTrackingIssueScene.js 77.8 KiB 🔺 +10.3 KiB (+15.3%)
posthog-app/_parent/products/engineering_analytics/frontend/scenes/EngineeringAnalyticsScene.js 71.7 KiB 🔺 +9.9 KiB (+16.1%)
posthog-app/src/scenes/AuthenticatedShell.js 220.3 KiB 🔺 +3.5 KiB (+1.6%)
posthog-app/_parent/products/customer_analytics/frontend/CustomerAnalyticsScene.js 266.1 KiB 🔺 +2.7 KiB (+1.0%)
posthog-app/_parent/products/signals/frontend/inbox/InboxScene.js 759.9 KiB 🔺 +1.6 KiB (+0.2%)
posthog-app/_parent/products/metrics/frontend/MetricsScene.js 67.8 KiB 🔺 +1.6 KiB (+2.4%)
posthog-app/_parent/products/feature_flags/frontend/staff/FeatureFlagsStaffToolsScene.js 49.9 KiB 🔺 +1.5 KiB (+3.2%)
posthog-app/src/scenes/models/NodeDetailScene.js 15.6 KiB 🟢 -1.5 KiB (-8.5%)
posthog-app/src/scenes/startups/StartupProgram.js 233.4 KiB 🔺 +1.3 KiB (+0.5%)

Posted automatically by build-bundle-size-report · uncompressed bytes from dist-report

Eager graph — within budget

How much code each root ships on the eager path — downloaded and parsed before the surface is interactive. Measured from the esbuild output chunks (post-tree-shake, static imports only); lazy import() / React.lazy chunks are not counted.

Root Eager (shipped) Δ vs base Budget
entry (logged-out pages, app bootstrap)
src/index.tsx
1.37 MiB · 22 files 🔺 +479 B (+0.0%) ███░░░░░░░ 30.3% of 4.51 MiB
authenticated shell (every logged-in page)
src/scenes/AuthenticatedShell.tsx
8.82 MiB · 3,247 files 🔺 +14.0 KiB (+0.2%) █████████░ 90.8% of 9.71 MiB

🟢 node_modules/monaco-editor/ stays out of src/index.tsx
🟢 src/lib/components/ActivityLog/describers stays out of src/index.tsx
🟢 [object Object] stays out of src/index.tsx
🟢 [object Object] stays out of src/index.tsx
🟢 node_modules/monaco-editor/ stays out of src/scenes/AuthenticatedShell.tsx
🟢 src/lib/components/ActivityLog/describers stays out of src/scenes/AuthenticatedShell.tsx
🟢 [object Object] stays out of src/scenes/AuthenticatedShell.tsx
🟢 [object Object] stays out of src/scenes/AuthenticatedShell.tsx

Largest files eagerly shipped from src/index.tsx
Size File
126.8 KiB ../node_modules/.pnpm/react-dom@18.3.1_react@18.3.1/node_modules/react-dom/cjs/react-dom.production.min.js
24.6 KiB ../node_modules/.pnpm/buffer@6.0.3/node_modules/buffer/index.js
6.3 KiB ../node_modules/.pnpm/react@18.3.1/node_modules/react/cjs/react.production.min.js
4.5 KiB ../node_modules/.pnpm/@jspm+core@2.1.0/node_modules/@jspm/core/nodelibs/browser/process.js
3.9 KiB ../node_modules/.pnpm/scheduler@0.23.2/node_modules/scheduler/cjs/scheduler.production.min.js
1.4 KiB ../node_modules/.pnpm/base64-js@1.5.1/node_modules/base64-js/index.js
1.3 KiB src/RootErrorBoundary.tsx
912 B ../node_modules/.pnpm/ieee754@1.2.1/node_modules/ieee754/index.js
789 B src/scenes/ChunkLoadErrorBoundary.tsx
762 B src/index.tsx
Largest files eagerly shipped from src/scenes/AuthenticatedShell.tsx
Size File
307.0 KiB ../node_modules/.pnpm/posthog-js@1.422.2_@types+react@18.3.27_react@18.3.1/node_modules/posthog-js/dist/rrweb.js
267.7 KiB ../node_modules/.pnpm/@posthog+icons@0.38.0_react-dom@18.3.1_react@18.3.1__react@18.3.1/node_modules/@posthog/icons/dist/posthog-icons.es.js
263.4 KiB ../node_modules/.pnpm/posthog-js@1.422.2_@types+react@18.3.27_react@18.3.1/node_modules/posthog-js/dist/module.js
252.5 KiB src/taxonomy/core-filter-definitions-by-group.json
154.2 KiB ../node_modules/.pnpm/re2js@0.4.1/node_modules/re2js/build/index.esm.js
126.8 KiB ../node_modules/.pnpm/react-dom@18.3.1_react@18.3.1/node_modules/react-dom/cjs/react-dom.production.min.js
104.7 KiB src/lib/api.ts
95.2 KiB ../packages/quill/packages/quill/dist/index.js
93.3 KiB ../node_modules/.pnpm/prosemirror-view@1.40.1/node_modules/prosemirror-view/dist/index.js
90.6 KiB ../node_modules/.pnpm/@tiptap+core@3.20.6_@tiptap+pm@3.20.6/node_modules/@tiptap/core/dist/index.js

Posted automatically by check-eager-graph · sizes are eager output bytes (shipped, post-tree-shake) from the esbuild metafile · part of #32479

Toolbar bundle — eager 2.26 MiB within budget

What the toolbar ships to customer pages, measured from the esbuild output (minified, post-tree-shake). The eager set is the entry plus everything statically imported from it — fetched before any feature runs; deferred chunks load lazily. The eager guardrail is 5.72 MiB. Each output file must also stay below 10 MB, where CloudFront stops compressing it. The module boundary is enforced separately by check-toolbar-graph.

Metric Size Δ vs base Budget
Eager (shipped)
entry + static imports
2.26 MiB · 18 files 🔺 +3.4 KiB (+0.1%) ████░░░░░░ 39.5% of 5.72 MiB
Deferred (lazy) 2.11 MiB · 45 files 🔺 +22 B (+0.0%) n/a — loads on demand
Loader dist/toolbar.js 1.1 KiB no change █░░░░░░░░░ 5.8% of 19.5 KiB
Largest eagerly-shipped chunks
Size File
749.3 KiB dist/toolbar/toolbar-app-J2MBJBF4.css
588.4 KiB dist/toolbar/chunk-chunk-PW7Z7VPL.js
484.7 KiB dist/toolbar/chunk-chunk-OX4ECFTJ.js
133.8 KiB dist/toolbar/chunk-chunk-QSEPKPQ6.js
131.8 KiB dist/toolbar/chunk-chunk-FDH2IBXT.js
71.3 KiB dist/toolbar/toolbar-app-WJLWPLBY.js
69.0 KiB dist/toolbar/chunk-chunk-TSAL54PB.js
35.6 KiB dist/toolbar/chunk-chunk-MALK2EVS.js
20.9 KiB dist/toolbar/chunk-chunk-LRHBPUK5.js
6.8 KiB dist/toolbar/chunk-chunk-DV7IWQNF.js

Posted automatically by check-toolbar-size · sizes are toolbar output bytes (shipped, post-tree-shake) from the esbuild metafile

Dist folder size — 🟢 -14.34 MiB (-1.0%)

Total size of the built frontend/dist folder (all assets), compared against the base branch.

Total: 1435.37 MiB · 🟢 -14.34 MiB (-1.0%)

ℹ️ MCP UI apps size — 33 app(s), 17662.9 KB JS

Built size of each MCP UI app (main.js + styles.css).

App JS CSS
debug 600.0 KB 195.2 KB
action 458.2 KB 195.2 KB
action-list 564.9 KB 195.2 KB
cohort 457.2 KB 195.2 KB
cohort-list 563.8 KB 195.2 KB
email-template 457.0 KB 195.2 KB
error-details 472.9 KB 195.2 KB
error-issue 457.9 KB 195.2 KB
error-issue-list 564.7 KB 195.2 KB
experiment 562.0 KB 195.2 KB
experiment-list 565.6 KB 195.2 KB
experiment-results 567.1 KB 195.2 KB
feature-flag 567.6 KB 195.2 KB
feature-flag-list 571.4 KB 195.2 KB
feature-flag-testing 461.4 KB 195.2 KB
inline-scan 457.7 KB 195.2 KB
insight-actors 563.0 KB 195.2 KB
invite-email-preview 456.4 KB 195.2 KB
llm-costs 560.0 KB 195.2 KB
session-recording 459.0 KB 195.2 KB
survey 458.8 KB 195.2 KB
survey-global-stats 562.7 KB 195.2 KB
survey-list 565.5 KB 195.2 KB
survey-stats 562.7 KB 195.2 KB
trace-span 457.6 KB 195.2 KB
trace-span-list 564.7 KB 195.2 KB
vision-observation-list 563.9 KB 195.2 KB
workflow 457.5 KB 195.2 KB
workflow-list 564.2 KB 195.2 KB
loops-review 461.9 KB 195.2 KB
query-results 755.2 KB 195.2 KB
render-ui 838.3 KB 195.2 KB
visual-review-snapshots 462.0 KB 195.2 KB
Playwright — all passed

All tests passed.

View test results →

⚠️ Backend coverage — 96.0% of changed backend lines covered — 10 uncovered

🧪 Backend test coverage

Patch coverage — changed backend lines (products + core): ███████████████████░ 96.0% (258 / 268)

File Patch Uncovered changed lines
products/tasks/backend/logic/repo_selection/agent.py 66.7% 506
products/signals/backend/repo_corrections.py 90.9% 101–102, 104, 113, 140–142
products/signals/backend/views.py 95.0% 2022, 2024

🤖 Agents: add a test covering the lines above, or note why under "How did you test this code?". Machine-readable gap list: the patch-coverage artifact on this run (gh run download 33180089150 -n patch-coverage), or the coverage-data block at the end of this comment.

Per-product line coverage (touched products)
Product Coverage Lines
platform_features ██░░░░░░░░░░░░░░░░░░ 12.1% 7 / 58
warehouse_sources_queue ██████░░░░░░░░░░░░░░ 29.4% 91 / 309
demo ███████████░░░░░░░░░ 56.9% 1,518 / 2,666
data_tools █████████████░░░░░░░ 66.2% 90 / 136
ai_gateway ███████████████░░░░░ 75.0% 9 / 12
batch_exports ████████████████░░░░ 79.8% 19,631 / 24,593
apm █████████████████░░░ 83.3% 1,234 / 1,481
signals █████████████████░░░ 84.0% 31,021 / 36,915
cdp █████████████████░░░ 85.2% 4,184 / 4,912
actions █████████████████░░░ 86.6% 716 / 827
wizard █████████████████░░░ 86.7% 1,090 / 1,257
data_modeling █████████████████░░░ 86.7% 9,654 / 11,133
notebooks ██████████████████░░ 87.7% 9,175 / 10,458
product_tours ██████████████████░░ 87.9% 1,303 / 1,482
cohorts ██████████████████░░ 88.4% 7,380 / 8,345
data_warehouse ██████████████████░░ 88.6% 12,759 / 14,399
business_knowledge ██████████████████░░ 89.1% 4,440 / 4,984
dashboards ██████████████████░░ 89.1% 6,390 / 7,170
tasks ██████████████████░░ 89.9% 61,723 / 68,682
streamlit_apps ██████████████████░░ 90.2% 2,500 / 2,773
exports ██████████████████░░ 90.2% 8,509 / 9,434
engineering_analytics ██████████████████░░ 90.3% 7,511 / 8,320
links ██████████████████░░ 90.6% 183 / 202
managed_warehouse ██████████████████░░ 90.7% 8,553 / 9,426
mcp_analytics ██████████████████░░ 90.9% 4,175 / 4,595
visual_review ██████████████████░░ 90.9% 6,880 / 7,567
conversations ██████████████████░░ 90.9% 20,694 / 22,760
alerts ██████████████████░░ 91.2% 5,216 / 5,719
error_tracking ██████████████████░░ 91.6% 12,601 / 13,758
notifications ██████████████████░░ 92.0% 1,075 / 1,168
canvas ██████████████████░░ 92.1% 4,693 / 5,096
early_access_features ███████████████████░ 92.5% 1,323 / 1,430
managed_migrations ███████████████████░ 92.6% 1,556 / 1,681
stamphog ███████████████████░ 92.6% 5,510 / 5,951
slack_app ███████████████████░ 92.6% 12,202 / 13,173
web_analytics ███████████████████░ 93.0% 17,634 / 18,969
context_layer ███████████████████░ 93.1% 2,313 / 2,485
mcp_store ███████████████████░ 93.3% 7,534 / 8,078
surveys ███████████████████░ 93.3% 6,006 / 6,436
reminders ███████████████████░ 93.4% 468 / 501
posthog_ai ███████████████████░ 93.5% 1,372 / 1,468
marketing_analytics ███████████████████░ 93.5% 17,263 / 18,467
ai_observability ███████████████████░ 93.7% 17,982 / 19,188
approvals ███████████████████░ 93.7% 3,492 / 3,725
product_analytics ███████████████████░ 93.8% 7,654 / 8,164
billing_alerts ███████████████████░ 93.9% 2,078 / 2,212
legal_documents ███████████████████░ 94.1% 1,970 / 2,093
workflows ███████████████████░ 94.3% 9,759 / 10,354
review_hog ███████████████████░ 94.3% 9,973 / 10,574
tracing ███████████████████░ 94.4% 2,678 / 2,837
endpoints ███████████████████░ 94.4% 8,920 / 9,448
customer_analytics ███████████████████░ 95.0% 17,530 / 18,452
annotations ███████████████████░ 95.2% 795 / 835
experiments ███████████████████░ 95.5% 29,265 / 30,654
replay_vision ███████████████████░ 95.6% 24,892 / 26,047
skills ███████████████████░ 95.6% 5,228 / 5,470
messaging ███████████████████░ 95.8% 3,591 / 3,750
logs ███████████████████░ 95.9% 13,457 / 14,039
growth ███████████████████░ 95.9% 7,790 / 8,121
feature_flags ███████████████████░ 96.1% 19,418 / 20,197
access_control ███████████████████░ 96.3% 5,449 / 5,660
revenue_analytics ███████████████████░ 96.3% 1,841 / 1,911
user_interviews ███████████████████░ 96.5% 2,639 / 2,735
data_quality ███████████████████░ 97.0% 3,685 / 3,799
warehouse_sources ███████████████████░ 97.2% 403,042 / 414,728
analytics_platform ████████████████████ 98.1% 2,286 / 2,330
data_catalog ████████████████████ 98.2% 3,140 / 3,198
metrics ████████████████████ 98.3% 3,453 / 3,511
pulse ████████████████████ 98.4% 2,017 / 2,049
live_debugger ████████████████████ 99.2% 613 / 618
field_notes ████████████████████ 99.4% 158 / 159

Report-only. Patch coverage = changed backend lines covered vs origin/master. Sorted lowest first.
Known gaps: lines covered only by Temporal tests show as uncovered; core line numbers may drift if master changed the same file.

⚠️ MCP snapshots — 2 updated (2 modified, 0 added, 0 deleted)

Snapshots: MCP unit test snapshots updated

Changes: 2 snapshots (2 modified, 0 added, 0 deleted)

What this means:

  • Snapshots have been automatically updated to match current output

Next steps:

  • Review the changes to ensure they're intentional
  • If unexpected, investigate what caused the output to change

Review snapshot changes →

⚠️ Django migration SQL — 1 new migration to review

We've detected new migrations on this PR. Review the SQL output for each migration:

products/signals/backend/migrations/0108_signalreportartefact_team_type_recent_idx.py

--
-- Concurrently create index signals_sig_team_type_ct_idx on field(s) team, type, -created_at of model signalreportartefact
--
SET lock_timeout = 0;
SET statement_timeout = 0;
CREATE INDEX CONCURRENTLY "signals_sig_team_type_ct_idx" ON "signals_signalreportartefact" ("team_id", "type", "created_at" DESC);

Last updated: 2026-08-28 14:29 UTC (e6a27f5)

Django migration risk — migration analysis complete

We've analyzed your migrations for potential risks.

Summary: 1 Safe | 0 Needs Review | 0 Blocked

✅ Safe

Brief or no lock, backwards compatible

signals.0108_signalreportartefact_team_type_recent_idx
  └─ #1 ✅ SafeAddIndexConcurrently
     PostHog concurrent-index helper: idempotent (timeout disabling + invalid-leftover recovery)
     model: signalreportartefact, index: signals_sig_team_type_ct_idx

Last updated: 2026-08-28 14:30 UTC (e6a27f5)

@github-actions

github-actions Bot commented Aug 27, 2026

Copy link
Copy Markdown
Contributor

React Doctor found 4 issues in 2 files · 1 error & 3 warnings.

Errors

3 warnings

packages/ui/src/features/inbox/components/DismissReportDialog.tsx

packages/ui/src/features/inbox/hooks/useInboxBulkActions.ts

Reviewed by React Doctor for commit e6a27f5.

@hosthog

hosthog Bot commented Aug 27, 2026

Copy link
Copy Markdown

HostHog preview — posthog-desktop-web

Latest build (e6a27f5): https://2649034b5be14de8b9ec7e4d520b9054.hosthog.dev

Earlier builds of this PR, still serving:

Employee-gated; every push gets a fresh URL whose content never changes. All previews stop serving when the PR closes.

@trunk-io

trunk-io Bot commented Aug 27, 2026

Copy link
Copy Markdown

Static BadgeStatic BadgeStatic Badge

View Full Report ↗︎Docs

@sortafreel sortafreel added the reviewhog ($$$) Reviews pull requests before humans do label Aug 27, 2026
@posthog

posthog Bot commented Aug 27, 2026

Copy link
Copy Markdown
Contributor

🦔 PostHog Review reviewed this pull request

Found 1 must fix, 3 should fix, 2 consider.

Published 6 findings (view the review).

Resolved comments: 4 fixed, 2 left for you

@posthog

posthog Bot commented Aug 27, 2026

Copy link
Copy Markdown
Contributor

PostHog Review alpha 🦔 If you find any issues helpful - please reply "valid", "invalid", etc., for evaluation purposes 🙏

@posthog posthog Bot left a comment

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.

PostHog Review

Found 3 must fix, 8 should fix, 6 consider.

Comment thread products/signals/frontend/generated/api.zod.ts Outdated
Comment thread products/signals/backend/report_generation/select_repo.py
Comment thread products/signals/backend/repo_corrections.py
Comment thread products/signals/backend/views.py Outdated
Comment thread products/signals/backend/views.py
Comment thread products/signals/frontend/inbox/components/shell/DismissCorrectedRepoField.tsx Outdated
Comment thread products/signals/ARCHITECTURE.md Outdated
Comment thread products/signals/ARCHITECTURE.md Outdated
@posthog posthog Bot removed the reviewhog ($$$) Reviews pull requests before humans do label Aug 27, 2026
posthog Bot added 7 commits August 27, 2026 18:30
Add min_length=1 to the corrected_repository field so the non-empty
constraint surfaces as minLength: 1 in the generated OpenAPI/Zod/MCP
schemas, matching the convention already documented on the sibling
title/summary fields. Server behavior is unchanged: allow_blank=False
already rejects empty strings, so this only raises the fidelity of the
published contract for clients and MCP tools.

Regenerated the frontend schemas, Zod, and MCP types.

Generated-By: PostHog Desktop
Task-Id: 6b59a8d9-cda3-4f6c-9150-8e5a28cb3053
The desktop dismiss dialog gated the repository query on the picker being
open. On a cold cache the picker renders a disabled "No GitHub repos"
trigger when its list is empty, which cannot be opened, so the query was
never enabled and the picker was permanently stuck. Enable the query on
the wrong-repo reason alone, matching the house pattern used by the cloud
repo picker, and keep the open state only for the popover.

Add a regression test that selects the wrong-repo reason with the picker
closed and asserts the openable trigger renders instead of the dead-end
disabled button.

Generated-By: PostHog Desktop
Task-Id: 6b59a8d9-cda3-4f6c-9150-8e5a28cb3053
The web dismiss dialog's repository picker read only repositoryNames and
loading from githubRepositorySearchLogic, so a failed lookup fell through
to LemonInputSelect's generic "No options" message, which reads as an
account with no repositories. Read the logic's error and pass it through
emptyStateComponent so the failure is visible; typing already clears the
error and retries. Mirrors how the quill sibling picker surfaces the same
error.

Generated-By: PostHog Desktop
Task-Id: 6b59a8d9-cda3-4f6c-9150-8e5a28cb3053
The dismissal_reason help text told callers to use 'wrong_repo' when the
report "or its PR" targets the wrong repository, but the server always
records the report's latest repo_selection, not a PR's repository. The
"or its PR" case is not modeled, and the text lands verbatim in the MCP
tool schema, so an agent could read it literally. Reword to describe the
repository the agent selected for the report, matching the inbox label
"Agent picked the wrong repository".

Regenerated the frontend and MCP schemas.

Generated-By: PostHog Desktop
Task-Id: 6b59a8d9-cda3-4f6c-9150-8e5a28cb3053
LemonInputSelect selects the highlighted repository on Enter but lets the
keydown bubble. The surrounding LemonFormDialog attaches its submit-on-Enter
handler to the form element, and it reads the form snapshot from the current
render, so it fired on the same Enter with the pre-selection state and
archived the report with corrected_repository unset, discarding the
reviewer's choice.

Wrap the picker so an Enter keydown stops propagating before it reaches the
form's handler. Enter now only selects the repository; the reviewer submits
with the button, so the correction is included.

Generated-By: PostHog Desktop
Task-Id: 6b59a8d9-cda3-4f6c-9150-8e5a28cb3053
The architecture doc and two code comments said restoring a wrong-repo
report re-researches against the corrected repository. Restore only returns
the report to its prior status; it starts no research. The corrected
repository is the newest repo_selection, so the report's next research run,
once new signals re-promote it, targets it. Reword all three so a reader
does not assume restore self-heals and skip building the missing step.

No behavior change; the correction is written and reused as before.

Generated-By: PostHog Desktop
Task-Id: 6b59a8d9-cda3-4f6c-9150-8e5a28cb3053
The architecture note justified dropping an unconnected correction's
selection override with "research cannot clone a repository the team has
not connected". That reason does not hold: signals research resolves the
GitHub source with the owner personal-connection fallback, so it can clone
repositories the team-level gate does not recognize. State the actual gate
instead: the override is written only for a repository connected through
the team's own GitHub integration.

Documentation only; no gate behavior changes here. Whether the gate should
match the wider source research uses is a separate, authorization-sensitive
decision left out of this change.

Generated-By: PostHog Desktop
Task-Id: 6b59a8d9-cda3-4f6c-9150-8e5a28cb3053
@sortafreel
sortafreel marked this pull request as ready for review August 27, 2026 21:33
Copilot AI lite review requested due to automatic review settings August 27, 2026 21:33
@github-actions
github-actions Bot requested a deployment to preview-pr-90022 August 27, 2026 21:34 In progress
@github-actions

github-actions Bot commented Aug 27, 2026

Copy link
Copy Markdown
Contributor

🦔 Hogbox preview · ✅ ready

▶ Open the preview

🔑 Login test@posthog.com / 12345678 (demo data)
🧩 Running this PR's backend and frontend, on the PostHog :master base
🔗 Link stable across rebuilds — a re-push swaps the box underneath, the URL stays
🔒 Access tailnet only (PostHog VPN)
🛠️ Admin inspect & debug state in hogland
💤 Idle sleeps after ~30 min idle (snapshot to S3, zero node cost) and wakes on your next visit in ~30s, behind a brief "waking up" screen

commit e6a27f5 · box box-41cdda087a98 · ready in 697s (push → usable) · build log · rebuilds on every push, torn down on close

@pr-assigner-resolver-posthog
pr-assigner-resolver-posthog Bot requested a review from a team August 27, 2026 21:34
@greptile-apps

greptile-apps Bot commented Aug 27, 2026

Copy link
Copy Markdown
Contributor
Prompt To Fix All With AI
### Issue 1
products/signals/frontend/inbox/components/shell/DismissCorrectedRepoField.tsx:18
**Correction integration mismatch**

If a team has multiple GitHub integrations or its first listed integration is unavailable, this picker searches `githubIntegrations[0]` while the backend validates the correction against its separately resolved healthy team integration. The dismissal succeeds, but a repository available only through the picker’s integration is not installed as the report’s latest `repo_selection`, so restoring the report continues using the old repository.

---

For each issue above, determine whether it is valid and should be fixed. If so, fix it directly.

Reviews (1): Last reviewed commit: "docs(signals): correct unconnected-corre..." | Re-trigger Greptile

Copilot AI left a comment

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.

Pull request overview

Adds a structured “wrong repository” dismissal path to Signals inbox reports and feeds reviewer corrections back into the repository selection agent, so repeated mis-targeting can be corrected once and learned across future report/agent/scout selections.

Changes:

  • Extends Signals report state transition API with dismissal_reason='wrong_repo' and optional corrected_repository (validated and recorded on dismissal artefacts; optionally updates latest repo_selection when connected).
  • Injects a “Past selection corrections” block into the tasks-side repo selection prompt, sourced from recent wrong-repo dismissals (deduped/capped/windowed).
  • Updates web + desktop inbox dismiss flows to capture the new reason and optional repo correction, and regenerates downstream OpenAPI/MCP/client types.

Reviewed changes

Copilot reviewed 36 out of 41 changed files in this pull request and generated 4 comments.

Show a summary per file
File Description
services/mcp/tests/unit/snapshots/tool-schemas/inbox-reports-set-state.json Updates MCP tool schema snapshot for new wrong_repo reason and corrected_repository field.
services/mcp/tests/unit/snapshots/tool-schemas/inbox-reports-bulk-set-state.json Updates bulk MCP tool schema snapshot to include corrected_repository and reason enum.
services/mcp/src/tools/generated/signals.ts Adds corrected_repository to generated MCP tool request bodies.
services/mcp/src/generated/signals/api.ts Regenerates Signals Zod schemas/types with wrong_repo and corrected_repository.
services/mcp/src/api/generated.ts Regenerates shared API schema types/enums for dismissal reasons and corrected repository.
products/tasks/backend/tests/test_repo_selection_prompt.py Adds unit test ensuring corrections block insertion behavior in prompt builder.
products/tasks/backend/logic/repo_selection/agent.py Extends repo selection prompt builder + select_repository to accept optional past_corrections.
products/signals/frontend/inbox/utils/dismissalReasons.ts Adds wrong_repo, introduces DismissalFeedback, and centralizes suppress payload construction.
products/signals/frontend/inbox/types.ts Removes now-redundant handwritten SignalReportStateRequest in favor of generated type usage.
products/signals/frontend/inbox/logics/reportListLogic.ts Switches archive flow to DismissalFeedback and shared suppress payload helper.
products/signals/frontend/inbox/logics/inboxTriageLogic.ts Passes structured dismissal (incl. corrected repo) through triage-mode dismissal + analytics.
products/signals/frontend/inbox/logics/inboxBulkActionsLogic.ts Updates bulk dismiss to use DismissalFeedback and shared suppress payload helper.
products/signals/frontend/inbox/components/shell/InboxBulkSelectionBar.tsx Wires bulk dismiss dialog confirmation to pass full dismissal feedback object.
products/signals/frontend/inbox/components/shell/DismissReportDialog.tsx Adds “wrong repo” reason UI behavior and conditional corrected-repo field + updated submit contract.
products/signals/frontend/inbox/components/shell/DismissCorrectedRepoField.tsx New web corrected-repo picker field backed by GitHub repo search logic.
products/signals/frontend/inbox/components/InboxReportList.tsx Updates card archive callback signature to accept DismissalFeedback.
products/signals/frontend/inbox/components/cards/useReportArchive.ts Updates archive hook to capture corrected-repo analytics and use shared suppress payload helper.
products/signals/frontend/inbox/components/cards/ReportCard.tsx Updates optional archive callback type to accept DismissalFeedback.
products/signals/frontend/generated/api.zod.ts Regenerates product frontend Zod schemas for corrected repository and wrong-repo reason.
products/signals/frontend/generated/api.ts Regenerates product frontend API client typings/docs for corrected repository and wrong-repo reason.
products/signals/frontend/generated/api.schemas.ts Regenerates product frontend schema types/enums for corrected repository and wrong-repo reason.
products/signals/backend/views.py Adds wrong_repo dismissal reason, validates/normalizes corrected_repository, records it, and optionally appends corrected repo_selection.
products/signals/backend/test/test_signal_report_api.py Extends API tests for corrected repository validation + wrong-repo recording/connected behavior.
products/signals/backend/test/test_repo_corrections.py New tests for correction-block building and chokepoint threading into repo selection.
products/signals/backend/scout_harness/prompt.py Clarifies scout taxonomy: wrong_repo is targeting feedback, not precision failure.
products/signals/backend/report_generation/select_repo.py Threads the corrections block into every signals-side repo selection chokepoint.
products/signals/backend/repo_corrections.py New module: queries recent wrong-repo dismissals, dedupes/caps, sanitizes, renders prompt block.
products/signals/backend/receivers.py Adds dismissal_corrected_repository to captured analytics fields mapping.
products/signals/backend/artefact_schemas.py Extends dismissal artefact schema with selected/corrected repository fields + shared reason constant.
products/signals/ARCHITECTURE.md Documents the wrong-repo dismissal flow and the selection prompt corrections injection behavior.
products/desktop/packages/ui/src/features/inbox/hooks/useInboxBulkActions.ts Uses shared suppress request builder to include corrected repository when dismissing.
products/desktop/packages/ui/src/features/inbox/components/DismissReportDialog.tsx Adds desktop UI corrected-repo picker for wrong_repo dismissals and passes it on confirm.
products/desktop/packages/ui/src/features/inbox/components/DismissReportDialog.test.tsx Adds vitest to ensure repo picker can open on cold cache once wrong-repo is selected.
products/desktop/packages/shared/src/dismissal-reasons.ts Adds wrong_repo reason option to shared desktop reasons list.
products/desktop/packages/shared/src/analytics-events.ts Extends inbox analytics event properties with dismissal_corrected_repository.
products/desktop/packages/core/src/inbox/engagement.ts Emits corrected repository value into bulk dismissal analytics events.
products/desktop/packages/core/src/inbox/bulkActions.ts Extends suppress request builder to optionally include corrected repository.
products/desktop/packages/api-client/src/posthog-client.ts Regenerates API client types to include corrected_repository in update state request.
products/customer_analytics/frontend/components/Feed/FeedTabContent.tsx Updates archive callback signature to pass DismissalFeedback through to feed logic.
products/customer_analytics/frontend/components/Feed/feedLogic.ts Uses shared suppress payload helper and DismissalFeedback for feed dismissal actions.
frontend/src/lib/api.ts Switches signalReports.setState to accept generated SignalReportStateRequestApi type.

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment thread products/signals/frontend/inbox/utils/dismissalReasons.ts
Comment thread products/desktop/packages/core/src/inbox/bulkActions.ts Outdated
Comment thread products/signals/backend/repo_corrections.py Outdated
Comment thread products/signals/backend/test/test_repo_corrections.py
Comment thread products/signals/backend/repo_corrections.py
@veria-ai

veria-ai Bot commented Aug 27, 2026

Copy link
Copy Markdown
Contributor

PR overview

All previously flagged issues have been addressed. No open security concerns remain on this pull request.

Security review

No open security issues remain on this pull request.

Fixed/addressed: 1 · PR risk: 0/10

Review batch from PR triage: both dismissal payload builders (web and desktop) now send corrected_repository only with the wrong_repo reason, and the corrections prompt feed lowercases repository names before shape-checking so they match the lowercased candidate list and dedupe case-insensitively.

Generated-By: PostHog Desktop
Task-Id: 70848b5e-fd66-4cf9-a68e-e1551e205f38

@posthog posthog Bot left a comment

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.

PostHog Review

Found 2 should fix, 1 consider.

Comment thread products/signals/backend/repo_corrections.py
Comment thread products/signals/backend/views.py
The quill combobox portals its popup outside Dialog.Content, so Radix treated popup clicks as outside-the-dialog and closed it, dropping the reason and note already entered. While the picker is open, outside clicks and Escape now close only the popup; the guard cancels Radix's own dismiss path, which also closes the pre-existing hole where the dialog could be dismissed mid-submit.

Generated-By: PostHog Desktop
Task-Id: 70848b5e-fd66-4cf9-a68e-e1551e205f38
Research reuses a report's newest repo_selection whenever it names a repository, so a wrong-repo dismissal without a usable correction left the rejected pick as the reusable row: a restored report went straight back to the repository the reviewer rejected, auto-start grant included. Such dismissals now append a cleared selection (repository=None, autostart_eligible=False), forcing the next run to re-select with the corrections feed in its prompt. Also rewrites the web picker comment that wrongly claimed the picker mirrors the backend's integration resolution.

Generated-By: PostHog Desktop
Task-Id: 70848b5e-fd66-4cf9-a68e-e1551e205f38
A research run resolves its repo selection at the start and re-persists that value when it finishes, so a wrong-repo dismissal landing mid-run was overwritten latest-wins, restoring the rejected repository and its auto-start grant. The workflow now captures a replay-safe timestamp before resolving the selection; at persist time the run skips re-appending its value when a person wrote a newer repo_selection after that point, and the stale value stops granting auto-start for that run. The new input field defaults to None so in-flight workflow histories replay with the guard off.

Generated-By: PostHog Desktop
Task-Id: 70848b5e-fd66-4cf9-a68e-e1551e205f38
The corrections feed queries artefacts team-wide (team, dismissal type, recent window) once per repository selection, and every existing index is report-anchored, so the query scanned a team's whole artefact history. Adds signals_sig_team_type_ct_idx through SafeAddIndexConcurrently, the same non-blocking shape as this app's 0090 precedent.

Generated-By: PostHog Desktop
Task-Id: 70848b5e-fd66-4cf9-a68e-e1551e205f38
Report deletion is a soft delete, and every other read path stops serving a deleted report's content — the corrections feed now does too, so a deleted report's title and reviewer note no longer reach the repo-selection prompt. Also updates the bundled inbox-exploration skill to the current dismissal contract: seven reason codes including wrong_repo, and the corrected_repository field on both the single and bulk state calls.

Generated-By: PostHog Desktop
Task-Id: 70848b5e-fd66-4cf9-a68e-e1551e205f38
@posthog

posthog Bot commented Aug 28, 2026

Copy link
Copy Markdown
Contributor

👋 Visual changes detected for this PR.

Review and approve in PostHog Visual Review

If these changes are unexpected, they may be caused by a flaky test or a broken snapshot on master. Don't approve — rerun the job or wait for a fix.

@posthog posthog Bot left a comment

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.

PostHog Review

Found 1 must fix, 3 should fix, 2 consider.

Comment thread services/mcp/src/tools/generated/signals.ts
Comment on lines +613 to 619
archiveReport: async ({ reportId, dismissal }) => {
actions.removeReport(reportId)
try {
await api.signalReports.setState(reportId, {
state: 'suppressed',
dismissal_reason: reason,
...(note ? { dismissal_note: note } : {}),
...suppressDismissalPayload(dismissal),
})

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.

“Already fixed” permanently suppresses reports

consider bug

Why we think it's a valid issue
  • Checked: every web suppress call site (products/signals/frontend/inbox/logics/reportListLogic.ts:616, products/signals/frontend/inbox/components/cards/useReportArchive.ts:65, products/signals/frontend/inbox/logics/inboxBulkActionsLogic.ts:155, products/customer_analytics/frontend/components/Feed/feedLogic.ts:270), the dialog that feeds them, the desktop equivalent, and the backend serializer.
  • Found: all four web paths post state: 'suppressed', and the web inbox has no snooze branch at all — isDismissalReasonSnooze is read only for the icon and tooltip at products/signals/frontend/inbox/components/shell/DismissReportDialog.tsx:34, which promises "it briefly leaves your inbox ... and it can come back if new signals match" (line 26).
  • Found: desktop routes the same reason code to a real snooze — products/desktop/packages/ui/src/features/inbox/hooks/useInboxReportDismissAction.tsx:45 branches on isDismissalReasonSnooze, and products/desktop/packages/core/src/inbox/bulkActions.ts:171 returns { state: "potential", snooze_for: 1 }. So the two clients disagree on what "Already fixed" does.
  • Found: the reviewer's backend premise is overstated. products/signals/backend/views.py:470 only advises reserving already_fixed for state='potential'; dismissal_reason is a plain ChoiceField (line 516) that the API accepts with any state. Nothing is rejected — the defect is UI copy against behavior, not a contract break.
  • Impact: a web user who picks "Already fixed" gets a permanent archive instead of the snooze the tooltip promises, and the suppression also closes the linked implementation PR through the post_save receiver noted at products/signals/backend/views.py:2220. The report never returns on new signals.
  • Priority: lowered to consider. The behavior is pre-existing, not introduced here: git show origin/master:...reportListLogic.ts already posted state: 'suppressed' with the raw reason, and master's dialog already carried the pause icon and snooze tooltip. This diff only routes the same fields through suppressDismissalPayload. The misleading promise also sits in a hover tooltip, while the dialog's own title and body say "Archive report" and "This report will be archived out of your inbox". The fix adds a snooze path the web client does not have yet and reaches into the customer analytics feed, so it earns its own change rather than a drive-by in a wrong-repo feedback PR.
Issue description

The dialog marks already_fixed as a temporary snooze. This listener always sends state: 'suppressed'. The other archive paths do the same. The backend reserves already_fixed for state: 'potential'. The web client therefore permanently archives a report after promising a temporary snooze.

Suggested fix

Build the full state request in one shared helper. Send state: 'potential' with snooze_for: 1 for snooze reasons. Send state: 'suppressed' for other reasons. Use this helper in all single, bulk, triage, and customer analytics paths.

Prompt to fix with AI (copy-paste)
## Context
@products/signals/frontend/inbox/logics/reportListLogic.ts#L613-619

<issue_description>
The dialog marks `already_fixed` as a temporary snooze. This listener always sends `state: 'suppressed'`. The other archive paths do the same. The backend reserves `already_fixed` for `state: 'potential'`. The web client therefore permanently archives a report after promising a temporary snooze.
</issue_description>

<issue_validation>
- **Checked:** every web suppress call site (`products/signals/frontend/inbox/logics/reportListLogic.ts:616`, `products/signals/frontend/inbox/components/cards/useReportArchive.ts:65`, `products/signals/frontend/inbox/logics/inboxBulkActionsLogic.ts:155`, `products/customer_analytics/frontend/components/Feed/feedLogic.ts:270`), the dialog that feeds them, the desktop equivalent, and the backend serializer.
- **Found:** all four web paths post `state: 'suppressed'`, and the web inbox has no snooze branch at all — `isDismissalReasonSnooze` is read only for the icon and tooltip at `products/signals/frontend/inbox/components/shell/DismissReportDialog.tsx:34`, which promises "it briefly leaves your inbox ... and it can come back if new signals match" (line 26).
- **Found:** desktop routes the same reason code to a real snooze — `products/desktop/packages/ui/src/features/inbox/hooks/useInboxReportDismissAction.tsx:45` branches on `isDismissalReasonSnooze`, and `products/desktop/packages/core/src/inbox/bulkActions.ts:171` returns `{ state: "potential", snooze_for: 1 }`. So the two clients disagree on what "Already fixed" does.
- **Found:** the reviewer's backend premise is overstated. `products/signals/backend/views.py:470` only advises reserving `already_fixed` for `state='potential'`; `dismissal_reason` is a plain `ChoiceField` (line 516) that the API accepts with any state. Nothing is rejected — the defect is UI copy against behavior, not a contract break.
- **Impact:** a web user who picks "Already fixed" gets a permanent archive instead of the snooze the tooltip promises, and the suppression also closes the linked implementation PR through the post_save receiver noted at `products/signals/backend/views.py:2220`. The report never returns on new signals.
- **Priority:** lowered to `consider`. The behavior is pre-existing, not introduced here: `git show origin/master:...reportListLogic.ts` already posted `state: 'suppressed'` with the raw reason, and master's dialog already carried the pause icon and snooze tooltip. This diff only routes the same fields through `suppressDismissalPayload`. The misleading promise also sits in a hover tooltip, while the dialog's own title and body say "Archive report" and "This report will be archived out of your inbox". The fix adds a snooze path the web client does not have yet and reaches into the customer analytics feed, so it earns its own change rather than a drive-by in a wrong-repo feedback PR.
</issue_validation>

## Task
Investigate the issue and solve it

<potential_solution>
Build the full state request in one shared helper. Send `state: 'potential'` with `snooze_for: 1` for snooze reasons. Send `state: 'suppressed'` for other reasons. Use this helper in all single, bulk, triage, and customer analytics paths.
</potential_solution>

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.

Confirmed this is a real bug, but it predates this PR and needs a product decision, so I'm flagging it rather than fixing it here. On the web inbox, already_fixed is marked as a snooze reason (snoozesInsteadOfDismiss: true), so the dismiss dialog shows the pause icon and a tooltip promising the report 'briefly leaves your inbox ... and can come back if new signals match'. But the shared payload helper never sets a state, and every web call site (report list, card archive, bulk actions, and the customer analytics feed) hardcodes state: 'suppressed' — so the report is permanently archived, and the archive also closes any linked implementation PR. Desktop, by contrast, routes already_fixed to a real snooze (state: 'potential', snooze_for: 1), so the two clients disagree. This behavior is on master already (the suppress call and the snooze tooltip both predate this branch); this PR only routed the existing fields through the new suppressDismissalPayload helper without touching the state. I'm not fixing it in this PR because it's outside the wrong-repo scope and the right fix isn't mechanical: someone needs to decide whether web should adopt desktop's snooze behavior for snooze reasons (which means adding a snooze path the web client doesn't have, across the single/bulk/triage inbox paths and the customer analytics feed, and would stop the linked-PR close for those cases) or instead correct the web tooltip so it stops promising a snooze it doesn't perform. Worth its own issue/PR against the self-driving inbox.


- `human`: steering someone wrote for you directly.
- `report_dismissal`: a reviewer's verdict on one or more of your reports, forwarded so it reaches you without you re-finding them. One note covers one verdict, so when a reviewer acted on several reports at once read it as their view of that batch, never as a fleet-wide rule. **Dismissed** is the only kind that should make you stop filing something, and the reason code decides whether it even means that: `analysis_wrong`, `report_unclear`, and `wontfix_*` speak to your precision, so fold a reason that generalizes into a `noise:`/`pattern:` entry; `already_fixed` means the issue was real and someone fixed it, so record that a fix shipped and keep watching for a recurrence. **Snoozed or restored** is about timing, not correctness: the report is still live, so keep watching what it describes.
- `report_dismissal`: a reviewer's verdict on one or more of your reports, forwarded so it reaches you without you re-finding them. One note covers one verdict, so when a reviewer acted on several reports at once read it as their view of that batch, never as a fleet-wide rule. **Dismissed** is the only kind that should make you stop filing something, and the reason code decides whether it even means that: `analysis_wrong`, `report_unclear`, and `wontfix_*` speak to your precision, so fold a reason that generalizes into a `noise:`/`pattern:` entry; `wrong_repo` says the report targeted the wrong repository, not that the finding was wrong — keep watching the topic, and note the corrected repository when the note names one; `already_fixed` means the issue was real and someone fixed it, so record that a fix shipped and keep watching for a recurrence. **Snoozed or restored** is about timing, not correctness: the report is still live, so keep watching what it describes.

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.

Correction-only wrong-repo feedback does not reach scouts

should_fix bug

Why we think it's a valid issue
  • Checked: forward_dismissal_note and _build_note_content in products/signals/backend/dismissal_notes.py, the state-API write path in products/signals/backend/views.py, the report read serializer in products/signals/backend/serializers.py, the scout repository resolution in products/signals/backend/scout_harness/tools/report.py, both dismiss dialogs, and the corrections feed in products/signals/backend/repo_corrections.py.
  • Found: products/signals/backend/dismissal_notes.py:97 returns [] when the note is empty or blank, before any grouping or forwarding. A dismissal that carries only dismissal_reason='wrong_repo' plus corrected_repository creates no SignalScoutNote.
  • Found: _build_note_content (products/signals/backend/dismissal_notes.py:262) renders the verb, the reason code, and the quoted free text only. It never renders selected_repository or corrected_repository, so the structured correction reaches a scout only when the reviewer also typed the repository name by hand.
  • Found: the note is optional in both dialogs. products/signals/frontend/inbox/components/shell/DismissReportDialog.tsx:100 labels it "Optional" and the form's only required field is reason; the desktop dialog at products/desktop/packages/ui/src/features/inbox/components/DismissReportDialog.tsx:154 does the same. Picking the reason and the repository with no text is the flow the new picker makes easiest.
  • Found: SignalReportSerializer exposes dismissal_reason and dismissal_note (products/signals/backend/serializers.py:516-520, 569-570) and no repository fields. The values are reachable through inbox-report-artefacts-list, but nothing points a scout there when no note was forwarded.
  • Found: _resolve_report_repository returns at products/signals/backend/scout_harness/tools/report.py:595 for an explicit owner/repo, and at line 600 for a repository inferred from the report content. Neither branch reaches select_repository_for_team, so the corrections block built at products/signals/backend/report_generation/select_repo.py:93 cannot cover them. The same file's docstring tells scouts to pass the repository explicitly because free-form selection is slow, so the uncovered branch is the recommended one.
  • Impact: for a scout that names its own repository, a wrong-repo correction filed through the new picker with no free text reaches nothing. The corrections prompt block does not apply to that path, and the guidance added at products/signals/backend/scout_harness/prompt.py:234 ("note the corrected repository when the note names one") never fires because no note is created. The scout repeats the rejected repository on its next report, which is the outcome the change set out to stop.
Issue description

The new guidance can act only after a report_dismissal note reaches a scout. forward_dismissal_note creates no note when dismissal_note is empty, even if corrected_repository is present. Both inbox dialogs allow correction-only dismissals. The report serializer also omits the selected and corrected repositories. An explicit scout repository bypasses select_repository_for_team, so the correction prompt cannot cover this path. That scout can repeat the rejected repository on its next report.

Suggested fix

Forward structured wrong_repo feedback when free text is empty. Include selected_repository and corrected_repository in the derived note and the report read shape. Add a test where an explicit-repository scout receives a correction-only dismissal.

Prompt to fix with AI (copy-paste)
## Context
@products/signals/backend/scout_harness/prompt.py#L234

<issue_description>
The new guidance can act only after a `report_dismissal` note reaches a scout. `forward_dismissal_note` creates no note when `dismissal_note` is empty, even if `corrected_repository` is present. Both inbox dialogs allow correction-only dismissals. The report serializer also omits the selected and corrected repositories. An explicit scout repository bypasses `select_repository_for_team`, so the correction prompt cannot cover this path. That scout can repeat the rejected repository on its next report.
</issue_description>

<issue_validation>
- **Checked:** `forward_dismissal_note` and `_build_note_content` in `products/signals/backend/dismissal_notes.py`, the state-API write path in `products/signals/backend/views.py`, the report read serializer in `products/signals/backend/serializers.py`, the scout repository resolution in `products/signals/backend/scout_harness/tools/report.py`, both dismiss dialogs, and the corrections feed in `products/signals/backend/repo_corrections.py`.
- **Found:** `products/signals/backend/dismissal_notes.py:97` returns `[]` when the note is empty or blank, before any grouping or forwarding. A dismissal that carries only `dismissal_reason='wrong_repo'` plus `corrected_repository` creates no `SignalScoutNote`.
- **Found:** `_build_note_content` (`products/signals/backend/dismissal_notes.py:262`) renders the verb, the reason code, and the quoted free text only. It never renders `selected_repository` or `corrected_repository`, so the structured correction reaches a scout only when the reviewer also typed the repository name by hand.
- **Found:** the note is optional in both dialogs. `products/signals/frontend/inbox/components/shell/DismissReportDialog.tsx:100` labels it "Optional" and the form's only required field is `reason`; the desktop dialog at `products/desktop/packages/ui/src/features/inbox/components/DismissReportDialog.tsx:154` does the same. Picking the reason and the repository with no text is the flow the new picker makes easiest.
- **Found:** `SignalReportSerializer` exposes `dismissal_reason` and `dismissal_note` (`products/signals/backend/serializers.py:516-520`, `569-570`) and no repository fields. The values are reachable through `inbox-report-artefacts-list`, but nothing points a scout there when no note was forwarded.
- **Found:** `_resolve_report_repository` returns at `products/signals/backend/scout_harness/tools/report.py:595` for an explicit `owner/repo`, and at line 600 for a repository inferred from the report content. Neither branch reaches `select_repository_for_team`, so the corrections block built at `products/signals/backend/report_generation/select_repo.py:93` cannot cover them. The same file's docstring tells scouts to pass the repository explicitly because free-form selection is slow, so the uncovered branch is the recommended one.
- **Impact:** for a scout that names its own repository, a wrong-repo correction filed through the new picker with no free text reaches nothing. The corrections prompt block does not apply to that path, and the guidance added at `products/signals/backend/scout_harness/prompt.py:234` ("note the corrected repository when the note names one") never fires because no note is created. The scout repeats the rejected repository on its next report, which is the outcome the change set out to stop.
</issue_validation>

## Task
Investigate the issue and solve it

<potential_solution>
Forward structured `wrong_repo` feedback when free text is empty. Include `selected_repository` and `corrected_repository` in the derived note and the report read shape. Add a test where an explicit-repository scout receives a correction-only dismissal.
</potential_solution>

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.

Confirmed this is a real gap, but the right fix is a design decision spanning several subsystems, so I'm flagging it rather than implementing it unattended. What I verified: a wrong-repo dismissal filed through the new picker with no free text (reason wrong_repo + corrected_repository, empty note) is recorded correctly — it lands as a DISMISSAL artefact and, when the corrected repo is connected, becomes the report's newest repo_selection, and it does feed the free-form repo-selection agent through the corrections block. But it never reaches a scout that names its own repository: that scout's path (_resolve_report_repository, explicit 'owner/repo' branch) returns before select_repository_for_team, which is the only place the corrections block is injected, and the scout-note channel that could otherwise carry the feedback is empty here because forward_dismissal_note skips empty notes and the derived note never renders the selected/corrected repository anyway. The report read serializer also omits both repositories, so nothing points such a scout at the correction. Since the report contract tells scouts to pass their repo explicitly (free-form selection is slow), that recommended path is exactly the uncovered one, so the scout can repeat the rejected repository on its next report. A human should decide: (a) whether correction-only wrong-repo dismissals should be forwarded as a scout note even with no free text, and what that note says (rendering selected→corrected repository); (b) whether SignalReportSerializer should expose the selected and corrected repositories in the read shape; and (c) confirm the explicit-repo scout path is meant to be covered here versus deferred as a follow-up (like the connected-repo guard already listed as out of scope). The change touches the note-forwarding subsystem, adds API surface that regenerates the OpenAPI/MCP types, and its effectiveness (the scout actually not repeating the repo) can only be judged with the agent running, which is why I'm not landing it blind. Worth its own follow-up with a test where an explicit-repository scout receives a correction-only dismissal.

Comment thread products/signals/backend/temporal/agentic/report.py Outdated
Comment thread products/signals/backend/temporal/agentic/report.py
Comment thread products/signals/frontend/inbox/components/shell/DismissCorrectedRepoField.tsx Outdated
The set-state and bulk-set-state tool descriptions in tools.yaml still
listed the six pre-existing dismissal reason codes and omitted wrong_repo,
and the bulk prose omitted corrected_repository — while the input schemas
shipped beside them already accept both. Add wrong_repo to the enumerated
codes and document corrected_repository (single and bulk), then propagate
the flattened descriptions into the generated tool definitions so the live
MCP surface matches the schema it advertises.

Generated-By: PostHog Desktop
Task-Id: 05a131fa-ad26-4f06-a5af-e55a6090afc8
@github-actions
github-actions Bot requested a deployment to preview-pr-90022 August 28, 2026 13:38 In progress
A wrong-repo dismissal that lands mid-run writes a person-attributed
repo_selection artefact, which sets superseded_by_reviewer in the report
persist activity. That flag only fed repository_autostart_eligible, which
maybe_autostart_implementation_task consults solely in the reviewer-less
fallback branch (task_user is None). When the run's findings resolve a
qualifying reviewer, the flag was never checked and an implementation task
started against the repository the reviewer had just rejected — cloning the
wrong repo and opening a draft PR under a resolved reviewer's identity.

Skip maybe_autostart_implementation_task entirely when the selection was
superseded, so no path can auto-start against the stale repository. The
artefact-suppression half of the guard already kept the stale selection off
the report. Update the mid-run supersede test to assert auto-start is not
awaited.

Generated-By: PostHog Desktop
Task-Id: 05a131fa-ad26-4f06-a5af-e55a6090afc8
The mid-run supersede guard (_reviewer_selection_written_since) only
matched repo_selection artefacts with a non-null created_by, so it saw a
human correction but missed one filed by an agent through the MCP surface:
that dismissal is attributed to the forwarded task id and carries a null
created_by (attribution is exclusive). The run therefore treated the agent
correction as absent, appended its stale selection as the newest
repo_selection row, and — because persisted_repo_selection reads the latest
row — the next research run deterministically reused the rejected
repository.

Also detect the companion wrong_repo dismissal artefact, regardless of
attribution. The pipeline never writes dismissal artefacts (only the
state-transition path does), so this cannot false-positive on the activity's
own retry, whose repo_selection_as_of does not advance. Reuse the existing
wrong-repo content needle from repo_corrections (promoted to a public
constant) so both readers share one source of the compact-JSON assumption.
Add a test for a task-attributed wrong-repo dismissal filed mid-run.

Generated-By: PostHog Desktop
Task-Id: 05a131fa-ad26-4f06-a5af-e55a6090afc8
@github-actions
github-actions Bot requested a deployment to preview-pr-90022 August 28, 2026 14:21 In progress
The corrected-repository picker uses LemonInputSelect in single mode
without allowCustomValues, which renders no clear button. A pointer or
touch user who picked a repository and then wanted no correction had no
visible way back to empty — only Backspace on an empty input, or cancelling
and reopening the dialog. Add an explicit Clear button beside the picker,
shown only when a repository is selected, mirroring the FlagCleanupRepository
pattern; it calls onChange(null). The existing Enter-stop-propagation wrapper
is preserved around the input.

Generated-By: PostHog Desktop
Task-Id: 05a131fa-ad26-4f06-a5af-e55a6090afc8
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.

2 participants