[GH-ISSUE #7328] [Bug] Local source-map limit guidance omits declaration costs in the shared record budget #4356

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

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

Before submitting

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

Area

Documentation

Observed behavior

The local source-location guide describes a limit of “262144 decoded rows/segments,” and successful trace-web-source-location Evidence repeats that description. The decoder actually shares a 262144-record budget across rows, segments, leaf maps, declarations and indexed sections. The local explanation omits those additional costs. This is an explanation defect; the aggregate capacity check itself behaves consistently.

A public CLI control with 87,380 generated rows, 174,760 mapping segments, one source and two unused names succeeds at exactly 262144 total records. Adding one unused name returns code: "truncated" with Source-map structure exceeds the 262144-record decoder budget. Both maps have the same 262140 combined rows and segments, so the missing accounting matters even if “rows/segments” is read as a combined count.

Reproduction

From the built source checkout at the stated baseline, with Node.js 24.22.1 selected as node, run this self-contained script. It creates a synthetic exported-script manifest and maps, isolates HOME/XDG/TMPDIR, and invokes the public CLI sequentially. The inert URL supplies resolution context only. No capture file, browser, network request or captured application execution is needed.

node --input-type=module <<'JS'
import { createHash } from 'node:crypto';
import { mkdirSync, mkdtempSync, realpathSync, writeFileSync } from 'node:fs';
import { tmpdir } from 'node:os';
import { join, resolve } from 'node:path';
import { spawnSync } from 'node:child_process';
const packageRoot = resolve('.');
const root = realpathSync(mkdtempSync(join(tmpdir(), 'rea-record-budget-')));
mkdirSync(join(root, 'files', 'modules'), { recursive: true });
const source = 'generated';
writeFileSync(join(root, 'files', 'modules', 'main.js'), source);
const manifest = {
  capture_path: join(root, 'capture.json'), capture_sha256: 'a'.repeat(64),
  capture_kind: 'browser-scenario', source_evidence_id: null,
  capture_completeness: {
    status: 'complete', equality_eligible: true,
    missing_sections: [], truncated_sections: []
  },
  output_directory: root,
  analysis_input: { input_path: root, format: 'directory' },
  limitations: [],
  scripts: [{
    url: 'https://example.test/main.js',
    source: {
      kind: 'scenario-response', transaction_id: 'fixture',
      request_sequence: 1, response_sequence: 2, status: 200
    },
    content: {
      state: 'exported', relative_path: 'modules/main.js',
      layout: 'isolated', layout_reason: 'fixture',
      sha256: createHash('sha256').update(source).digest('hex'),
      bytes: Buffer.byteLength(source), media_type: 'text/javascript',
      redacted: false, representation: 'browser-decoded-response-bytes'
    }
  }]
};
writeFileSync(join(root, 'manifest.json'), JSON.stringify(manifest));
const env = { PATH: process.env.PATH };
for (const [key, directory] of Object.entries({
  HOME: 'home', XDG_CONFIG_HOME: 'config', XDG_CACHE_HOME: 'cache',
  XDG_DATA_HOME: 'data', TMPDIR: 'runtime'
})) {
  env[key] = join(root, directory);
  mkdirSync(env[key], { recursive: true });
}
for (const count of [2, 3]) {
  const text = JSON.stringify({
    version: 3, sources: ['a.ts'],
    names: Array.from({ length: count }, (_, i) => `n${i}`),
    mappings: 'AAAA,AAAA;'.repeat(87379) + 'AAAA,AAAA'
  });
  const map = join(root, `names-${count}.map`);
  writeFileSync(map, text);
  const run = spawnSync(process.execPath, [
    join(packageRoot, 'scripts', 'rea.mjs'), 'trace-web-source-location',
    join(root, 'manifest.json'), '0', map,
    'https://example.test/app.js.map', '1', '0', '--json'
  ], { env, cwd: root, encoding: 'utf8', timeout: 60000,
       maxBuffer: 4 * 1024 * 1024 });
  if (run.error) throw run.error;
  writeFileSync(join(root, `names-${count}.stdout.json`), run.stdout);
  writeFileSync(join(root, `names-${count}.stderr.txt`), run.stderr);
  const value = JSON.parse(run.stdout);
  console.log(JSON.stringify({
    names: count, bytes: Buffer.byteLength(text), exit: run.status,
    code: value.code ?? null,
    message: value.message?.replaceAll(root, '<work>') ?? null,
    matches: value.normalized_result?.matches.length ?? null,
    budgetText: value.limitations?.find(x => x.includes('decoded rows/segments'))
  }));
}
JS

The two-name case succeeds with two equal-position matches. The three-name case fails explicitly. Both initial controls and the self-contained script replay produced these outcomes, without a cleanup error.

Expected behavior

The local guide and explanatory Evidence should identify the actual shared record allowance and the kinds of structures that consume it. Callers should be able to understand why adding a declaration can change admission while the row and segment counts remain unchanged. This report does not assume that individual row/segment ceilings guarantee acceptance of every combination below them.

Impact and scope

P3 documentation and explanatory-text omission. The runtime returns a clear capacity refusal and the successful control returns correct point matches; no silent truncation, incorrect lookup or artifact loss was observed. The missing accounting makes the published local-limit description insufficient for predicting this boundary. The browser-collection paragraph separately names a record budget, but the local-decoder explanation does not identify its declaration costs. Real-capture frequency and any consequential workflow impact attributable to this wording are NOT_ENOUGH_INFO: only synthetic fixtures were tested.

REA version

6.8.0 source revision ace4dbf36e, with its existing compiled runtime and locked dependencies.

Runtime and platform

Node.js v24.22.1; macOS arm64; the successful codec reports node-v8 and a 226492416-byte V8 heap limit. MCP, Bun, Windows and browser collection were not executed.

Target and artifact context

Synthetic export_web_scripts-shaped manifest with one retained nine-byte script, selected position line 1/column 0, and regular v3 maps with 87,380 rows, 174,760 four-component segments, one source, no sourcesContent and two or three unused names. Map sizes are 873865 and 873870 bytes. Capture metadata is inert historical context; the trace verifies the selected local bytes.

Interface

CLI

Earliest failure stage

Unknown

Relevant output or evidence

  • Expected source: CONTRIBUTING.md, Reporting issues, accepts documentation reports identifying missing guidance and the resulting confusion. docs/web-source-location.md:104-109 supplies the local resource-limit description, while :124-128 explicitly describes the browser collection's record budget.
  • Failure source: The local description and src/application/WebSourceLocationService.ts:58-67 name only decoded rows/segments. src/javascript/sourceMaps/SourceMapFormat.ts:118-124,141,263-280 enforces a shared allowance that also charges each leaf, source/name/sourcesContent slot and indexed section. Executed public CLI output confirms the two-name/three-name boundary and repeats the incomplete description on success.
  • Evidence provenance: observed public CLI outcomes and successful Evidence text; static comparison of that description with its production accounting owner.
  • Local verification: reproduced through the built public CLI, including replay of the inline script. All relevant stderr streams were empty; no process-ownership substitution or resource-check bypass was used.
  • Reproduction completeness: complete.

Observed result fragments, with the fixture path replaced by <work>:

names=2, bytes=873865: exit=0, matches=2
names=3, bytes=873870: exit=1, code=truncated
message: <work>/names-3.map: Source-map structure exceeds the 262144-record decoder budget.
control limitation: Complete evidence has a 4 MiB map, 32 MiB reply, 262144 decoded rows/segments and 64-level section budget.

Source-derived accounting for these regular maps is 87380 rows + 174760 segments + 1 leaf + 1 source + names: 262144 records with two names and 262145 with three. Individual row/segment limits, map/reply byte limits and section depth remain separate constraints. “Unknown” above reflects that this report concerns documentation and explanatory text, rather than an incorrect runtime stage.

Additional context

Restoration check: the local resource explanation and successful Evidence should describe the shared record budget sufficiently to account for the declaration-only difference. Repeating the fixture should retain the two-name success and three-name explicit refusal, with no partial mapping for the refused case. The explanation should agree with those results without relaxing resource admission.

The supplied open/closed issue snapshot was searched by source-map, trace command, 262144, rows/segments, decodedRecords and aggregate-budget terms. The original source-location feature and later deadline changes do not report this accounting-description omission. Related open reports #7132 and #7299 concern generated-position bounds and URL-derivation attribution respectively. No exact duplicate was found in that snapshot or the current upstream issue/PR search before submission. No code change or restoration run was performed.

Triage assessment

  • Impact level: P3
  • Assessment status: supported
  • Impact basis: The local guide and successful explanatory Evidence omit declaration costs from an intentionally enforced aggregate allowance. The control returns correct mapping facts and the over-budget fixture fails explicitly; no consequential behavior defect attributable to the wording was observed.
  • Workaround status: available
  • Workaround basis: The actual capacity refusal names the record budget, and the existing accounting source distinguishes admission correctly. The demonstrated successful point result remains usable; interpreting the record costs does not require changing the artifact or weakening a check.
Originally created by @coygeek on GitHub (Oct 26, 2026). Original GitHub issue: https://github.com/morluto/rea/issues/7328 ## 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 Documentation ## Observed behavior The local source-location guide describes a limit of “262144 decoded rows/segments,” and successful `trace-web-source-location` Evidence repeats that description. The decoder actually shares a 262144-record budget across rows, segments, leaf maps, declarations and indexed sections. The local explanation omits those additional costs. This is an explanation defect; the aggregate capacity check itself behaves consistently. A public CLI control with 87,380 generated rows, 174,760 mapping segments, one source and two unused names succeeds at exactly 262144 total records. Adding one unused name returns `code: "truncated"` with `Source-map structure exceeds the 262144-record decoder budget.` Both maps have the same 262140 combined rows and segments, so the missing accounting matters even if “rows/segments” is read as a combined count. ## Reproduction From the built source checkout at the stated baseline, with Node.js 24.22.1 selected as `node`, run this self-contained script. It creates a synthetic exported-script manifest and maps, isolates HOME/XDG/TMPDIR, and invokes the public CLI sequentially. The inert URL supplies resolution context only. No capture file, browser, network request or captured application execution is needed. ```sh node --input-type=module <<'JS' import { createHash } from 'node:crypto'; import { mkdirSync, mkdtempSync, realpathSync, writeFileSync } from 'node:fs'; import { tmpdir } from 'node:os'; import { join, resolve } from 'node:path'; import { spawnSync } from 'node:child_process'; const packageRoot = resolve('.'); const root = realpathSync(mkdtempSync(join(tmpdir(), 'rea-record-budget-'))); mkdirSync(join(root, 'files', 'modules'), { recursive: true }); const source = 'generated'; writeFileSync(join(root, 'files', 'modules', 'main.js'), source); const manifest = { capture_path: join(root, 'capture.json'), capture_sha256: 'a'.repeat(64), capture_kind: 'browser-scenario', source_evidence_id: null, capture_completeness: { status: 'complete', equality_eligible: true, missing_sections: [], truncated_sections: [] }, output_directory: root, analysis_input: { input_path: root, format: 'directory' }, limitations: [], scripts: [{ url: 'https://example.test/main.js', source: { kind: 'scenario-response', transaction_id: 'fixture', request_sequence: 1, response_sequence: 2, status: 200 }, content: { state: 'exported', relative_path: 'modules/main.js', layout: 'isolated', layout_reason: 'fixture', sha256: createHash('sha256').update(source).digest('hex'), bytes: Buffer.byteLength(source), media_type: 'text/javascript', redacted: false, representation: 'browser-decoded-response-bytes' } }] }; writeFileSync(join(root, 'manifest.json'), JSON.stringify(manifest)); const env = { PATH: process.env.PATH }; for (const [key, directory] of Object.entries({ HOME: 'home', XDG_CONFIG_HOME: 'config', XDG_CACHE_HOME: 'cache', XDG_DATA_HOME: 'data', TMPDIR: 'runtime' })) { env[key] = join(root, directory); mkdirSync(env[key], { recursive: true }); } for (const count of [2, 3]) { const text = JSON.stringify({ version: 3, sources: ['a.ts'], names: Array.from({ length: count }, (_, i) => `n${i}`), mappings: 'AAAA,AAAA;'.repeat(87379) + 'AAAA,AAAA' }); const map = join(root, `names-${count}.map`); writeFileSync(map, text); const run = spawnSync(process.execPath, [ join(packageRoot, 'scripts', 'rea.mjs'), 'trace-web-source-location', join(root, 'manifest.json'), '0', map, 'https://example.test/app.js.map', '1', '0', '--json' ], { env, cwd: root, encoding: 'utf8', timeout: 60000, maxBuffer: 4 * 1024 * 1024 }); if (run.error) throw run.error; writeFileSync(join(root, `names-${count}.stdout.json`), run.stdout); writeFileSync(join(root, `names-${count}.stderr.txt`), run.stderr); const value = JSON.parse(run.stdout); console.log(JSON.stringify({ names: count, bytes: Buffer.byteLength(text), exit: run.status, code: value.code ?? null, message: value.message?.replaceAll(root, '<work>') ?? null, matches: value.normalized_result?.matches.length ?? null, budgetText: value.limitations?.find(x => x.includes('decoded rows/segments')) })); } JS ``` The two-name case succeeds with two equal-position matches. The three-name case fails explicitly. Both initial controls and the self-contained script replay produced these outcomes, without a cleanup error. ## Expected behavior The local guide and explanatory Evidence should identify the actual shared record allowance and the kinds of structures that consume it. Callers should be able to understand why adding a declaration can change admission while the row and segment counts remain unchanged. This report does not assume that individual row/segment ceilings guarantee acceptance of every combination below them. ## Impact and scope P3 documentation and explanatory-text omission. The runtime returns a clear capacity refusal and the successful control returns correct point matches; no silent truncation, incorrect lookup or artifact loss was observed. The missing accounting makes the published local-limit description insufficient for predicting this boundary. The browser-collection paragraph separately names a record budget, but the local-decoder explanation does not identify its declaration costs. Real-capture frequency and any consequential workflow impact attributable to this wording are NOT_ENOUGH_INFO: only synthetic fixtures were tested. ## REA version 6.8.0 source revision ace4dbf36e8c8f36a0e192aff9a11484ed428358, with its existing compiled runtime and locked dependencies. ## Runtime and platform Node.js v24.22.1; macOS arm64; the successful codec reports node-v8 and a 226492416-byte V8 heap limit. MCP, Bun, Windows and browser collection were not executed. ## Target and artifact context Synthetic export_web_scripts-shaped manifest with one retained nine-byte script, selected position line 1/column 0, and regular v3 maps with 87,380 rows, 174,760 four-component segments, one source, no sourcesContent and two or three unused names. Map sizes are 873865 and 873870 bytes. Capture metadata is inert historical context; the trace verifies the selected local bytes. ## Interface CLI ## Earliest failure stage Unknown ## Relevant output or evidence - Expected source: `CONTRIBUTING.md`, Reporting issues, accepts documentation reports identifying missing guidance and the resulting confusion. `docs/web-source-location.md:104-109` supplies the local resource-limit description, while `:124-128` explicitly describes the browser collection's record budget. - Failure source: The local description and `src/application/WebSourceLocationService.ts:58-67` name only decoded rows/segments. `src/javascript/sourceMaps/SourceMapFormat.ts:118-124,141,263-280` enforces a shared allowance that also charges each leaf, source/name/sourcesContent slot and indexed section. Executed public CLI output confirms the two-name/three-name boundary and repeats the incomplete description on success. - Evidence provenance: observed public CLI outcomes and successful Evidence text; static comparison of that description with its production accounting owner. - Local verification: reproduced through the built public CLI, including replay of the inline script. All relevant stderr streams were empty; no process-ownership substitution or resource-check bypass was used. - Reproduction completeness: complete. Observed result fragments, with the fixture path replaced by `<work>`: ```text names=2, bytes=873865: exit=0, matches=2 names=3, bytes=873870: exit=1, code=truncated message: <work>/names-3.map: Source-map structure exceeds the 262144-record decoder budget. control limitation: Complete evidence has a 4 MiB map, 32 MiB reply, 262144 decoded rows/segments and 64-level section budget. ``` Source-derived accounting for these regular maps is 87380 rows + 174760 segments + 1 leaf + 1 source + names: 262144 records with two names and 262145 with three. Individual row/segment limits, map/reply byte limits and section depth remain separate constraints. “Unknown” above reflects that this report concerns documentation and explanatory text, rather than an incorrect runtime stage. ## Additional context Restoration check: the local resource explanation and successful Evidence should describe the shared record budget sufficiently to account for the declaration-only difference. Repeating the fixture should retain the two-name success and three-name explicit refusal, with no partial mapping for the refused case. The explanation should agree with those results without relaxing resource admission. The supplied open/closed issue snapshot was searched by source-map, trace command, 262144, rows/segments, decodedRecords and aggregate-budget terms. The original source-location feature and later deadline changes do not report this accounting-description omission. Related open reports [#7132](https://github.com/morluto/rea/issues/7132) and [#7299](https://github.com/morluto/rea/issues/7299) concern generated-position bounds and URL-derivation attribution respectively. No exact duplicate was found in that snapshot or the current upstream issue/PR search before submission. No code change or restoration run was performed. ## Triage assessment - Impact level: P3 - Assessment status: supported - Impact basis: The local guide and successful explanatory Evidence omit declaration costs from an intentionally enforced aggregate allowance. The control returns correct mapping facts and the over-budget fixture fails explicitly; no consequential behavior defect attributable to the wording was observed. - Workaround status: available - Workaround basis: The actual capacity refusal names the record budget, and the existing accounting source distinguishes admission correctly. The demonstrated successful point result remains usable; interpreting the record costs does not require changing the artifact or weakening a check.
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#4356
No description provided.