[GH-ISSUE #6878] [Bug] Environment comparison treats numeric measurement arrays as clocks and reports false agreement #4215

Closed
opened 2026-10-31 00:47:47 +00:00 by bsakaguchi · 0 comments
Owner

Originally created by @coygeek on GitHub (Oct 23, 2026).
Original GitHub issue: https://github.com/morluto/rea/issues/6878

Before submitting

  • I searched the open and closed issues for a related report.
  • I removed credentials, tokens, private paths, and sensitive target data.

Area

Evidence, artifacts, or reconstruction

Observed behavior

The production compareEnvironment application boundary reports full agreement for {"samples":[1800000000]} versus {"samples":[1700000000]}. It records one normalized clock and no differences, although samples contains numeric measurements and has no clock field. The same numbers as a scalar samples field produce the expected value difference. Small differing array numbers also produce a value difference.

This is an executed source-module probe using synthetic authenticated capture Evidence and the repository's fixture constructors. No CLI process, MCP transport, live browser, network service, or analysis provider was executed. Source reachability: the CLI command loads the source and calls this same application boundary.

Reproduction

With the source checkout and its locked dependencies available, save the following as array-clock-probe.mjs in a disposable directory. From the checkout root, with the pinned Node executable on PATH, run node --import "$PWD/node_modules/tsx/dist/loader.mjs" /tmp/array-clock-probe.mjs (replace the final path with the saved file's path). The script writes its synthetic requests and results beside itself; it makes no network calls.

import { pathToFileURL } from 'node:url';
import { resolve } from 'node:path';
import { writeFileSync } from 'node:fs';
const load = p => import(pathToFileURL(resolve(p)).href);
const { timedCaptureEvidence } = await load('tests/fixtures/analysisView.ts');
const { environmentStateServeFixture, environmentServeSource } = await load('tests/fixtures/environmentServe.ts');
const { compareEnvironment } = await load('src/application/environment/environmentCompare.ts');
for (const [label, recorded, probe] of [
 ['numeric-field', {samples:1700000000}, {samples:1800000000}],
 ['numeric-array', {samples:[1700000000]}, {samples:[1800000000]}],
 ['small-array-control', {samples:[17]}, {samples:[18]}],
 ['clock-control', {updated_at:1700000000}, {updated_at:1800000000}],
 ['same-array-control', {samples:[1700000000]}, {samples:[1700000000]}]
]) {
 const capture=timedCaptureEvidence([['GET','/api/measurements',recorded]]);
 const fixture=environmentStateServeFixture(capture);
 const probeCapture=timedCaptureEvidence([['GET','/api/measurements',probe]]);
 const result=compareEnvironment({source:environmentServeSource(fixture),captureValue:capture,probes:[{label,value:probeCapture}],operation:'environment-compare'});
 if(!result.ok)throw result.error;
 writeFileSync(new URL('array-'+label+'.json',import.meta.url),JSON.stringify({capture,probe:probeCapture,manifest:fixture.manifest,result:result.value},null,2));
 console.log(JSON.stringify({label,agreement:result.value.summary.agreement,normalised:result.value.summary.normalised,differences:result.value.probes[0].results[0].differences}));
}

This exact five-case script ran once. The scalar, small-number, named-clock, and identical-array cases are controls.

Expected behavior

The changed samples[0] measurement should be reported as a value difference at /samples/0, with agreement 0 and no clock normalization. Identical measurements should agree, and a genuine updated_at clock should retain the documented clock-format normalization.

The comparison guide restricts numeric epoch clock normalization to time-named fields and says JSON values compare structurally. These are JSON numbers in a non-time-named measurement array, not string tokens or IDs.

Impact and scope

A fidelity report can hide changes in arrays of counters, measurements, or numeric format values that fall in the epoch-number range. The executed sample contains one response and one array element; it establishes a deterministic false agreement for this bounded input, not real-world frequency or downstream deployment harm. No stored evidence or source files were modified.

REA version

rea-agents package version 6.7.0; fixed main source SHA 3e7e102a518b9112a2a8aa0590ae6f6951fd0635. The source checkout, not an installed REA runtime, was tested.

Runtime and platform

Node.js v24.22.1; Linux x86_64; the clone's locked TSX loader and dependencies. No Hopper, Ghidra, browser, or other provider was involved.

Target and artifact context

Synthetic capture Evidence for GET http://app.example/api/measurements; recorded and probe JSON bodies contain only inert sample numbers. The test fixture creates its manifest through the production analysis-view service.

Interface

Other

Earliest failure stage

Native analysis or reconstruction

Relevant output or evidence

Actual probe stdout:

{"label":"numeric-field","agreement":0,"normalised":{"ids":0,"clocks":0,"generated":0,"large_bodies":0,"unobserved_bodies":0},"differences":[{"category":"value","pointer":"/samples","live":"1800000000","env":"1700000000"}]}
{"label":"numeric-array","agreement":1,"normalised":{"ids":0,"clocks":1,"generated":0,"large_bodies":0,"unobserved_bodies":0},"differences":[]}
{"label":"small-array-control","agreement":0,"normalised":{"ids":0,"clocks":0,"generated":0,"large_bodies":0,"unobserved_bodies":0},"differences":[{"category":"value","pointer":"/samples/0","live":"18","env":"17"}]}
{"label":"clock-control","agreement":1,"normalised":{"ids":0,"clocks":1,"generated":0,"large_bodies":0,"unobserved_bodies":0},"differences":[]}
{"label":"same-array-control","agreement":1,"normalised":{"ids":0,"clocks":0,"generated":0,"large_bodies":0,"unobserved_bodies":0},"differences":[]}

Evidence provenance: observed. Local verification: reproduced. Reproduction completeness: complete. The observed property is the source application boundary's comparison result, not live-provider behavior.

Source: numeric clock detection allows numbers when key is undefined; array traversal visits each element without its containing field name. The resulting numbers reach clock-format comparison without a time-named field.

Typed outcome or recovery expected

Return a value difference at /samples/0 and agreement 0 for the changed measurement array. Keep the identical-array control equal and the named-clock control normalized. The scalar measurement control must continue to differ.

Analysis or suspected cause

Source-derived explanation: the clock classifier excludes numeric non-time fields only when key !== undefined. Array traversal leaves the key undefined, so epoch-shaped array measurements are admitted as clocks. This explains the observed scalar/array discrepancy; no implementation change is proposed here.

Additional context

Related: #6551 concerns non-time scalar field names ending in at. This report concerns a different path: array traversal loses the field name entirely, so the current field-name guard does not prevent the false clock normalization. The scalar measurement control distinguishes this trigger.

Triage assessment

  • Impact level: P2
  • Assessment status: supported
  • Impact basis: The production comparison boundary reports full agreement while a valid non-time numeric response element changed, suppressing a consequential fidelity difference in this bounded case.
  • Workaround status: unverified
  • Workaround basis: The scalar control detects the difference, but changing the application's response shape is not an established practical workaround for existing captures; no supported per-field clock opt-out was verified.
Originally created by @coygeek on GitHub (Oct 23, 2026). Original GitHub issue: https://github.com/morluto/rea/issues/6878 ### Before submitting - [x] I searched the open and closed issues for a related report. - [x] I removed credentials, tokens, private paths, and sensitive target data. ### Area Evidence, artifacts, or reconstruction ### Observed behavior The production `compareEnvironment` application boundary reports full agreement for `{"samples":[1800000000]}` versus `{"samples":[1700000000]}`. It records one normalized clock and no differences, although `samples` contains numeric measurements and has no clock field. The same numbers as a scalar `samples` field produce the expected value difference. Small differing array numbers also produce a value difference. This is an executed source-module probe using synthetic authenticated capture Evidence and the repository's fixture constructors. No CLI process, MCP transport, live browser, network service, or analysis provider was executed. Source reachability: [the CLI command](https://github.com/morluto/rea/blob/3e7e102a518b9112a2a8aa0590ae6f6951fd0635/src/cli/environmentCommands.ts#L311-L346) loads the source and calls this same application boundary. ### Reproduction With the source checkout and its locked dependencies available, save the following as `array-clock-probe.mjs` in a disposable directory. From the checkout root, with the pinned Node executable on PATH, run `node --import "$PWD/node_modules/tsx/dist/loader.mjs" /tmp/array-clock-probe.mjs` (replace the final path with the saved file's path). The script writes its synthetic requests and results beside itself; it makes no network calls. ```js import { pathToFileURL } from 'node:url'; import { resolve } from 'node:path'; import { writeFileSync } from 'node:fs'; const load = p => import(pathToFileURL(resolve(p)).href); const { timedCaptureEvidence } = await load('tests/fixtures/analysisView.ts'); const { environmentStateServeFixture, environmentServeSource } = await load('tests/fixtures/environmentServe.ts'); const { compareEnvironment } = await load('src/application/environment/environmentCompare.ts'); for (const [label, recorded, probe] of [ ['numeric-field', {samples:1700000000}, {samples:1800000000}], ['numeric-array', {samples:[1700000000]}, {samples:[1800000000]}], ['small-array-control', {samples:[17]}, {samples:[18]}], ['clock-control', {updated_at:1700000000}, {updated_at:1800000000}], ['same-array-control', {samples:[1700000000]}, {samples:[1700000000]}] ]) { const capture=timedCaptureEvidence([['GET','/api/measurements',recorded]]); const fixture=environmentStateServeFixture(capture); const probeCapture=timedCaptureEvidence([['GET','/api/measurements',probe]]); const result=compareEnvironment({source:environmentServeSource(fixture),captureValue:capture,probes:[{label,value:probeCapture}],operation:'environment-compare'}); if(!result.ok)throw result.error; writeFileSync(new URL('array-'+label+'.json',import.meta.url),JSON.stringify({capture,probe:probeCapture,manifest:fixture.manifest,result:result.value},null,2)); console.log(JSON.stringify({label,agreement:result.value.summary.agreement,normalised:result.value.summary.normalised,differences:result.value.probes[0].results[0].differences})); } ``` This exact five-case script ran once. The scalar, small-number, named-clock, and identical-array cases are controls. ### Expected behavior The changed `samples[0]` measurement should be reported as a `value` difference at `/samples/0`, with agreement `0` and no clock normalization. Identical measurements should agree, and a genuine `updated_at` clock should retain the documented clock-format normalization. The [comparison guide](https://github.com/morluto/rea/blob/3e7e102a518b9112a2a8aa0590ae6f6951fd0635/docs/agent-environments.md#L773-L785) restricts numeric epoch clock normalization to time-named fields and says JSON values compare structurally. These are JSON numbers in a non-time-named measurement array, not string tokens or IDs. ### Impact and scope A fidelity report can hide changes in arrays of counters, measurements, or numeric format values that fall in the epoch-number range. The executed sample contains one response and one array element; it establishes a deterministic false agreement for this bounded input, not real-world frequency or downstream deployment harm. No stored evidence or source files were modified. ### REA version `rea-agents` package version `6.7.0`; fixed main source SHA `3e7e102a518b9112a2a8aa0590ae6f6951fd0635`. The source checkout, not an installed REA runtime, was tested. ### Runtime and platform Node.js `v24.22.1`; Linux x86_64; the clone's locked TSX loader and dependencies. No Hopper, Ghidra, browser, or other provider was involved. ### Target and artifact context Synthetic capture Evidence for `GET http://app.example/api/measurements`; recorded and probe JSON bodies contain only inert sample numbers. The test fixture creates its manifest through the production analysis-view service. ### Interface Other ### Earliest failure stage Native analysis or reconstruction ### Relevant output or evidence Actual probe stdout: ```jsonl {"label":"numeric-field","agreement":0,"normalised":{"ids":0,"clocks":0,"generated":0,"large_bodies":0,"unobserved_bodies":0},"differences":[{"category":"value","pointer":"/samples","live":"1800000000","env":"1700000000"}]} {"label":"numeric-array","agreement":1,"normalised":{"ids":0,"clocks":1,"generated":0,"large_bodies":0,"unobserved_bodies":0},"differences":[]} {"label":"small-array-control","agreement":0,"normalised":{"ids":0,"clocks":0,"generated":0,"large_bodies":0,"unobserved_bodies":0},"differences":[{"category":"value","pointer":"/samples/0","live":"18","env":"17"}]} {"label":"clock-control","agreement":1,"normalised":{"ids":0,"clocks":1,"generated":0,"large_bodies":0,"unobserved_bodies":0},"differences":[]} {"label":"same-array-control","agreement":1,"normalised":{"ids":0,"clocks":0,"generated":0,"large_bodies":0,"unobserved_bodies":0},"differences":[]} ``` Evidence provenance: observed. Local verification: reproduced. Reproduction completeness: complete. The observed property is the source application boundary's comparison result, not live-provider behavior. Source: [numeric clock detection](https://github.com/morluto/rea/blob/3e7e102a518b9112a2a8aa0590ae6f6951fd0635/src/application/environment/environmentCompareDiff.ts#L77-L90) allows numbers when `key` is undefined; [array traversal](https://github.com/morluto/rea/blob/3e7e102a518b9112a2a8aa0590ae6f6951fd0635/src/application/environment/environmentCompareDiff.ts#L230-L249) visits each element without its containing field name. The resulting numbers reach clock-format comparison without a time-named field. ### Typed outcome or recovery expected Return a `value` difference at `/samples/0` and agreement `0` for the changed measurement array. Keep the identical-array control equal and the named-clock control normalized. The scalar measurement control must continue to differ. ### Analysis or suspected cause Source-derived explanation: the clock classifier excludes numeric non-time fields only when `key !== undefined`. Array traversal leaves the key undefined, so epoch-shaped array measurements are admitted as clocks. This explains the observed scalar/array discrepancy; no implementation change is proposed here. ### Additional context Related: #6551 concerns non-time scalar field names ending in `at`. This report concerns a different path: array traversal loses the field name entirely, so the current field-name guard does not prevent the false clock normalization. The scalar measurement control distinguishes this trigger. ## Triage assessment - Impact level: P2 - Assessment status: supported - Impact basis: The production comparison boundary reports full agreement while a valid non-time numeric response element changed, suppressing a consequential fidelity difference in this bounded case. - Workaround status: unverified - Workaround basis: The scalar control detects the difference, but changing the application's response shape is not an established practical workaround for existing captures; no supported per-field clock opt-out was verified.
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
morluto/rea#4215
No description provided.