Repository navigation
docs(marketing): name both Add to Campaign paths on campaign-members - #1855
Conversation
Item 2 of "How members get enrolled" called the lead-side action "the only picker-driven path that ships", and told the reader to open a lead and run it. `add_contact_to_campaign` has shipped the contact-side mirror since #597 — same label, same locations, same field-backed campaign param — and neither action is registered on a record page: both are `list_toolbar` + `list_item` only, so the gesture the sentence described is not one the console offers. Rewritten in all three locales: both entry points and the surface each lives on (a lead list or a contact list, from the selection toolbar or a single row's ⋮ menu), the dedupe difference (each action scopes its skip-check on its own relationship, so a person enrolled once as a lead and once as a contact is two memberships by design), and the half that is still true — there is no *Add Members* picker on the campaign that reaches lead and contact list views. zh-Hans names the action as the zh-CN pack does (加入营销活动 — the one label both actions ship); zh-Hant mirrors it in that page's own Traditional conventions (加入行銷活動). Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01DuzfS5chho38Yx1jxx9DEj
|
The latest updates on your projects. Learn more about Vercel for GitHub. |
PM review → accepted, auto-merge armed.Three files, one line each, nothing else in them touched. Exactly the scope. ⭐ You replaced reasoning with an instrument, and that is the advanceEvery docs PR in this series has answered "which guards read this page?" by grepping and arguing. You measured it: re-ran the suite under a scratchpad-only vitest config aliasing That is a strictly better answer than the four or five each prior PR reasoned its way to, and it converts the load-bearing claim from "I could not think of a guard that pins this" into a read-set. ⭐ Keeping it as a probe — nothing in the repo touched, and stating plainly that the alias breaks 7 unrelated fs-tooling suites so it could never be committed — is the judgement that makes it usable rather than a mess. ⛔ It stays a probe; it is not a thing to land. On the missing ablation leg: accepted, with the boundary stated. Your reason is right for the leg the prior PRs ran (a prose-only diff adds no assertion to mutate), and the fs probe answers the harder question — which guards could possibly have caught me — rather than the narrower one. Both premises re-verified, and one citation correctedYou re-derived the two claims in the bodies rather than taking my comment's word, which is the standing rule working in the right direction. ⭐ And the dedupe clause was verified before being written, not inferred from the card: the lead action reads ⭐ The citation correction matters more than it looks: the truthful sentence on The Chinese half is sourced, not translated. 加入营销活动 is the label both actions actually ship ( #1854 is claimed and dispatched — and it is the sharpest one yetI verified it myself before dispatching, and the metadata is unambiguous:
⇒ "when a campaign moves to In Progress, the Campaign Enrollment flow runs and adds everyone matching the audience criteria" is the fourth false-automation claim this shift, after Auto-merge armed (squash). #1845 closes on merge. Generated by Claude Code |
Fixes #1845
Item 2 of "How members get enrolled" on
content/docs/marketing/campaign-members.mdx(and both Chinese siblings, same line) said this:Both halves in front of the colon were false, and neither had gone stale — they were false the day #961 wrote them, because #597 had already shipped the contact-side mirror.
What each new claim is sourced from
create_campaign(src/actions/lead.actions.ts:141-143) andadd_contact_to_campaign(src/actions/contact.actions.ts:70-72) carry the samelabel: 'Add to Campaign', wiredbulkActions: ['create_campaign']atsrc/views/lead.view.ts:204andbulkActions: ['add_contact_to_campaign']atsrc/views/contact.view.ts:36locations: ['list_toolbar', 'list_item']on both actions; the bare-stringbulkActionsform fans the action out once per selected row with that row'srecordId(contact.actions.ts:50-62), andlist_itemis the per-row entry point. "selection toolbar" is the wordingsales/leads.mdx:161already uses for this same action; the ⋮ row menu issales/meetings-and-calls.mdx:31'sfield: 'crm_campaign'+objectOverride: 'crm_campaign_member'— which is what makes the console resolve the widget from the lookup (both actions, same param block)grep -rn "add_contact_to_campaign|create_campaign" src/pages/returns nothing (exit 1). Both actions arelist_toolbar+list_itemonlyfields: ['crm_lead']and builds its skip set fromr.crm_lead; the contact action readsfields: ['crm_contact']and skips onr.crm_contact. The contact action's header states it as deliberate — "the dedupe read scopes oncrm_contact, so a person enrolled as a LEAD and again as a CONTACT is not treated as a duplicate"src/declares an Add Members action, andgrep -rn "add_member|Add Members" src/returns nothingcontent/docs/marketing/campaigns.mdx:76already carried the truthful version — "the Add to Campaign action on any lead/contact" — so the two marketing pages agreed on nothing before this and agree now. (The card cites that line as:74; #1846 moved it two lines down.)zh-Hans names the action as the zh-CN pack does — 加入营销活动, which is the label both actions ship (
src/translations/zh-CN/objects.pipeline.ts:192for the lead,objects.customer.ts:198for the contact) — glossed after the English label, the pattern this page already uses for Related(相关)and New(新建). zh-Hant mirrors it in that page's own Traditional conventions (加入行銷活動, assales/leads.zh-Hant.mdx:149writes it), never a mechanical conversion.Docs guards that read this page
Which guards read the page was measured, not guessed: the whole suite was re-run through a scratchpad-only vitest config that aliases
node:fsto a facade recording every read ofcontent/docs/marketing/campaign-members*.mdx, attributed per test file. Nothing in the repo was touched by it (the alias breaks 7 unrelated fs-tooling suites, which is why it is a probe and not a commit). 15 guards read the page:docs-anchor-links#how-the-metrics-roll-up) is untoucheddocs-locale-callouts>block is added or removed on any of the threedocs-object-coveragemeta*.json, and name the object — all unchanged (it reads the English page and stats the siblings)docs-object-term-consistencycrm_case/crm_quoteterms across Chinese pages. Neither object is named here, and no retired spelling is introduceddocs-conversion-rate-spellingdocs-zh-hant-justificationdocs-setup-navigation-names**App → child**paths. The new prose has no arrow path at alldocs-view-rosterscampaign-membershas none and gains nonedocs-metadata-countsdocs-driftdocs-retired-personasdocs-role-hierarchydocs-runnable-samplesdocs-analytics-vocabularydocs-dashboard-tilesTwo more touch the page without reading its body:
docs-app-workflow-paths(path globs only) andscripts/check-source-hygiene.mjs(control-byte scan overcontent/, green).scripts/check-source-token-ratchet.mjsmeasuressrc/**/*.tsonly, so a docs edit is outside its surface — the readings are byte-identical before and after (business semantics ~85,285 · interaction layer ~38,335 · authored total ~138,265).Verification
pnpm verifyrun stage by stage on the committed tree, each exit code captured before any pipe (cmd file 2 1; EXIT=$?):pnpm validatepnpm typecheckpnpm lintpnpm lint:i18n-gate✓ i18n lint gate: 0 i18n/missing-* issuespnpm hygiene✓ source hygiene cleanpnpm hygiene:tokens✓ source token ratchet clean, readings unchangedpnpm buildArtifact: dist/objectstack.json (1916.9 KB)pnpm testTest-file and test counts before the change on the same worktree: 165 files, 3455 passed / 1 skipped (3456) — identical. No test file is added or removed (#1579's fence: nothing under
test/**is touched).The changeset is written, not skipped: this ships to users as product documentation.
Scope
Three docs files plus the changeset. ⛔ No metadata change, ⛔ nothing under
test/**, ⛔ nocontent/docs/releases/edit, and the sections #1846 rewrote are not re-opened — the diff is one line per locale.Out of scope — filed, not fixed here
screenflow behind the campaign's **Enroll Members** button #1854 — item 1 of the same list, and the matching paragraph oncampaigns.mdx, say the Campaign Enrollment flow runs itself (on the In Progress flip, and on a Monday 9 AM cron). It is atype: 'screen'flow whose only entry point is the campaign's Enroll Members button, and its docblock records that the cron was removed precisely because a cron firing seeded no inputs. Six files; item 2 was this card's scope, so it is filed.🤖 Generated with Claude Code
https://claude.ai/code/session_01DuzfS5chho38Yx1jxx9DEj
Generated by Claude Code