Skip to content

test(campaign-enrollment): give every world()-seeding case an explicit budget sized from measured runs - #2358

Merged
objectstack-fleet[bot] merged 2 commits into
mainfrom
claude/project-thread-8h6n2g
Oct 28, 2026
Merged

objectstack-fleet[bot] merged 2 commits into
mainfrom
claude/project-thread-8h6n2g

Conversation

@objectstack-fleet

Copy link
Copy Markdown
Contributor

Requested by Irene · project thread
Fixes #2357
Clause-②: no
Metadata-protocol: none

维护者速读

  • 改了什么:test/flow-campaign-enrollment.test.ts 里 17 个会建 world() 的用例,每个都显式带 30 秒预算(WORLD_BUDGET_MS),不再吃 vitest 默认的 5 秒。只改测试,不发布任何东西。
  • 为什么改:每次「添加营销成员」都会把营销员看得到的所有合格人员一起入组,而每个 world() 多加 2 个线索、2 个联系人,所以文件越往后入组人数越多(7 → 51)。最后那个线索用例本机全量跑要 4.9 秒,main CI 上两次超过 5 秒变红。
  • 风险与回滚:风险很低,只放宽了单个用例的超时,断言一个没动;回滚就是 revert 这一个 commit。
  • 先看:test/flow-campaign-enrollment.test.ts 第 46–58 行(预算常量和测量说明)。
  • 两分钟验证:npx vitest run test/flow-campaign-enrollment.test.ts,预期 30 个用例全过,最慢的约 2.5 秒。
  • 需要你做的:无,席位落地。

What changes

  • test/flow-campaign-enrollment.test.ts: one constant, WORLD_BUDGET_MS = 30_000, with the measurement and the reason beside it, passed as the per-case timeout (the trailing argument, this repo's per-case convention) to every case that seeds a world(): 13 it and 4 it.each call sites, 17 in all. The cases that seed nothing (the structural reads and the two "names no campaign" runs) keep the default.
  • .changeset/2357-campaign-enrollment-test-budget.md: an empty-frontmatter changeset, since nothing ships.
  • No retry, no skip, no global testTimeout. The size matches ENROLL_BUDGET_MS in test/campaign-enrollment-doors.test.ts, which is 30 s for the same growth.

Why the late cases are slower than the early ones. The mechanism assumption holds: this is a slow case under load, not a hang. The cases are not equal, though. An Enroll Members run enrols every eligible person the marketer can see, not only its own world's people. Each world() adds two eligible leads (status: 'new') and two eligible contacts (department: 'engineering'), all owned by the marketer, and every member is inserted through a per-person subflow. So the run's work grows with each case before it in the file. A probe that counted each run's member rows (a scratch copy of the file, not committed) read 7, 9, 11, 15, 17 members for the leads cases, 16 to 24 for the contacts cases, and then 46 (the #2241 contacts case) and 51 (the #2241 leads case at :438, the last enrolling run in the file). Duration tracks that count. The file's own header already says the filter "rightly enrols the eligible ones among them too", so the growth is how the fixture works, not a defect. It is bounded by the number of cases in this file, because every file boots its own stack.

Tests

Per-case durations, in ms, on base f660fd8 (the change only adds timeouts, so the durations are the same on this head). "Full" is one full local vitest run --coverage (CI's pnpm run test:coverage) on a 4-core container: 239 files, 4592 passed, 1 skipped, 0 failed, about 17 minutes.

Case (world()-seeding only) Alone Full run
enrols the eligible leads in the chosen status 507 799
never enrols an opted-out, converted or off-status lead… 518 1032
tops up rather than double-enrolling on a re-run 541 1198
does not treat an enrolment in ANOTHER campaign as a duplicate 970 1651
enrols into a campaign still in planning 780 1672
contacts: enrols the eligible contacts in the chosen department 689 1456
contacts: never enrols an opted-out or off-segment contact… 878 1478
contacts: tops up rather than double-enrolling a contact 881 1488
contacts: does not treat a LEAD enrolment as a duplicate 1031 2247
contacts: picking contacts enrols no leads 1189 2299
contacts: picking leads enrols no contacts 1381 2662
#2121 completed / aborted × leads / contacts (refused, 4 cases) 87–153 165–212
#2121 control: in_progress leads 1739 4003
#2121 control: in_progress contacts 2079 4294
#2121 control: planning leads 2205 4272
#2241 Enroll = leads / contacts shows only its criterion 92 / 67 205 / 129
#2241 a contacts run with no lead status submits… 2061 4947
#2241 a leads run with no department submits… 2528 4906
#2241 control: contacts / leads run without its criterion 154 / 164 250 / 122

The local full run passed, but the two #2241 submit cases finished 53 ms and 94 ms under the 5 s default. On main CI the leads case went past 5000 ms on 0f17c6c and on 173b488 (job 122642586187: "Test timed out in 5000ms", test/flow-campaign-enrollment.test.ts:424:3 on that tree). 30 s is about 6× the slowest full-run reading and 12× the slowest alone.

Acceptance notes

  • CI per-case durations: NOT MEASURED. CI's default vitest reporter prints one total per file and names a case only when it fails. So the CI reading is the two 5000 ms timeouts above. This PR's own CI run gives the file total.
  • A different fix would retire each world's eligible people after its case, so every run enrols about 7 members and the file stops growing. That changes the fixture's lifecycle, which this card did not ask for, so it is noted here and not done.

Verification

  • pnpm verify (validate, typecheck, lint --strict, hygiene, token ratchet, build, vitest run) on this branch, exit 0: Test Files 239 passed (239), Tests 4592 passed, 1 skipped (4593), 824 s. The changeset gained its two claim lines during that run, so pnpm hygiene and pnpm hygiene:tokens were re-run on head cb2c9c3: "source hygiene clean", "source token ratchet clean", both exit 0.
  • The isolated run on this head: npx vitest run test/flow-campaign-enrollment.test.ts, 30 passed.

Generated by Claude Code

🤖 Generated with Claude Code

https://claude.ai/code/session_01J8VVAjZErQs1eFzRrtWuDe

…t budget sized from measured runs

Each enrolment run enrols every eligible person the marketer can see,
so the file's runs grow from 7 to 51 members and the last #2241 leads
case ran past vitest's 5 s default on main CI twice. Measured 0.5-2.5 s
alone and 0.8-4.9 s in a full local test:coverage run.

Co-Authored-By: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01J8VVAjZErQs1eFzRrtWuDe
@vercel

vercel Bot commented Oct 28, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

1 Skipped Deployment
Project Deployment Actions Updated
hotcrm Ignored Ignored Oct 28, 2026 6:27am UTC

Request Review

@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

席位核对

Verdict: ACCEPT · repo:hotcrm seat 1 · huangyiirene · session session_01J8VVAnPkgK6CWrJa6t7Fk5 · 2026-10-28T06:30Z · head cb2c9c3
Implemented-by: card thread session session_01J8VVAjZErQs1eFzRrtWuDe (report 6462342379 on #2357). Reviewed-by: the seat, against the 2-file diff and hotcrm origin/main f660fd8.

  • Shape:
    • Draft, base main, assignee huangyiirene, and the body reads Fixes #2357.
    • No governed path is touched.
    • The diff stays within claim 6461825148. It changes the test file plus an empty changeset carrying the claim lines.
    • git merge-tree against f660fd8 is clean.
  • Mechanism assumption 1 holds: not a hang.
  • Ruling held:
    • The 17 world()-seeding cases carry WORLD_BUDGET_MS = 30_000 as the per-case trailing argument. That is the repo's existing convention and the same size as ENROLL_BUDGET_MS in test/campaign-enrollment-doors.test.ts, which covers the same growth.
    • There is no retry, no skip, and no global testTimeout change.
    • The reason for the growth is written beside the constant.
    • The seat considered scoping each run to its own world, for example with a marketer per world. It does not require that here: the sibling suite already accepts the same growth with the same budget, and the cases' assertions read only their own world's keys.
  • Tests:
    • The head passes 30 of 30 in isolation.
    • pnpm verify exit 0: 239 files, 4592 passed, 1 skipped.
    • CI per-case times are not measured, because the reporter prints per file only. The PR says so.
  • Gates: CI on cb2c9c3 has 8 of 9 check runs success. Build and Test (22.x) is still running.
  • Deviation noted: a model-named trailer was amended out before the PR, with a force-with-lease push on the thread's own branch. History on main is untouched.
  • Landing: non-governed. The seat flips the PR to ready and arms auto-merge in this stroke.

Generated by Claude Code

@objectstack-fleet
objectstack-fleet Bot marked this pull request as ready for review October 28, 2026 06:31
@objectstack-fleet
objectstack-fleet Bot added this pull request to the merge queue Oct 28, 2026
Merged via the queue into main with commit 511b5f2 Oct 28, 2026
10 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

ci/cd CI plumbing and the verification pipeline

Projects

None yet

Development

Successfully merging this pull request may close these issues.

test/flow-campaign-enrollment: the #2241 "leads run with no department" case times out at the 5 s default on main CI (2 of the last ~8 main runs)

2 participants