cross-tab-worker-databus 0.21.34 → 0.21.35
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 +25 -0
- package/dist/centrifuge.js +1 -1
- package/dist/{chunk-A3UG46FI.js → chunk-EVVTH6BQ.js} +2 -2
- package/dist/chunk-EVVTH6BQ.js.map +7 -0
- package/dist/cjs/centrifuge.cjs +1 -1
- package/dist/cjs/centrifuge.cjs.map +2 -2
- package/dist/cjs/index.cjs +1 -1
- package/dist/cjs/index.cjs.map +2 -2
- package/dist/core/routing.d.ts +3 -2
- package/dist/core/routing.d.ts.map +1 -1
- package/dist/core/trace.d.ts +2 -1
- package/dist/core/trace.d.ts.map +1 -1
- package/dist/index.js +1 -1
- package/docs/release-checklist.md +2 -2
- package/docs/roadmap.md +12 -1
- package/docs/zh/release-checklist.md +2 -2
- package/docs/zh/roadmap.md +12 -1
- package/package.json +1 -1
- package/dist/chunk-A3UG46FI.js.map +0 -7
package/dist/core/routing.d.ts
CHANGED
|
@@ -64,8 +64,9 @@ export declare function selectActiveWorkers(workers: readonly WorkerRecord[], ma
|
|
|
64
64
|
* WorkerClusterRuntime intentionally does not use it — it has no caller in `src/`
|
|
65
65
|
* besides the barrel — so nothing here migrates a route on any cadence:
|
|
66
66
|
* established routes are sticky and load applies only when selecting a *new*
|
|
67
|
-
* owner. Pinned by tests/cluster.test.ts's
|
|
68
|
-
* tests/routing.test.ts's
|
|
67
|
+
* owner. Pinned by tests/cluster.test.ts's "keeps existing topic owners and
|
|
68
|
+
* balances only newly introduced topics" and tests/routing.test.ts's
|
|
69
|
+
* "keeps a live owner sticky before considering load".
|
|
69
70
|
*/
|
|
70
71
|
export declare function selectRebalanceTarget(workers: readonly WorkerRecord[], currentWorkerId: string): WorkerRecord | null;
|
|
71
72
|
/** True when a route's owner is still a live, active Worker in the given set. */
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"routing.d.ts","sourceRoot":"","sources":["../../src/core/routing.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AACH,OAAO,KAAK,EAAE,oBAAoB,EAAE,YAAY,EAAE,WAAW,EAAE,MAAM,SAAS,CAAC;AAG/E;;4EAE4E;AAC5E,eAAO,MAAM,0BAA0B,IAAI,CAAC;AAE5C;;;;;;;;GAQG;AACH,wBAAgB,mBAAmB,CACjC,MAAM,EAAE,YAAY,EACpB,OAAO,CAAC,EAAE,oBAAoB,GAC7B,MAAM,CA2CR;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,uBAAuB,CAAC,OAAO,EAAE,OAAO,GAAG,MAAM,CAEhE;AA4CD;;;;;;GAMG;AACH,wBAAgB,uBAAuB,CACrC,OAAO,EAAE,SAAS,YAAY,EAAE,EAChC,iBAAiB,CAAC,EAAE,MAAM,EAC1B,OAAO,CAAC,EAAE,oBAAoB,GAC7B,YAAY,GAAG,SAAS,CAY1B;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,mBAAmB,CACjC,OAAO,EAAE,SAAS,YAAY,EAAE,EAChC,gBAAgB,SAA6B,GAC5C,YAAY,EAAE,CAchB;AAED
|
|
1
|
+
{"version":3,"file":"routing.d.ts","sourceRoot":"","sources":["../../src/core/routing.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AACH,OAAO,KAAK,EAAE,oBAAoB,EAAE,YAAY,EAAE,WAAW,EAAE,MAAM,SAAS,CAAC;AAG/E;;4EAE4E;AAC5E,eAAO,MAAM,0BAA0B,IAAI,CAAC;AAE5C;;;;;;;;GAQG;AACH,wBAAgB,mBAAmB,CACjC,MAAM,EAAE,YAAY,EACpB,OAAO,CAAC,EAAE,oBAAoB,GAC7B,MAAM,CA2CR;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,uBAAuB,CAAC,OAAO,EAAE,OAAO,GAAG,MAAM,CAEhE;AA4CD;;;;;;GAMG;AACH,wBAAgB,uBAAuB,CACrC,OAAO,EAAE,SAAS,YAAY,EAAE,EAChC,iBAAiB,CAAC,EAAE,MAAM,EAC1B,OAAO,CAAC,EAAE,oBAAoB,GAC7B,YAAY,GAAG,SAAS,CAY1B;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,mBAAmB,CACjC,OAAO,EAAE,SAAS,YAAY,EAAE,EAChC,gBAAgB,SAA6B,GAC5C,YAAY,EAAE,CAchB;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,qBAAqB,CACnC,OAAO,EAAE,SAAS,YAAY,EAAE,EAChC,eAAe,EAAE,MAAM,GACtB,YAAY,GAAG,IAAI,CAYrB;AAED,iFAAiF;AACjF,wBAAgB,cAAc,CAAC,KAAK,EAAE,WAAW,GAAG,IAAI,EAAE,OAAO,EAAE,SAAS,YAAY,EAAE,GAAG,OAAO,CAEnG;AAED;;oDAEoD;AACpD,wBAAgB,eAAe,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAExD;AAED;;;sCAGsC;AACtC,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAM3E"}
|
package/dist/core/trace.d.ts
CHANGED
|
@@ -122,7 +122,8 @@ type DataBusTraceEventInput = DataBusTraceEvent extends infer TEvent ? TEvent ex
|
|
|
122
122
|
export interface DataBusTraceOptions {
|
|
123
123
|
/** Tracing is opt-in: when `false` the reporter is inert, and the default is
|
|
124
124
|
* `false` — so `trace: { sink }` alone emits nothing and `enabled: true` is
|
|
125
|
-
* required. Pinned by `tests/data-bus.test.ts`'s
|
|
125
|
+
* required. Pinned by `tests/data-bus.test.ts`'s "keeps tracing disabled by
|
|
126
|
+
* default". */
|
|
126
127
|
enabled?: boolean;
|
|
127
128
|
/** Which event categories to emit. Default `all`. */
|
|
128
129
|
mode?: DataBusTraceMode;
|
package/dist/core/trace.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"trace.d.ts","sourceRoot":"","sources":["../../src/core/trace.ts"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"trace.d.ts","sourceRoot":"","sources":["../../src/core/trace.ts"],"names":[],"mappings":"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"}
|
package/dist/index.js
CHANGED
|
@@ -22,7 +22,7 @@ Only `pnpm bench:browser` / `pnpm bench:compare` / `pnpm bench:trend` stay local
|
|
|
22
22
|
3. Gate dependency security: `pnpm audit --registry=https://registry.npmjs.org` (the configured mirror registry lacks the audit endpoint; CI runs it on the public registry in the verify job). Fail the release on any known-vulnerability advisory; `pnpm-workspace.yaml` overrides pin patched ranges.
|
|
23
23
|
4. Gate browser benchmark regressions: run `pnpm bench:browser` (twice at minimum; the gate compares the newest report against the median of the same metric in up to the five preceding archived reports, **and additionally requires the new value to exceed the highest of those samples**) and then `pnpm bench:compare --fail-above-pct 50`. The median baseline exists because the in-page hot-path metrics alternate between a fast and a slow mode on identical code — two consecutive runs of `dedup1000Ms` measured 12.7 ms then 25.6 ms, and a single noisy previous report could fail the documented ceiling with no code change at all. The second leg exists because a median over an alternating sample is only stable while the modes stay mixed: when several consecutive reports land in the same mode the median itself jumps, which is how one comment-only tree read +121% against one baseline and −56.7% against another minutes apart. A metric excused by that leg is printed under `within-baseline, not gated` rather than passing silently — measured on the current archive, `publish/dedicated/perMessageMs`, `publish/shared/perMessageMs` and `databus/publishBatch1000Ms` were all inside their own five-report range, so treat that list as the metric's real resolution, not as a clean bill. `publish/dedicated/perMessageMs` is noisy the same way: two consecutive runs of identical code measured 49.9 ms then 71.1 ms, failing the gate at +75.7% while `dedup1000Ms` and `traceAndPublish1000Ms` *improved* in the same report — that single-metric-up-everything-else-down shape is the noise signature, and the next run reported 49.4 ms and passed. Before accepting or acting on a failure, re-run and check the spread; if the change touches a hot path, size it directly (a micro-benchmark of the 0.20.95 bounded-map rewrites measured 80–86 ns/op across all three variants, i.e. inside run-to-run variance). A baseline shift (e.g. a metric becoming real instead of a no-op) is an expected one-time failure. Refresh the long-run trend doc with `pnpm bench:trend` and commit it when the tables change. **Re-running a failure can manufacture its own excuse, and that was measured.** `bench:compare`'s baseline — both the median and the maximum leg — is drawn from the most recent archived reports, and every `bench:browser` run appends to that same archive, so the samples you add while investigating a failure become the baseline that judges the next attempt. On 2026-09-24, at host load averages 7.7–15.1 with a zero non-comment `src/` diff in the tree, the first two runs measured `publish/dedicated 77.3 ms` and `publish/shared 67.7 ms` against a ~38/~34 ms fast mode and failed the 50% ceiling; five further re-runs reported **OK**, because the baseline median had slid to `70.6`. One clean run after taking those samples out of the archive showed every metric rising together (`dedicated +92.5%`, `shared +99.2%`, `dedup +43.4%`, `trace +21.0%`), which is the contention signature and the inverse of the single-metric-up shape above. So read the load average *before* re-running, and when a suspect failure occurs on a loaded host, record it as deferred with its numbers and move the off-mode samples aside instead of letting them into the baseline — and do not regenerate `docs/benchmarks.md` from them. **The standing decision, recorded at 0.21.17 (the fifth release in that state): when no quiet window exists across a cycle, defer the sample with its load numbers instead of taking it on a loaded host.** The deferral is what keeps `docs/benchmarks.md` stale, and staleness-by-recorded-opportunity is the intended outcome of this step, not a gate that passed quietly; what would not be acceptable is a loaded sample entering the archive, because that becomes the baseline the next release is judged against.
|
|
24
24
|
5. Confirm the package contains only intended files with `npm pack --dry-run --json`.
|
|
25
|
-
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; 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 (measured on the day `v0.21.33` published; re-derive with `npm view cross-tab-worker-databus time --json`), 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.
|
|
25
|
+
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; 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 (measured on the day `v0.21.33` published; re-derive with `npm view cross-tab-worker-databus time --json --registry https://registry.npmjs.org`), 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.
|
|
26
26
|
|
|
27
27
|
## Security and dependency scanning
|
|
28
28
|
|
|
@@ -42,6 +42,6 @@ For a manual release (no `NPM_TOKEN` in the workflow), run `npm publish --access
|
|
|
42
42
|
2. Install the published tarball or version in a clean consumer and import the root plus every public subpath.
|
|
43
43
|
3. Record the result in the release notes. Do not advance to `1.0.0` until the public API and protocol deprecation policy are explicitly frozen.
|
|
44
44
|
|
|
45
|
-
The tagged-release workflow already ran the consumer verification above. Run `PUBLISHED_VERSION=<version> pnpm verify:published` by hand only when you need an offline repeat. The workflow's 12-minute ceiling is not padding, and the number is a sum of two measured lags. First: the registry records the publish minutes after `npm publish` returns — the ack→`time[<version>]` gap measured on 0.21.27 through 0.21.
|
|
45
|
+
The tagged-release workflow already ran the consumer verification above. Run `PUBLISHED_VERSION=<version> pnpm verify:published` by hand only when you need an offline repeat. The workflow's 12-minute ceiling is not padding, and the number is a sum of two measured lags. First: the registry records the publish minutes after `npm publish` returns — the ack→`time[<version>]` gap measured on 0.21.27 through 0.21.34 was 74.8–310.0 s, and 248.7 / 96.8 / 74.8 / 310.0 / 127.2 / 76.1 / 75.5 / 75.8 s in version order (read `time[<version>]` from the packument and compare it with the `Publish to npm` step's completion time). The last three of those — 76.1, 75.5, 75.8 s — span half a second across three consecutive releases, so the record lag is not what moves; the step still took ~304 s each time, which is the second lag, not this one. Second: the packument that `npm pack` resolves against is served with `cache-control: public, max-age=300`, so a copy of it anywhere in the path is allowed to be up to five minutes stale, and resolution cannot be trusted to beat one cache lifetime after a write that lands inside that window. Which copy — npm's local cache or an intermediate — served the stale views below is *not* established, and the counter-observation this note used to offer against a purely local account does not survive being read carefully, because it joins two machines: the 21:44:34Z "fresh fetch" was a `npm pack` run on a laptop, while the 1 s green at 21:47:10Z was a *new runner job*, and a new job's npm cache starts empty — which is what the local account predicts rather than what contradicts it. What a purely-local account still cannot explain is one specific reading: the runner's own `ETARGET` at 21:45:07Z — 364 s after the step that opened its first poll (21:39:03Z), so ~60 s *after* the point where a copy taken then must have gone stale (21:44:03Z, give or take the second the request actually left). The ceiling is therefore arithmetic rather than a model: worst-case ack→`time[]` gap plus one cache lifetime. That sum is not theoretical: the previous 6-minute ceiling was exhausted by a *successful* publish, the 0.21.30 tag run spending its whole 360 s budget (48 × 7.5 s) on `ETARGET` while that version's tarball HEAD and a local `npm pack` had both already succeeded, and the same step 123 s later taking 1 s. The 0.20.89 run had shown the same shape against that release's 2-minute budget. So a long retrying verify is normal propagation, not an unusually slow mirror — raise `PUBLISHED_VERIFY_ATTEMPTS` / `PUBLISHED_VERIFY_DELAY_MS` further only if the ack→`time[]` gap has itself grown. Note the asymmetry when repeating by hand: `verify-published-consumer.mjs` defaults to `6` attempts × `5_000` ms — 30 seconds, not 12 minutes — because the tag run passes the larger pair as env rather than the script owning it. So a manual repeat can report `ETARGET` on a version whose CI run was green, which reads like a failed publish and is not one; reproduce the CI budget explicitly with `PUBLISHED_VERSION=<version> PUBLISHED_VERIFY_ATTEMPTS=96 PUBLISHED_VERIFY_DELAY_MS=7500 pnpm verify:published`, or judge a manual failure against the 30 s budget you actually ran. Read the step's duration as a diagnostic rather than a mood: the five green tag runs from 0.21.25 to 0.21.29 took 307, 304, 305, 304 and 304 s, 0.21.31's took 308 s and 0.21.32's, 0.21.33's and 0.21.34's took 304 s each — nine samples in a 5-second band, which is what one 300 s cache lifetime plus a few seconds of command time predicts (take the numbers from each release run's `Verify published npm consumers` step, `steps[].started_at`/`completed_at` from the same `gh api` call). 0.21.31 through 0.21.34 are the first four releases to run under the raised 720 s ceiling, and their lags split differently, which is the useful contrast: 127.2 s of record plus ~181 s of cache for the first, 76.1 s of record plus ~228 s before the runner saw it for the next, 75.5 s plus ~229 s for the third, and 75.8 s plus ~228 s for the fourth — on 0.21.32 a `no-cache` packument read from a laptop reported the new `latest` 151 s in, while the runner's own poll kept failing until 304 s, i.e. one lifetime after *its* first fetch. So the sample that set the previous ceiling has not been beaten: if the *ack→`time[]`* figure grows past 310 s, raise `PUBLISHED_VERIFY_ATTEMPTS` again; a verify that finishes in seconds means whatever copy it read already carried the new version.
|
|
46
46
|
|
|
47
47
|
**Do not adjudicate a publish from `dist-tags.latest`.** The package document endpoint and the tarball URL are two different read paths, and their timestamps are worth more than the impression they first give. Read during the 0.21.30 release, in order: `npm publish` returned success at `21:39:03Z`; a `curl -s -H "Cache-Control: no-cache" https://registry.npmjs.org/<pkg>` at `21:43:15Z` reported `latest` still on the previous version, no `0.21.30` in `versions`, and no `time['0.21.30']`; npm's own stamp for that version is `21:44:12.987Z` and the document's `last-modified` is `21:44:14 GMT`. So the 21:43:15 read was **not** a replica refusing an artifact that already existed — the registry had not finished recording the publish, and `no-cache` could not have made it see a version that did not exist yet. Absence from `versions` and from `latest`, minutes after a green publish step, is the expected state of a successful release. The genuine read-path disagreement came 60 s later: between `21:44:14Z` and `21:44:35Z`, `curl -I …/-/<pkg>-0.21.30.tgz` returned `HTTP 200` and a local `npm pack <pkg>@0.21.30 --registry https://registry.npmjs.org` completed and wrote the tarball, while the workflow runner's own `npm pack` kept returning `ETARGET` until it exhausted its budget at `21:45:07Z`. Same version, two machines, opposite answers a minute apart. To answer "did the publish happen", HEAD the tarball URL and try the pack — those reach the artifact — and try it **from the place that is failing**, because "fetchable" is a per-machine answer; read `latest` only for the question "which version do consumers get by default", which is a different one and settles later. `npm view <pkg> version` adds a third path on top: it can be served from npm's *local* packument cache, so on a machine that queried the registry before the publish it can return the superseded version even after the global `latest` has moved (that is why this gate resolves its target from `PUBLISHED_VERSION` and refuses an inferred version that disagrees with `package.json`). Never re-push, move, or replace a tag because a metadata read looked stale; the tag is immutable once published.
|
package/docs/roadmap.md
CHANGED
|
@@ -1,6 +1,17 @@
|
|
|
1
1
|
# Roadmap
|
|
2
2
|
|
|
3
|
-
0.21.
|
|
3
|
+
0.21.35 was released on September 25, 2026. The project is intentionally continuing through reliability-focused releases before a 1.0.0 stability freeze.
|
|
4
|
+
|
|
5
|
+
## 0.21.35 delivered scope
|
|
6
|
+
|
|
7
|
+
- **The release gate could declare a published version unpublished, and it did.** `scripts/verify-release-version.mjs` read the packument with a bare `npm view <pkg> time --json`, so it consulted the *configured* registry. Measured while preparing this release on a machine whose `npm` points at `registry.npmmirror.com`: its copy still reported `modified: 2026-09-25T00:32:51.767Z` — the `0.21.33` publish — when the gate ran at `03:41:18Z`, 3,207 s (53 min) after npmjs recorded `0.21.34` at `02:47:50.782Z`; the mirror's `modified` moved to `03:44:48.778Z`, so it had been answering without the version for 3,418 s, and the gate exited 1 with `CHANGELOG names 1 release(s) the registry has never recorded: 0.21.34`. (An earlier draft of this sentence said “75 minutes”, which is the gap between two *previous* stamps and not an elapsed time in this story.) The header's inference ("`time[]` records never expire, so a version missing from a read that succeeded was never published") failed on its second premise: a mirror that has not *learned* of a version is not a record of its absence, and since a mirror's sync lag is not HTTP caching, `cache-control: no-cache` returns the same stale document. The read now names `https://registry.npmjs.org`, which is what makes "the registry" mean the thing whose absence is proof; re-run after the fix it prints `the 48 earlier CHANGELOG releases from 0.20.85 on are all in the registry's time map`, exit 0. `verify-published-consumer.mjs` had already pinned `--registry` on both of its reads for the same reason — this was the last npm-based registry read in the repository that did not, and both checklist languages carried the unpinned command as the re-derive instruction. Every existing case for this gate injects `RELEASE_REGISTRY_TIME`, which is exactly why none of them could see which registry is consulted; the new case runs the un-injected path against a stub `npm` on `PATH` and asserts the flag and the URL arrive as a pair.
|
|
8
|
+
- **A citation to a test is now checked against the suite.** `tests/documentation.test.ts` gained "resolve to a case that exists in the file they name": every `tests/<file>`'s `<name>` in `src/**`, `AGENTS.md`, `README.md` and the public docs must match exactly one `it()`/`test()` title in the file it names (18 citations across 52 files today; a trailing `…` may prefix-match, against one hit). Both halves of the scan are controlled, because both can go empty and an empty failure list looks like a clean bill either way — the title collector must find the suite's cases (977 across 40 files, floored at 500 rather than at the corpus size), the citation pattern must examine at least one, and the `e2e` corpus is asserted on its own since 937 of those 977 come from `tests/` and the total could not notice it going empty. Two scope decisions are measured: the two exempt surfaces hold 11 more citations between them (`docs/progress.md` 8, `CHANGELOG.md` 3) while the public docs hold 1 — an earlier draft of this sentence wrote "`docs/` holds 8", which had quietly counted `progress.md`, the file the gate does not scan — and exactly one of the exempt 11 does not resolve, because it quotes this gate's own failure message rather than pointing at a case; and the looser `filename … ('a name')` window was tried and rejected — across the whole scope it yields exactly one candidate, and that candidate is the `new Error(…)` message inside the very function whose doc comment names the test. Five mutants, each with its own named failure; one earlier mutant was discarded for not compiling (a dropped backslash made the regex unterminated, which reports as a suite collection error rather than as a killed behavior). The gate reddened the `AGENTS.md` bullet written about it, because the first draft quoted the two defective tokens in citation form — so the rule now says to name a token bare when the sentence is about a defect.
|
|
9
|
+
- **Two shipped comments named a fixture instead of a case, and both are published.** `src/core/routing.ts` cited `sticky-existing-routes` and `src/core/trace.ts` cited `no-trace`; neither is an `it()` title, both are `clusterKey` literals written inside a test body, so each pointer landed mid-case and the first rename of either arbitrary string would have pointed nowhere — with nothing in the build, the type check or the suite to contradict it, which is the same silence as a drifted `:NNN`. Both sit in declaration JSDoc, so the corrected text reaches consumers: after the build the case titles are in `dist/core/routing.d.ts` and `dist/core/trace.d.ts` and the fixture strings are not. `AGENTS.md` gained the decay rule for this citation class beside the one for line numbers.
|
|
10
|
+
- **The last copy of the `knownTopics` cap absolute is retired.** `AGENTS.md`, the surface that steers every future session, still read "max 500 entries" and "never evicts a key the worker still owns" two releases after `src/` and both architecture languages were corrected. It now carries the measured rule: the 500 bound covers entries this worker does not own, the scan skips two candidates (an owned key, and the key being remembered — a fresh `Map.set` lands at the back, so the front-scan reaches it only when everything older is owned), and in that state the cap slips. 1,200 adopted peer `CONTROL/SUBSCRIBE` frames grew `knownTopics` and `assignedTopics` to 1,200 together and one reconcile tick returned both to `0`, so for a flood the bound is the sweep. Both skips keep their own named case.
|
|
11
|
+
- **A load-bearing harness count had rotted in two directions at once, and one file held both values.** What `ChannelHub`'s synchronous delivery default costs is a measured number: flipping `asyncDelivery` reddens the suite. `tests/fakes.ts`, `tests/coordination-invariants.test.ts` and one `AGENTS.md` bullet said 15 post-and-assert tests and 17 failures; a different bullet in the same `AGENTS.md` said 18. Re-measured by editing the field and running the suite: **18** fail — 9 `cluster`, 6 `data-bus`, 1 `stability` (16 that post a frame and assert in the same stack) plus the hub's two own pins in `coordination-invariants`, one of which asserts the default by name. All three surfaces now carry the pair and the edit that regenerates it. **The finding is not the off-by-one — it is that one file can hold a stale count and its correction at the same time**, in two bullets written a cycle apart, with nothing to contradict either; a drifted line number one level up.
|
|
12
|
+
- **A correction can break the sentence it lands in, and the zh mirror can keep the figure the English already retired.** Three survivors of the sweep that replaced an unmeasured "75 minutes" with the four timestamps that produce it: `docs/zh/release-checklist.md` still said the mirror ran an hour behind after its English counterpart carried 3,418 s; both zh copies of the measurement read `3,207 s 秒`, the unit written twice, which is what a numeric insert leaves in a sentence whose unit was already spelled out; and two copies of the retraction itself were spliced into running text, producing `…not an elapsed time in this story. so the gate exited 1 with …` and `…) and the gate exited 1 …`. Nothing compiles prose, so no gate can see the last class, and no phrase-grep finds the first — a paraphrase shares no substring with the text that was fixed, which is why the closing check greps for the *figure* across languages. `AGENTS.md` gained the rule plus the scan recipe (a full stop followed by a lowercase function word over the 70 living-surface files, inline code stripped, a lone letter before the dot read as an abbreviation); it reported **one** real hit in the tree — a sentence in `docs/progress.md`, repaired earlier in this branch at `7168810`, so a run after that one finds none — and the scan is graded by the two pre-fix commits it still finds — its first pattern required whitespace straight after the stop and so cleared the bracketed form, and its first abbreviation filter matched the `e` of `evidence.` and called the tree clean over a genuine defect. The scan then caught this session splicing a second instance while writing the bullet about the first, which is the argument for the check being a scan rather than a re-read.
|
|
13
|
+
- **The publication samples are eight gaps and nine durations, and the ceiling's derivation is unchanged.** `0.21.34`: `Publish to npm` completed `02:46:35Z`, the registry recorded the version at `02:47:50.782Z` (**75.8 s**), and `Verify published npm consumers` took **304 s**. The three newest record lags — 76.1, 75.5, 75.8 s — span half a second across three consecutive releases while the step still consumes ~304 s each time, which puts the variance in the reader's blindness rather than in the registry's write; that reading is now in all four copies beside the `gh api` lookup that re-derives it. 75.8 s does not beat the 310 s that sizes the 720 s budget, so `maxMeasuredAckToRecordMs` stays put and the floor passes unmodified.
|
|
14
|
+
- **What did not move.** No library code, export, option, default, frame or storage key changed; `verify:compat`, `verify:types` and `verify:pack` run against `v0.21.34`, and the arm ledger and coverage aggregates are `0.21.34`'s. The suite is 37 files / 952 tests. `docs/benchmarks.md` and its zh mirror were re-generated and came back byte-identical (91 archived reports), so the generated tables and the committed copies have not drifted — the archive itself is untracked, which is why that check is a run rather than a gate. A sweep of every `name()` token in the living docs (955 of them) found no fabricated symbol: the 3 that resolve nowhere are one `e2e/` helper and two Playwright methods. Dependency patrol: no known vulnerabilities against `registry.npmjs.org`, and `typescript@7.0.2` stays blocked upstream because `typescript-eslint@8.70.1` declares `>=4.8.4 <6.1.0`. The completeness gate now reports 48 gated releases rather than 47, because `0.21.34` joined the set it audits.
|
|
4
15
|
|
|
5
16
|
## 0.21.34 delivered scope
|
|
6
17
|
|
|
@@ -23,7 +23,7 @@
|
|
|
23
23
|
4. 浏览器基准回归门禁:运行 `pnpm bench:browser`(至少两次;门禁将最新报告与之前最多五份归档报告中同一指标的中位数比较,**并且额外要求新值超过这些样本中的最大值**),随后执行 `pnpm bench:compare --fail-above-pct 50`。采用中位数基线是因为页内热路径指标在同一份代码上会在快/慢两档之间来回跳——`dedup1000Ms` 相邻两次实测分别为 12.7 ms 与 25.6 ms,只看"上一份报告"时,一次偶发噪声就能在无代码改动的情况下击穿既定上限。第二个条件则是因为:在两档交替的样本上,中位数只有当两档保持混合时才稳定——一旦连续几份报告落在同一档,中位数本身就会跳变,这正是同一份纯注释改动在一版基线前读出 +121%、几分钟后在另一版基线前读出 −56.7% 的原因。被该条件放过的指标会打印在 `within-baseline, not gated` 下,而不是静默通过——在当前归档上实测,`publish/dedicated/perMessageMs`、`publish/shared/perMessageMs` 与 `databus/publishBatch1000Ms` 三项都落在各自五份报告的区间内,因此应把这份清单读作该指标的真实分辨率,而不是一张合格证。`publish/dedicated/perMessageMs` 同样如此:同一份代码相邻两次实测为 49.9 ms 与 71.1 ms,令门禁以 +75.7% 失败,而同一份报告里 `dedup1000Ms` 与 `traceAndPublish1000Ms` 反而*变快*——"单指标上涨、其余下降"正是噪声的特征,第三次实测为 49.4 ms 并通过。因此在采信或处理失败前先复跑并观察离散度;若改动涉及热路径,就直接量化其开销(0.20.95 有界映射改写的微基准显示三种写法均为 80–86 ns/op,落在运行间波动之内)。基线迁移(例如某指标从空操作变为真实路径)属预期内的一次性失败。用 `pnpm bench:trend` 刷新长期趋势文档,表格变化时一并提交。**复跑失败可能亲手制造出让它通过的基线,这一点已被实测。** `bench:compare` 的基线(中位数与最大值两个条件)取自最近若干份归档报告,而每一次 `pnpm bench:browser` 都会往同一个归档里追加——于是排查失败时新增的样本,就变成了裁决下一次尝试的基线。2026-09-24:主机负载均值 7.7–15.1、待发布树的非注释 `src/` 差异为 0 行,前两次实测 `publish/dedicated 77.3 ms`、`publish/shared 67.7 ms`,相对约 38/34 ms 的快档在 50% 上限上失败;随后五次复跑报告 **OK**,因为基线中位数已经滑到 `70.6`。把这些样本移出归档后的一次干净实测显示所有指标同时上涨(`dedicated +92.5%`、`shared +99.2%`、`dedup +43.4%`、`trace +21.0%`),那是资源竞争的形态,与上文"单指标上涨、其余下降"的噪声形态相反。因此:复跑之前先看负载(`uptime`);在满载主机上遇到可疑失败,就把数字连同负载一起记为"本轮推迟",并将偏离常态的样本移出基线——也不要据它们重新生成 `docs/benchmarks.md`。
|
|
24
24
|
**这条既定决策在 0.21.17 被写下,那是处于该状态下的第五个发布周期:一个发布周期里始终没有安静的窗口时,就把这份采样连同当时的负载数字一起记为"本轮推迟",而不是在满载主机上取它。** 推迟正是 `docs/benchmarks.md` 保持陈旧的原因,而"按机会更新"的这份陈旧是本步骤期望的结果,不是一道悄悄通过的门禁;真正不可接受的,是把一个受资源竞争的样本放进归档——那会变成下一次发布用来判定回归的基线。
|
|
25
25
|
5. 用 `npm pack --dry-run --json` 确认发布包只包含预期文件。
|
|
26
|
-
6. 在功能分支提交并推送该分支,PR 验证通过后使用 squash 或 fast-forward 合入(不创建 merge commit)。获取合入后的精确提交并打 tag,只推送该版本 tag;禁止直接推送 `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(该计数取自 `v0.21.33` 发布当天;自己重算:`npm view cross-tab-worker-databus time --json`),这道门禁要拦的是新的漏洞,不是去翻历史旧账。而 registry 读取失败时,这一半会被跳过并打印一行警告、退出 0,而不是把一次好发布判红:为一个瞬时的 HTTP
|
|
26
|
+
6. 在功能分支提交并推送该分支,PR 验证通过后使用 squash 或 fast-forward 合入(不创建 merge commit)。获取合入后的精确提交并打 tag,只推送该版本 tag;禁止直接推送 `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(该计数取自 `v0.21.33` 发布当天;自己重算:`npm view cross-tab-worker-databus time --json --registry https://registry.npmjs.org`),这道门禁要拦的是新的漏洞,不是去翻历史旧账。而 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 上。
|
|
27
27
|
|
|
28
28
|
## 安全与依赖扫描
|
|
29
29
|
|
|
@@ -43,6 +43,6 @@
|
|
|
43
43
|
2. 在干净消费者中安装已发布版本或 tarball,并导入主入口及所有公开子路径。
|
|
44
44
|
3. 将结果记录到发布说明。在公开 API 和协议弃用策略明确冻结前,不进入 `1.0.0`。
|
|
45
45
|
|
|
46
|
-
打 tag 的发布工作流已自动执行上述消费者验证;仅在需要离线复验时才手动运行 `PUBLISHED_VERSION=<version> pnpm verify:published`。工作流的 12 分钟上限不是凑数,而是两段实测延迟之和。第一段:`npm publish` 返回成功后,registry 还要几分钟才把这条版本记进去——0.21.27 到 0.21.
|
|
46
|
+
打 tag 的发布工作流已自动执行上述消费者验证;仅在需要离线复验时才手动运行 `PUBLISHED_VERSION=<version> pnpm verify:published`。工作流的 12 分钟上限不是凑数,而是两段实测延迟之和。第一段:`npm publish` 返回成功后,registry 还要几分钟才把这条版本记进去——0.21.27 到 0.21.34 的"发布确认 → `time[<version>]`"间隔实测为 74.8–310.0 秒,按版本顺序分别是 248.7 / 96.8 / 74.8 / 310.0 / 127.2 / 76.1 / 75.5 / 75.8 秒(自己去 packument 读 `time[<version>]`,再和工作流 `Publish to npm` 步骤的完成时刻相减)。最后三个——76.1、75.5、75.8 秒——跨三次连续发布只差半秒,所以会动的不是"落库"这一段;那一步每一次仍然花掉约 304 秒,那是第二段的事,不是这一段。第二段:`npm pack` 用来解析版本的 packument 是以 `cache-control: public, max-age=300` 提供的,也就是说路径上任何一份副本按设计都可以旧五分钟,落在这一窗口内写入的版本,解析不太可能快过一整个缓存生命周期。究竟是哪个副本(npm 本地缓存,还是中间的 CDN)在下面提供了过期视图**并未确立**,而这段文字此前用作"反对纯本地解释"的那个观测,仔细读过之后站不住,因为它把两台机器拼成了一句话:21:44:34Z 那次"新鲜读取"是在笔记本上跑 `npm pack`,而 21:47:10Z 一秒就绿的是一次*全新的 runner 任务*,新任务的 npm 缓存本来是空的——这恰好是纯本地模型预测的结果,不是反驳它的结果。纯本地模型仍然解释不了的,只有一个读数:runner 自己在 21:45:07Z 仍然报 `ETARGET`——距离开始轮询的那一步(21:39:03Z)已经 364 秒,也就是比"那一刻抓到的副本必然已过期"的时间点(21:44:03Z,误差在请求真正发出的那一秒之内)还要晚约 60 秒。因此上限靠的是算术而不是模型:最坏情况的"确认 → `time[]`"间隔,加上一个缓存生命周期。这个和并非理论值——上一个 6 分钟上限被一次*成功*的发布排空过:0.21.30 的 tag 运行把整整 360 秒预算(48 × 7.5 秒)花在 `ETARGET` 上,而那时该版本的 tarball HEAD 和本地 `npm pack` 都已经成功;123 秒后同一步骤只用了 1 秒。0.20.89 针对它当时的 2 分钟预算也是同一形状。所以长时间的反复重试是正常的传播延迟,不是镜像异常慢——只有"确认 → `time[]`"间隔本身变长了,才需要继续调大 `PUBLISHED_VERIFY_ATTEMPTS` 和 `PUBLISHED_VERIFY_DELAY_MS`。手动复验时要注意这个不对称:`verify-published-consumer.mjs` 的默认值是 `6` 次 × `5_000` ms——30 秒而不是 12 分钟——因为更大的那一对是由 tag 工作流以 env 传入、而非脚本自带。于是手动复跑可能在一个 CI 全绿的版本上报出 `ETARGET`,这看起来像发布失败而实际不是;要对齐 CI 预算就显式执行 `PUBLISHED_VERSION=<version> PUBLISHED_VERIFY_ATTEMPTS=96 PUBLISHED_VERIFY_DELAY_MS=7500 pnpm verify:published`,否则请按你实际跑到的 30 秒预算来判断手动失败。把这个步骤的耗时当诊断读数而不是情绪指标:0.21.25 到 0.21.29 那五次*绿色* tag 运行分别用了 307、304、305、304、304 秒,0.21.31 用了 308 秒,0.21.32、0.21.33 与 0.21.34 各用了 304 秒——九个样本落在 5 秒的区间里,而这正是"一个 300 秒缓存生命周期 + 几秒命令时间"会预测出的形状(数字自己取:对每次发布运行 `gh api` 出来的 jobs 里,`Verify published npm consumers` 这一步的 `steps[].started_at`/`completed_at`)。0.21.31 到 0.21.34 是头四个跑在提高后的 720 秒上限下的发布,而它们几段延迟的切分方式不同,这个对照正是有用的地方:第一次是 127.2 秒落库 + 约 181 秒缓存;第二次是 76.1 秒落库 + 之后约 228 秒 runner 仍看不见它;第三次是 75.5 秒落库 + 约 229 秒;第四次是 75.8 秒落库 + 约 228 秒——在 0.21.32 上,笔记本在 151 秒时用 `no-cache` 读 packument 已经能看到新的 `latest`,而 runner 自己的轮询一直到 304 秒才成功,也就是从*它自己*首次抓取算起整整一个生命周期。所以当初定这个上限的那个样本至今没有被超过:如果*确认 → `time[]`*这一段自己长过了 310 秒,就再调大 `PUBLISHED_VERIFY_ATTEMPTS`;而几秒就结束的 verify 只说明它读到的那份副本已经带上了新版本。
|
|
47
47
|
|
|
48
48
|
**不要用 `dist-tags.latest` 来判断发布是否发生。** 包文档端点与 tarball URL 是两条不同的读取路径,而它们的时间戳比这两条路径给的第一手印象更值得读。0.21.30 发布期间按顺序读到:`npm publish` 在 `21:39:03Z` 返回成功;`21:43:15Z` 的一次 `curl -s -H "Cache-Control: no-cache" https://registry.npmjs.org/<pkg>` 仍报 `latest` 是上一个版本、`versions` 里没有 `0.21.30`、`time['0.21.30']` 也不存在;而 npm 自己给这个版本盖的时间戳是 `21:44:12.987Z`,文档的 `last-modified` 是 `21:44:14 GMT`。所以 21:43:15 那次读取**不是**一份副本在拒绝已经存在的产物——registry 当时确实还没把这次发布记完,`no-cache` 也不可能让它看见尚不存在的版本;在发布步骤全绿之后的几分钟里,`versions` 与 `latest` 里没有这个版本,正是一次成功发布的正常状态。真正的读取路径分歧出现在 60 秒之后:`21:44:14Z` 到 `21:44:35Z` 之间,`curl -I …/-/<pkg>-0.21.30.tgz` 返回 `HTTP 200`,本地一次 `npm pack <pkg>@0.21.30 --registry https://registry.npmjs.org` 也成功写出了 tarball,而工作流 runner 自己的 `npm pack` 一直 `ETARGET` 到 `21:45:07Z` 把预算排空。同一个版本、两台机器、相隔一分钟给出相反答案。要回答"发布到底成没成",去 HEAD 那个 tarball URL 并试一次 pack——它们摸到的是产物本身——而且要从**正在失败的那台机器**上去试,因为"能取到"是一台机器才有的答案;`latest` 只回答"消费者默认拿到哪个版本",那是另一个问题,而且会更晚才稳定。`npm view <pkg> version` 在这之上还有第三条路径:它可能来自 npm 的**本地** packument 缓存,于是在发布前查过一次的机器上,即使全局 `latest` 已经前进,它仍会返回被取代的旧版本——这正是本门禁把目标版本固定取自 `PUBLISHED_VERSION`、并拒绝与 `package.json` 不一致的推断值的原因。不要因为一次元数据读取看起来过期就重推、移动或替换 tag;发布出去的 tag 是不可变的。
|
package/docs/zh/roadmap.md
CHANGED
|
@@ -1,6 +1,17 @@
|
|
|
1
1
|
# 路线图
|
|
2
2
|
|
|
3
|
-
0.21.
|
|
3
|
+
0.21.35 已于 2026 年 9 月 25 日发布。项目会先持续完成可靠性发布,再进入 1.0.0 稳定性冻结。
|
|
4
|
+
|
|
5
|
+
## 0.21.35 已完成范围
|
|
6
|
+
|
|
7
|
+
- **发布门禁曾把一次已经公开的发布判成"从未发布",而且真的判了。** `scripts/verify-release-version.mjs` 原先用裸的 `npm view <pkg> time --json` 读 packument,于是它读的是*当前配置*的那个 registry。本次发布准备期间在本机量到:这台机器的 `npm` 指向 `registry.npmmirror.com`,它那份文档仍报 `modified: 2026-09-25T00:32:51.767Z`——也就是 `0.21.33` 那次发布——而门禁运行在 `03:41:18Z`,比 npmjs 记下 `0.21.34` 的 `02:47:50.782Z` 晚 3,207 秒(53 分钟);镜像自己的 `modified` 后来才走到 `03:44:48.778Z`,也就是说它已经有 3,418 秒答不出这个版本。(本条初稿写的是“75 分钟”,那是两个更早时间戳之差,和这件事里的任何一段耗时都对不上。)于是门禁以退出码 1 报出 `CHANGELOG names 1 release(s) the registry has never recorded: 0.21.34`。它自己注释里的推理("`time[]` 记录不会过期,所以一次成功的读取里缺少的版本就是从未发布过")错在第二个前提:一份*还不知道*某个版本的镜像,并不是该版本不存在的记录;而镜像的同步滞后不是 HTTP 缓存,所以 `cache-control: no-cache` 取回的还是同一份旧文档。现在这次读取显式点名 `https://registry.npmjs.org`,"registry" 才终于指那个"缺失即证据"的对象;修好后重跑,打印的是 `the 48 earlier CHANGELOG releases from 0.20.85 on are all in the registry's time map`,退出码 0。`verify-published-consumer.mjs` 早就为它的两次读取钉住了 `--registry`,理由相同——这是仓库里最后一处没钉的 npm 读取,而两种语言的检查清单都把未钉版本的那条命令当作"自己重算"的指引。这道门禁既有用例全都注入 `RELEASE_REGISTRY_TIME`,这正是没有任何一条能看见"读的是哪个 registry"的原因;新用例走未注入的真实路径,对着一个放在 `PATH` 上的 `npm` 桩运行,并断言那个参数与 URL 是成对到达的。
|
|
8
|
+
- **指向测试的引用,现在要拿去和测试套件对账。** `tests/documentation.test.ts` 新增"resolve to a case that exists in the file they name":`src/**`、`AGENTS.md`、`README.md` 与公开文档里每一处 `tests/<file>`'s `<name>`,都必须在被点名的那个文件里恰好匹配一条 `it()`/`test()` 标题(当前是 52 个文件里的 18 处引用;结尾带 `…` 的允许前缀匹配,但也只能命中一条)。扫描的两半各自都有对照,因为它们都可能空掉,而"没有未解析项"在两种情况下看起来都是一次干净通过——标题收集器必须找到套件的用例(40 个文件共 977 条,下限写 500 而不是写当前总数)、引用模式必须至少检查一处,而 `e2e` 语料单独断言,因为那 977 条里有 937 条来自 `tests/`,总数那一项根本看不见 `e2e` 抓取整个空掉。两处范围决定是量出来的而不是定的:两处豁免文本加起来另有 11 处引用(`docs/progress.md` 8 处、`CHANGELOG.md` 3 处),而公开文档里只有 1 处——本句更早的版本写的是"`docs/` 里有 8 处",那是把闸门并不扫描的 `progress.md` 也算了进去;那 11 处里恰好只有 1 处解析不出,因为它是逐字引用了这道闸门自己打印的失败文案,而不是一枚指针;更宽松的 `文件名 …('a name')` 窗口试过也被排除,因为整个范围内它只产生一个候选,而那个候选正是"文档注释点了这个测试的那个函数"里抛出的 `new Error(…)` 文案。五个变异体,各自报出自己的失败;更早的一个变异体因编译不过而被丢弃(少了一个反斜杠,正则变成未闭合,报告的是套件收集失败,而不是行为被杀掉)。这道闸门把写来描述它自己的那条 `AGENTS.md` 规则判红了——因为初稿按引用格式写出了那两个坏 token;于是规则现在写明:句子讲的是缺陷本身时,要把 token 裸写,不要写成指向用例的样子。
|
|
9
|
+
- **两句已发布的注释写的是 fixture,不是用例名,而且两者都会发出去。** `src/core/routing.ts` 引用了 `sticky-existing-routes`,`src/core/trace.ts` 引用了 `no-trace`;两者都不是 `it()` 标题,而是写在测试*体内*的 `clusterKey` 字面量,所以指针落在用例中间,而任意一个 fixture 字符串一旦改名就会指向虚无——构建、类型检查、测试套件都不会提出反对,这和漂移的 `:NNN` 是同一种沉默。两句都在声明的 JSDoc 里,也就是说错指针是发出去的:构建之后,用例标题出现在 `dist/core/routing.d.ts` 与 `dist/core/trace.d.ts` 里,而 fixture 串不在。`AGENTS.md` 在行号那一条旁边,补上了这一类引用的衰减规则。
|
|
10
|
+
- **`knownTopics` 上限的最后一份绝对说法退役了。** `AGENTS.md`——决定每一次后续会话怎么读这段代码的那一份——在 `src/` 和两种架构文档都已修正两个发布之后,仍然写着"max 500 entries"和"never evicts a key the worker still owns"。现在是量出来的说法:500 这条界覆盖的是本 worker *未持有*的条目,淘汰扫描跳过两个候选(持有的 key,以及本次正在记忆的 key——新写入的 `Map.set` 落在末尾,所以只有更早的条目全被持有时,从头扫描才会走到它),而在那个状态下上限会失守。1,200 条被收养的 peer `CONTROL/SUBSCRIBE` 让 `knownTopics` 和 `assignedTopics` 一起涨到 1,200,一轮 reconcile 把两者带回 `0`——所以对洪水而言界是清扫。两个跳过各自保留自己的具名用例。
|
|
11
|
+
- **一个承重的测试工装计数朝两个方向同时腐坏了,而同一个文件里同时存着新旧两个值。** `ChannelHub` 同步投递默认值的代价是一个实测数字:把 `asyncDelivery` 翻过去就会让套件变红。`tests/fakes.ts`、`tests/coordination-invariants.test.ts` 和 `AGENTS.md` 的一条项目符号写的是"15 条同栈断言、红 17 条";而同一份 `AGENTS.md` 的另一条写的是 18。把字段改掉后重跑套件实测:**18** 条失败——`cluster` 9、`data-bus` 6、`stability` 1(也就是同栈投递并断言的 16 条),加上 `coordination-invariants` 里两条 hub 自身的对照(其中一条就是按名字钉住这个同步默认值)。三处文本现在一致,并且每一处都写出了能把它重新算出来的那次改动。**值得记下的不是那个差一,而是同一个文件可以同时持有过期计数和它的更正**——两条相隔一个周期写下的项目符号,谁也不会反驳谁;这是漂移的 `:NNN` 往上提一层的版本。
|
|
12
|
+
- **一句修补本身可以把它落进去的那句话打断,而中文镜像会留着英文已经换掉的数字。** 那次把"75 分钟"换成四个时间戳的清扫留下了三处幸存:`docs/zh/release-checklist.md` 仍写镜像"落后一个小时",而英文对应处已经改成 3,418 秒;两处中文的实测都变成了 `3,207 s 秒`——单位写了两遍,因为往一句已经把单位 spelled out 的话里插数字就是这个残留;而两处更正本身被插进了正在流动的句子中间——英文的 `CHANGELOG.md` 与 `docs/roadmap.md` 因此分别留下了 `…not an elapsed time in this story. so the gate exited 1 with …` 和 `…) and the gate exited 1 …`。散文不参与编译,所以最后一类没有任何闸门能看见,而第一类也没有任何短语 grep 能抓到——改述后的文本与被改掉的原文没有共同子串,这也是为什么收尾的那一步是按*数字*跨语言 grep。`AGENTS.md` 补上了这条规则与扫描配方(在 70 个存活文件上找"句号后接小写虚词",先剥掉行内代码,紧邻句号之前是单个字母则视为缩写);它在全仓库报出**一处**真实命中(就是本分支更早的 `7168810` 里修掉的 `docs/progress.md` 那一句,那次之后重跑就是零),而这个扫描是按它仍然能抓到的那两个"修复前提交"来判分的——第一版要求句号之后紧跟空白,于是漏掉了带右括号的那一处;第一版缩写过滤把 `evidence.` 结尾的 `e` 也当成了缩写,于是在确有缺陷的树上报告"干净"。随后这个扫描抓到:本次会话在写下"不要打断句子"这条规则的同时,又打断了一句——这正是它该是扫描而不是重读的理由。
|
|
13
|
+
- **发布延迟样本扩到八段间隔、九个耗时,而上限的推导没动。** `0.21.34`:`Publish to npm` 在 `02:46:35Z` 完成,registry 在 `02:47:50.782Z` 记下这个版本(**75.8 秒**),`Verify published npm consumers` 用了 **304 秒**。最近三段落库延迟——76.1、75.5、75.8 秒——跨三次连续发布只差半秒,而那一步每次仍然吃掉约 304 秒,也就是说方差在读者看不见,而不在 registry 写得慢;这个读法已经写进四份文本,旁边就是能把它重新算出来的 `gh api` 取法。75.8 秒没有超过决定 720 秒预算的 310 秒,所以 `maxMeasuredAckToRecordMs` 原样保留,下限未作修改仍然通过。
|
|
14
|
+
- **没有变动的部分。** 库代码、导出、选项、默认值、帧格式与存储键均未改动;`verify:compat`、`verify:types`、`verify:pack` 针对 `v0.21.34` 运行,零计数分支账本与覆盖率聚合仍是 `0.21.34` 的。测试规模是 37 个文件 / 952 条。`docs/benchmarks.md` 与其中文镜像被重新生成后与提交版本逐字节相同(91 份归档报告),也就是生成的表格和仓库里的副本没有漂移——报告归档本身不入库,所以这项检查是一次运行而不是闸门。把存活文档里所有 `name()` 形态的 token(共 955 个)扫了一遍,没有虚构标识符:三处在全仓库符号表里找不到的,一个是 `e2e/` 里的辅助函数,另两个是 Playwright 的方法。依赖巡检:对 `registry.npmjs.org` 无已知漏洞,而 `typescript@7.0.2` 仍被上游挡住——`typescript-eslint@8.70.1` 的 peer 声明是 `>=4.8.4 <6.1.0`。发布记录完整性闸门现在报 48 条受检发布而不是 47 条,因为 `0.21.34` 已经进入了它自己审计的集合。
|
|
4
15
|
|
|
5
16
|
## 0.21.34 已完成范围
|
|
6
17
|
|
package/package.json
CHANGED