[GH-ISSUE #3993] [Bug] A quoted executable pathname containing groups=[ prevents GDB session closure #1588

Closed
opened 2026-10-16 21:07:32 +00:00 by bsakaguchi · 4 comments
Owner

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

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

Closing a GDB session fails when its complete inferior inventory contains groups=[ inside a quoted executable pathname, even when no inferior has a live PID. The MCP close returns cleanup_incomplete, leaves the owned debugger session ready, and does not send the normal exit command. The failure was reproduced with a simulated provider through the registered production MCP tools; real GDB output generation was not tested.

In the completed four-case probe, fixture/plain.out, fixture/groups=.out, and fixture/bracket[.out each closed successfully, returned none_observed, and sent -gdb-exit. Only fixture/groups=[.out failed. Its first close issued two inventory requests, sent no exit command, and returned the following relevant error fields:

{
  "code": "cleanup_incomplete",
  "details": {
    "operation": "close_gdb_session",
    "cleanup_reason": "Owned GDB inferior detach could not be verified: GDB returned an unknown recursive inferior list."
  }
}

A status request after the failed close still returned state: "ready". The subsequent file console call returned an error without dispatching the command to the simulated provider, and a repeated close returned cleanup_incomplete again. An earlier probe attempt reproduced the same failure but failed its own assertion that clearing the file would recover; the completed probe checks the observed failed recovery instead. These are observations of REA's behavior against a simulated production boundary, not measurements of a real GDB session.

Reproduction

Use the baseline under Runtime or environment with its dependencies installed and compiled runtime prepared. From the repository root, run the following command. It uses the repository's simulated-process approach and the production tool registration. The Linux platform option permits the Linux-only lifecycle on the test host; the process is an in-memory event emitter, and its kill method affects only simulated streams. It does not open an executable or run a real target.

The probe starts a fresh session for each filename, selects the file through gdb_console, then closes and checks session status. The simulated provider returns a complete MI inventory with the selected path in a quoted executable field and no PID. For the failing case, it also attempts gdb_console with file and retries close.

node --input-type=module <<'JS'
import { EventEmitter } from 'node:events';
import { PassThrough } from 'node:stream';
import { pathToFileURL } from 'node:url';
import assert from 'node:assert/strict';
const load = p => import(pathToFileURL(`${process.cwd()}/${p}`).href);
const { Client, InMemoryTransport } = await load('node_modules/@modelcontextprotocol/client/dist/index.mjs');
const { McpServer } = await load('node_modules/@modelcontextprotocol/server/dist/index.mjs');
const { GdbSessionManager } = await load('dist/gdb/GdbSessionManager.js');
const { registerGdbTools } = await load('dist/server/registerGdbTools.js');
const { createToolResultDelivery } = await load('dist/server/toolResult.js');
const { silentLogger } = await load('dist/logger.js');
class SimulatedGdb extends EventEmitter {
  pid = process.pid + 1;
  stdout = new PassThrough(); stderr = new PassThrough(); stdin = new PassThrough();
  exitCode = null; signalCode = null;
  kill(signal) {
    this.exitCode = 0; this.signalCode = signal;
    this.stdout.end(); this.stderr.end(); this.stdin.destroy();
    this.emit('exit', 0, signal); this.emit('close', 0, signal);
    return true;
  }
}
for (const filename of ['fixture/plain.out', 'fixture/groups=[.out', 'fixture/groups=.out', 'fixture/bracket[.out']) {
  let child, selected;
  const commands = [], inventories = [];
  const manager = new GdbSessionManager({ platform: 'linux', spawnProcess: async options => {
    child = new SimulatedGdb();
    child.stdin.setEncoding('utf8');
    let buffer = '';
    child.stdin.on('data', chunk => {
      buffer += chunk;
      for (let end; (end = buffer.indexOf('\n')) >= 0;) {
        const line = buffer.slice(0, end); buffer = buffer.slice(end + 1);
        const match = /^(\d+)(.*)$/.exec(line); if (!match) continue;
        const [, token, command] = match; commands.push(command);
        if (command.startsWith('-interpreter-exec console ')) {
          const consoleCommand = JSON.parse(command.slice('-interpreter-exec console '.length));
          if (consoleCommand.startsWith('file ')) selected = consoleCommand.slice(5);
          if (consoleCommand === 'file') selected = undefined;
        }
        let answer = `${token}^done`;
        if (command === '-list-thread-groups --recurse 1') {
          answer += `,groups=${selected ? `[{id="i1",type="process",executable=${JSON.stringify(selected)}}]` : '[]'}`;
          inventories.push(answer);
        }
        child.stdout.write(`${answer}\n`);
      }
    });
    child.stdout.write('(gdb)\n');
    return { process: child, ownership: {runId: options.runId, leaderPid: child.pid, processGroupId: child.pid, expectedParentPid: process.pid} };
  }});
  const server = new McpServer({name:'verification',version:'1'});
  registerGdbTools(server, manager, silentLogger, createToolResultDelivery(undefined));
  const client = new Client({name:'verification',version:'1'});
  const [clientTransport, serverTransport] = InMemoryTransport.createLinkedPair();
  await server.connect(serverTransport); await client.connect(clientTransport);
  const call = (name, args) => client.callTool({name, arguments:args});
  try {
    const start = await call('start_gdb_session', {}); assert(!start.isError);
    const session_id = start.structuredContent.session_id;
    const open = await call('gdb_console', {session_id, command:`file ${filename}`}); assert(!open.isError);
    const closed = await call('close_gdb_session', {session_id});
    const status = await call('gdb_session_status', {session_id});
    const firstCloseCommands = [...commands];
    let recovery;
    if (closed.isError) {
      const clear = await call('gdb_console', {session_id, command:'file'});
      const retry = await call('close_gdb_session', {session_id});
      recovery = {clearError:!!clear.isError, retryError:!!retry.isError, retryResult:retry.structuredContent};
    }
    const outcome = closed.isError ? JSON.parse(closed.content[0].text).error : closed.structuredContent;
    console.log(JSON.stringify({filename, closeError:!!closed.isError, outcome, retained:!status.isError, state:status.structuredContent?.state, firstCloseCommands, inventories, recovery}));
    assert.equal(!!closed.isError, filename.includes('groups=['));
    if (recovery) assert.equal(recovery.retryError, true);
  } finally {
    await client.close(); await server.close();
    await manager.closeAll().catch(() => {});
    if (child?.exitCode === null) child.kill('SIGTERM');
  }
}
JS

Expected behavior

A complete, valid inventory with no live PID should allow close_gdb_session to release its owned debugger and return target_state: "none_observed". Literal text in a quoted executable pathname should remain a string value when interpreting the inventory. The durable output TTY requirement applies to live inferiors, so it does not apply to these inputs.

Impact and scope

A bounded class of executable pathnames makes ordinary shutdown fail for an idle debugger and retains the session. The attempted console recovery and repeated close also failed in the simulation. Severe harm, target termination, and data loss were not observed; real-provider incidence is unmeasured.

Workaround status: unverified. All three fresh-session control filenames closed successfully, but that does not recover an already retained session. Clearing the selected file through MCP after the failure was blocked, and no alternative recovery route was verified.

REA version

rea 6.5.0 at source commit 4692bb3330

Runtime and platform

Node 24.18.0; macOS arm64

Target and artifact context

Registered MCP GDB tools with an in-memory simulated Linux GDB process on macOS; no real debugger, inferior, or executable ran.

Interface

MCP

Earliest failure stage

Cleanup, cancellation, or lifecycle

Relevant output or evidence

  • Expected source: src/contracts/gdbToolContracts.ts:112, the close contract and none_observed result; docs/reverse-engineering-tools.md:28, Linux GDB lifecycle; AGENTS.md, Architecture and Cleanup, format-aware parsing and preservation of source values.
  • Failure source: The executed MCP probe above, especially its marker-only error, absent exit command, retained ready status, and failed recovery. Source tracing through src/gdb/GdbSessionManager.ts:159, close at line 1072, and #detachInferior at line 1534 explains the result.

The cause is source-derived: after scanning past quoted text, the parser separately searches the raw list body for groups=[ and rejects the whole inventory. The prepared compiled runtime contains the same search. Cleanup repeats the rejected inventory query. The registration's cleanup admission retries the retained failure before allowing a subsequent console command.

An existing case at tests/boundary/providers/GdbSessionManagerCoverage.test.ts:56 is named "nested groups field", but its input puts the marker inside a quoted extra string and expects rejection. That expectation does not distinguish a genuine nested field from literal string contents. The production server installs these tools at src/server/createServer.ts:481; this probe uses that same registration with its optional binary-analysis admission absent.

Typed outcome or recovery expected

A complete valid inventory without a live PID should close with target_state: "none_observed", release the owned debugger and remove the session. Preserve rejection of genuinely malformed/nested inventories.

Analysis or suspected cause

Source-derived: src/gdb/GdbSessionManager.ts:159 searches the raw inventory body for groups=[ after its scanner has skipped quoted text. close at line 1072 and detach at line 1534 consume that rejection. Cleanup admission retries the failure before permitting a subsequent console command.

Additional context

Freshness check: the cited failing implementation is unchanged on origin/main at fc712b3750. The only change to a cited reproduction entry point normalizes global CLI flag syntax. The baseline reproduction was not rerun on that revision.

Restoration check: Run the same MCP lifecycle for all four filenames. Each valid inventory without a live PID must close successfully with none_observed, release the owned debugger, and leave no retained session. Confirm that literal marker text inside a quoted field is accepted while genuinely malformed or unsupported nested inventories still produce an unknown result and retain ownership when safe cleanup cannot be established. Keep the successful control cases so passing cannot come from bypassing inferior-state verification.

Originally created by @coygeek on GitHub (Oct 15, 2026). Original GitHub issue: https://github.com/morluto/rea/issues/3993 ### 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 Closing a GDB session fails when its complete inferior inventory contains `groups=[` inside a quoted executable pathname, even when no inferior has a live PID. The MCP close returns `cleanup_incomplete`, leaves the owned debugger session ready, and does not send the normal exit command. The failure was reproduced with a simulated provider through the registered production MCP tools; real GDB output generation was not tested. In the completed four-case probe, `fixture/plain.out`, `fixture/groups=.out`, and `fixture/bracket[.out` each closed successfully, returned `none_observed`, and sent `-gdb-exit`. Only `fixture/groups=[.out` failed. Its first close issued two inventory requests, sent no exit command, and returned the following relevant error fields: ```json { "code": "cleanup_incomplete", "details": { "operation": "close_gdb_session", "cleanup_reason": "Owned GDB inferior detach could not be verified: GDB returned an unknown recursive inferior list." } } ``` A status request after the failed close still returned `state: "ready"`. The subsequent `file` console call returned an error without dispatching the command to the simulated provider, and a repeated close returned `cleanup_incomplete` again. An earlier probe attempt reproduced the same failure but failed its own assertion that clearing the file would recover; the completed probe checks the observed failed recovery instead. These are observations of REA's behavior against a simulated production boundary, not measurements of a real GDB session. ### Reproduction Use the baseline under Runtime or environment with its dependencies installed and compiled runtime prepared. From the repository root, run the following command. It uses the repository's simulated-process approach and the production tool registration. The Linux platform option permits the Linux-only lifecycle on the test host; the process is an in-memory event emitter, and its kill method affects only simulated streams. It does not open an executable or run a real target. The probe starts a fresh session for each filename, selects the file through `gdb_console`, then closes and checks session status. The simulated provider returns a complete MI inventory with the selected path in a quoted executable field and no PID. For the failing case, it also attempts `gdb_console` with `file` and retries close. ```sh node --input-type=module <<'JS' import { EventEmitter } from 'node:events'; import { PassThrough } from 'node:stream'; import { pathToFileURL } from 'node:url'; import assert from 'node:assert/strict'; const load = p => import(pathToFileURL(`${process.cwd()}/${p}`).href); const { Client, InMemoryTransport } = await load('node_modules/@modelcontextprotocol/client/dist/index.mjs'); const { McpServer } = await load('node_modules/@modelcontextprotocol/server/dist/index.mjs'); const { GdbSessionManager } = await load('dist/gdb/GdbSessionManager.js'); const { registerGdbTools } = await load('dist/server/registerGdbTools.js'); const { createToolResultDelivery } = await load('dist/server/toolResult.js'); const { silentLogger } = await load('dist/logger.js'); class SimulatedGdb extends EventEmitter { pid = process.pid + 1; stdout = new PassThrough(); stderr = new PassThrough(); stdin = new PassThrough(); exitCode = null; signalCode = null; kill(signal) { this.exitCode = 0; this.signalCode = signal; this.stdout.end(); this.stderr.end(); this.stdin.destroy(); this.emit('exit', 0, signal); this.emit('close', 0, signal); return true; } } for (const filename of ['fixture/plain.out', 'fixture/groups=[.out', 'fixture/groups=.out', 'fixture/bracket[.out']) { let child, selected; const commands = [], inventories = []; const manager = new GdbSessionManager({ platform: 'linux', spawnProcess: async options => { child = new SimulatedGdb(); child.stdin.setEncoding('utf8'); let buffer = ''; child.stdin.on('data', chunk => { buffer += chunk; for (let end; (end = buffer.indexOf('\n')) >= 0;) { const line = buffer.slice(0, end); buffer = buffer.slice(end + 1); const match = /^(\d+)(.*)$/.exec(line); if (!match) continue; const [, token, command] = match; commands.push(command); if (command.startsWith('-interpreter-exec console ')) { const consoleCommand = JSON.parse(command.slice('-interpreter-exec console '.length)); if (consoleCommand.startsWith('file ')) selected = consoleCommand.slice(5); if (consoleCommand === 'file') selected = undefined; } let answer = `${token}^done`; if (command === '-list-thread-groups --recurse 1') { answer += `,groups=${selected ? `[{id="i1",type="process",executable=${JSON.stringify(selected)}}]` : '[]'}`; inventories.push(answer); } child.stdout.write(`${answer}\n`); } }); child.stdout.write('(gdb)\n'); return { process: child, ownership: {runId: options.runId, leaderPid: child.pid, processGroupId: child.pid, expectedParentPid: process.pid} }; }}); const server = new McpServer({name:'verification',version:'1'}); registerGdbTools(server, manager, silentLogger, createToolResultDelivery(undefined)); const client = new Client({name:'verification',version:'1'}); const [clientTransport, serverTransport] = InMemoryTransport.createLinkedPair(); await server.connect(serverTransport); await client.connect(clientTransport); const call = (name, args) => client.callTool({name, arguments:args}); try { const start = await call('start_gdb_session', {}); assert(!start.isError); const session_id = start.structuredContent.session_id; const open = await call('gdb_console', {session_id, command:`file ${filename}`}); assert(!open.isError); const closed = await call('close_gdb_session', {session_id}); const status = await call('gdb_session_status', {session_id}); const firstCloseCommands = [...commands]; let recovery; if (closed.isError) { const clear = await call('gdb_console', {session_id, command:'file'}); const retry = await call('close_gdb_session', {session_id}); recovery = {clearError:!!clear.isError, retryError:!!retry.isError, retryResult:retry.structuredContent}; } const outcome = closed.isError ? JSON.parse(closed.content[0].text).error : closed.structuredContent; console.log(JSON.stringify({filename, closeError:!!closed.isError, outcome, retained:!status.isError, state:status.structuredContent?.state, firstCloseCommands, inventories, recovery})); assert.equal(!!closed.isError, filename.includes('groups=[')); if (recovery) assert.equal(recovery.retryError, true); } finally { await client.close(); await server.close(); await manager.closeAll().catch(() => {}); if (child?.exitCode === null) child.kill('SIGTERM'); } } JS ``` ### Expected behavior A complete, valid inventory with no live PID should allow `close_gdb_session` to release its owned debugger and return `target_state: "none_observed"`. Literal text in a quoted executable pathname should remain a string value when interpreting the inventory. The durable output TTY requirement applies to live inferiors, so it does not apply to these inputs. ### Impact and scope A bounded class of executable pathnames makes ordinary shutdown fail for an idle debugger and retains the session. The attempted console recovery and repeated close also failed in the simulation. Severe harm, target termination, and data loss were not observed; real-provider incidence is unmeasured. Workaround status: unverified. All three fresh-session control filenames closed successfully, but that does not recover an already retained session. Clearing the selected file through MCP after the failure was blocked, and no alternative recovery route was verified. ### REA version rea 6.5.0 at source commit 4692bb33300c7cb77b095b6cc0c33da861c1b860 ### Runtime and platform Node 24.18.0; macOS arm64 ### Target and artifact context Registered MCP GDB tools with an in-memory simulated Linux GDB process on macOS; no real debugger, inferior, or executable ran. ### Interface MCP ### Earliest failure stage Cleanup, cancellation, or lifecycle ### Relevant output or evidence - Expected source: `src/contracts/gdbToolContracts.ts:112`, the close contract and `none_observed` result; `docs/reverse-engineering-tools.md:28`, Linux GDB lifecycle; `AGENTS.md`, Architecture and Cleanup, format-aware parsing and preservation of source values. - Failure source: The executed MCP probe above, especially its marker-only error, absent exit command, retained ready status, and failed recovery. Source tracing through `src/gdb/GdbSessionManager.ts:159`, `close` at line 1072, and `#detachInferior` at line 1534 explains the result. The cause is source-derived: after scanning past quoted text, the parser separately searches the raw list body for `groups=[` and rejects the whole inventory. The prepared compiled runtime contains the same search. Cleanup repeats the rejected inventory query. The registration's cleanup admission retries the retained failure before allowing a subsequent console command. An existing case at `tests/boundary/providers/GdbSessionManagerCoverage.test.ts:56` is named "nested groups field", but its input puts the marker inside a quoted `extra` string and expects rejection. That expectation does not distinguish a genuine nested field from literal string contents. The production server installs these tools at `src/server/createServer.ts:481`; this probe uses that same registration with its optional binary-analysis admission absent. ### Typed outcome or recovery expected A complete valid inventory without a live PID should close with target_state: "none_observed", release the owned debugger and remove the session. Preserve rejection of genuinely malformed/nested inventories. ### Analysis or suspected cause Source-derived: src/gdb/GdbSessionManager.ts:159 searches the raw inventory body for groups=[ after its scanner has skipped quoted text. close at line 1072 and detach at line 1534 consume that rejection. Cleanup admission retries the failure before permitting a subsequent console command. ### Additional context Freshness check: the cited failing implementation is unchanged on origin/main at fc712b3750cbe16755c4f78c1f90fdf09fbaca31. The only change to a cited reproduction entry point normalizes global CLI flag syntax. The baseline reproduction was not rerun on that revision. Restoration check: Run the same MCP lifecycle for all four filenames. Each valid inventory without a live PID must close successfully with `none_observed`, release the owned debugger, and leave no retained session. Confirm that literal marker text inside a quoted field is accepted while genuinely malformed or unsupported nested inventories still produce an unknown result and retain ownership when safe cleanup cannot be established. Keep the successful control cases so passing cannot come from bypassing inferior-state verification.
Author
Owner

@XinwangTao commented on GitHub (Oct 15, 2026):

Confirmed: the pathname parsing treats the [ inside a quoted executable field as the start of a groups= marker, so the inferior list never resolves and close cannot verify detach — leaving the session ready with no exit command sent. The literal-marker-inside-quoted-field case should be treated as plain text (or bracket-escaped) when building the inventory, with genuinely malformed lists still rejected.

<!-- gh-comment-id:6186767948 --> @XinwangTao commented on GitHub (Oct 15, 2026): Confirmed: the pathname parsing treats the [ inside a quoted executable field as the start of a groups= marker, so the inferior list never resolves and close cannot verify detach — leaving the session ready with no exit command sent. The literal-marker-inside-quoted-field case should be treated as plain text (or bracket-escaped) when building the inventory, with genuinely malformed lists still rejected.
Author
Owner

@yankikucuk commented on GitHub (Oct 15, 2026):

Working on this. Reproduced at the manager boundary with the simulated inventory: an idle session whose quoted executable is fixture/groups=[.out could not be closed. The parser no longer searches the raw list body for the marker after its quote-aware scan; a nested groups= field is still refused when the unquoted field name precedes a [, while the same text inside a quoted value is read as a string. The idle close now returns none_observed and sends -gdb-exit; malformed and genuinely nested inventories are still rejected. Real GDB was not run. Draft PR follows shortly.

<!-- gh-comment-id:6191539868 --> @yankikucuk commented on GitHub (Oct 15, 2026): Working on this. Reproduced at the manager boundary with the simulated inventory: an idle session whose quoted executable is `fixture/groups=[.out` could not be closed. The parser no longer searches the raw list body for the marker after its quote-aware scan; a nested `groups=` field is still refused when the unquoted field name precedes a `[`, while the same text inside a quoted value is read as a string. The idle close now returns `none_observed` and sends `-gdb-exit`; malformed and genuinely nested inventories are still rejected. Real GDB was not run. Draft PR follows shortly.
Author
Owner

@XinwangTao commented on GitHub (Oct 16, 2026):

yankikucuk, the quote-aware scan is the right fix — the groups= marker should only be meaningful outside quoted fields, so searching the raw list body after the scan mis-reads it. Keeping genuinely nested groups= rejected while treating quoted occurrences as plain text preserves both the safety check and the quoted-pathname case. Good that the idle close now sends -gdb-exit.

<!-- gh-comment-id:6208758790 --> @XinwangTao commented on GitHub (Oct 16, 2026): yankikucuk, the quote-aware scan is the right fix — the groups= marker should only be meaningful outside quoted fields, so searching the raw list body after the scan mis-reads it. Keeping genuinely nested groups= rejected while treating quoted occurrences as plain text preserves both the safety check and the quoted-pathname case. Good that the idle close now sends -gdb-exit.
Author
Owner

@N0zoM1z0 commented on GitHub (Oct 16, 2026):

Verified on current main d72d856f58d4747c5eb4307426e80d20934e017c using real GNU GDB 13.1 on Linux, through both native CLI/MI output and the actual stdio MCP server. The reported pathname no longer reproduces the failure.

All six names passed: plain.out, groups=[.out, groups=.out, bracket[.out, a space-containing name and a quote-containing name. For each, an authored ELF was selected without launching an inferior, GDB emitted its real recursive inventory, REA parsed no live PIDs, and MCP close_gdb_session returned target_state: none_observed. A fresh subsequent session also opened/closed; all executable digests stayed unchanged (20 tool calls total).

Smallest actual MI example, with only the fixture directory shortened:

2^done,groups=[{id="i1",type="process",executable="/tmp/fixture/groups=[.out",threads=[]}]

Source history identifies the format-aware inventory validation in d9aa06d93; the current parser distinguishes quoted values from structural fields. This is a source-derived explanation plus current-version runtime verification, not a claim to have run the original older revision against real GDB.

Completed matrix: 5.46 s elapsed, 6.23 s CPU, 717 MiB sampled process-family peak; two CPU cores. No target execution or external attach was used, and no other-OS claim. Full MI records, calls and checks remain in /tmp/rea-3993-gdb-inventory-20261016/.

Closing as verified on current main; no additional production change or duplicate PR is necessary. Follow-up evidence also contributes to #4227.

<!-- gh-comment-id:6209925584 --> @N0zoM1z0 commented on GitHub (Oct 16, 2026): Verified on current main `d72d856f58d4747c5eb4307426e80d20934e017c` using **real GNU GDB 13.1 on Linux**, through both native CLI/MI output and the actual stdio MCP server. The reported pathname no longer reproduces the failure. All six names passed: `plain.out`, `groups=[.out`, `groups=.out`, `bracket[.out`, a space-containing name and a quote-containing name. For each, an authored ELF was selected without launching an inferior, GDB emitted its real recursive inventory, REA parsed no live PIDs, and MCP `close_gdb_session` returned `target_state: none_observed`. A fresh subsequent session also opened/closed; all executable digests stayed unchanged (20 tool calls total). Smallest actual MI example, with only the fixture directory shortened: ```text 2^done,groups=[{id="i1",type="process",executable="/tmp/fixture/groups=[.out",threads=[]}] ``` Source history identifies the format-aware inventory validation in [d9aa06d93](https://github.com/morluto/rea/commit/d9aa06d93d95aa237927b83a433b495133302ef9); the current parser distinguishes quoted values from structural fields. This is a source-derived explanation plus current-version runtime verification, not a claim to have run the original older revision against real GDB. Completed matrix: 5.46 s elapsed, 6.23 s CPU, 717 MiB sampled process-family peak; two CPU cores. No target execution or external attach was used, and no other-OS claim. Full MI records, calls and checks remain in `/tmp/rea-3993-gdb-inventory-20261016/`. Closing as verified on current main; no additional production change or duplicate PR is necessary. Follow-up evidence also contributes to #4227.
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#1588
No description provided.