Repository navigation
Conversation
|
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 |
|
Important Review skippedWe couldn't safely recover the incremental review. No full review was started, and the last reviewed checkpoint was preserved. Retry later, or explicitly request a full review by commenting You can disable this status message by setting the Use the checkbox below for a quick retry:
📝 WalkthroughWalkthroughThe change adds a feature-flagged sandbox email sender with backend provisioning, organization-member recipient checks, and dedicated SES delivery behavior. Workflow email editors and test-send interfaces can select the sandbox sender and apply its recipient restrictions. The change also adds start-condition metadata to workflow templates and introduces a first-run gallery that recommends templates based on observed events, supports email editing and test sends, and creates workflows. Priority: ➖ Normal Merge Risk: 🟡 Moderate · up to The onboarding template can create a workflow that responds to the wrong pageviews. Correct its trigger filters before merging. The other open template and sandbox-configuration concerns also need resolution or explicit acceptance. Security Architecture ReviewSecurity architecture risk: 🟡 Moderate · up to The managed sender has strong server-side identity and recipient restrictions. However, it bypasses ordinary project sending budgets, so activation must remain blocked until independent sandbox abuse controls are available. Production configuration and those rollout prerequisites were not established. Retained concerns
Security review detailsSecurity Blast Radius
Security Findings and Attack Paths
Trust Boundaries and Controls
Resilience and Maintainability Implications
Hardening Proposals
🚥 Pre-merge checks | ✅ 1✅ Passed checks (1 passed)
✨ Finishing Touches 💡 1⚔️ Resolve merge conflicts 💡
🧪 Generate unit tests (beta)
Comment |
There was a problem hiding this comment.
Actionable comments posted: 2
Note
Quiet mode is enabled, so only the most important comments were posted inline. Other review comments are grouped below.
🟡 Other comments (3)
products/workflows/frontend/firstRun/WorkflowsFirstRunMakeItYours.stories.tsx-83-88 (1)
83-88: 🩺 Stability & Availability | 🟡 Minor | ⚡ Quick winDo not mutate the global app context during render.
This story sets
hog_flow = 'viewer'on the sharedPOSTHOG_APP_CONTEXTand does not restore it. The setting can leak into stories that run later in the same runtime. Ifresource_access_controlis undefined, the assignment throws. Set the context in a decorator that restores the original value, or pass the value as a story parameter.products/workflows/backend/templates/celebrate-milestone.json-4-8 (1)
4-8: 🗄️ Data Integrity & Integration | 🟡 Minor | ⚡ Quick winKeep
milestone_reachedin the trigger filters.When a user selects this template before
milestone_reachedhas been seen and enables it,matchedEventis null.withMatchedEventthen leaves the trigger filters empty, and the backend rejects the active workflow. Add the event filter below. The server compiles bytecode; do not add it by hand.🐛 Suggested fix
- "filters": {} + "filters": { + "events": [ + { + "id": "milestone_reached", + "name": "milestone_reached", + "type": "events", + "order": 0 + } + ] + }nodejs/src/cdp/cdp-services.ts-464-472 (1)
464-472: 🔒 Security & Privacy | 🛡️ Detected with Advanced Tier | 🟡 Minor | ⚡ Quick winDisable the sandbox sender when SES settings are incomplete.
When the feature flag is enabled, empty values are passed to SES for
FromEmailAddress,TenantName, orConfigurationSetName. This can cause sandbox sends to fail. The route does not fall back to the team's SES tenant or configuration set.Proposed fix
new SandboxEmailSender( { - enabled: config.WORKFLOWS_SANDBOX_SENDER_ENABLED, + enabled: + config.WORKFLOWS_SANDBOX_SENDER_ENABLED && + !!config.SES_SANDBOX_TENANT_NAME && + !!config.SES_SANDBOX_CONFIGURATION_SET && + !!config.SES_SANDBOX_FROM_ADDRESS,
🧹 Nitpick comments (2)
products/workflows/frontend/firstRun/firstRunSenderLogic.ts (1)
8-8: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low valueImport
IntegrationTypefrom~/types.The relative path
../../../../frontend/src/typescreates a second import path for the same symbol. Other files use the~/typesalias.nodejs/src/cdp/services/messaging/email.service.ts (1)
1168-1170: 🩺 Stability & Availability | 🔵 Trivial | 💤 Low valueFail closed when
sandboxSenderis missing in the From resolution.If
sandboxSenderis null, this returns an empty From address. The earlier gate at Line 570 already skips sandbox sends whensandboxSender?.config.enabledis falsy, so this path cannot run today.sesRouteForhas the same problem: without a sender it falls back to the team tenant. If someone reorders these checks later, a sandbox send can reach SES with an empty From or the wrong tenant. Throw an error instead of returning''.Proposed fix
if (integration.config.provider === 'sandbox') { - return { email: this.sandboxSender?.config.fromAddress ?? '', name: integration.config.name } + if (!this.sandboxSender) { + throw new Error('The sandbox sender is not configured') + } + return { email: this.sandboxSender.config.fromAddress, name: integration.config.name } }
ℹ️ Review info
⚙️ Run configuration
- Configuration used: Repository: PostHog/posthog/.coderabbit.yaml
- Review profile: QUIET
- Plan: Enterprise
- Run ID:
68d1eaa6-ec9e-4d3a-a3ed-30b2dc4b9812
⛔ Files ignored due to path filters (3)
pnpm-lock.yamlis excluded by!**/pnpm-lock.yamlproducts/integrations/frontend/generated/api.tsis excluded by!**/generated/**products/workflows/frontend/generated/api.schemas.tsis excluded by!**/generated/**
📒 Files selected for processing (99)
docs/internal/workflows-email-sending-tiers.mdfrontend/src/lib/components/CyclotronJob/CyclotronJobInputs.tsxfrontend/src/lib/constants.tsxfrontend/src/lib/integrations/IntegrationsList.tsxfrontend/src/lib/integrations/integrationsLogic.test.tsfrontend/src/lib/integrations/integrationsLogic.tsfrontend/src/lib/integrations/utils.tsfrontend/src/scenes/hog-functions/email-templater/EmailTemplater.test.tsxfrontend/src/scenes/hog-functions/email-templater/EmailTemplater.tsxfrontend/src/scenes/hog-functions/email-templater/NativeEmailIntegrationChoice.stories.tsxfrontend/src/scenes/hog-functions/email-templater/emailTemplaterLogic.test.tsfrontend/src/scenes/hog-functions/email-templater/emailTemplaterLogic.tsxfrontend/src/scenes/hog-functions/email-templater/selectSenders.test.tsfrontend/src/scenes/hog-functions/email-templater/selectSenders.tsnodejs/package.jsonnodejs/src/cdp/cdp-services.tsnodejs/src/cdp/config.tsnodejs/src/cdp/services/managers/organization-members.service.tsnodejs/src/cdp/services/messaging/email.service.test.tsnodejs/src/cdp/services/messaging/email.service.tsnodejs/src/cdp/services/messaging/sandbox-email-sender.tsnodejs/src/cdp/types.tsnodejs/src/cdp/utils.tsposthog/api/integration.pyposthog/cdp/test/test_validation.pyposthog/cdp/validation.pyposthog/models/integration/__init__.pyposthog/models/integration/email.pyposthog/settings/ses.pyproducts/integrations/mcp/tools.yamlproducts/workflows/backend/facade/api.pyproducts/workflows/backend/facade/contracts.pyproducts/workflows/backend/presentation/views/hog_flow.pyproducts/workflows/backend/presentation/views/hog_flow_template.pyproducts/workflows/backend/services/sandbox_sender.pyproducts/workflows/backend/templates/announce_a_new_feature_template.jsonproducts/workflows/backend/templates/celebrate-milestone.jsonproducts/workflows/backend/templates/feature-adoption-tips.jsonproducts/workflows/backend/templates/heavy-usage-detected.jsonproducts/workflows/backend/templates/high-intent-notification.jsonproducts/workflows/backend/templates/negative-survey-response.jsonproducts/workflows/backend/templates/onboarding_started_but_not_completed_template.jsonproducts/workflows/backend/templates/re-engagement-workflow.jsonproducts/workflows/backend/templates/repeated-failure-alert.jsonproducts/workflows/backend/templates/support-ticket-notification.jsonproducts/workflows/backend/templates/ticket-unresolved-alert.jsonproducts/workflows/backend/templates/trial-ending-reminder.jsonproducts/workflows/backend/templates/trial_started_upgrade_nudge_template.jsonproducts/workflows/backend/templates/unlocking_advanced_features.jsonproducts/workflows/backend/templates/unused-features-education.jsonproducts/workflows/backend/templates/user-stuck-onboarding.jsonproducts/workflows/backend/templates/user-upgrade-webhook.jsonproducts/workflows/backend/templates/welcome_email_sequence_template.jsonproducts/workflows/backend/test/test_sandbox_sender_lifecycle.pyproducts/workflows/backend/tests/api/test_email_reputation.pyproducts/workflows/backend/tests/api/test_hog_flow_template.pyproducts/workflows/backend/tests/api/test_sandbox_sender.pyproducts/workflows/backend/tests/api/test_sandbox_sender_validation.pyproducts/workflows/frontend/Broadcasts/broadcastTestSendLogic.tsproducts/workflows/frontend/Broadcasts/steps/BroadcastContentStep.tsxproducts/workflows/frontend/Channels/MessageChannels.stories.tsxproducts/workflows/frontend/Channels/MessageChannels.tsxproducts/workflows/frontend/Channels/SandboxEmailSenderRow.tsxproducts/workflows/frontend/TemplateLibrary/SendTestEmailModal.stories.tsxproducts/workflows/frontend/TemplateLibrary/SendTestEmailModal.test.tsxproducts/workflows/frontend/TemplateLibrary/SendTestEmailModal.tsxproducts/workflows/frontend/TemplateLibrary/messageTemplateTestSendLogic.test.tsproducts/workflows/frontend/TemplateLibrary/messageTemplateTestSendLogic.tsproducts/workflows/frontend/Workflows/hogflows/findTestSendSkipReason.test.tsproducts/workflows/frontend/Workflows/hogflows/findTestSendSkipReason.tsproducts/workflows/frontend/Workflows/hogflows/steps/components/HogFlowFunctionConfiguration.tsxproducts/workflows/frontend/Workflows/templates/WorkflowTemplateCard.tsxproducts/workflows/frontend/WorkflowsScene.tsxproducts/workflows/frontend/emptyState/workflowsEmptyState.tsxproducts/workflows/frontend/firstRun/GalleryTemplateCard.tsxproducts/workflows/frontend/firstRun/MakeItYoursActions.tsxproducts/workflows/frontend/firstRun/MakeItYoursEmailList.tsxproducts/workflows/frontend/firstRun/TemplateStartsOnLine.tsxproducts/workflows/frontend/firstRun/WorkflowsFirstRun.stories.tsxproducts/workflows/frontend/firstRun/WorkflowsFirstRunGallery.tsxproducts/workflows/frontend/firstRun/WorkflowsFirstRunMakeItYours.stories.tsxproducts/workflows/frontend/firstRun/WorkflowsFirstRunMakeItYours.tsxproducts/workflows/frontend/firstRun/WorkflowsFirstRunTab.test.tsxproducts/workflows/frontend/firstRun/WorkflowsFirstRunTab.tsxproducts/workflows/frontend/firstRun/buildFirstRunTestSend.tsproducts/workflows/frontend/firstRun/buildFirstRunWorkflow.tsproducts/workflows/frontend/firstRun/firstRunGalleryLogic.test.tsproducts/workflows/frontend/firstRun/firstRunGalleryLogic.tsproducts/workflows/frontend/firstRun/firstRunMakeItYoursLogic.test.tsproducts/workflows/frontend/firstRun/firstRunMakeItYoursLogic.tsproducts/workflows/frontend/firstRun/firstRunSenderLogic.test.tsproducts/workflows/frontend/firstRun/firstRunSenderLogic.tsproducts/workflows/frontend/firstRun/firstRunStoryFixtures.tsproducts/workflows/frontend/firstRun/firstRunWorkflowStorage.tsproducts/workflows/frontend/firstRun/templateEmails.tsproducts/workflows/frontend/firstRun/withFirstRunSender.test.tsproducts/workflows/frontend/firstRun/withFirstRunSender.tsproducts/workflows/package.jsonservices/mcp/src/api/generated.ts
Included review availability: This review used your included allowance. Your plan provides up to 12 included reviews per hour; 5 remain after this review.
| 3, | ||
| 2 | ||
| ] | ||
| "actions": [] |
There was a problem hiding this comment.
🎯 Functional Correctness | 🟠 Major | ⚡ Quick win
🔎 Supported by static analysis
🏁 Script executed:
#!/bin/bash
rg -nP 'bytecode' products/workflows/backend/presentation/views/hog_flow.py products/workflows/backend -g '*.py' | head -40Repository: PostHog/posthog
Length of output: 5785
🏁 Script executed:
#!/bin/bash
set -u
file=products/workflows/backend/templates/onboarding_started_but_not_completed_template.json
printf '%s\n' '--- PR diff for template ---'
git diff --no-ext-diff --unified=30 2a289c4dacbcd27e28a0197e3dbdd9f594538fa6 9777b8c10d38c0351957ef5f5a43c2192e53231a -- "$file"
printf '%s\n' '--- Current template, numbered ---'
nl -ba "$file"
printf '%s\n' '--- Template references ---'
rg -n -F -- 'onboarding_started_but_not_completed_template' products/workflows
printf '%s\n' '--- Relevant recompile/save implementation blocks ---'
sed -n '3700,3805p' products/workflows/backend/presentation/views/hog_flow.py
sed -n '5830,5915p' products/workflows/backend/presentation/views/hog_flow.py
printf '%s\n' '--- Template workflow endpoints/references by template loader terms ---'
rg -n 'templates/|template.*content|create.*template|template.*create|_recompil|recompil' products/workflows/backendRepository: PostHog/posthog
Length of output: 41989
🏁 Script executed:
#!/bin/bash
set -u
printf '%s\n' '--- starts_on consumers and template paths ---'
rg -n 'starts_on|sync_hog_flow_template|sync_template_to_db|global.*template|template.*workflow' products/workflows/frontend products/workflows/backend
printf '%s\n' '--- HogFlow serializer: validation/save and event bytecode handling ---'
sed -n '1360,1455p' products/workflows/backend/presentation/views/hog_flow.py
sed -n '1480,1625p' products/workflows/backend/presentation/views/hog_flow.py
printf '%s\n' '--- Template API creation/detail binding ---'
sed -n '1,130p' products/workflows/backend/presentation/views/hog_flow_template.py
sed -n '300,355p' products/workflows/backend/presentation/views/hog_flow_template.py
printf '%s\n' '--- Template sync/load implementation ---'
rg -n 'def sync_.*template|def get_.*template|templates/.*json|glob\\(' products/workflows/backend
printf '%s\n' '--- workflow create endpoint and serializer save definitions ---'
rg -n 'class HogFlowSerializer|def create\\(|def update\\(|HogFlowSerializer\\(' products/workflows/backend/presentation/views/hog_flow.py | head -60Repository: PostHog/posthog
Length of output: 42451
🏁 Script executed:
#!/bin/bash
set -u
printf '%s\n' '--- starts_on consumers and template paths ---'
rg -n 'starts_on|sync_hog_flow_template|sync_template_to_db|global.*template|template.*workflow' products/workflows/frontend products/workflows/backend
printf '%s\n' '--- HogFlow serializer: validation/save and event bytecode handling ---'
sed -n '1360,1455p' products/workflows/backend/presentation/views/hog_flow.py
sed -n '1480,1625p' products/workflows/backend/presentation/views/hog_flow.py
printf '%s\n' '--- Template API creation/detail binding ---'
sed -n '1,130p' products/workflows/backend/presentation/views/hog_flow_template.py
sed -n '300,355p' products/workflows/backend/presentation/views/hog_flow_template.py
printf '%s\n' '--- Template sync/load implementation ---'
rg -n 'def sync_.*template|def get_.*template|templates/.*json|glob\(' products/workflows/backend
printf '%s\n' '--- workflow create endpoint and serializer save definitions ---'
rg -n 'class HogFlowSerializer|def create\(|def update\(|HogFlowSerializer\\(' products/workflows/backend/presentation/views/hog_flow.py | head -60Repository: PostHog/posthog
Length of output: 42759
🏁 Script executed:
#!/bin/bash
set -u
printf '%s\n' '--- Global template service and source loader ---'
nl -ba products/workflows/backend/services/hog_flow_templates.py | sed -n '1,145p'
nl -ba products/workflows/backend/templates/__init__.py | sed -n '120,195p'
printf '%s\n' '--- starts_on gallery presentation/use ---'
nl -ba products/workflows/frontend/firstRun/firstRunGalleryLogic.ts | sed -n '30,60p;150,190p'
rg -n 'startsOn|starts_on|workflowTemplate|template.*actions|createWorkflow|hogFlows' products/workflows/frontend/firstRun products/workflows/frontend/Workflows/NewWorkflowModal.tsx
printf '%s\n' '--- HogFlow trigger validation and event filter serializer ---'
nl -ba products/workflows/backend/presentation/views/hog_flow.py | sed -n '1280,1435p'
nl -ba products/workflows/backend/presentation/views/hog_flow.py | sed -n '1560,1635p'
printf '%s\n' '--- Workflow API create/update methods ---'
rg -n -F 'class HogFlowSerializer' products/workflows/backend/presentation/views/hog_flow.py
rg -n -F 'def create(' products/workflows/backend/presentation/views/hog_flow.py
rg -n -F 'def update(' products/workflows/backend/presentation/views/hog_flow.pyRepository: PostHog/posthog
Length of output: 33159
🏁 Script executed:
#!/bin/bash
set -u
printf '%s\n' '--- First-run workflow creation and build ---'
nl -ba products/workflows/frontend/firstRun/buildFirstRunWorkflow.ts | sed -n '1,130p'
nl -ba products/workflows/frontend/firstRun/firstRunMakeItYoursLogic.ts | sed -n '230,265p;280,315p'
printf '%s\n' '--- starts_on display contract ---'
nl -ba products/workflows/frontend/firstRun/TemplateStartsOnLine.tsx | sed -n '1,40p'
printf '%s\n' '--- HogFunctionFiltersSerializer declaration and bytecode generation ---'
rg -n -F 'class HogFunctionFiltersSerializer' posthog productsRepository: PostHog/posthog
Length of output: 8853
🏁 Script executed:
#!/bin/bash
set -u
printf '%s\n' '--- Filter serializer implementation ---'
nl -ba posthog/cdp/validation.py | sed -n '1070,1185p'
printf '%s\n' '--- HogFlow serializer and create/update implementation ---'
nl -ba products/workflows/backend/presentation/views/hog_flow.py | sed -n '2805,2855p'
nl -ba products/workflows/backend/presentation/views/hog_flow.py | sed -n '3265,3310p'Repository: PostHog/posthog
Length of output: 11669
🏁 Script executed:
#!/bin/bash
set -u
nl -ba posthog/cdp/validation.py | sed -n '1180,1245p'Repository: PostHog/posthog
Length of output: 3291
Match both trigger filters to the onboarding URL.
The gallery advertises $pageview on /onboarding, but both trigger filters still match $current_url containing messaging/campaigns. The workflow builder preserves those filters, so the workflow can enroll campaign-page visitors and miss onboarding visits. Update both values:
🐛 Suggested fix
@@ -39 +39 @@
- "value": "messaging/campaigns",
+ "value": "/onboarding",
@@ -133 +133 @@
- "value": "messaging/campaigns",
+ "value": "/onboarding",| for trigger in triggers: | ||
| if trigger["type"] == "event": | ||
| for event in trigger.get("filters", {}).get("events", []): | ||
| if event.get("id"): | ||
| assert event["id"] == starts_on["events"][0] |
There was a problem hiding this comment.
🎯 Functional Correctness | 🟠 Major | ⚡ Quick win
Fail the consistency check when an event trigger has no event filters.
If an event trigger has no filters.events, the loop runs zero times and the test passes. celebrate-milestone.json passes this way even though its trigger has empty filters. For a non-schedule starts_on, assert that each event trigger declares at least one event.
Proposed fix
if trigger["type"] == "event":
- for event in trigger.get("filters", {}).get("events", []):
+ events = trigger.get("filters", {}).get("events", [])
+ if starts_on["kind"] == "event":
+ assert events, "event trigger must declare its starts_on event"
+ for event in events:📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| for trigger in triggers: | |
| if trigger["type"] == "event": | |
| for event in trigger.get("filters", {}).get("events", []): | |
| if event.get("id"): | |
| assert event["id"] == starts_on["events"][0] | |
| for trigger in triggers: | |
| if trigger["type"] == "event": | |
| events = trigger.get("filters", {}).get("events", []) | |
| if starts_on["kind"] == "event": | |
| assert events, "event trigger must declare its starts_on event" | |
| for event in events: | |
| if event.get("id"): | |
| assert event["id"] == starts_on["events"][0] |
There was a problem hiding this comment.
Note
Quiet mode is enabled, so only the most important comments were posted inline. Other review comments are grouped below.
Caution
Some comments are outside the diff and can’t be posted inline due to GitHub limitations.
🟡 Minor · Normalize structured create errors before rendering them. · firstRunMakeItYoursLogic.ts:275
products/workflows/frontend/firstRun/firstRunMakeItYoursLogic.ts:275
🩺 Stability & Availability | 🟡 Minor | ⚡ Quick winNormalize structured create errors before rendering them.
If
hogFlowsCreaterejects with a plain-objectdetail, this stores that object increateError.MakeItYoursActionsrenders it directly as a React child, which can throw and prevent the error banner from rendering. Convert non-string details to readable text before callingsetCreateError.
🟡 Other comments (2)
products/workflows/frontend/firstRun/firstRunMakeItYoursLogic.ts-401-403 (1)
401-403: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick winKeep test-send outcomes with the email that was sent.
If the user switches emails after a test send,
testSendResultremains visible beside the newly opened email. A request started before the switch can also finish afterward and show a success banner there. Capture the email ID when sending, and show or apply the outcome only while that email remains open. Based on learnings: “capture the entity's id before starting the async work, and re-check that the active entity id still matches before applying the result.”Source: Learnings
products/workflows/frontend/firstRun/MakeItYoursActions.tsx-78-78 (1)
78-78: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick winDescribe when the workflow sends.
An enabled workflow does not necessarily send right away. The supplied trial template waits for its trigger and schedules emails after 10, 13, and 20 days. Replace this text with wording that says sends follow a matching trigger and the template's schedule.
ℹ️ Review info
⚙️ Run configuration
- Configuration used: Repository: PostHog/posthog/.coderabbit.yaml
- Review profile: QUIET
- Plan: Enterprise
- Run ID:
8e3735bb-b5dc-4724-af7f-f1106c19ee2d
⛔ Files ignored due to path filters (1)
pnpm-lock.yamlis excluded by!**/pnpm-lock.yaml
📒 Files selected for processing (4)
products/workflows/frontend/firstRun/MakeItYoursActions.tsxproducts/workflows/frontend/firstRun/WorkflowsFirstRunMakeItYours.tsxproducts/workflows/frontend/firstRun/firstRunMakeItYoursLogic.test.tsproducts/workflows/frontend/firstRun/firstRunMakeItYoursLogic.ts
Included review availability: This review used your included allowance. Your plan provides up to 12 included reviews per hour; 5 remain after this review.
a8c22c7 to
6be2622
Compare
|
Note 🤖 GPT-6.1 Sol responding on behalf of Michael Replaced by the draft fork PR #332 in its native fork stack. @meikelmosby will start migrating the stack from the fork into PostHog/posthog for upstream review and CI. Closing this master-targeted copy to keep the handoff clear. |
Problem
A new user who picks a card in the first-run gallery lands in the blank workflow editor, with no sender set and nothing tested.
Most gallery templates send two to four emails, so a screen with one email editor would hide the rest.
Stacked on #111816 (tailored gallery) and #112003 (first-run sender seam). Review only the last two commits.
Spec: Silthus#256, section "Make it yours". Ticket: Silthus#260. Behind
workflows-first-run.Changes
?template=<id>. Back and reload return to it.activeordraftper the "Enable workflow" switch. It then opens the editor and remembers the id in the browser. With the switch off the button reads "Open workflow".workflows first run test sent(skipped) andworkflows first run workflow created(template_id,enabled).findTestSendSkipReasonmoves frombroadcastTestSendLogictoWorkflows/hogflows/, the gallery story fixtures move tofirstRunStoryFixtures.ts, and the workflows product manifest gains@testing-library/domfor story play functions.How did you test this code?
Test rationale:
firstRunMakeItYoursLogic.test.tsdrives the logic with the API mocked at the HTTP layer. Each case names a regression no existing test catches: emails out of flow order or with wrong timing; an edit lost when switching emails; a create payload missing an edited email, the sender on a step, the matched trigger event, or the status per switch; a test invocation that sends the wrong email or recipient; a hidden skip reason; a sender-less state that lets a create go active; a rejected create that still navigates; a viewer who can create; a switch, test send or create that runs while the templater still holds an unexported canvas edit.firstRunGalleryLogic.test.tsnow asserts a pick routes to?template=instead of the blank editor.findTestSendSkipReason.test.tsmoved with its helper, unchanged.fix, the first-run, Broadcasts and hogflows Jest suites, and the pre-push preflight.WorkflowsFirstRunMakeItYours.stories.tsx, one per state above plus a narrow one, with play functions for the skipped send and the rejected create. The screenshots above come from them, rendered headless with the Unlayer editor loaded from its CDN.Release status
Automatic notifications
Docs update
None. The first run is behind a flag.
🤖 Agent context
Session: a background worker of the wayfinder map Silthus#253, ticket Silthus#260.
Skills invoked: /tdd, /writing-kea-logics, /writing-ui-components, /writing-user-facing-copy, /writing-tests, /writing-code-comments, /adopting-generated-api-types, /writing-pr-descriptions.
CodeRabbit CLI pass: skipped, the CLI is signed out and no person was available.
Codex adversarial review (GPT-6.1 Sol), two findings, both fixed: a canvas edit made in the 500ms before switching, sending or creating was lost or stale, so those actions now wait for the templater's pending export (covered by three logic tests); the editor offered sender controls the payload overrode, so the editor now runs as a template editor without From and To.
Decisions along the way: the create uses the generated
hogFlowsCreate, while the test send keepsapi.hogFlows.createTestInvocationbecause the generated invocation function has no response type. The screen reads the template and its matched event from the gallery logic instead of loading the template again. Screenshots live on the fork branchassets/fr-260-make-it-yoursbecause the PR asset uploader refused this account.🤖 Generated with Claude Code