[GH-ISSUE #4679] [Bug] Page inspection drops repeated Link fields from Document responses #2437

Closed
opened 2026-10-17 21:04:40 +00:00 by bsakaguchi · 2 comments
Owner

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

Before submitting

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

Area

Process capture or runtime observation

Observed behavior

rea inspect-web-page exits successfully but returns metadata.responses[].links: [] for same-origin iframe Document responses containing two separate Link header fields. The corresponding link_rel hint is absent from metadata.agent_hints. A separate CDP session records both Link values in the same response, joined by an actual newline. Equivalent comma-joined Document headers, a single-link Document header, and repeated Link fields on a Fetch response all preserve their links.

Reproduction

I ran this fixture against a fresh source checkout and a separate installation of the published npm package. Both final runs captured three responses per case. Every repeated-header Document response lost both links; every control retained its expected links. These are real Chrome and CLI runs, with no simulated CDP peer.

  1. For source, use the repository's pinned development toolchain, run npm ci and npm run build:cached, then save the fixture below as link-repro.mjs in the disposable checkout. Run node link-repro.mjs "$PWD/scripts/rea.mjs" "$PWD/node_modules/playwright-core/index.mjs".
  2. For the published package, save the same fixture in a disposable directory, run npm install --prefix ./package rea-agents@6.5.0, then run node link-repro.mjs "$PWD/package/node_modules/rea-agents/scripts/rea.mjs" "$PWD/package/node_modules/playwright-core/index.mjs".
  3. Provide a local Chrome-family executable through REA_BROWSER_EXECUTABLE, or have the browser for the installed Playwright version available. I supplied the executable from that pinned Playwright browser installation. The fixture launches its own headless browser profile and uses a fresh HOME and a minimal environment for REA and Chrome. It starts only a loopback HTTP fixture and closes its browser and server afterward.
  4. Compare /repeated with /comma, /single, and /fetched in the output. Inspect hint_paths for the distinct /repeated-mcp destination.
Executed fixture
import { createServer } from 'node:http';
import { execFile } from 'node:child_process';
import { promisify } from 'node:util';
import { mkdtemp, mkdir, readFile, rm } from 'node:fs/promises';
import { tmpdir } from 'node:os';
import { join } from 'node:path';
const { chromium } = await import(process.argv[3]);
const exec = promisify(execFile);
const work = await mkdtemp(join(process.cwd(), 'browser-run-'));
await mkdir(join(work, 'home'));
const env = { HOME: join(work, 'home'), PATH: process.env.PATH };
const pairs = prefix => [`</${prefix}-next>; rel=next`, `</${prefix}-mcp>; rel=mcp`];
const headers = {
  '/repeated': ['Link', pairs('repeated')[0], 'Link', pairs('repeated')[1]],
  '/comma': ['Link', pairs('comma').join(', ')],
  '/single': ['Link', '</single-mcp>; rel=mcp'],
  '/fetched': ['Link', pairs('fetched')[0], 'Link', pairs('fetched')[1]]
};
const server = createServer((req, res) => {
  const path = new URL(req.url, 'http://127.0.0.1').pathname;
  res.writeHead(200, ['Content-Type', 'text/html', 'Cache-Control', 'no-store', ...(headers[path] ?? [])]);
  res.end(path === '/app' ? `<!doctype html><title>Response fixture</title><script>
    setInterval(() => {
      ['/fetched'].forEach(p => fetch(p).catch(() => {}));
      document.querySelectorAll('iframe').forEach(f => f.remove());
      ['/repeated','/comma','/single'].forEach(p => { const f = document.createElement('iframe'); f.src = p; document.body.append(f); });
    }, 250);
  </script>` : '<!doctype html><title>Response fixture</title>');
});
await new Promise(resolve => server.listen(0, '127.0.0.1', resolve));
const origin = `http://127.0.0.1:${server.address().port}`;
let context;
try {
  context = await chromium.launchPersistentContext(join(work, 'profile'), {
    executablePath: process.env.REA_BROWSER_EXECUTABLE ?? chromium.executablePath(), headless: true, env,
    args: ['--remote-debugging-address=127.0.0.1', '--remote-debugging-port=0', '--disable-background-networking', '--disable-component-update', '--disable-sync']
  });
  const page = context.pages()[0];
  const cdp = await context.newCDPSession(page);
  const wire = [];
  for (const method of ['Network.responseReceived', 'Network.responseReceivedExtraInfo']) cdp.on(method, params => wire.push({ method, params }));
  await cdp.send('Network.enable');
  await page.goto(origin + '/app');
  const port = (await readFile(join(work, 'profile', 'DevToolsActivePort'), 'utf8')).split('\n')[0];
  const endpoint = `http://127.0.0.1:${port}`;
  const target = (await (await fetch(endpoint + '/json/list')).json()).find(t => t.url === origin + '/app');
  const { stdout, stderr } = await exec(process.execPath, [process.argv[2], 'inspect-web-page', endpoint, target.id, '--observation-ms', '800', '--json'], { env, maxBuffer: 8 * 1024 * 1024 });
  const result = JSON.parse(stdout);
  const inspection = result.normalized_result;
  if (!inspection) throw new Error(stdout);
  const fields = h => Object.fromEntries(Object.entries(h).filter(([k]) => /^link$/i.test(k)));
  const samples = {};
  for (const path of Object.keys(headers)) {
    const rows = inspection.metadata.responses.filter(r => new URL(r.url).pathname === path);
    if (!rows.length) throw new Error('No captured response for ' + path);
    const event = wire.find(e => e.method === 'Network.responseReceived' && new URL(e.params.response.url).pathname === path);
    const extra = wire.find(e => e.method === 'Network.responseReceivedExtraInfo' && e.params.requestId === event.params.requestId);
    samples[path] = { count: rows.length, type: event.params.type, headers: fields(event.params.response.headers), extra_headers: extra ? fields(extra.params.headers) : null, results: [...new Set(rows.map(r => JSON.stringify({ links: r.links.map(l => ({ path: new URL(l.href).pathname, rel: l.rel })) })))].map(s => JSON.parse(s)) };
  }
  const version = await cdp.send('Browser.getVersion');
  console.log(JSON.stringify({ operation: result.operation, chrome: version.product, revision: version.revision, stderr, samples, hint_paths: inspection.metadata.agent_hints.map(h => new URL(h.url).pathname) }, null, 2));
} finally {
  if (context) await context.close();
  await new Promise(resolve => server.close(resolve));
  await rm(work, { recursive: true, force: true });
}

Expected behavior

Retain both document links and their individual relation lists when CDP represents separate Link field lines with a newline, and emit the supported MCP relation hint. docs/browser-observation.md, Data minimization, promises a structured Link projection before raw headers are discarded. RFC 8288 section 3 defines the Link field as a list of link-values, and RFC 9110 section 5.2 permits repeated field lines for list-valued fields. The fixture sends separate HTTP field lines; the newline appears only in Chromium's CDP representation.

Impact and scope

A valid, bounded response-header form silently loses every declared link and an associated agent-discovery hint in successful page-inspection output. In each final run the failure occurred in all three captured repeated-header iframe Document responses. Browser navigation still succeeded. This does not establish a security-boundary failure or a failure for every CDP resource type.

  • Impact level: P2
  • Assessment status: supported
  • Impact basis: The observed omission changes response evidence and agent-discovery results for valid repeated Document Link fields. The CLI and the remaining inspection results still work.
  • Workaround status: available
  • Workaround basis: When the server is under the caller's control, the tested comma-combined Link field preserves both links and the MCP hint. For an unchanged server, the separate CDP recording retained the raw Link values for manual inspection, though REA's result remains incomplete.

REA version

Published npm rea-agents@6.5.0, confirmed by CLI version output; latest registry release when checked. Source main commit e31aa72c9d44b22ae4fb86a21700d877496d785b, whose CLI also reports the same package version.

Runtime and platform

Node v24.18.0; npm 11.16.0; macOS 27.0.1 arm64. Chrome/145.0.7632.6, revision @47e20adcc15fc15f01825aa17e570c8f5492ac0f, from the browser installation for locked playwright-core 1.58.2. Other browser versions and platforms were not tested.

Target and artifact context

A self-owned loopback HTTP fixture with an unchanged top-level app, periodically replaced same-origin iframes, and a Fetch control. Each case has distinct destination paths so a successful control cannot supply the failing case's MCP hint. No external page, credentials, or private artifact is needed.

Interface

CLI

Earliest failure stage

Capture or observation

Relevant output or evidence

  • Expected source: docs/browser-observation.md:293, Data minimization, and the HTTP field semantics cited above.
  • Failure source: Executed public inspect-web-page CLI output compared with independently captured Network.responseReceived events; source parsing at src/browser/CdpSafeMetadata.ts:188-190 and :212-238.
  • Evidence provenance: observed
  • Local verification: reproduced
  • Reproduction completeness: complete

Both final commands exited 0 with empty stderr and returned operation inspect_web_page. The fixture invokes node <cli-entry> inspect-web-page <local-endpoint> <target-id> --observation-ms 800 --json after discovering the dedicated browser target.

Case CDP response type Link joining in responseReceived Source result Published-package result
/repeated Document Newline No links, 3/3 responses No links, 3/3 responses
/comma Document Comma and space Both links, 3/3 Both links, 3/3
/single Document One value One link, 3/3 One link, 3/3
/fetched Fetch Comma and space Both links, 3/3 Both links, 3/3

In both runs, the relevant Document CDP input and REA projection were:

{
  "headers": {
    "Link": "</repeated-next>; rel=next\n</repeated-mcp>; rel=mcp"
  },
  "results": [{ "links": [] }],
  "hint_paths": ["/comma-mcp", "/fetched-mcp", "/single-mcp"]
}

This is a reduced view of the fixture's output. The JSON \n encodes an actual newline. Network.responseReceivedExtraInfo used newline joining for both Document and Fetch cases, but REA consumes responseReceived; the Fetch control used comma joining in that event and worked.

Typed outcome or recovery expected

The repeated-header Document case should return links to /repeated-next and /repeated-mcp with relation lists ["next"] and ["mcp"], plus a link_rel hint for the latter. Preserve the passing comma-joined Document, single-link Document, and repeated-field Fetch controls. The passing result must retain both declarations rather than report successful inspection with an empty link list.

Analysis or suspected cause

Source-derived explanation: splitLinkHeader separates comma-delimited entries but does not separate the newline-joined field values. The following entry regex uses a dot without dot-all mode, so it cannot match the combined value across the newline and discards it. Agent hints derive from retained links. In the executed case both links disappear; I did not observe corruption of the first link's relation list.

Additional context

Open and closed issue searches and all-state PR searches for Link headers, repeated fields, newline joining, CdpSafeMetadata, link_rel, and responseReceived found no matching report. Related PR #805 addressed delimiters inside URI targets and quoted parameters; it did not address newline-joined repeated fields. No last-known-good version was established.

Originally created by @coygeek on GitHub (Oct 17, 2026). Original GitHub issue: https://github.com/morluto/rea/issues/4679 ### 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 Process capture or runtime observation ### Observed behavior `rea inspect-web-page` exits successfully but returns `metadata.responses[].links: []` for same-origin iframe Document responses containing two separate Link header fields. The corresponding `link_rel` hint is absent from `metadata.agent_hints`. A separate CDP session records both Link values in the same response, joined by an actual newline. Equivalent comma-joined Document headers, a single-link Document header, and repeated Link fields on a Fetch response all preserve their links. ### Reproduction I ran this fixture against a fresh source checkout and a separate installation of the published npm package. Both final runs captured three responses per case. Every repeated-header Document response lost both links; every control retained its expected links. These are real Chrome and CLI runs, with no simulated CDP peer. 1. For source, use the repository's pinned development toolchain, run `npm ci` and `npm run build:cached`, then save the fixture below as `link-repro.mjs` in the disposable checkout. Run `node link-repro.mjs "$PWD/scripts/rea.mjs" "$PWD/node_modules/playwright-core/index.mjs"`. 2. For the published package, save the same fixture in a disposable directory, run `npm install --prefix ./package rea-agents@6.5.0`, then run `node link-repro.mjs "$PWD/package/node_modules/rea-agents/scripts/rea.mjs" "$PWD/package/node_modules/playwright-core/index.mjs"`. 3. Provide a local Chrome-family executable through `REA_BROWSER_EXECUTABLE`, or have the browser for the installed Playwright version available. I supplied the executable from that pinned Playwright browser installation. The fixture launches its own headless browser profile and uses a fresh HOME and a minimal environment for REA and Chrome. It starts only a loopback HTTP fixture and closes its browser and server afterward. 4. Compare `/repeated` with `/comma`, `/single`, and `/fetched` in the output. Inspect `hint_paths` for the distinct `/repeated-mcp` destination. <details> <summary>Executed fixture</summary> ```js import { createServer } from 'node:http'; import { execFile } from 'node:child_process'; import { promisify } from 'node:util'; import { mkdtemp, mkdir, readFile, rm } from 'node:fs/promises'; import { tmpdir } from 'node:os'; import { join } from 'node:path'; const { chromium } = await import(process.argv[3]); const exec = promisify(execFile); const work = await mkdtemp(join(process.cwd(), 'browser-run-')); await mkdir(join(work, 'home')); const env = { HOME: join(work, 'home'), PATH: process.env.PATH }; const pairs = prefix => [`</${prefix}-next>; rel=next`, `</${prefix}-mcp>; rel=mcp`]; const headers = { '/repeated': ['Link', pairs('repeated')[0], 'Link', pairs('repeated')[1]], '/comma': ['Link', pairs('comma').join(', ')], '/single': ['Link', '</single-mcp>; rel=mcp'], '/fetched': ['Link', pairs('fetched')[0], 'Link', pairs('fetched')[1]] }; const server = createServer((req, res) => { const path = new URL(req.url, 'http://127.0.0.1').pathname; res.writeHead(200, ['Content-Type', 'text/html', 'Cache-Control', 'no-store', ...(headers[path] ?? [])]); res.end(path === '/app' ? `<!doctype html><title>Response fixture</title><script> setInterval(() => { ['/fetched'].forEach(p => fetch(p).catch(() => {})); document.querySelectorAll('iframe').forEach(f => f.remove()); ['/repeated','/comma','/single'].forEach(p => { const f = document.createElement('iframe'); f.src = p; document.body.append(f); }); }, 250); </script>` : '<!doctype html><title>Response fixture</title>'); }); await new Promise(resolve => server.listen(0, '127.0.0.1', resolve)); const origin = `http://127.0.0.1:${server.address().port}`; let context; try { context = await chromium.launchPersistentContext(join(work, 'profile'), { executablePath: process.env.REA_BROWSER_EXECUTABLE ?? chromium.executablePath(), headless: true, env, args: ['--remote-debugging-address=127.0.0.1', '--remote-debugging-port=0', '--disable-background-networking', '--disable-component-update', '--disable-sync'] }); const page = context.pages()[0]; const cdp = await context.newCDPSession(page); const wire = []; for (const method of ['Network.responseReceived', 'Network.responseReceivedExtraInfo']) cdp.on(method, params => wire.push({ method, params })); await cdp.send('Network.enable'); await page.goto(origin + '/app'); const port = (await readFile(join(work, 'profile', 'DevToolsActivePort'), 'utf8')).split('\n')[0]; const endpoint = `http://127.0.0.1:${port}`; const target = (await (await fetch(endpoint + '/json/list')).json()).find(t => t.url === origin + '/app'); const { stdout, stderr } = await exec(process.execPath, [process.argv[2], 'inspect-web-page', endpoint, target.id, '--observation-ms', '800', '--json'], { env, maxBuffer: 8 * 1024 * 1024 }); const result = JSON.parse(stdout); const inspection = result.normalized_result; if (!inspection) throw new Error(stdout); const fields = h => Object.fromEntries(Object.entries(h).filter(([k]) => /^link$/i.test(k))); const samples = {}; for (const path of Object.keys(headers)) { const rows = inspection.metadata.responses.filter(r => new URL(r.url).pathname === path); if (!rows.length) throw new Error('No captured response for ' + path); const event = wire.find(e => e.method === 'Network.responseReceived' && new URL(e.params.response.url).pathname === path); const extra = wire.find(e => e.method === 'Network.responseReceivedExtraInfo' && e.params.requestId === event.params.requestId); samples[path] = { count: rows.length, type: event.params.type, headers: fields(event.params.response.headers), extra_headers: extra ? fields(extra.params.headers) : null, results: [...new Set(rows.map(r => JSON.stringify({ links: r.links.map(l => ({ path: new URL(l.href).pathname, rel: l.rel })) })))].map(s => JSON.parse(s)) }; } const version = await cdp.send('Browser.getVersion'); console.log(JSON.stringify({ operation: result.operation, chrome: version.product, revision: version.revision, stderr, samples, hint_paths: inspection.metadata.agent_hints.map(h => new URL(h.url).pathname) }, null, 2)); } finally { if (context) await context.close(); await new Promise(resolve => server.close(resolve)); await rm(work, { recursive: true, force: true }); } ``` </details> ### Expected behavior Retain both document links and their individual relation lists when CDP represents separate Link field lines with a newline, and emit the supported MCP relation hint. `docs/browser-observation.md`, Data minimization, promises a structured Link projection before raw headers are discarded. [RFC 8288 section 3](https://www.rfc-editor.org/rfc/rfc8288.html#section-3) defines the Link field as a list of link-values, and [RFC 9110 section 5.2](https://www.rfc-editor.org/rfc/rfc9110.html#section-5.2) permits repeated field lines for list-valued fields. The fixture sends separate HTTP field lines; the newline appears only in Chromium's CDP representation. ### Impact and scope A valid, bounded response-header form silently loses every declared link and an associated agent-discovery hint in successful page-inspection output. In each final run the failure occurred in all three captured repeated-header iframe Document responses. Browser navigation still succeeded. This does not establish a security-boundary failure or a failure for every CDP resource type. - Impact level: P2 - Assessment status: supported - Impact basis: The observed omission changes response evidence and agent-discovery results for valid repeated Document Link fields. The CLI and the remaining inspection results still work. - Workaround status: available - Workaround basis: When the server is under the caller's control, the tested comma-combined Link field preserves both links and the MCP hint. For an unchanged server, the separate CDP recording retained the raw Link values for manual inspection, though REA's result remains incomplete. ### REA version Published npm `rea-agents@6.5.0`, confirmed by CLI version output; latest registry release when checked. Source `main` commit `e31aa72c9d44b22ae4fb86a21700d877496d785b`, whose CLI also reports the same package version. ### Runtime and platform Node v24.18.0; npm 11.16.0; macOS 27.0.1 arm64. Chrome/145.0.7632.6, revision `@47e20adcc15fc15f01825aa17e570c8f5492ac0f`, from the browser installation for locked playwright-core 1.58.2. Other browser versions and platforms were not tested. ### Target and artifact context A self-owned loopback HTTP fixture with an unchanged top-level app, periodically replaced same-origin iframes, and a Fetch control. Each case has distinct destination paths so a successful control cannot supply the failing case's MCP hint. No external page, credentials, or private artifact is needed. ### Interface CLI ### Earliest failure stage Capture or observation ### Relevant output or evidence - Expected source: `docs/browser-observation.md:293`, Data minimization, and the HTTP field semantics cited above. - Failure source: Executed public `inspect-web-page` CLI output compared with independently captured `Network.responseReceived` events; source parsing at `src/browser/CdpSafeMetadata.ts:188-190` and `:212-238`. - Evidence provenance: observed - Local verification: reproduced - Reproduction completeness: complete Both final commands exited 0 with empty stderr and returned operation `inspect_web_page`. The fixture invokes `node <cli-entry> inspect-web-page <local-endpoint> <target-id> --observation-ms 800 --json` after discovering the dedicated browser target. | Case | CDP response type | Link joining in responseReceived | Source result | Published-package result | | --- | --- | --- | --- | --- | | `/repeated` | Document | Newline | No links, 3/3 responses | No links, 3/3 responses | | `/comma` | Document | Comma and space | Both links, 3/3 | Both links, 3/3 | | `/single` | Document | One value | One link, 3/3 | One link, 3/3 | | `/fetched` | Fetch | Comma and space | Both links, 3/3 | Both links, 3/3 | In both runs, the relevant Document CDP input and REA projection were: ```json { "headers": { "Link": "</repeated-next>; rel=next\n</repeated-mcp>; rel=mcp" }, "results": [{ "links": [] }], "hint_paths": ["/comma-mcp", "/fetched-mcp", "/single-mcp"] } ``` This is a reduced view of the fixture's output. The JSON `\n` encodes an actual newline. `Network.responseReceivedExtraInfo` used newline joining for both Document and Fetch cases, but REA consumes `responseReceived`; the Fetch control used comma joining in that event and worked. ### Typed outcome or recovery expected The repeated-header Document case should return links to `/repeated-next` and `/repeated-mcp` with relation lists `["next"]` and `["mcp"]`, plus a `link_rel` hint for the latter. Preserve the passing comma-joined Document, single-link Document, and repeated-field Fetch controls. The passing result must retain both declarations rather than report successful inspection with an empty link list. ### Analysis or suspected cause Source-derived explanation: `splitLinkHeader` separates comma-delimited entries but does not separate the newline-joined field values. The following entry regex uses a dot without dot-all mode, so it cannot match the combined value across the newline and discards it. Agent hints derive from retained links. In the executed case both links disappear; I did not observe corruption of the first link's relation list. ### Additional context Open and closed issue searches and all-state PR searches for Link headers, repeated fields, newline joining, `CdpSafeMetadata`, `link_rel`, and `responseReceived` found no matching report. Related [PR #805](https://github.com/morluto/rea/pull/805) addressed delimiters inside URI targets and quoted parameters; it did not address newline-joined repeated fields. No last-known-good version was established.
Author
Owner

@morluto commented on GitHub (Oct 17, 2026):

Published in df2fc251e3 on main.

Root cause: the Link list parser treated CDP's newline-joined repeated fields as one link-value, so the entry matcher rejected the combined Document header. The producing Link boundary now parses each field line independently, then applies the existing URI/quoted-parameter-aware comma parser. Declaration order and derived untrusted agent hints are preserved; no downstream hint repair is needed. This is consistent with Link list semantics and repeated HTTP field semantics.

Executed the report's real pinned Chrome/145.0.7632.6 public CLI fixture on baseline 0ec482663 and candidate, with an 800ms observation window:

  • Both runs captured three responses for each of repeated Document, comma-combined Document, single-link Document and repeated-field Fetch cases.
  • Baseline repeated Documents had no links and no /repeated-mcp hint. Candidate retained /repeated-next with ["next"], /repeated-mcp with ["mcp"], and the hint in all three responses.
  • All three control projections were unchanged. Both commands succeeded with empty CLI stderr. The fixture closed its browser/server and removed its dedicated directories; no fixture directories remain.

Validation: three new source controls failed before the fix; final 42 metadata controls passed. A 61-check metadata/network/existing MCP run passed, followed by 15 overlapping network checks including a new persistent Document-event regression. LF, CRLF, comma-in-URI, quoted delimiters, relation lists, original destination classification and field-local quote state are covered. check:fast, docs:check with five README checks, and normal commit/pre-push hooks passed.

Scope: real public CLI with the pinned browser on Darwin arm64, plus simulated socket boundary and existing MCP contracts. Other browser/platform combinations and a repeated-field-specific MCP call were not runtime-tested. Performance, full-task token costs and peak-family memory were not measured; no efficiency claim is made. Separate #4677 structured COOP/COEP item parsing remains pending.

<!-- gh-comment-id:6230127817 --> @morluto commented on GitHub (Oct 17, 2026): Published in df2fc251e318a1255309dba232f9f73c700777b6 on main. Root cause: the Link list parser treated CDP's newline-joined repeated fields as one link-value, so the entry matcher rejected the combined Document header. The producing Link boundary now parses each field line independently, then applies the existing URI/quoted-parameter-aware comma parser. Declaration order and derived untrusted agent hints are preserved; no downstream hint repair is needed. This is consistent with [Link list semantics](https://www.rfc-editor.org/rfc/rfc8288.html#section-3) and [repeated HTTP field semantics](https://www.rfc-editor.org/rfc/rfc9110.html#section-5.2). Executed the report's real pinned Chrome/145.0.7632.6 public CLI fixture on baseline 0ec482663 and candidate, with an 800ms observation window: - Both runs captured three responses for each of repeated Document, comma-combined Document, single-link Document and repeated-field Fetch cases. - Baseline repeated Documents had no links and no `/repeated-mcp` hint. Candidate retained `/repeated-next` with `["next"]`, `/repeated-mcp` with `["mcp"]`, and the hint in all three responses. - All three control projections were unchanged. Both commands succeeded with empty CLI stderr. The fixture closed its browser/server and removed its dedicated directories; no fixture directories remain. Validation: three new source controls failed before the fix; final 42 metadata controls passed. A 61-check metadata/network/existing MCP run passed, followed by 15 overlapping network checks including a new persistent Document-event regression. LF, CRLF, comma-in-URI, quoted delimiters, relation lists, original destination classification and field-local quote state are covered. `check:fast`, `docs:check` with five README checks, and normal commit/pre-push hooks passed. Scope: real public CLI with the pinned browser on Darwin arm64, plus simulated socket boundary and existing MCP contracts. Other browser/platform combinations and a repeated-field-specific MCP call were not runtime-tested. Performance, full-task token costs and peak-family memory were not measured; no efficiency claim is made. Separate #4677 structured COOP/COEP item parsing remains pending.
Author
Owner

@coygeek commented on GitHub (Oct 18, 2026):

Closing as addressed on main by df2fc251e. The Link parser now handles newline-separated CDP field lines before parsing individual link values, retaining repeated Document links and their agent hints alongside comma-joined controls. I verified the committed source and regression coverage; I have not rerun the runtime reproduction.

<!-- gh-comment-id:6244109652 --> @coygeek commented on GitHub (Oct 18, 2026): Closing as addressed on `main` by [df2fc251e](https://github.com/morluto/rea/commit/df2fc251e318a1255309dba232f9f73c700777b6). The Link parser now handles newline-separated CDP field lines before parsing individual link values, retaining repeated Document links and their agent hints alongside comma-joined controls. I verified the committed source and regression coverage; I have not rerun the runtime reproduction.
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#2437
No description provided.