cross-tab-worker-databus 0.21.53 → 0.21.54

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,3 +1,22 @@
1
+ ## [0.21.54] - 2026-10-24
2
+
3
+ The release checklist tells its reader to take two browser benchmark samples and re-derive a lag series at release step 4, and until now it never said what those tools' failure words mean. Two of them — `NOT ARCHIVED` and `NOT TRUSTED` — describe opposite states of the one artifact the loaded-host rule protects, and a reader who cannot tell them apart does exactly the wrong thing. Documentation only; no behavior, API shape, export, default, frame or storage key changed, and the coverage floors and the zero-count arm ledger are `0.21.51`'s.
4
+
5
+ ### Documentation
6
+
7
+ - **The browser-bench verdict vocabulary, in both languages**, keyed on what each verdict did to `bench-results/` rather than glossed: `archived for trend comparison` means the sample **is** now part of the baseline the next five comparisons are judged against; `NOT ARCHIVED` fires *before* the write, so the archive is unchanged and the numbers printed are diagnosis; `NOT TRUSTED` fires *after* it, so the offending report is the baseline until someone moves it out — which is the difference between re-running and removing, and the reason the rule says to move suspect samples aside; `failed to archive results` is a write failure deliberately not treated as a refusal; and four harness self-checks (`Timed out waiting for`, the dispatch, batch and dedup `mis-measured` cases at targets of 1000, 1000 and 500) mean the measurement never completed at all, fired before the report is built, so there is nothing to excuse or defer and no sample to move.
8
+ - **The lag sweep's seven verdict labels** were named in the previous release; this one completes the pair of tools a release actually runs.
9
+
10
+ ### Fixed
11
+
12
+ - **Four user-facing messages that reached readers with no marker at all.** `bench-browser.mjs` delivered its timeout and three self-check failures through `throw new Error` without the `[bench]` prefix, so they appeared in no shipped documentation and were invisible to the new gate that claims to enumerate the vocabulary. They now carry the prefix, following the convention `verify-release-version.mjs` already sets with `throw new Error('[release] …')`: a user-facing string carries its prefix wherever it is delivered. A probe that throws and is caught two lines later (`'unhealthy'`, the demo-server check) deliberately stays unmarked — it is control flow, never a verdict anyone reads.
13
+
14
+ ### Verification
15
+
16
+ - `pnpm check` — 40 files and **1040 tests**, plus the 5 perf gates; `pnpm lint`; `tests/documentation.test.ts` at 27 in both languages.
17
+ - The vocabulary gate's set is **derived from the scripts**, not restated: it reads both delivery channels and its floor moved 8 → 12 so that a channel going blind is a failure rather than a pass. Two mutants, each with the inserted-diff count asserted before the run — a new `console` verdict (`applied=1`) and a renamed *thrown* one (`applied=2`) — each fail the named case; the second is the proof the new channel feeds the set.
18
+ - Every line number, matrix target and before-or-after-the-write claim above was read off the source and the archive path this cycle, not carried from a previous reading.
19
+
1
20
  ## [0.21.53] - 2026-10-24
2
21
 
3
22
  The release checklist tells the reader to re-derive the publish-lag series after cutting a release. Until now it never said what the tool's failure words mean, and two of the seven it can print read as "your release did not publish" when the instrument is actually saying "this read is not usable". Documentation only; no behavior, API shape, export, default, frame or storage key changed, and the coverage floors and the zero-count arm ledger are `0.21.51`'s.
@@ -7,7 +7,7 @@ import {
7
7
  publicationMetadata,
8
8
  selectWorkerBackend,
9
9
  serializeError
10
- } from "./chunk-PCBQYYOA.js";
10
+ } from "./chunk-53EV7J5P.js";
11
11
  import {
12
12
  CENTRIFUGE_INPUT_TYPE,
13
13
  CENTRIFUGE_OUTPUT_TYPE,
@@ -2548,7 +2548,7 @@ var SDK_VERSION = (
2548
2548
  // declaration's type is `string | undefined` and the export's is `string`
2549
2549
  // (dropping the conditional is TS2322, measured). Recorded here so the leg reads
2550
2550
  // as a shape the harness cannot reach rather than as a case nobody exercised.
2551
- true ? "0.21.53" : ""
2551
+ true ? "0.21.54" : ""
2552
2552
  );
2553
2553
 
2554
2554
  // src/core/data-bus.ts
@@ -3801,4 +3801,4 @@ export {
3801
3801
  parseDataBusPublication,
3802
3802
  selectWorkerBackend
3803
3803
  };
3804
- //# sourceMappingURL=chunk-PCBQYYOA.js.map
3804
+ //# sourceMappingURL=chunk-53EV7J5P.js.map
@@ -2677,7 +2677,7 @@ var SDK_VERSION = (
2677
2677
  // declaration's type is `string | undefined` and the export's is `string`
2678
2678
  // (dropping the conditional is TS2322, measured). Recorded here so the leg reads
2679
2679
  // as a shape the harness cannot reach rather than as a case nobody exercised.
2680
- true ? "0.21.53" : ""
2680
+ true ? "0.21.54" : ""
2681
2681
  );
2682
2682
 
2683
2683
  // src/core/data-bus.ts
@@ -2643,7 +2643,7 @@ var SDK_VERSION = (
2643
2643
  // declaration's type is `string | undefined` and the export's is `string`
2644
2644
  // (dropping the conditional is TS2322, measured). Recorded here so the leg reads
2645
2645
  // as a shape the harness cannot reach rather than as a case nobody exercised.
2646
- true ? "0.21.53" : ""
2646
+ true ? "0.21.54" : ""
2647
2647
  );
2648
2648
 
2649
2649
  // src/core/data-bus.ts
package/dist/index.js CHANGED
@@ -20,7 +20,7 @@ import {
20
20
  selectRebalanceTarget,
21
21
  selectWorkerBackend,
22
22
  topicMatchesPattern
23
- } from "./chunk-PCBQYYOA.js";
23
+ } from "./chunk-53EV7J5P.js";
24
24
  import {
25
25
  DEFAULT_STORAGE_PREFIX,
26
26
  PRUNE_STRATEGY,
@@ -25,7 +25,17 @@ Only `pnpm bench:browser` / `pnpm bench:compare` / `pnpm bench:trend` stay local
25
25
  - **With a recorded baseline**, a reading above four times the baseline's own median is refused. The threshold is a ratio rather than an absolute number because the archive spans machines and a load average means nothing without its core count.
26
26
  - **With no recorded baseline** — which is the archive's entire current state, since every report predates the field — a reading above the host's own core count is refused, because more runnable work than cores is oversubscribed by definition and needs no history to establish. Without this leg the guard is weakest exactly where the archive is weakest, and the first sample taken on a busy host is the one that would end a twenty-five-tag deferral streak.
27
27
 
28
- Both boundaries are inclusive and both are a refusal to *trust* rather than a claim the sample is invalid, so a false positive costs one deferred run rather than a release. A failed *write* is deliberately not treated as a refusal: the numbers were produced, a full disk is not a judgment about the host, and conflating the two would fail a release over a sample that was correctly taken. A platform that publishes neither a finite reading nor a core count admits the sample, since there is nothing to judge it on.
28
+ Both boundaries are inclusive and both are a refusal to *trust* rather than a claim the sample is invalid, so a false positive costs one deferred run rather than a release.
29
+
30
+ **Which verdict entered the archive is the fact a reader needs, and the labels say it in words no procedure used to name.** Eight are printed across the two steps, and three of them are exit 1 for reasons that are *not* a performance regression:
31
+
32
+ - `archived for trend comparison` — the sample is in `bench-results/`, so it is part of the baseline the next five comparisons are judged against. This is the sentence the whole loaded-host rule is about, and it is the only one that says it happened.
33
+ - `NOT ARCHIVED: <reason>` (from `bench:browser`, exit 1) — the host-load refusal fired *before* the write, so it prints `the numbers above are printed for diagnosis; re-run on an idle host` and the archive is unchanged: the numbers above it are diagnosis, not a member. A refusal printed about a file that does not exist is a real refusal.
34
+ - `NOT TRUSTED: <reason>` (from `bench:compare`, exit 1) — the opposite state: the sample **is** on disk and the judge is declining to believe it, printing `the numbers below are printed for diagnosis; re-run on an idle host`. This one needs acting on, not just re-running: the offending report is the baseline until it is moved out of `bench-results/`, which is why the rule above says to move suspect samples aside rather than re-run them into place.
35
+ - `failed to archive results: <reason>` (warn, exit 0) — a write failure, deliberately *not* treated as a refusal: the numbers were produced and a full disk says nothing about the host.
36
+ - `no metric in <the newer|newest report> has a baseline sample, so nothing was compared — check that the metric labels still line up` (exit 1) — zero metrics were compared. It is not a pass and not a regression: it means the metric names in the report and the tool have drifted apart.
37
+ - `FAIL: <n>% regression threshold exceeded`, `OK: no metric regressed more than <n>`, and `within-baseline, not gated:` — the three comparison verdicts, where the last lists the metrics the ceiling leg excused rather than letting them pass silently (see above: an empty list is the norm on a quiet host, not a clean bill).
38
+ - `Timed out waiting for <url>`, `wildcard dispatch mis-measured:`, `publishBatch mis-measured:` and `dedup mis-measured:` (exit 1) — a fourth kind, and the one most likely to be mistaken for the first: these are `bench:browser`'s **own self-checks**, fired when the page delivered fewer publications than the harness asked it to (its targets are 1000 dispatched for the two publish matrices and 500 delivered for the dedup matrix), or when the demo server never answered. They mean the *measurement* did not complete, so no report was produced and the archive is untouched — neither a loaded-host refusal nor a regression, and nothing to move aside. Re-run; if it repeats on an idle host, the demo page or the harness changed, which is a code finding rather than a timing one. A failed *write* is deliberately not treated as a refusal: the numbers were produced, a full disk is not a judgment about the host, and conflating the two would fail a release over a sample that was correctly taken. A platform that publishes neither a finite reading nor a core count admits the sample, since there is nothing to judge it on.
29
39
  5. Confirm the package contains only intended files with `npm pack --dry-run --json`.
30
40
  6. Commit on a feature branch, push that branch, and merge its green PR using squash or fast-forward (no merge commit). Fetch the merged commit, tag that exact commit, and push only the specific version tag (with the local tag present, run the gate set once more before that push — see *Tagged-release workflow* below for why that moment is the one no earlier CI sees); never push `main`/`master` directly or force-push. The workflow runs `node scripts/verify-release-version.mjs` to require `RELEASE_TAG` to equal `v` + the package version and to require exactly one non-empty CHANGELOG section. The same step compares every version heading in `CHANGELOG.md` against the registry's own `time` map and refuses a release record that names a version npm never had — but only from `0.20.85` upward, because below that floor 40 sections name versions the registry never recorded, 27 of them tagged (both re-derived 2026-09-25; the command to re-derive them intersects three sets — `grep -oE '^## \[[0-9]+\.[0-9]+\.[0-9]+\]' CHANGELOG.md`, `npm view cross-tab-worker-databus time --json --registry https://registry.npmjs.org`, and `git tag -l` — because the registry read alone answers neither count, let alone the tagged half), and the gate exists to stop a new hole rather than to litigate history. The version being tagged is exempt, since it legitimately cannot be on npm yet, and a registry read that fails prints a skip warning and exits 0: refusing a good release over one transient HTTP call is the failure mode the published-consumer ceiling was sized to avoid, so only a *confirmed* absence is fatal. Note the `--registry` in that re-derive command, and that the gate passes it too: a configured **mirror** can answer successfully while 3,418 s behind. Measured while preparing `0.21.35` on a machine whose `npm` points at `registry.npmmirror.com` — its packument still reported `modified: 00:32:51Z` (the `0.21.33` publish) when the gate ran at `03:41:18Z` — 3,207 s (53 min) after npmjs had recorded `0.21.34` at `02:47:50Z`, and the mirror's own `modified` did not move until `03:44:48.778Z`, 3,418 s after the record — so the gate declared a public version "never recorded", from a read that *succeeded*. A mirror's sync lag is not HTTP caching, so `cache-control: no-cache` returns the same stale document; naming the registry is the only thing that fixes it. `verify-published-consumer.mjs` had already pinned it for both of its reads; this was the last npm-based registry read in the repository that did not.
31
41
 
package/docs/roadmap.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # Roadmap
2
2
 
3
- 0.21.52 was released on October 24, 2026. The project is intentionally continuing through reliability-focused releases before a 1.0.0 stability freeze.
3
+ 0.21.53 was released on October 24, 2026. The project is intentionally continuing through reliability-focused releases before a 1.0.0 stability freeze.
4
4
 
5
5
  ## 0.21.44 delivered scope
6
6
 
@@ -26,7 +26,17 @@
26
26
  - **归档中已有可比较的读数时**,超过基线中位数四倍的读数被拒绝。取比值而非绝对值,是因为归档跨机器,而负载均值脱离核心数没有意义。
27
27
  - **归档中没有任何读数时**——这正是当前归档的全部状态,因为每份报告都早于该字段——超过主机自身核心数的读数被拒绝,因为可运行负载超过核心数按定义就是超订,且这不需要任何历史即可成立。没有这一条,守卫恰好在归档最弱的地方也最弱,而第一个在繁忙主机上采到的样本,正是那个会让二十五次发布推迟 streak 结束的样本。
28
28
 
29
- 两条边界都取闭区间,且都是"不予*采信*"而不是"样本无效"的判定,因此误报只代价一次推迟,而不是一次发布。写入*失败*被刻意区别对待、不算拒绝:数字已经产出,磁盘写满不是对主机的判断,把两者混为一谈会让一个采样正确、只是存不下来的样本卡住一次发布。既不发布有限读数也不发布核心数的平台会放行该样本,因为没有任何东西可以据以判定它。
29
+ 两条边界都取闭区间,且都是"不予*采信*"而不是"样本无效"的判定,因此误报只代价一次推迟,而不是一次发布。
30
+
31
+ **「哪条裁决进了归档」才是读者需要的事实,而这些标签用词说出的这件事,此前没有任何流程命名过。** 两个步骤一共打印八个裁决,其中三个是退出码 1,而原因都**不是**性能回归:
32
+
33
+ - `archived for trend comparison` —— 样本已进入 `bench-results/`,于是它就是接下来五次比较所依据的基线的一部分。整条「负载主机」规则说的就是这件事,而这是唯一一句说明它发生了的话。
34
+ - `NOT ARCHIVED: <原因>`(来自 `bench:browser`,退出码 1)—— 宿主负载的拒绝在写入*之前*触发,所以它会打印 `the numbers above are printed for diagnosis; re-run on an idle host`,而归档没有任何变化:上面那些数字只是诊断,不是基线成员。一份不存在的文件上打印的拒绝,才是真正的拒绝。
35
+ - `NOT TRUSTED: <原因>`(来自 `bench:compare`,退出码 1)—— 相反的状态:样本**已经**在磁盘上,是判定方拒绝采信它,并打印 `the numbers below are printed for diagnosis; re-run on an idle host`。这条需要动手处理,而不只是重跑:惹事的那份报告会成为基线,直到它被移出 `bench-results/`——这正是上面的规则说「把可疑样本挪开」而不是「重跑把它冲掉」的原因。
36
+ - `failed to archive results: <原因>`(warn,退出码 0)—— 一次写入失败,故意**不**当作拒绝:数字已经产出,而磁盘满了并不说明宿主的情况。
37
+ - `no metric in <the newer|newest report> has a baseline sample, so nothing was compared — check that the metric labels still line up`(退出码 1)—— 零个指标被比较。它既不是通过也不是回归:它意味着报告里的指标名和工具用的名字已经对不上了。
38
+ - `FAIL: <n>% regression threshold exceeded`、`OK: no metric regressed more than <n>` 与 `within-baseline, not gated:` —— 三条比较裁决,最后一条会列出被百分比上限放过、而不是静默通过的指标(见上文:在空闲主机上「空清单」才是常态,那并不是一张合格证)。
39
+ - `Timed out waiting for <url>`、`wildcard dispatch mis-measured:`、`publishBatch mis-measured:` 与 `dedup mis-measured:`(退出码 1)—— 第四类,也是最容易被误当成第一类的那几种:它们是 `bench:browser` **自己的自检**,在页面实际投递的发布数少于工具要求的目标时触发(两个 publish 矩阵的目标是 1000 次派发,dedup 矩阵是 500 次送达),或者 demo 服务器一直没有应答。它们意味着*测量*没有完成,因此没有产出报告,归档也没有被动过 —— 既不是负载主机的拒绝,也不是回归,更没有东西需要挪开。重跑即可;若在空闲主机上重复出现,那就是 demo 页面或工具本身变了,这是一个代码发现,而不是时序问题。写入*失败*被刻意区别对待、不算拒绝:数字已经产出,磁盘写满不是对主机的判断,把两者混为一谈会让一个采样正确、只是存不下来的样本卡住一次发布。既不发布有限读数也不发布核心数的平台会放行该样本,因为没有任何东西可以据以判定它。
30
40
  5. 用 `npm pack --dry-run --json` 确认发布包只包含预期文件。
31
41
  6. 在功能分支提交并推送该分支,PR 验证通过后使用 squash 或 fast-forward 合入(不创建 merge commit)。获取合入后的精确提交并打 tag,只推送该版本 tag(推送之前,带着本地已存在的 tag 再把门禁跑一遍——原因见下文“打 tag 的发布工作流”,那是此前的 CI 覆盖不到的构建状态);禁止直接推送 `main`/`master` 或 force-push。工作流运行 `node scripts/verify-release-version.mjs`,要求 `RELEASE_TAG` 等于 `v` 加 package 版本,且 CHANGELOG 中恰好有一个非空的对应版本章节。同一步骤还会把 `CHANGELOG.md` 里每个版本标题与 registry 自己的 `time` 映射相对照,拒绝一份"点名了 npm 从未有过的版本"的发布记录——但只从 `0.20.85` 往上要求:在那之下有 40 个章节点名了 registry 从未记录过的版本,其中 27 个还带着 git tag(两个数都在 2026-09-25 重新核过;重算的命令要把三个集合求交——`grep -oE '^## \[[0-9]+\.[0-9]+\.[0-9]+\]' CHANGELOG.md`、`npm view cross-tab-worker-databus time --json --registry https://registry.npmjs.org`、`git tag -l`——因为只读 registry 既得不出 40,更得不出其中带 tag 的 27),这道门禁要拦的是新的漏洞,不是去翻历史旧账。而 registry 读取失败时,这一半会被跳过并打印一行警告、退出 0,而不是把一次好发布判红:为一个瞬时的 HTTP 失败拒绝一次成功发布,正是已发布消费者上限当初要避免的那类失败,所以只有*确认存在*的缺失才会中止发布。请注意上面那条重算命令里的 `--registry`,也请注意门禁自己也会带上它:一台配置了**镜像**的机器可以成功应答,却落后 3,418 秒。这是准备 `0.21.35` 时在本机量到的——这台机器的 `npm` 指向 `registry.npmmirror.com`,它的 packument 仍报 `modified: 00:32:51Z`(也就是 `0.21.33` 那次发布),而门禁运行在 `03:41:18Z`——比 npmjs 在 `02:47:50Z` 记下 `0.21.34` 晚 3,207 秒(53 分钟)——镜像自己的 `modified` 直到 `03:44:48.778Z` 才动,比那条记录晚 3,418 秒,于是门禁把一个已经公开的版本判成"registry 从未记录",而这一次读取是**成功**的。镜像的同步滞后不是 HTTP 缓存,所以 `cache-control: no-cache` 拿回来的还是同一份旧文档;只有显式点名 registry 才能纠正。`verify-published-consumer.mjs` 早已为它的两次读取钉住了 registry,这里是仓库里最后一处没有钉的 npm 读取。正在打 tag 的那个版本本身被豁免,因为它此刻本来就不该在 npm 上。
32
42
 
@@ -1,6 +1,6 @@
1
1
  # 路线图
2
2
 
3
- 0.21.52 已于 2026 年 10 月 24 日发布。项目会先持续完成可靠性发布,再进入 1.0.0 稳定性冻结。
3
+ 0.21.53 已于 2026 年 10 月 24 日发布。项目会先持续完成可靠性发布,再进入 1.0.0 稳定性冻结。
4
4
 
5
5
  ## 0.21.44 已完成范围
6
6
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "cross-tab-worker-databus",
3
- "version": "0.21.53",
3
+ "version": "0.21.54",
4
4
  "description": "Framework-agnostic cross-tab data bus with Dedicated/Shared Worker clustering and Centrifuge support.",
5
5
  "type": "module",
6
6
  "license": "MIT",