Skip to content

feat(customer-analytics): rename announcement api, models and mcp tools to shoutouts - #108431

Draft
posthog[bot] wants to merge 3 commits into
posthog-self-driving/featcustomer-analytics-rename-7cd976from
posthog-self-driving/featcustomer-analytics-rename-91a88d
Draft

posthog[bot] wants to merge 3 commits into
posthog-self-driving/featcustomer-analytics-rename-7cd976from
posthog-self-driving/featcustomer-analytics-rename-91a88d

Conversation

@posthog

@posthog posthog Bot commented Sep 29, 2026 •

Copy link
Copy Markdown
Contributor

Problem

  • The Customer Analytics tab is now called Shoutouts (feat(customer-analytics): rename announcements tab to shoutouts #108416), but the API, the MCP tools and the models behind it still say "announcement".
  • Agents that use the MCP tools see announcements-* names for a feature that the UI calls shoutouts.
  • The rename costs more as usage grows, so this layer does it now.

Closes #108348

Origin

  • Inbox report: open
  • Task started by: auto-start, after the report was rated P3 and ready to fix

Changes

  • MCP tools: announcements-list, -retrieve, -create and -channels-list become shoutouts-*, with the tool copy updated. Agents that call the old names get a missing-tool error.
  • API route: /api/projects/:id/announcements/ becomes /api/projects/:id/shoutouts/. The frontend uses the new generated shoutouts* client and Shoutout*Api types. No UI change.
  • Models: Announcement and AnnouncementDelivery become Shoutout and ShoutoutDelivery. The logic module, viewset, serializers, facade functions and contracts get the same rename.
  • Celery: the send task registers as customer_analytics.send_shoutout. A customer_analytics.send_announcement alias stays registered so tasks queued before the deploy still send.
  • Repo guards: test_pagination_contract.py and the IDOR semgrep rule list the new names.
  • Mechanical: everything else is a find-and-replace plus regenerated OpenAPI and MCP output.

Note

Migration 0059 changes Django state only (sqlmigrate prints a no-op). The tables, the announcement_id column, the index and the unique constraint keep their old names through db_table and db_column. Old and new pods read the same schema during the deploy. A real table rename would break the old pods mid-deploy.

Warning

Order with #102862: that PR edits the same announcement files and adds its own migration. It is in the merge queue, so the proposed order is #102862 first, then this stack rebases onto it and applies its send_as changes under the shoutout names. This PR stays a draft until that order is agreed.

Two names stay as they were on purpose:

  • The analytics event customer analytics announcement created keeps its name, so existing insights on it keep working.
  • The old Celery task alias stays until the broker has no queued tasks left under that name. A follow-up PR removes it.

How did you test this code?

  • The renamed shoutout model, API and send-task tests, test_facade.py and test_pagination_contract.py ran locally against the dev database.
  • test_send_shoutout.py now runs its delivery and retry-config cases against both task names. It catches an alias that is registered under the wrong name or with inert retries, which would drop sends queued before the deploy.
  • makemigrations --check shows no state drift. sqlmigrate customer_analytics 0059 emits no SQL.
  • The shoutouts Jest tests and the MCP unit tests ran locally.
  • Not checked: the full frontend typecheck. It fails in this sandbox on an unbuilt @posthog/quill package, and none of those errors touch the changed files. Semgrep is not installed here, so the IDOR rule edit relies on CI.

Release status

  • No feature flag controls this change
  • This change is behind a feature flag and is not available to users
  • This change makes a previously flagged feature available to everyone

Automatic notifications

  • Publish to changelog?

Docs update

None. No doc under docs/ covers this feature.

🤖 Agent context

Autonomy: Fully autonomous

Agent: Claude Code, claude-opus-5-5

  • Layer 2 of 2, from a PostHog inbox report. The base layer (feat(customer-analytics): rename announcements tab to shoutouts #108416) renamed the tab and the frontend components.
  • Skills invoked: /django-migrations, /writing-tests, /writing-pr-descriptions.
  • Rejected: a real RenameModel with table and column renames. It would break old pods during the deploy.
  • No duplicate: an open-PR search for "shoutout" found only the base layer.
  • Public artifact: nothing from the agent session is in this PR.

Created with PostHog Desktop from this inbox report.

🤖 Generated with Claude Code

…ls to shoutouts

Rename the Announcement and AnnouncementDelivery models, the logic module, the viewset and its route, the facade contracts, the send task and the announcements-* MCP tools to shoutout names.

The migration changes Django state only. The tables, the foreign key column, the index and the constraint keep their old names, so the deploy runs no DDL.

The send task registers under customer_analytics.send_shoutout. The old customer_analytics.send_announcement name stays registered until queued tasks drain.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

Generated-By: PostHog Desktop
Task-Id: 87cd43f9-33a9-4151-b7c7-ec5b0ad4294c
@github-actions

github-actions Bot commented Sep 29, 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.

✅ Complexity (TypeScript) — clean

Cyclomatic complexity above the limit in changed typescript files (10 for production files, 15 for test files). Warn only: worth simplifying when you next touch these functions.

✅ Duplication (Python) — clean

New Python code duplication introduced by this branch. Fails at 70+ tokens in app code, or 150+ tokens when both copies live in test files. Advisory while the gate proves itself: extract a shared helper instead of copying.

✅ Duplication (TypeScript) — clean

New TypeScript code duplication introduced by this branch. Fails at 70+ tokens in app code, or 150+ tokens when both copies live in test files. Advisory while the gate proves itself: extract a shared helper instead of copying.

⚠️ Comment density — 4% of added code lines are comments (15 of 386)

This section warns when comments are more than 3% of the code lines a PR adds, and alerts above 6%. Before agent-assisted PRs, the typical share was about 2%. Only full-line comments count. Docstrings, generated files, snapshots, migrations, and workflow files are left out.

Comments that restate the code, record how the change came about, or narrate the next line add noise for the next reader. Keep the comments that explain a reason the code cannot show, and remove the rest. See .agents/skills/writing-code-comments/SKILL.md for the house rules.

Files with the most added comment lines:

File Comment lines Added lines
products/customer_analytics/backend/test/test_shoutout.py 9 39
products/customer_analytics/backend/facade/api.py 1 34
products/customer_analytics/backend/models/shoutout.py 1 4
products/customer_analytics/backend/models/shoutout_delivery.py 1 10
products/customer_analytics/backend/presentation/views/shoutouts.py 1 35
products/customer_analytics/backend/tasks/tasks.py 1 13
products/customer_analytics/backend/test/test_send_shoutout.py 1 116

This check does not block merging. It updates on every push and clears when the share drops.

✅ Bundle size — no change

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

Total: 68.92 MiB · no change

No file changed by more than 1000 B.

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.58 MiB · 22 files no change █████████░ 85.8% of 1.84 MiB
logged-out boot: index + App + bootApp (preloaded by every page, including /login)
src/index.tsx + src/scenes/App.tsx + src/scenes/bootApp.ts
3.52 MiB · 629 files no change █████████░ 87.3% of 4.03 MiB
authenticated shell (every logged-in page)
src/scenes/AuthenticatedShell.tsx
7.35 MiB · 2,339 files 🟢 -16 B (-0.0%) █████████░ 88.1% of 8.34 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/index.tsx + src/scenes/App.tsx + src/scenes/bootApp.ts
🟢 src/layout/navigation-3000/navigationLogic.tsx stays out of src/index.tsx + src/scenes/App.tsx + src/scenes/bootApp.ts
🟢 src/scenes/dashboard/dashboardLogic.tsx stays out of src/index.tsx + src/scenes/App.tsx + src/scenes/bootApp.ts
🟢 src/lib/lemon-ui/LemonMarkdown/ stays out of src/index.tsx + src/scenes/App.tsx + src/scenes/bootApp.ts
🟢 src/lib/components/RichContentEditor/ stays out of src/index.tsx + src/scenes/App.tsx + src/scenes/bootApp.ts
🟢 src/lib/components/CodeSnippet/ stays out of src/index.tsx + src/scenes/App.tsx + src/scenes/bootApp.ts
🟢 src/taxonomy/core-filter-definitions-by-group.json stays out of src/index.tsx + src/scenes/App.tsx + src/scenes/bootApp.ts
🟢 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
🟢 src/scenes/session-recordings/player/sessionRecordingPlayerLogic.ts 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
🟢 [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/index.tsx
1.3 KiB src/RootErrorBoundary.tsx
912 B ../node_modules/.pnpm/ieee754@1.2.1/node_modules/ieee754/index.js
854 B src/scenes/ChunkLoadErrorBoundary.tsx
Largest files eagerly shipped from src/index.tsx + src/scenes/App.tsx + src/scenes/bootApp.ts
Size File
301.8 KiB ../node_modules/.pnpm/posthog-js@1.434.14_@types+react@18.3.27_react@18.3.1/node_modules/posthog-js/dist/module.mjs
216.9 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
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
100.5 KiB src/lib/api.ts
88.5 KiB src/products.tsx
69.4 KiB src/lib/lemon-ui/icons/icons.tsx
40.1 KiB src/lib/utils/eventUsageLogic.ts
38.7 KiB ../node_modules/.pnpm/@dnd-kit+core@6.0.8_react-dom@18.3.1_react@18.3.1__react@18.3.1/node_modules/@dnd-kit/core/dist/core.esm.js
33.9 KiB ../node_modules/.pnpm/kea@4.0.0-pre.6_patch_hash=139b8d1f1304f9d9da452a9a1244c94ea679dbcb85687d8999563146879fb6f5_react@18.3.1/node_modules/kea/lib/index.cjs.js
28.4 KiB src/scenes/scenes.ts
Largest files eagerly shipped from src/scenes/AuthenticatedShell.tsx
Size File
301.8 KiB ../node_modules/.pnpm/posthog-js@1.434.14_@types+react@18.3.27_react@18.3.1/node_modules/posthog-js/dist/module.mjs
272.2 KiB src/taxonomy/core-filter-definitions-by-group.json
216.9 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
153.7 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
100.5 KiB src/lib/api.ts
98.8 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
88.5 KiB src/products.tsx

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.16 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.16 MiB · 19 files no change ████░░░░░░ 37.8% of 5.72 MiB
Deferred (lazy) 2.10 MiB · 44 files no change n/a — loads on demand
Loader dist/toolbar.js 1.2 KiB no change █░░░░░░░░░ 6.0% of 19.5 KiB
Largest eagerly-shipped chunks
Size File
805.4 KiB dist/toolbar/toolbar-app-5UT2PX3W.css
651.7 KiB dist/toolbar/chunk-chunk-SEZBMT4M.js
259.4 KiB dist/toolbar/chunk-chunk-RK3L52NC.js
138.3 KiB dist/toolbar/chunk-chunk-YMECCIGG.js
131.8 KiB dist/toolbar/chunk-chunk-FDH2IBXT.js
75.2 KiB dist/toolbar/toolbar-app-SN7ERW5L.js
69.0 KiB dist/toolbar/chunk-chunk-TSAL54PB.js
35.6 KiB dist/toolbar/chunk-chunk-A6ER72L5.js
21.0 KiB dist/toolbar/chunk-chunk-E6HGNIFN.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 — 🟢 -870 B (-0.0%)

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

Total: 947.32 MiB · 🟢 -870 B (-0.0%)

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

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

App JS CSS
debug 597.9 KB 199.2 KB
action 454.1 KB 199.2 KB
action-list 564.2 KB 199.2 KB
cohort 453.1 KB 199.2 KB
cohort-list 563.2 KB 199.2 KB
email-template 452.9 KB 199.2 KB
error-details 469.6 KB 199.2 KB
error-issue 454.5 KB 199.2 KB
error-issue-list 564.8 KB 199.2 KB
experiment 561.3 KB 199.2 KB
experiment-list 564.9 KB 199.2 KB
experiment-results 566.3 KB 199.2 KB
feature-flag 566.8 KB 199.2 KB
feature-flag-list 570.5 KB 199.2 KB
feature-flag-testing 457.3 KB 199.2 KB
inline-scan 453.6 KB 199.2 KB
insight-actors 562.3 KB 199.2 KB
invite-email-preview 452.3 KB 199.2 KB
llm-costs 559.3 KB 199.2 KB
session-recording 455.3 KB 199.2 KB
survey 454.7 KB 199.2 KB
survey-global-stats 561.9 KB 199.2 KB
survey-list 564.9 KB 199.2 KB
survey-stats 561.9 KB 199.2 KB
trace-span 453.5 KB 199.2 KB
trace-span-list 564.1 KB 199.2 KB
vision-observation-list 563.3 KB 199.2 KB
workflow 453.4 KB 199.2 KB
workflow-list 563.5 KB 199.2 KB
loops-review 457.8 KB 199.2 KB
query-results 774.1 KB 199.2 KB
render-ui 858.1 KB 199.2 KB
visual-review-snapshots 457.9 KB 199.2 KB
⚠️ Django migration SQL — 1 new migration to review

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

products/customer_analytics/backend/migrations/0059_rename_announcement_to_shoutout.py

/opt/hostedtoolcache/Python/3.14.7/x64/lib/python3.14/site-packages/anyio/from_thread.py:119: SyntaxWarning: 'return' in a 'finally' block
  return result
/opt/hostedtoolcache/Python/3.14.7/x64/lib/python3.14/site-packages/structlog/stdlib.py:1166: UserWarning: Remove `format_exc_info` from your processor chain if you want pretty exceptions.
  ed = p(logger, meth_name, ed)  # type: ignore[arg-type]
2026-09-29T15:05:05.046596Z [error    ] Path must be a valid database or directory containing databases. [posthog.exceptions_capture] pid=8175 tid=140114836024192
Traceback (most recent call last):
  File "/home/runner/work/posthog/posthog/posthog/geoip.py", line 15, in <module>
    geoip: Optional[GeoIP2] = GeoIP2(cache=8)
                              ~~~~~~^^^^^^^^^
  File "/opt/hostedtoolcache/Python/3.14.7/x64/lib/python3.14/site-packages/django/contrib/gis/geoip2.py", line 116, in __init__
    raise GeoIP2Exception(
        "Path must be a valid database or directory containing databases."
    )
django.contrib.gis.geoip2.GeoIP2Exception: Path must be a valid database or directory containing databases.
/opt/hostedtoolcache/Python/3.14.7/x64/lib/python3.14/site-packages/sshtunnel.py:1040: SyntaxWarning: 'return' in a 'finally' block
  return (ssh_host,
/opt/hostedtoolcache/Python/3.14.7/x64/lib/python3.14/site-packages/langchain_core/_api/deprecation.py:27: UserWarning: Core Pydantic V1 functionality isn't compatible with Python 3.14 or greater.
  from pydantic.v1.fields import FieldInfo as FieldInfoV1
System check identified some issues:

WARNINGS:
?: (axes.W001) You are using the django-axes cache handler for login attempt tracking. Your cache configuration is however invalid and will not work correctly with django-axes. This can leave security holes in your login systems as attempts are not tracked correctly. Reconfigure settings.AXES_CACHE and settings.CACHES per django-axes configuration documentation.
?: (staticfiles.W004) The directory '/home/runner/work/posthog/posthog/frontend/dist' in the STATICFILES_DIRS setting does not exist.
BEGIN;
--
-- Custom state/database change combination
--
-- (no-op)
COMMIT;

Last updated: 2026-09-29 15:05 UTC (8fd9b20)

✅ 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

customer_analytics.0059_rename_announcement_to_shoutout
  └─ #1 ✅ SeparateDatabaseAndState: Only state operations (no database changes)

Last updated: 2026-09-29 15:05 UTC (8fd9b20)

…amed model

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

Generated-By: PostHog Desktop
Task-Id: 87cd43f9-33a9-4151-b7c7-ec5b0ad4294c
@coderabbitai

coderabbitai Bot commented Sep 29, 2026 •

Copy link
Copy Markdown
Contributor

Review in Change Stack →

Navigate logical layers of code changes, visualize relationships, and explore their blast radius.

📝 Walkthrough

Walkthrough

Customer Analytics models, backend APIs, frontend integrations, and MCP tools now use shoutout names instead of announcement names. The migration updates model state without database operations and retains existing table and column names. The backend creates shoutouts and schedules delivery after commit. Delivery processing updates per-channel results and overall status.

Priority: ⬇️ Low

Merge Risk: 🔵 Low · up to 8fd9b

This is a rename of the Customer Analytics announcements feature to shoutouts, with the database schema preserved. One concern remains before merge: the send confirmation may not show the destination channels. The generated MCP types may also differ slightly from the actual API responses.

Security Architecture Review

Security architecture risk: 🟡 Moderate · up to 8fd9b

The renamed interfaces retain the visible access and destination controls, but delivery compatibility is one-way: older queued tasks can run on new workers, while the reverse is not established. A mixed-version deployment or rollback could leave customer messages unsent.

Retained concerns

  • Medium · reliability · inferred: Compatibility for queued delivery tasks is one-way. During a mixed-version rollout or rollback, an older worker may receive a newly named task it cannot execute, leaving a customer message pending. The deployment topology and recovery procedure are not established.
Security review details

Security Blast Radius

  • inferred — A permitted create request can queue outward-facing Slack messages to its selected bot-member channels, up to the view’s channel limit; the evidence does not show a new cross-team route or broader service credential.

Trust Boundaries and Controls

  • observed — The MCP create handler verifies a confirmation bound to the selected project before POSTing, while the API enforces its shared permission chain and creation-time bot-membership filter before delivery reaches Slack.

Resilience and Maintainability Implications

  • inferred — The old-name task alias protects forward deployment of queued work, but does not itself protect newly queued work when older workers remain active or after rollback. Whether that state is reachable depends on deployment behavior not supplied here.

Hardening Proposals

  • proposed — Confirm that all active workers accept the new task name before producers enqueue it, and define how pending deliveries are reconciled during rollback. Independently, an atomic per-row claim would strengthen duplicate-send protection under concurrent execution.
🚥 Pre-merge checks | ✅ 1
✅ Passed checks (1 passed)
Check name Status Explanation
Description check ✅ Passed The description covers the problem, user-visible changes, compatibility decisions, testing, release status, documentation status, and agent context. It also identifies untested frontend typecheck and …
✨ Finishing Touches
📝 Generate docstrings
  • Commit to this branch
  • Create a new PR
🧪 Generate unit tests (beta)
  • Commit to this branch
  • Create a new PR

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai 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.

Actionable comments posted: 1

Note

Quiet mode is enabled, so only the most important comments were posted inline. Other review comments are grouped below.

🟡 Other comments (2)
products/customer_analytics/mcp/tools.yaml-1283-1285 (1)

1283-1285: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Show the destination channels in the confirmation message.

The confirmation message interpolates {message} but not {channels}. prepareConfirmedAction returns that message and instructs the model to surface it. Unless a separate UI adds the channels, the user can confirm an irreversible Slack send without seeing its destinations. Include the signed channel list in the confirmation display.

services/mcp/src/api/generated.ts-67105-67105 (1)

67105-67105: 🗄️ Data Integrity & Integration | 🟡 Minor | ⚡ Quick win

Represent a deleted creator as null.

The shoutout model permits created_by = null. The facade passes that null through to the response, but Shoutout.created_by requires UserBasic. Change the generated response contract to UserBasic | null so consumers must handle shoutouts whose creator was deleted. (raw.githubusercontent.com)


ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository: PostHog/posthog/.coderabbit.yaml

Review profile: QUIET

Plan: Enterprise

Run ID: 2086be8e-f7d8-42fc-a438-bd552ab1e644

📥 Commits

Reviewing files that changed from the base of the PR and between 506722d and 8fd9b20.

⛔ Files ignored due to path filters (5)
  • products/customer_analytics/frontend/generated/api.schemas.ts is excluded by !**/generated/**
  • products/customer_analytics/frontend/generated/api.ts is excluded by !**/generated/**
  • products/customer_analytics/frontend/generated/api.zod.ts is excluded by !**/generated/**
  • services/mcp/src/generated/customer_analytics/api.ts is excluded by !**/generated/**
  • services/mcp/src/tools/generated/customer_analytics.ts is excluded by !**/generated/**
📒 Files selected for processing (30)
  • .semgrep/rules/security/idor-team-scoped-models.yaml
  • posthog/test/repo_invariants/test_pagination_contract.py
  • products/customer_analytics/backend/COMPROMISES.md
  • products/customer_analytics/backend/constants.py
  • products/customer_analytics/backend/facade/api.py
  • products/customer_analytics/backend/facade/constants.py
  • products/customer_analytics/backend/facade/contracts.py
  • products/customer_analytics/backend/logic/shoutouts.py
  • products/customer_analytics/backend/migrations/0059_rename_announcement_to_shoutout.py
  • products/customer_analytics/backend/migrations/max_migration.txt
  • products/customer_analytics/backend/models/__init__.py
  • products/customer_analytics/backend/models/shoutout.py
  • products/customer_analytics/backend/models/shoutout_delivery.py
  • products/customer_analytics/backend/presentation/views/shoutouts.py
  • products/customer_analytics/backend/routes.py
  • products/customer_analytics/backend/tasks/__init__.py
  • products/customer_analytics/backend/tasks/tasks.py
  • products/customer_analytics/backend/test/test_announcement.py
  • products/customer_analytics/backend/test/test_send_announcement.py
  • products/customer_analytics/backend/test/test_send_shoutout.py
  • products/customer_analytics/backend/test/test_shoutout.py
  • products/customer_analytics/backend/test/test_shoutouts.py
  • products/customer_analytics/frontend/components/Shoutouts/ShoutoutsTabContent.test.tsx
  • products/customer_analytics/frontend/components/Shoutouts/ShoutoutsTabContent.tsx
  • products/customer_analytics/frontend/components/Shoutouts/shoutoutsLogic.test.ts
  • products/customer_analytics/frontend/components/Shoutouts/shoutoutsLogic.ts
  • products/customer_analytics/mcp/tools.yaml
  • services/mcp/schema/generated-tool-definitions.json
  • services/mcp/schema/tool-definitions-all.json
  • services/mcp/src/api/generated.ts
💤 Files with no reviewable changes (2)
  • products/customer_analytics/backend/test/test_send_announcement.py
  • products/customer_analytics/backend/test/test_announcement.py

Included review availability: This review used your included allowance. Your plan provides up to 12 included reviews per hour; 9 remain after this review.

Comment on lines +67108 to +67109
/** Slack channel IDs to send to. Each must be a channel the SupportHog bot is a member of; names are resolved server-side. */
channels: string[];

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.

🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win

Remove channels from the shoutout response type.

The endpoint marks channels write-only, so list, retrieve, and create responses omit it. The required Shoutout.channels field tells MCP consumers that every response contains channel IDs. Keep channels in the create-request type, and regenerate a response type without it. (raw.githubusercontent.com)

@posthog

posthog Bot commented Sep 29, 2026

Copy link
Copy Markdown
Contributor Author

👋 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.

Install the Visual Review Chrome extension to see visual review results at the top of your pull requests.

@trunk-io

trunk-io Bot commented Sep 29, 2026 •

Copy link
Copy Markdown

Static Badge   Static Badge   Static Badge

View Full Report ↗︎ ⋅ Docs

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant