Repository navigation
test(plugin-sharing): size the three scrypt cases near vitest's 5000 ms default to their measured work - #22426
Conversation
…ms default to their measured work The share-link password cases derive scrypt keys at the production parameters on purpose, so their wall time scales with the shard that runs them. Three of them read over half of the 5000 ms default on Test Core: the NFKC case (timed out at 5030 and 5039 ms), the cross-verify case (4136 ms) and the no-log case (3805 ms). Each now carries an explicit budget of 3x its slowest reading, rounded up to the next second. The other 49 scrypt-reaching cases in the package keep the default. No parameter, production file or vitest config changes. Co-authored-by: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01WYYhVJ78u7PhwFViWo1EmQ
📓 Docs Drift CheckNothing in this diff resolved to a documentable surface (no symbol, route or SDK anchor derived from 0 changed package(s)), so this run has no opinion about the docs. What this run could not see
Coarse fallback — 0 page(s) merely mention a changed package (the pre-#9192 predicate, kept for the deliberately-wide backstop): |
⛔ merge queue 构建失败 — 先分诊,再决定要不要重排队列构建 37893672824 红了。队列跑的是全量套件(PR 侧 CI 只跑 affected 子集), 分类: 失败的 job(日志抽取,best effort):
跨 PR 相同签名(24h,按失败测试文件聚合):
历史信号:
分诊清单:
Generated by Claude Code · merge-queue-triage workflow (#4859) |
⛔ merge queue 构建失败 — 先分诊,再决定要不要重排队列构建 37894053453 红了。队列跑的是全量套件(PR 侧 CI 只跑 affected 子集), 分类: 失败的 job(日志抽取,best effort):
跨 PR 相同签名(24h,按失败测试文件聚合):
历史信号:
分诊清单:
Generated by Claude Code · merge-queue-triage workflow (#4859) |
Fixes #22418
Clause-②: no
What changes
Three cases in
packages/plugins/plugin-sharingget an explicit per-case timeout, sized to the work each one measures. Every other scrypt-reaching case in the package keeps vitest's 5000 ms default; the enumeration below gives each one's reading and the reason.Test Corereadingshare-link-password-webcontainer.test.ts:95, "a password whose NFKC form differs from its input is one password on both paths" (the card's case,:72onca135dcc)17_000share-link-password-webcontainer.test.ts:74, "a hash minted on WebContainer verifies natively, and the reverse"13_000share-link-password.test.ts:481, "no log line carries the presented password, on any outcome"12_000Spelled the way this repo spells a per-case budget, as the third argument of
it: two named constants in the webcontainer file and a literal12_000in the other. A comment beside each records the rule, the derivation count and the readings, so the next author can re-size from a fresh reading.Not touched: the scrypt parameters (N, r, p), any production file, the vitest config,
testTimeoutanywhere, and the case bodies. No skip, no quarantine, no retry. No changeset: only two*.test.tsfiles change, and the package'sfiles[]isdist,README.mdandCHANGELOG.md.The rule and the margin
/resolve still accepts the ?password= query parameter, 588 ms in one run and 1104 ms in the other).040184752c, chore(ci): refresh the Test Core shard-timings dataset #22368), which can change a shard's load again without touching this package.Readings
CI (per-case durations from vitest's default reporter in the two red
Test Core (5/6)job logs on the card, read through the job-log API): job113665992253(push,e75dceddd7) and job113668129564(PR #22215 merge ofe75dceddd7). The log API returns only the last 5000 lines of a 19k to 48k line log. In both red jobs, plugin-sharing's last 24 files fall in that window, and the twoshare-link-eligibility.test.tscases do not. Three hourly scheduledTest Core (5/6)jobs (113678118904onca135dcc,113663806311,113649948997) and the two green jobs on the card had no plugin-sharing lines in their windows. The direct REST log download is refused by this container's egress proxy, so no other log was reachable.Local (a 4-vCPU container; every timing run was taken under the shared verify lock, before the gate battery started, with no other run alongside; the load average was 0.2 when the work began). The census and idle readings use
--maxWorkers=2. The contention runs pin the four scrypt-reaching files to one core beside 5 busy loops on that core, with--maxWorkers=1. That load was picked because the NFKC case's CI-to-local ratio is at least 5.4x (at least 5030 ms against 926 ms). Case by case, it reads 0.95x to 1.48x the slowest CI reading, across the 22 cases that read over 1 s in CI and passed under it. So it is a load as heavy as the red shard, or heavier.ca135dcc)616794ce1):95)Test timed out in 5000ms(5024 ms):74)share-link-password.test.ts:481)Test timed out in 5000ms(5067 ms)The before contention run reproduces the card's failure shape: the same case, as a timeout and not an assertion. It also shows the no-log case is the next one to go.
The enumeration pin: every case in the package that runs a scrypt-class derivation
The only scrypt-class key derivation in the package is
share-link-password.ts:deriveKeyNode(node:cryptoscrypt) andderiveKeyPureJs(@noble/hashesscrypt). A test reaches them throughhashShareLinkPassword/verifyShareLinkPassword, throughShareLinkService.createLinkwith a password, throughresolveTokenagainst a storedscrypt$hash, or through the legacy upgrade.The list is measured, not grepped: a temporary vitest setup file, never committed, wrapped
node:crypto'sscryptandscryptSync, synced them into the builtin ESM namespace, and counted the calls per case. It ran over the whole package atca135dcc: 40 files, 1002 cases, 1002 census rows. 52 cases in 4 files reach a derivation:share-link-password-webcontainer.test.ts: 4 cases;share-link-password.test.ts: 42 of its 44 cases. The two others are the injected-hasher case and the authenticated list-route case;share-link-internal-columns.test.ts: 4 cases;share-link-eligibility.test.ts: 2 cases.Pure-JS derivations cannot be counted that way, because
@noble/hashesis a native ESM namespace. They are counted by reading the four webcontainer cases, which are the only cases in the package that set a WebContainer signal (a grep forSTACKBLITZ,/bin/jshandwebcontaineracross the package's tests finds no other)."Local, idle" is the census run. "CI" is the push / PR red run.
share-link-password-webcontainer.test.ts:95share-link-password-webcontainer.test.ts:74share-link-password-webcontainer.test.ts:121share-link-password-webcontainer.test.ts:64share-link-password.test.ts:481share-link-password.test.ts:352share-link-password.test.ts:352share-link-password.test.ts:617share-link-password.test.ts:338share-link-password.test.ts:284share-link-password.test.ts:617share-link-password.test.ts:582share-link-password.test.ts:391share-link-password.test.ts:552share-link-password.test.ts:445share-link-password.test.ts:535share-link-password.test.ts:600share-link-password.test.ts:552share-link-password.test.ts:523share-link-password.test.ts:600share-link-password.test.ts:552share-link-password.test.ts:552share-link-password.test.ts:535share-link-password.test.ts:326share-link-password.test.ts:523share-link-password.test.ts:433share-link-password.test.ts:445share-link-password.test.ts:552share-link-password.test.ts:460share-link-password.test.ts:552share-link-password.test.ts:267share-link-password.test.ts:433share-link-password.test.ts:305share-link-password.test.ts:460share-link-password.test.ts:523share-link-password.test.ts:582share-link-password.test.ts:460share-link-password.test.ts:460share-link-password.test.ts:523share-link-password.test.ts:568share-link-password.test.ts:568share-link-password.test.ts:568share-link-password.test.ts:568share-link-password.test.ts:568share-link-password.test.ts:568share-link-password.test.ts:375share-link-eligibility.test.ts:1191password_hashshape — NEEDS_PASSWORD and WRONG_PASSWORD both become the unknown-token answershare-link-eligibility.test.ts:1291share-link-internal-columns.test.ts:143share-link-internal-columns.test.ts:155share-link-internal-columns.test.ts:130share-link-internal-columns.test.ts:182The eligibility note. Neither
share-link-eligibility.test.tscase has a CI reading, because both finished before either red log's window opened. They keep the default, on two measured comparisons:password_hash-shape case does 5 native derivations, exactly as many as each legacy row, and its idle reading is between theirs: 662 ms against 731 and 584 ms. The legacy rows' slowest CI reading is 2163 ms, 43% of the default.Its CI time is NOT MEASURED, and is stated as such rather than inferred into the table. The fail-closed case does 2 derivations and read 336 ms idle and 1113 ms under contention.
Mechanism assumptions, checked
share-link-internal-columns.test.ts(4 cases) andshare-link-eligibility.test.ts(2 cases) reach the derivation throughShareLinkService. Neither is anywhere near the budget, so neither is edited, and the file surface stays the two claimed files.meta-state-route-engine-outage.test.ts's multi-kernel wiring case times out at vitest's 5000 ms default under the hourly full run, and has turnedmain's hourly run red three times in two days #21920's timeout spelling and margin comment." The precedent has no per-case timeout. test(rest):meta-state-route-engine-outage.test.ts's multi-kernel wiring case times out at vitest's 5000 ms default under the hourly full run, and has turnedmain's hourly run red three times in two days #21920 landed as PR test(rest): pay the state route's objectql load at collection, so the multi-kernel case stops timing out on the hourly run #21925 (7b6c65290), which states "No globaltestTimeoutchange, no per-case timeout". It moved a module load out of the clocked window into collection, because that cost was loading, not the case's own work. The shape does not transfer here. The cost here is the case's own work, the triage ruled that work stays, and the derivation's only module load (@noble/hashes, on the first pure-JS call) is small: the two one-derivation-per-path cases read 1305 / 1393 and 1435 / 1137 ms in CI. So the spelling follows the repo's own per-case convention instead: the third argument ofit, with a comment stating why and how much.packages/drivers/driver-sql's "Why the it() below carries an explicit 60_000 budget" blocks are an example.Tests and gates
All at
616794ce1(git rev-parse --short HEAD), the PR head.pnpm --filter '@objectstack/plugin-sharing^...' build(the dependency closure), under the verify lock:VERDICT command-exit 0.pnpm --filter @objectstack/plugin-sharing test, under the lock:Test Files 40 passed (40),Tests 1002 passed (1002),VERDICT command-exit 0.pnpm --filter @objectstack/plugin-sharing typecheck, under the lock:VERDICT command-exit 0, withcheck:test-typecheck: OK, 3 pre-existing ledgered errors held.tsc -p tsconfig.test.json --listFilescounts 40src/**/*.test.tsin the program, both edited files among them.The two named files,
pnpm --filter @objectstack/plugin-sharing exec vitest run --maxWorkers=2 FILES: 48 passed (48), before and after. Per-case readings are in the table above.The contention reproduction, before and after: shown in the readings table above.
Gates:
node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstackderived 57 commands for this diff, and all 57 exit 0. Three needed a second run, which is declared here:check:i18nandcheck:dual-build-cjs-loadsfirst answeredPREREQUISITE NOT MET(exit 3, nodist/). Each was run again after building its prerequisite (the i18n closure the gate names, then the workspace build), and both exit 0.check:type-check-debtfirst answered exit 3 because its own build collided with my concurrent closure build:plugin-webhooks' DTS step read a half-writtenservice-messagingdist/. Run again alone, it exits 0.--ranreconciliation: "57 derived famil(ies) accounted for — 57 run, 0 NOT-MEASURED (a DERIVED zero — all 57 recorded an exit code and none of them is 3)".Lint, narrowed to the two changed files:
eslint --no-inline-config --format jsonon both files gives 0 errors and 0 warnings. The narrowing loses nothing, for three reasons:eslint.config.mjs's population, since an ignored file would come back with an ignore warning;parserOptions.project, no typed rules) and loads no import-resolving plugin, so this diff cannot move the verdict on any file it does not touch.The full
pnpm lintis CI's run.No ablation leg is needed beyond the before/after pair. The "before" leg is the committed
ca135dcctree, which is exactly this change removed, and it was run under the same load as the "after" leg.Acceptance notes
share-link-password.test.ts's header lists, as pinned, "the pure-JS scrypt the WebContainer path uses andnode:crypto's produce interchangeable hashes". No case in that file pins it: the pin lives inshare-link-password-webcontainer.test.ts. This is a stale doc bullet, an observation only, and not filed. Carrier: none.Generated by Claude Code