NORTHSET
Proof-of-Pass Receipt — M-011
CONTRIBUTOR SELF-RUN — NOT MAINTAINER VERIFICATION
Project
Work
Cluster should always aggregate metrics in a same order · PR #771
Verification execution
runtime: northset-oss executor v0
human operator: aeziz
Code
- base
c51c2cd36f6a14e32fa305a164ccded7c250612a- recorded patch commit
e345d993c4d2e1ad54e7367e38e07bcc283ccbca
declared metadata; not execution-bound- patch diff SHA-256
sha256:a42626ab3db4f7f1e5ab68068b2818e3f4c522092c54b786c671a3e969c3ebbf
bound to executed patch bytes
Environment
- image reference
- node:22-bookworm
- repository digest
node@sha256:a25c9934ff6382cd4f08b6bc26c82bf4ea69b1e6f8dabfb2ead457374127c365- network
- phaseA:bridge,phaseB:none
Declared checks
Execution summary
1/1 declared command returned exit 0 in the recorded environment
npm run test-unitexit 0 · 5.9s
unclassified executor time (derived residual) 48.5s
run wall (derived from recorded timestamps) 54.4s
PASS — 1/1 declared command
Every command listed returned exit 0 in the declared environment. Only the listed commands are in scope. Unlisted test, lint, typecheck, build, coverage, compiler, full-suite, and CI gates are not implied or recorded.
Public scope interpretation
The declared network-off check runs the prom-client jest unit suite on node:22 after a disclosed online phase-A npm ci. It does not run lint, prettier, or the TypeScript type-check gates that upstream CI also runs.
Record details
- payment
- none · not merge-contingent
- redactions
- 1 email
- Bundle contents digest
sha256:553f6250fb4c7ced453ec2e9bba65dd6634be17e0608f8ab8480893b4e67bc72- Signed asset SHA-256
sha256:5b5e4910280b590f9669865161086644cf9f04178229e4f7c25be20d331f7935- Signed provenance recorded
- verified 2026-07-14T14:21:48Z
NOT INCLUDED
- Does not prove code quality
- Does not prove security
- Contributor self-run record of Northset's own contribution; not the maintainer's verification.
- The declared network-off check runs the prom-client jest unit suite on node:22 after a disclosed online phase-A npm ci. It does not run lint, prettier, or the TypeScript type-check gates that upstream CI also runs.
Signed bundle
Verify this receipt
gh attestation verify run-record-M-011.tar.gz --repo northset-oss/verification-pilot --signer-workflow northset-oss/verification-pilot/.github/workflows/attest-bundle.ymlAttestation confirms that Northset's signing workflow produced this exact bundle. The signer does not witness the recorded run, and verification does not turn it into maintainer verification.
QR → receipt pageCommitted patch.diff
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 74bd4a5..da34780 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -37,6 +37,7 @@ This release marks our first release under the Prometheus umbrella.
- AggregatorRegistry renamed to ClusterRegistry, old name deprecated
- chore: update faceoff to 1.1
- perf: Stat aggregation uses similar strategy to collection. 60% faster aggregation
+- Make cluster metric aggregation independent of worker response order
### Added
diff --git a/lib/metricAggregators.js b/lib/metricAggregators.js
index 8ea0528..6c2a1dd 100644
--- a/lib/metricAggregators.js
+++ b/lib/metricAggregators.js
@@ -51,6 +51,13 @@ function AggregatorFactory(aggregatorFn) {
// Export for users to define their own aggregation methods.
exports.AggregatorFactory = AggregatorFactory;
+function sumValues(values) {
+ return values
+ .slice()
+ .sort((a, b) => a.value - b.value)
+ .reduce((p, c) => p + c.value, 0);
+}
+
/**
* Functions that can be used to aggregate metrics from multiple registries.
*/
@@ -58,7 +65,7 @@ exports.aggregators = {
/**
* @returns The sum of values.
*/
- sum: AggregatorFactory(v => v.reduce((p, c) => p + c.value, 0)),
+ sum: AggregatorFactory(sumValues),
/**
* @returns The first value.
*/
@@ -70,9 +77,7 @@ exports.aggregators = {
/**
* @returns The arithmetic mean of the values.
*/
- average: AggregatorFactory(
- v => v.reduce((p, c) => p + c.value, 0) / v.length,
- ),
+ average: AggregatorFactory(v => sumValues(v) / v.length),
/**
* @returns The minimum of the values.
*/
diff --git a/test/aggregatorsTest.js b/test/aggregatorsTest.js
index 0010a65..f3f77ad 100644
--- a/test/aggregatorsTest.js
+++ b/test/aggregatorsTest.js
@@ -34,6 +34,20 @@ describe('aggregators', () => {
{ value: 6, labels: ['label1'] },
]);
});
+
+ it('sums values independently of their order', () => {
+ const workerMetrics = [0.1, 0.2, 0.3].map(value => ({
+ help: 'metric_help',
+ name: 'metric_name',
+ type: 'does not matter',
+ values: [{ labels: [], value }],
+ }));
+
+ const result = aggregators.sum(workerMetrics);
+ const reorderedResult = aggregators.sum(workerMetrics.slice().reverse());
+
+ expect(result.values[0].value).toBe(reorderedResult.values[0].value);
+ });
});
describe('first', () => {
Redacted stdout
=== cmd 1: npm run test-unit ===
> @[REDACTED:email] test-unit
> jest
Redacted stderr
=== cmd 1: npm run test-unit ===
PASS test/histogramTest.js
PASS test/summaryTest.js
PASS test/registerTest.js
PASS test/utilTest.js
PASS test/gaugeTest.js
PASS test/exemplarsTest.js
PASS test/counterTest.js
PASS test/pushgatewayWithPathTest.js
PASS test/aggregatorsTest.js
PASS test/pushgatewayTest.js
PASS test/defaultMetricsTest.js
PASS test/metrics/heapSpacesSizeAndUsedTest.js
PASS test/metrics/eventLoopLagTest.js
PASS test/timeWindowQuantilesTest.js
PASS test/clusterTest.js
PASS test/metrics/maxFileDescriptorsTest.js
PASS test/bucketGeneratorsTest.js
PASS test/metrics/versionTest.js
PASS test/metrics/heapSizeAndUsedTest.js
PASS test/workerTest.js
PASS test/metrics/gcTest.js
PASS test/metrics/processRequestsTest.js
PASS test/metrics/processHandlesTest.js
PASS test/browserCompatibilityTest.js
PASS test/metrics/processStartTimeTest.js
PASS test/validationTest.js
PASS test/metrics/processOpenFileDescriptorsTest.js
PASS test/metrics/processResourcesTest.js
A worker process has failed to exit gracefully and has been force exited. This is likely caused by tests leaking due to improper teardown. Try running with --detectOpenHandles to find leaks. Active timers can also cause this, ensure that .unref() was called on them.
Test Suites: 28 passed, 28 total
Tests: 543 passed, 543 total
Snapshots: 34 passed, 34 total
Time: 4.076 s
Ran all test suites.