feat(signals): wrong-repo dismissals feed repo selection - #90022
feat(signals): wrong-repo dismissals feed repo selection#90022sortafreel wants to merge 25 commits into
Conversation
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
|
Merging to
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 |
|
Hey @sortafreel! 👋 It looks like your git author email on this PR isn't your
You can fix it for this repo with: git config user.email "you@posthog.com"Or set it globally with |
🤖 CI report
|
| 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 |
⚠️ 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
⚠️ 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)
|
React Doctor found 4 issues in 2 files · 1 error & 3 warnings. Errors
3 warnings
Reviewed by React Doctor for commit |
HostHog preview —
|
🦔 PostHog Review reviewed this pull requestFound 1 must fix, 3 should fix, 2 consider. Published 6 findings (view the review). Resolved comments: 4 fixed, 2 left for you |
|
PostHog Review alpha 🦔 If you find any issues helpful - please reply "valid", "invalid", etc., for evaluation purposes 🙏 |
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
🦔 Hogbox preview · ✅ ready▶ Open the preview
commit |
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 |
There was a problem hiding this comment.
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 optionalcorrected_repository(validated and recorded on dismissal artefacts; optionally updates latestrepo_selectionwhen 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.
PR overviewAll previously flagged issues have been addressed. No open security concerns remain on this pull request. Security reviewNo 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
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
|
👋 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. |
| archiveReport: async ({ reportId, dismissal }) => { | ||
| actions.removeReport(reportId) | ||
| try { | ||
| await api.signalReports.setState(reportId, { | ||
| state: 'suppressed', | ||
| dismissal_reason: reason, | ||
| ...(note ? { dismissal_note: note } : {}), | ||
| ...suppressDismissalPayload(dismissal), | ||
| }) |
There was a problem hiding this comment.
“Already fixed” permanently suppresses reports
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 —isDismissalReasonSnoozeis read only for the icon and tooltip atproducts/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:45branches onisDismissalReasonSnooze, andproducts/desktop/packages/core/src/inbox/bulkActions.ts:171returns{ 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:470only advises reservingalready_fixedforstate='potential';dismissal_reasonis a plainChoiceField(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.tsalready postedstate: 'suppressed'with the raw reason, and master's dialog already carried the pause icon and snooze tooltip. This diff only routes the same fields throughsuppressDismissalPayload. 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>
There was a problem hiding this comment.
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. |
There was a problem hiding this comment.
Correction-only wrong-repo feedback does not reach scouts
Why we think it's a valid issue
- Checked:
forward_dismissal_noteand_build_note_contentinproducts/signals/backend/dismissal_notes.py, the state-API write path inproducts/signals/backend/views.py, the report read serializer inproducts/signals/backend/serializers.py, the scout repository resolution inproducts/signals/backend/scout_harness/tools/report.py, both dismiss dialogs, and the corrections feed inproducts/signals/backend/repo_corrections.py. - Found:
products/signals/backend/dismissal_notes.py:97returns[]when the note is empty or blank, before any grouping or forwarding. A dismissal that carries onlydismissal_reason='wrong_repo'pluscorrected_repositorycreates noSignalScoutNote. - 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 rendersselected_repositoryorcorrected_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:100labels it "Optional" and the form's only required field isreason; the desktop dialog atproducts/desktop/packages/ui/src/features/inbox/components/DismissReportDialog.tsx:154does the same. Picking the reason and the repository with no text is the flow the new picker makes easiest. - Found:
SignalReportSerializerexposesdismissal_reasonanddismissal_note(products/signals/backend/serializers.py:516-520,569-570) and no repository fields. The values are reachable throughinbox-report-artefacts-list, but nothing points a scout there when no note was forwarded. - Found:
_resolve_report_repositoryreturns atproducts/signals/backend/scout_harness/tools/report.py:595for an explicitowner/repo, and at line 600 for a repository inferred from the report content. Neither branch reachesselect_repository_for_team, so the corrections block built atproducts/signals/backend/report_generation/select_repo.py:93cannot 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>
There was a problem hiding this comment.
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.
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
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
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
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
corrected_repository(only withdismissal_reason='wrong_repo'); the dismissal artefact records the repository the pipeline had selected and the correction.repo_selectionartefact withautostart_eligible=False, so restoring the report re-researches against the right repository without granting auto-start.wrong_repois a targeting verdict, not a precision failure, so a scout does not stop filing a valid topic.setStatenow 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_wrongare 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, andcorrected_repositoryvalidation (format, wrong-reason pairing).test_repo_selection_prompt.py(new): catches the corrections section being dropped from the selection prompt.dismissal-reasons, desktop package typechecks (shared, api-client, core, ui), the full webtypescript:check, ruff, Biome, oxlint, andhogli ci:preflight --fix(clean).Automatic notifications
Docs update
products/signals/ARCHITECTURE.mdis updated in this PR (dismissal artefact shape, wrong-repo dismissals, past-corrections injection).🤖 Agent context
Autonomy: Human-driven (agent-assisted)
autostart_eligible=False; per-lesson dedupe against bulk flooding; SQL prefilter, typedDismissalparse, and repo-shape sanitization in the corrections feed; scout prompt taxonomy; a shared suppress-payload helper; the generatedsetStatetype; 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 quillGitHubRepositoryComboboxinside the Lemon tree (quill is not forfrontend/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).Created with PostHog Desktop