Skip to content

feat(perf): add opt-in local performance telemetry (Fixes #3167) - #3207

Merged
acoliver merged 5 commits into
mainfrom
issue3167
Aug 10, 2026
Merged

feat(perf): add opt-in local performance telemetry (Fixes #3167)#3207
acoliver merged 5 commits into
mainfrom
issue3167

Conversation

@acoliver

@acoliver acoliver commented Aug 10, 2026

Copy link
Copy Markdown
Collaborator

TLDR

Adds strictly opt-in, local-only client performance and memory telemetry so LLxprt users can measure operation trends, inspect retained data, and diagnose regressions without uploading telemetry. Collection remains disabled by default, and memory sampling requires a separate opt-in.

Dive Deeper

This PR implements the accepted issue 3167 design end to end:

  • Adds nested telemetry.perf.enabled and telemetry.perf.memory settings, both defaulting to false.
  • Persists versioned NDJSON operation and memory records through a serialized no-drop sink with exclusive per-run files, bounded retention, live-writer claims, and rate-limited fail-open filesystem diagnostics.
  • Measures client preparation/finalization, stream handling, provider attempts, tool activity, Ink render passes, stdout writes, approval wait, elapsed time, and an honest unclamped residual while preserving overlap semantics.
  • Tracks all terminal outcomes exactly once, including pre-send cancellation, API/tool/approval cancellation, errors, completion, and supersession.
  • Reuses the existing memory monitor timer, keeps a fixed-capacity live ring, persists optional samples, and reports read-time memory slopes by operation and uptime.
  • Adds tolerant streaming consumers, continuation-aware token-usage joins, grouped p50 trend reports, exact-version-or-SHA baselines, inspect/delete operations, and current-process health snapshots.
  • Adds the interactive /perf, /perf inspect, /perf report, and /perf delete commands with composition-root-owned lifecycle and deterministic cleanup.
  • Documents privacy, local storage, opt-in behavior, retention, and deletion.

The package dependency direction is preserved: packages/agents is unchanged, telemetry owns persistence/reporting, core owns narrow observer bridges, and CLI owns lifecycle and user-facing controls.

Internal observer and programming errors fail fast. Only genuinely external filesystem persistence and maintenance failures fail open.

Reviewer Test Plan

  1. Confirm the default path is inert:

    • Start the interactive CLI without performance telemetry enabled.
    • Run /perf and verify it reports that telemetry is not active.
    • Run /perf inspect and verify it can inspect stored data without activating collection.
  2. Enable collection in settings:

    {
      "telemetry": {
        "perf": {
          "enabled": true,
          "memory": false
        }
      }
    }
    
  3. Start an interactive session, submit prompts including a tool-using prompt, then run /perf and /perf report. Verify operation counts and health are shown and the local perf directory contains versioned NDJSON records.

  4. Enable telemetry.perf.memory, repeat a session, and verify memory fields/samples appear. Disable memory again and verify they are omitted.

  5. Run /perf inspect, /perf report with an exact version or SHA baseline, and /perf delete. Verify delete preserves live writers while removing eligible retained artifacts.

  6. Exercise cancellation during provider, tool, and approval phases and verify each operation finalizes once with the appropriate status.

  7. Run the repository gates:

    • npm run test
    • npm run lint
    • npm run typecheck
    • npm run format
    • npm run build
    • bun scripts/start.ts --profile-load stepfun-37 "write me a haiku and nothing else"

Local validation completed on macOS: the full workspace suite, lint, typecheck before and after build, format, build, StepFun smoke test, mechanical policy guards, and real tmux TUI scenarios for /perf, /perf inspect, and /perf report all passed.

Testing Matrix

🍏 🪟 🐧
npm run
npx
Docker
Podman - -
Seatbelt - -

Linked issues / bugs

Fixes #3167

Summary by CodeRabbit

  • New Features
    • Added optional, local-only performance telemetry, disabled by default.
    • Added privacy-aware memory sampling controls.
    • Added /perf commands for inspecting, reporting, snapshotting, and deleting telemetry data.
    • Added performance reports with timing percentiles, cancellation statuses, token totals, baselines, and memory trends.
    • Added automatic retention and safe cleanup for local telemetry.
    • Added resilient lifecycle tracking across provider, tool, rendering, and cancellation phases.
  • Documentation
    • Documented telemetry settings, stored data, privacy behavior, and validation rules.

Add default-off client performance and memory telemetry with lifecycle, provider, render, and stdout instrumentation.

Persist versioned local records with bounded retention, tolerant reporting, token-usage joins, and inspect/delete controls through /perf.

Cover settings, cleanup, failure handling, concurrency, memory trends, reporting, and TUI integration with Bun behavioral tests.
@github-actions github-actions Bot added the maintainer:e2e:ok Trusted contributor; maintainer-approved E2E run label Aug 10, 2026
@coderabbitai

coderabbitai Bot commented Aug 10, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

Changes

The PR adds optional local performance telemetry. It adds configuration, lifecycle instrumentation, memory sampling, JSONL storage and retention, reports, and /perf commands. Telemetry remains disabled by default and is not transmitted externally.

Performance telemetry configuration

Layer / File(s) Summary
Configuration and privacy contract
docs/telemetry-privacy.md, packages/cli/src/config/*, packages/core/src/config/*
Adds disabled-by-default performance and memory settings. Adds schema validation, precedence handling, defensive cloning, master-switch gating, accessors, and privacy documentation.
Telemetry records and storage
packages/telemetry/src/perf/*, packages/telemetry/src/telemetry/*
Adds versioned records, streaming readers, serialized writes, retention, artifact protection, deletion, inspection, reports, token joins, memory slopes, and interval unions.
Interactive lifecycle instrumentation
packages/cli/src/session/*, packages/cli/src/ui/hooks/agentStream/*, packages/cli/src/ui/hooks/memoryTrend/*, packages/cli/src/ui/hooks/perf/*, packages/core/src/utils/stdio.ts, packages/providers/src/logging/*
Adds operation tracking, phase observers, cancellation classification, stdout and render measurements, memory sampling, immutable snapshots, provider/tool correlation, and transactional startup cleanup.
CLI commands and application wiring
packages/cli/src/ui/commands/*, packages/cli/src/services/*, packages/cli/src/ui/App*, packages/cli/src/ui/cliUiRuntime.ts
Adds /perf inspection, reporting, deletion, and snapshot behavior. Registers the command and passes optional telemetry capabilities through the UI runtime.

Estimated code review effort: 5 (Critical) | ~120 minutes

Possibly related issues

  • #3165: The issue defines the performance telemetry objectives implemented by this PR, including client-side timing, memory trends, bounded logs, and /perf reporting.

Possibly related PRs

🚥 Pre-merge checks | ✅ 3 | ❌ 2

❌ Failed checks (2 warnings)

Check name Status Explanation Resolution
Linked Issues check ⚠️ Warning The PR covers most telemetry objectives but does not show /stats attribution or acceptance coverage for archive overwrite and negative-mtime handling. Add /stats provider, tool, and LLxprt attribution, and implement or test archive overwrite protection and negative-mtime handling required by issue #3167.
Docstring Coverage ⚠️ Warning Docstring coverage is 37.57% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (3 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly summarizes the primary change: opt-in local performance telemetry.
Description check ✅ Passed The description covers the required sections, implementation scope, testing plan, validation results, matrix, and linked issue.
Out of Scope Changes check ✅ Passed The documented code, tests, commands, configuration, and telemetry modules support the linked performance-telemetry objectives.
✨ Finishing Touches 💡 1
📝 Generate docstrings 💡
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch issue3167

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 19

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
packages/cli/src/ui/hooks/useMemoryMonitor.test.tsx (1)

19-28: 📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Restore the rss spy after the suite.

vi.spyOn(process.memoryUsage, 'rss') patches a global and is never restored. vi.clearAllMocks() clears call state, but it does not reinstall the original implementation. Restore the spy so other suites in the same worker observe the real process.memoryUsage.rss. Per repository learnings, mock cleanup must be scoped locally because no global restoreMocks behavior is assumed.

🧹 Proposed cleanup
   afterEach(() => {
     vi.useRealTimers();
   });
+
+  afterAll(() => {
+    rssSpy.mockRestore();
+  });

Add afterAll to the bun:test import list.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/cli/src/ui/hooks/useMemoryMonitor.test.tsx` around lines 19 - 28,
Add afterAll to the bun:test imports and restore the process.memoryUsage.rss spy
in an afterAll hook, ensuring the original global implementation is reinstated
after the suite while preserving the existing per-test timer and mock cleanup.

Source: Learnings

🧹 Nitpick comments (32)
packages/telemetry/src/perf/perfReport.tokenJoin.behavior.test.ts (2)

26-27: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Merge the two imports from the same module.

-import { buildReport, assembleReport } from './perfReport.js';
-import { aggregateTokenUsageByOperation } from './perfReport.js';
+import {
+  buildReport,
+  assembleReport,
+  aggregateTokenUsageByOperation,
+} from './perfReport.js';
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/telemetry/src/perf/perfReport.tokenJoin.behavior.test.ts` around
lines 26 - 27, Merge the separate imports from perfReport.js into a single
import declaration containing buildReport, assembleReport, and
aggregateTokenUsageByOperation.

163-165: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Use map instead of flatMap for scalar values.

g.sampleCount is a number, so flatMap behaves as map here and hides the intent.

-    const ops = report.groups.flatMap((g) => g.sampleCount);
+    const ops = report.groups.map((g) => g.sampleCount);
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/telemetry/src/perf/perfReport.tokenJoin.behavior.test.ts` around
lines 163 - 165, Replace flatMap with map when collecting scalar sampleCount
values from report.groups in the test, preserving the expected [1] assertion and
the existing single-operation-group behavior.
packages/cli/src/session/buildPerfOwner.behavior.test.ts (2)

43-61: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Remove the unused temporary directory fixture.

No test in this file reads or writes dir. The mkdtempSync in beforeEach and the rm in afterEach add filesystem work without coverage value.

♻️ Proposed cleanup
-let dir: string;
-
 beforeEach(() => {
-  dir = fs.mkdtempSync(join(tmpdir(), 'perf-disabled-'));
   setInteractiveStdoutObserver(null);
   setInteractiveRenderObserver(null);
   setPerfPhaseObserver(null);
 });
 
-afterEach(async () => {
+afterEach(() => {
   setInteractiveStdoutObserver(null);
   setInteractiveRenderObserver(null);
   setPerfPhaseObserver(null);
-  try {
-    await fsp.rm(dir, { recursive: true, force: true });
-  } catch {
-    // ignore
-  }
 });

The node:fs, node:fs promises, node:path, and node:os imports then become unnecessary.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/cli/src/session/buildPerfOwner.behavior.test.ts` around lines 43 -
61, Remove the unused dir fixture by deleting the dir declaration, its
mkdtempSync setup in beforeEach, and the corresponding fsp.rm cleanup in
afterEach; then remove the now-unused node:fs, node:fs promises, node:path, and
node:os imports while preserving the observer resets.

167-176: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

This test cannot fail if the implementation changes format.

The assertion recomputes the expected value with the same expression the implementation uses. Any change in resolvePlatformArch that still uses process.platform and process.arch passes, and a swap of the two fields would need a separate check. Consider asserting the literal shape, for example that the string contains - and starts with process.platform.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/cli/src/session/buildPerfOwner.behavior.test.ts` around lines 167 -
176, Update the test around resolvePlatformArch so it independently validates
the output format instead of constructing the expected value with the same
process.platform-process.arch expression. Assert that the result starts with
process.platform, contains the separator, and places process.arch after it,
while keeping the test focused on the platform string format.
packages/telemetry/src/perf/perfReport.ts (3)

514-524: 🚀 Performance & Scalability | 🔵 Trivial | 💤 Low value

Count the terminal statuses in one pass.

countTerminalStatuses runs seven filter passes over the operation list. A single loop with a pre-seeded record gives the same result with one pass.

♻️ Proposed refactor
 function countTerminalStatuses(
   opsRecords: readonly PerfOperationRecord[],
 ): Record<PerfTerminalStatus, number> {
   const terminalStatusCounts = {} as Record<PerfTerminalStatus, number>;
   for (const status of TERMINAL_STATUSES) {
-    terminalStatusCounts[status] = opsRecords.filter(
-      (o) => o.status === status,
-    ).length;
+    terminalStatusCounts[status] = 0;
   }
+  for (const o of opsRecords) {
+    terminalStatusCounts[o.status] += 1;
+  }
   return terminalStatusCounts;
 }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/telemetry/src/perf/perfReport.ts` around lines 514 - 524, Refactor
countTerminalStatuses to initialize every TERMINAL_STATUSES entry to zero, then
iterate over opsRecords once and increment the count for each record whose
status is terminal. Preserve the returned Record shape and zero counts for
terminal statuses absent from the input.

676-683: 🎯 Functional Correctness | 🔵 Trivial | 💤 Low value

found reports the presence of baseline rows, not a dimension match.

found is true when any baseline operation exists, even when no non-baseline group shares its dimensions. The formatter then prints "matched" while every group prints the UNMATCHED warning. Consider naming the field to reflect "baseline rows exist", or deriving the printed label from the per-group match results.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/telemetry/src/perf/perfReport.ts` around lines 676 - 683, Correct
the baseline status reporting around the returned baselineInfo.found field so it
reflects whether baseline dimensions match the compared groups, not merely
whether pooledBaselineByDims contains rows. Either rename the field to represent
baseline-row presence and update consumers, or derive the formatter’s matched
label from each group’s actual match result, keeping the UNMATCHED warning
consistent.

469-509: 🚀 Performance & Scalability | 🔵 Trivial | 💤 Low value

Resolve the join once per operation instead of once per metric.

computeP50Values iterates the 20 metric keys in the outer loop and calls joinedTokenValue for every operation inside. That performs 20 map lookups per operation, and 18 of them return undefined because only context_tokens and output_tokens can join. Restrict the lookup to the two token keys.

♻️ Proposed refactor
 function computeP50Values(
   opsRecords: readonly PerfOperationRecord[],
   aggregatedTokens?: ReadonlyMap<string, AggregatedTokenUsage>,
 ): Record<string, number | null> {
   const p50Values: Record<string, number | null> = {};
   for (const metricKey of P50_METRIC_KEYS) {
+    const isTokenKey =
+      metricKey === 'context_tokens' || metricKey === 'output_tokens';
     const values = opsRecords.map((o) => {
-      if (aggregatedTokens !== undefined) {
+      if (isTokenKey && aggregatedTokens !== undefined) {
         const joined = joinedTokenValue(o, metricKey, aggregatedTokens);
         if (joined !== undefined) return joined;
       }
       return extractMetric(o, metricKey);
     });
     p50Values[metricKey] = p50(values);
   }
   return p50Values;
 }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/telemetry/src/perf/perfReport.ts` around lines 469 - 509, Refactor
computeP50Values so each operation resolves aggregatedTokens.get(o.operation_id)
at most once, then reuse that joined record while computing all metric keys.
Apply joined contextTokens/outputTokens only for context_tokens and
output_tokens, preserving extractMetric fallback for unmatched operations and
non-token metrics; update joinedTokenValue or remove it as needed.
packages/cli/src/session/interactiveUI.startup.transaction.behavior.test.ts (1)

523-526: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Import replacePreviousInstanceAndOwner statically.

The file already imports from ./interactiveUI.js at line 28. A dynamic import here adds no isolation, because the module is already loaded and its state is shared. Add replacePreviousInstanceAndOwner to the static import list for consistency.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/cli/src/session/interactiveUI.startup.transaction.behavior.test.ts`
around lines 523 - 526, Update the existing static import from
./interactiveUI.js to include replacePreviousInstanceAndOwner, then call the
imported function directly in the test instead of using the dynamic import
expression.
packages/cli/src/ui/hooks/agentStream/__tests__/useSubmitQuery.lifecycle.cancellation.bun.tsx (1)

165-174: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Use satisfies ObservableAgentEvent here for type safety.

The other three tests in this file assert the event shape with satisfies ObservableAgentEvent. This case uses as never, which disables type checking on the payload (including the extra name: 'shell' field). If the event shape drifts, this test keeps compiling and silently loses coverage.

♻️ Proposed change
       registry.observeAgentEvent(
         {
           type: 'tool-status',
-          update: { id: 'c1', name: 'shell', status: 'executing' },
-        } as never,
+          update: { id: 'c1', status: 'executing' },
+        } satisfies ObservableAgentEvent,
         signal,
         0,
       );
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@packages/cli/src/ui/hooks/agentStream/__tests__/useSubmitQuery.lifecycle.cancellation.bun.tsx`
around lines 165 - 174, In the cancellation test’s registry.observeAgentEvent
call, replace the `as never` cast with a `satisfies ObservableAgentEvent`
assertion on the event payload. Preserve the existing event values while
enabling compile-time validation of the full tool-status shape, including the
nested update fields.
packages/telemetry/src/perf/tokenUsageReader.behavior.test.ts (1)

25-33: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Merge the duplicated import and correct the comment.

streamTokenUsageFromReadable is imported from the same specifier as the other symbols, so the second import statement is redundant. The comment states the seam is imported "directly from the internal module", but both statements target ./tokenUsageReader.js.

♻️ Proposed change
 import {
   consumeTokenUsageDirectory,
   streamTokenUsageDirectory,
   streamTokenUsageRecords,
+  // Package-private seam: not re-exported from the package barrel.
+  streamTokenUsageFromReadable,
 } from './tokenUsageReader.js';
 import type { TokenUsageStreamEntry } from './tokenUsageReader.js';
-// The controlled-readable seam is package-private (not exported from the
-// barrel); same-package tests import it directly from the internal module.
-import { streamTokenUsageFromReadable } from './tokenUsageReader.js';
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/telemetry/src/perf/tokenUsageReader.behavior.test.ts` around lines
25 - 33, Merge streamTokenUsageFromReadable into the existing import from
./tokenUsageReader.js and remove the redundant second import. Update the
adjacent comment to accurately describe the package-private seam without
claiming it is imported from a different internal module.
packages/cli/src/ui/hooks/memoryTrend/memoryTelemetry.behavior.test.ts (2)

283-308: 🎯 Functional Correctness | 🔵 Trivial | 💤 Low value

Choose distinct fixture values so the assertion discriminates pre- and post-operation idle.

The first tick runs at mono = 10_000 before any operation ends, so its ms_since_last_operation equals uptime, which is 10_000. The second tick computes 25_000 - 15_000, which is also 10_000. Both assertions at Lines 305-306 pass with the same number, so the test cannot detect a regression that returns uptime instead of the idle interval.

Move the first tick to a different uptime, for example mono = 4_000, and assert 4_000 for records[0].

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/cli/src/ui/hooks/memoryTrend/memoryTelemetry.behavior.test.ts`
around lines 283 - 308, Update the test around MemoryTelemetryController so the
pre-operation tick uses a distinct monotonic time, such as 4,000, before
markOperationEnd; assert records[0].ms_since_last_operation is 4,000 while
preserving the post-operation tick at 25,000 and its 10,000 idle assertion.

390-398: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Both tests parse a newline-delimited JSON file with a single JSON.parse call. PerfSink appends one JSON document per line. Reading the whole file and calling JSON.parse once works only while exactly one record exists. As soon as a second record lands in the same file, the call throws a syntax error that hides the real assertion.

  • packages/cli/src/ui/hooks/memoryTrend/memoryTelemetry.behavior.test.ts#L390-L398: split the file body on newlines, drop empty lines, and run the slope key check over every parsed record.
  • packages/cli/src/ui/hooks/agentStream/operationLifecycle.p10.memory.behavior.test.ts#L238-L243: apply the same line-by-line parse, and iterate every .jsonl file instead of only files[0].
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/cli/src/ui/hooks/memoryTrend/memoryTelemetry.behavior.test.ts`
around lines 390 - 398, Update the JSONL parsing in
packages/cli/src/ui/hooks/memoryTrend/memoryTelemetry.behavior.test.ts:390-398
to split the file contents into non-empty lines, parse each record individually,
and check the slope key across every parsed record. Apply the same line-by-line
parsing in
packages/cli/src/ui/hooks/agentStream/operationLifecycle.p10.memory.behavior.test.ts:238-243,
iterating over every .jsonl file rather than only files[0].
packages/cli/src/ui/hooks/agentStream/operationLifecycle.p10.memory.behavior.test.ts (1)

41-43: 🩺 Stability & Availability | 🔵 Trivial | ⚡ Quick win

Dispose the sink and retention in afterEach.

Each test creates a PerfRetention with maintenanceIntervalMs: 60_000, which starts an interval timer once the sink starts. Tests 1, 2, and 4 dispose only on the success path. If an assertion throws first, the timer and the claim file survive the test, and fs.rmSync removes the directory underneath a live retention owner.

Track the created sinks and dispose them in afterEach, in the same way packages/cli/src/ui/hooks/memoryTrend/memoryTelemetry.behavior.test.ts Lines 34-52 does with activeSinks.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@packages/cli/src/ui/hooks/agentStream/operationLifecycle.p10.memory.behavior.test.ts`
around lines 41 - 43, Track each created PerfRetention sink in an active-sinks
collection, and update afterEach to dispose every tracked sink before removing
the test directory. Register sinks as they are created and clear the collection
after disposal, following the activeSinks pattern used by the memoryTelemetry
behavior test so cleanup also runs when assertions fail.
packages/telemetry/src/perf/PerfSink.ts (1)

289-291: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Rename byteCount or document that it resets on each file roll.

The getter is named byteCount, but it returns bytesSinceStat, which Line 331 resets to zero on every roll. A consumer that reads byteCount as a per-run total gets the bytes written since the last day roll only. Either rename the getter to bytesSinceRoll, or add a doc comment that states the reset behavior.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/telemetry/src/perf/PerfSink.ts` around lines 289 - 291, Clarify the
reset semantics of PerfSink’s byte-count getter by either renaming byteCount to
bytesSinceRoll and updating its consumers, or adding a doc comment to byteCount
stating that it resets to zero after each file roll. Keep the existing
bytesSinceStat behavior unchanged.
packages/cli/src/ui/inkRenderOptions.observer.behavior.test.ts (1)

112-128: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Consider resetting the observers in an afterEach.

This test leaves both module-level observers installed when it ends. setInteractiveStdoutObserver also leaves a cached stdio built with the observer. Every describe here resets in beforeEach, so this suite stays correct. A different suite that runs later in the same Bun process and does not reset first would observe the leftover state.

An afterEach that clears both observers makes the isolation independent of test-file ordering.

♻️ Proposed cleanup hook
+  afterEach(() => {
+    setInteractiveStdoutObserver(null);
+    setInteractiveRenderObserver(null);
+  });

Import afterEach from bun:test if you adopt this.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/cli/src/ui/inkRenderOptions.observer.behavior.test.ts` around lines
112 - 128, Import afterEach from bun:test and add a cleanup hook for this suite
that clears both interactive render and stdout observers after every test,
including any cached stdio state created by setInteractiveStdoutObserver.
Preserve the existing beforeEach setup and test behavior.
packages/cli/src/ui/hooks/perf/interactivePerfRuntime.ts (1)

361-371: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Use the exported PERF_MAINTENANCE_INTERVAL_MS constant instead of the literal 60_000.

packages/telemetry/src/perf/index.ts exports PERF_MAINTENANCE_INTERVAL_MS for this purpose. A literal here can drift from the retention policy constant.

♻️ Proposed refactor
-    maintenanceIntervalMs: 60_000,
+    maintenanceIntervalMs: PERF_MAINTENANCE_INTERVAL_MS,

Add the import:

 import {
   PerfSink,
   PerfRetention,
+  PERF_MAINTENANCE_INTERVAL_MS,
 } from '`@vybestack/llxprt-code-telemetry/perf/index.js`';
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/cli/src/ui/hooks/perf/interactivePerfRuntime.ts` around lines 361 -
371, Update the PerfRetention configuration in the interactive performance
runtime to use the exported PERF_MAINTENANCE_INTERVAL_MS constant for
maintenanceIntervalMs instead of the 60_000 literal, importing it from the
telemetry perf module.
packages/cli/src/ui/cliUiRuntime.ts (1)

436-442: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Clarify where getPerfSnapshotCapability is actually supplied.

AppStateRuntime declares getPerfSnapshotCapability, but buildUiRuntimeFromSource never maps it into the app slice. Only buildSlashCommandRuntime attaches it, from its own parameter. A reader who follows the interface will expect the app slice to carry it. State in the doc comment that the flattened CliUiRuntime supplies this member, not the app slice.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/cli/src/ui/cliUiRuntime.ts` around lines 436 - 442, Update the doc
comment for getPerfSnapshotCapability in AppStateRuntime to clarify that this
member is supplied by the flattened CliUiRuntime, not mapped into the app slice
by buildUiRuntimeFromSource. Keep the existing optional/null behavior and
references to the interactive perf runtime owner unchanged.
packages/cli/src/session/interactiveUI.tsx (1)

247-255: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Extract the repeated alternate-buffer condition.

The expression settings.merged.ui.useAlternateBuffer === true && !config.getScreenReader() appears three times inside the same callback. Compute it once before resolveRenderMode.

♻️ Proposed refactor
         renderMode: () => {
-          resolveRenderMode(
-            config.getScreenReader(),
-            settings.merged.ui.useAlternateBuffer === true &&
-              !config.getScreenReader(),
-            settings.merged.ui.useAlternateBuffer === true &&
-              !config.getScreenReader() &&
-              settings.merged.ui.incrementalRendering !== false,
-          ),
+          const screenReader = config.getScreenReader();
+          const altBuffer =
+            settings.merged.ui.useAlternateBuffer === true && !screenReader;
+          return resolveRenderMode(
+            screenReader,
+            altBuffer,
+            altBuffer && settings.merged.ui.incrementalRendering !== false,
+          );
+        },
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/cli/src/session/interactiveUI.tsx` around lines 247 - 255, In the
renderMode callback, compute the repeated condition combining
settings.merged.ui.useAlternateBuffer and !config.getScreenReader() once in a
local variable, then reuse it for both alternate-buffer arguments to
resolveRenderMode while preserving the incrementalRendering check.
packages/cli/src/ui/commands/perfCommand.ts (1)

433-438: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Remove the unused perfCommand export and comment.

BuiltinCommandLoader registers a command created by createPerfCommand(...). No in-repository code imports perfCommand, which otherwise resolves Storage.getGlobalLogDir() during module initialization.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/cli/src/ui/commands/perfCommand.ts` around lines 433 - 438, Remove
the unused exported perfCommand constant and its accompanying comment. Keep
command creation exclusively in BuiltinCommandLoader through createPerfCommand({
snapshotCapability}), and do not trigger Storage.getGlobalLogDir() during module
initialization.
packages/telemetry/src/perf/perfRecords.ts (1)

200-213: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Delegate joinKeyFromPromptId to deriveOperationId.

The two functions have byte-identical bodies. The join contract requires both sides to use the same split rule. If one body changes, the write-time key and the read-time key diverge silently. Delegation makes the shared rule structural instead of conventional, and keeps both public names for intent.

♻️ Proposed refactor
 export function joinKeyFromPromptId(promptId: string): string {
-  return promptId.split('`#continuation`#')[0];
+  return deriveOperationId(promptId);
 }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/telemetry/src/perf/perfRecords.ts` around lines 200 - 213, Update
joinKeyFromPromptId to delegate directly to deriveOperationId(promptId) instead
of duplicating the split logic, while preserving both public function names and
the existing continuation join-key behavior.
packages/telemetry/src/perf/perfConsumer.behavior.test.ts (1)

68-75: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Two perf test helpers hand-roll temp-directory names instead of calling fs.mkdtemp. Both helpers build a name from Date.now() and Math.random() and then call fs.mkdir. fs.mkdtemp creates the directory atomically and guarantees uniqueness. packages/telemetry/src/perf/perfInspect.behavior.test.ts in the same directory already uses fs.mkdtemp, so the suite is inconsistent.

  • packages/telemetry/src/perf/perfConsumer.behavior.test.ts#L68-L75: replace the body of makeTempDir with return fs.mkdtemp(join(tmpdir(), 'perf-test-'));.
  • packages/telemetry/src/perf/perfRecords.v0.behavior.test.ts#L229-L236: replace the body of makeTempDir with return fs.mkdtemp(join(tmpdir(), 'perf-v0-'));.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/telemetry/src/perf/perfConsumer.behavior.test.ts` around lines 68 -
75, Update makeTempDir in
packages/telemetry/src/perf/perfConsumer.behavior.test.ts (lines 68-75) to
return fs.mkdtemp(join(tmpdir(), 'perf-test-')). Update makeTempDir in
packages/telemetry/src/perf/perfRecords.v0.behavior.test.ts (lines 229-236) to
return fs.mkdtemp(join(tmpdir(), 'perf-v0-')), removing the Date.now(),
Math.random(), and fs.mkdir-based naming.
packages/telemetry/src/perf/perfConsumer.ts (1)

162-195: 🚀 Performance & Scalability | 🔵 Trivial | ⚡ Quick win

Retain only ok entries in consumePerfDirectory; keep all counters.

perfReport.ts and perfInspect.ts skip non-ok entries. Move entries.push(...) into the ok case. This reduces memory use without changing diagnostic counts.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/telemetry/src/perf/perfConsumer.ts` around lines 162 - 195, Update
consumePerfDirectory’s entry collection so entries.push(...) runs only within
the 'ok' case of the entry.kind switch. Preserve all existing diagnostic counter
increments for non-ok kinds and retain the exhaustive default handling.
packages/telemetry/src/perf/retention.claim.behavior.test.ts (1)

214-229: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Dispose the retention instance in the crash test.

This test calls start() and never calls dispose(). The instance keeps a real 60 s interval and a claim file for the remainder of the test process. The interval is unref'd, so it does not block exit, but the leaked owner can touch the claim during later tests in the same file. Capture the assertion first, then dispose.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/telemetry/src/perf/retention.claim.behavior.test.ts` around lines
214 - 229, Update the crash test around PerfRetention.start so it captures the
stale-claim assertion result, then calls retention.dispose() before the test
exits. Preserve the existing claim-file assertions and ensure disposal occurs
after validation.
packages/telemetry/src/perf/retention.ts (1)

628-628: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Use isClaimFile instead of endsWith('.claim').

maintain classifies claims with isClaimFile (backed by CLAIM_FILE_RE), but countNonStaleClaims uses a raw suffix test. A file named exactly .claim matches the suffix test but not CLAIM_FILE_RE, so the two paths can disagree. The file header states perfArtifacts.ts is the single source of truth for artifact predicates.

♻️ Proposed change
-    const claimNames = names.filter((n) => n.endsWith('.claim'));
+    const claimNames = names.filter(isClaimFile);
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/telemetry/src/perf/retention.ts` at line 628, Update
countNonStaleClaims to filter claim names with the shared isClaimFile predicate
instead of a raw .claim suffix check, keeping claim classification consistent
with maintain and the perfArtifacts.ts source of truth.
packages/telemetry/src/perf/retention.eviction.behavior.test.ts (2)

38-74: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Extract the shared retention test helpers into one module.

writePerfFile, createClaimFile, setMtime, and listFiles are duplicated in retention.claim.behavior.test.ts, retention.eviction.behavior.test.ts, retention.faults.behavior.test.ts, retention.multiowner.behavior.test.ts, and retention.scheduling.behavior.test.ts. The dir variable binding differs per file, so the helpers should take dir as a parameter.

Move them to a shared file such as packages/telemetry/src/perf/__tests__/retentionTestHelpers.ts and import them in each suite.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/telemetry/src/perf/retention.eviction.behavior.test.ts` around lines
38 - 74, Extract writePerfFile, createClaimFile, setMtime, and listFiles into a
shared retentionTestHelpers module, adding dir as an explicit parameter to each
helper. Remove the duplicated local definitions from all five retention behavior
suites and import the shared helpers, updating every call site to pass the
suite’s dir binding.

342-348: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Import utcDayKey from perfArtifacts.ts instead of redefining it.

packages/telemetry/src/perf/perfArtifacts.ts exports utcDayKey with the same implementation. The test currently keeps a private copy. If the production day-key derivation changes, this copy hides the change instead of failing.

♻️ Proposed change
-function utcDayKey(now: number): string {
-  const date = new Date(now);
-  const year = date.getUTCFullYear();
-  const month = String(date.getUTCMonth() + 1).padStart(2, '0');
-  const day = String(date.getUTCDate()).padStart(2, '0');
-  return `${year}${month}${day}`;
-}

Add to the imports:

import { utcDayKey } from './perfArtifacts.js';
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/telemetry/src/perf/retention.eviction.behavior.test.ts` around lines
342 - 348, Remove the local utcDayKey definition from the test and import the
existing utcDayKey export from perfArtifacts.ts, preserving all current call
sites and behavior.
packages/telemetry/src/perf/retention.lifecycle.behavior.test.ts (1)

615-625: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

The concurrentReaddirs counter leaks if readdir rejects.

The decrement at Line 623 runs only on the success path. If fsp.readdir throws, the counter stays incremented and maxConcurrent becomes permanently wrong for the rest of the test. Use try/finally.

♻️ Proposed change
       async readdir(d: string): Promise<string[]> {
         concurrentReaddirs += 1;
         maxConcurrent = Math.max(maxConcurrent, concurrentReaddirs);
-        // Only the first readdir is gated; subsequent ones proceed.
-        if (!gateReleased) {
-          await gate.promise;
-        }
-        const result = await fsp.readdir(d);
-        concurrentReaddirs -= 1;
-        return result;
+        try {
+          // Only the first readdir is gated; subsequent ones proceed.
+          if (!gateReleased) {
+            await gate.promise;
+          }
+          return await fsp.readdir(d);
+        } finally {
+          concurrentReaddirs -= 1;
+        }
       },
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/telemetry/src/perf/retention.lifecycle.behavior.test.ts` around
lines 615 - 625, Update the readdir mock method to decrement concurrentReaddirs
in a finally block surrounding the awaited fsp.readdir call, ensuring the
counter is restored whether the filesystem operation resolves or rejects while
preserving the existing result return behavior.
packages/telemetry/src/perf/retention.capSelection.behavior.test.ts (1)

89-101: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Reduce the disk volume of this test and correct the byte comment.

This test writes 8 files of Math.ceil(PERF_MAX_BYTES / 7) bytes, which is about 77 MiB total. The comment on Line 90 says "8 files × ~8 MiB each = ~64 MiB", which does not match Line 91. Large temporary writes can fail with ENOSPC on CI runners that use a small tmpfs for os.tmpdir().

The cap is injectable, so you can prove the same binding behavior with a scaled-down maxBytes while keeping the constant assertion separate.

♻️ Proposed change
-    // Create fewer than MAX_FILES files but exceeding MAX_BYTES total.
-    // 8 files × ~8 MiB each = ~64 MiB ≈ MAX_BYTES.
-    const bytesPerFile = Math.ceil(PERF_MAX_BYTES / 7); // ~9.6 MiB each
+    // Create fewer than MAX_FILES files but exceeding the byte cap total.
+    // 8 files × (cap / 7) bytes ≈ 8/7 × cap, so the byte cap binds first.
+    const maxBytes = 8 * 1024 * 1024; // scaled-down cap: 8 files ≈ 9.1 MiB
+    const bytesPerFile = Math.ceil(maxBytes / 7);

Then pass maxBytes to the PerfRetention options and assert against it instead of PERF_MAX_BYTES.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/telemetry/src/perf/retention.capSelection.behavior.test.ts` around
lines 89 - 101, Reduce the temporary data written by this test by introducing a
small injectable maxBytes value for the PerfRetention setup, generating the
eight files relative to that scaled cap, and asserting retention against the
injected value rather than PERF_MAX_BYTES. Update the size comment to accurately
describe the scaled totals, while keeping any separate PERF_MAX_BYTES constant
assertion unchanged.
packages/telemetry/src/perf/perfDelete.ts (1)

96-115: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Move the perfDelete JSDoc to the perfDelete function.

This documentation block describes perfDelete, but it is attached to interface StatedArtifact. Editors and API docs will show the delete contract on the wrong symbol. perfDelete is declared at Line 295 with no documentation.

♻️ Proposed change
-/**
- * Deletes owned perf JSONL and stale claim artifacts from a directory,
- * respecting live-writer safety.
- *
- * - A missing directory is a no-op (fail open, returns zero counts).
- * - External filesystem failures fail open and are counted.
- * - Internal invalid options (NaN/negative timing) fail fast.
- *
- * Protection rules (shared with retention via `perfArtifacts.ts`):
- *   - A perf JSONL whose day-key is today UTC AND mtime is within the
- *     maintenance interval is protected (active writer).
- *   - A perf JSONL whose run UUID has a non-stale/future claim is protected.
- *   - A claim that is non-stale (now - mtime ≤ lease) is protected.
- */
 interface StatedArtifact {

Then add the same block immediately above export async function perfDelete( at Line 295.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/telemetry/src/perf/perfDelete.ts` around lines 96 - 115, Move the
existing deletion-contract JSDoc from the StatedArtifact interface to
immediately above the export async function perfDelete declaration, leaving the
interface undocumented and preserving the documentation text unchanged.
packages/cli/src/ui/hooks/agentStream/operationLifecycle.ts (2)

245-246: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

apiActive is written but never read.

enterApiPhase sets op.apiActive = true, and PendingOp declares the field. No code reads it. classifyCancellation derives the API case from the absence of approval/tool state instead. Remove the field and the assignment, or read it in classifyCancellation if the API phase must be distinguished from "no phase started".

Also applies to: 585-588

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/cli/src/ui/hooks/agentStream/operationLifecycle.ts` around lines 245
- 246, Remove the unused apiActive field from PendingOp and remove its
assignment in enterApiPhase, since classifyCancellation already derives the API
case without reading it.

889-900: 🩺 Stability & Availability | 🔵 Trivial | ⚖️ Poor tradeoff

Consider bounding the poisoned lifecycle chain.

queueWrite chains every write on lifecycleChain. After one internal (non-errno) rejection, the chain stays rejected for the remaining session. Every later finalise then rejects, and those rejections propagate through submitQueryTurnLifecycle into the user's submit path. One instrumentation fault therefore fails all later turns, not just the failed record.

The behavior is intentional and tested (D8 fail-fast), so this is a design question rather than a defect. Consider recording the first internal failure, disabling further instrumentation, and resetting lifecycleChain to a resolved promise so telemetry degrades instead of breaking interactive turns.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/cli/src/ui/hooks/agentStream/operationLifecycle.ts` around lines 889
- 900, Update queueWrite to record the first non-errno persistence failure,
disable subsequent lifecycle instrumentation, and reset lifecycleChain to a
resolved promise after that failure. Preserve D8 fail-fast behavior for the
failed write and drain(), while ensuring later finalise calls no longer reject
user submit paths once instrumentation has been disabled.
packages/cli/src/ui/hooks/agentStream/operationLifecycle.behavior.test.ts (1)

157-171: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

runUuid override does not reach the sink.

startAndCreate applies overrides.runUuid to PerfRetention only. The PerfSink always receives a fresh crypto.randomUUID(). A future test that passes runUuid to correlate the claim file with the record file will silently get mismatched UUIDs. Pass runUuid to the sink as well, or remove the unused override.

🧹 Proposed fix
   const sink =
     overrides.sink ??
     new PerfSink({
       dir,
-      runUuid: crypto.randomUUID(),
+      runUuid,
       retention,
     });
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/cli/src/ui/hooks/agentStream/operationLifecycle.behavior.test.ts`
around lines 157 - 171, Update startAndCreate so the PerfSink constructor
receives the same runUuid resolved from overrides and used by PerfRetention,
instead of generating a second UUID; preserve override behavior and shared UUID
correlation between retention and sink.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: 04aa8be8-abce-4df7-aa45-0b7729b43a65

📥 Commits

Reviewing files that changed from the base of the PR and between 62f5fcd and 73342c6.

⛔ Files ignored due to path filters (27)
  • project-plans/issue3167/.completed/P13.md is excluded by !project-plans/**
  • project-plans/issue3167/acceptance-criteria.md is excluded by !project-plans/**
  • project-plans/issue3167/analysis/domain-model.md is excluded by !project-plans/**
  • project-plans/issue3167/analysis/pseudocode/01-schema-and-reader.md is excluded by !project-plans/**
  • project-plans/issue3167/analysis/pseudocode/02-perfsink-and-interval-union.md is excluded by !project-plans/**
  • project-plans/issue3167/analysis/pseudocode/03-stdout-observer.md is excluded by !project-plans/**
  • project-plans/issue3167/analysis/pseudocode/04-operation-lifecycle.md is excluded by !project-plans/**
  • project-plans/issue3167/analysis/pseudocode/05-client-phases.md is excluded by !project-plans/**
  • project-plans/issue3167/analysis/pseudocode/06-retention.md is excluded by !project-plans/**
  • project-plans/issue3167/analysis/pseudocode/07-memory-trend.md is excluded by !project-plans/**
  • project-plans/issue3167/analysis/pseudocode/08-consumer-and-perf-command.md is excluded by !project-plans/**
  • project-plans/issue3167/analysis/pseudocode/09-overhead-harness.md is excluded by !project-plans/**
  • project-plans/issue3167/execution-tracker.md is excluded by !project-plans/**
  • project-plans/issue3167/plan/00-overview.md is excluded by !project-plans/**
  • project-plans/issue3167/plan/01-preflight-verification.md is excluded by !project-plans/**
  • project-plans/issue3167/plan/02-analysis-pseudocode.md is excluded by !project-plans/**
  • project-plans/issue3167/plan/03-interval-union.md is excluded by !project-plans/**
  • project-plans/issue3167/plan/04-schema-perfsink-reader.md is excluded by !project-plans/**
  • project-plans/issue3167/plan/05-stdout-observer-onrender.md is excluded by !project-plans/**
  • project-plans/issue3167/plan/06-operation-lifecycle-identity.md is excluded by !project-plans/**
  • project-plans/issue3167/plan/07-client-phases.md is excluded by !project-plans/**
  • project-plans/issue3167/plan/08-retention.md is excluded by !project-plans/**
  • project-plans/issue3167/plan/09-settings.md is excluded by !project-plans/**
  • project-plans/issue3167/plan/10-memory-trend.md is excluded by !project-plans/**
  • project-plans/issue3167/plan/11-reader-consumer-perf-command.md is excluded by !project-plans/**
  • project-plans/issue3167/plan/12-integration-overhead-harness.md is excluded by !project-plans/**
  • project-plans/issue3167/plan/13-final-verification.md is excluded by !project-plans/**
📒 Files selected for processing (125)
  • docs/telemetry-privacy.md
  • packages/cli/src/__tests__/cliSessionDispatch.characterization.test.tsx
  • packages/cli/src/cli.provider-init.test.ts
  • packages/cli/src/cli.startInteractiveUI.test.tsx
  • packages/cli/src/cli.test.tsx
  • packages/cli/src/config/configBuilder.ts
  • packages/cli/src/config/perfSettingsMerge.behavior.test.ts
  • packages/cli/src/config/perfSettingsValidation.behavior.test.ts
  • packages/cli/src/config/settingsSchema.ts
  • packages/cli/src/services/BuiltinCommandLoader.test.ts
  • packages/cli/src/services/BuiltinCommandLoader.ts
  • packages/cli/src/services/cliCommandApiMap.ts
  • packages/cli/src/services/commandApiMapCompleteness.test.ts
  • packages/cli/src/session/buildPerfOwner.behavior.test.ts
  • packages/cli/src/session/interactiveUI.startup.transaction.behavior.test.ts
  • packages/cli/src/session/interactiveUI.tsx
  • packages/cli/src/session/interactiveUiLifecycle.ts
  • packages/cli/src/ui/App.tsx
  • packages/cli/src/ui/AppContainerRuntime.tsx
  • packages/cli/src/ui/cliUiRuntime.ts
  • packages/cli/src/ui/commands/perfCommand.behavior.test.ts
  • packages/cli/src/ui/commands/perfCommand.ts
  • packages/cli/src/ui/commands/perfCommand.wiring.behavior.test.ts
  • packages/cli/src/ui/containers/AppContainer/hooks/useAppBootstrap.ts
  • packages/cli/src/ui/containers/AppContainer/hooks/useAppInput.ts
  • packages/cli/src/ui/hooks/agentStream/__tests__/lifecyclePerfFixtures.ts
  • packages/cli/src/ui/hooks/agentStream/__tests__/overheadHarness.useSubmitQuery.test.tsx
  • packages/cli/src/ui/hooks/agentStream/__tests__/useAgentEventStream.defaultoff.p07.bun.tsx
  • packages/cli/src/ui/hooks/agentStream/__tests__/useSubmitQuery.lifecycle.cancellation.bun.tsx
  • packages/cli/src/ui/hooks/agentStream/__tests__/useSubmitQuery.lifecycle.failfast.bun.tsx
  • packages/cli/src/ui/hooks/agentStream/__tests__/useSubmitQuery.lifecycle.test.tsx
  • packages/cli/src/ui/hooks/agentStream/operationLifecycle.behavior.test.ts
  • packages/cli/src/ui/hooks/agentStream/operationLifecycle.p07.behavior.test.ts
  • packages/cli/src/ui/hooks/agentStream/operationLifecycle.p07.contract.behavior.test.ts
  • packages/cli/src/ui/hooks/agentStream/operationLifecycle.p10.memory.behavior.test.ts
  • packages/cli/src/ui/hooks/agentStream/operationLifecycle.snapshot.behavior.test.ts
  • packages/cli/src/ui/hooks/agentStream/operationLifecycle.ts
  • packages/cli/src/ui/hooks/agentStream/submitQueryTurnLifecycle.ts
  • packages/cli/src/ui/hooks/agentStream/useAgentEventStream.ts
  • packages/cli/src/ui/hooks/agentStream/useAgentStream.ts
  • packages/cli/src/ui/hooks/agentStream/useAgentStreamOrchestration.ts
  • packages/cli/src/ui/hooks/agentStream/useSubmitQuery.ts
  • packages/cli/src/ui/hooks/memoryTrend/index.ts
  • packages/cli/src/ui/hooks/memoryTrend/memoryRing.behavior.test.ts
  • packages/cli/src/ui/hooks/memoryTrend/memoryRing.ts
  • packages/cli/src/ui/hooks/memoryTrend/memorySlope.behavior.test.ts
  • packages/cli/src/ui/hooks/memoryTrend/memorySlope.ts
  • packages/cli/src/ui/hooks/memoryTrend/memoryTelemetry.behavior.test.ts
  • packages/cli/src/ui/hooks/memoryTrend/memoryTelemetry.ts
  • packages/cli/src/ui/hooks/memoryTrend/useMemoryMonitor.behavior.test.ts
  • packages/cli/src/ui/hooks/perf/dynamicIdentity.behavior.test.ts
  • packages/cli/src/ui/hooks/perf/interactiveLifecycle.behavior.test.ts
  • packages/cli/src/ui/hooks/perf/interactivePerfRuntime.behavior.test.ts
  • packages/cli/src/ui/hooks/perf/interactivePerfRuntime.startup.behavior.test.ts
  • packages/cli/src/ui/hooks/perf/interactivePerfRuntime.ts
  • packages/cli/src/ui/hooks/useMemoryMonitor.test.tsx
  • packages/cli/src/ui/hooks/useMemoryMonitor.ts
  • packages/cli/src/ui/inkRenderOptions.observer.behavior.test.ts
  • packages/cli/src/ui/inkRenderOptions.ts
  • packages/core/package.json
  • packages/core/src/config/config.ts
  • packages/core/src/config/configBaseCore.ts
  • packages/core/src/config/configConstructor.ts
  • packages/core/src/config/configPerfGetters.behavior.test.ts
  • packages/core/src/config/configTypes.ts
  • packages/core/src/config/perfSettings.behavior.test.ts
  • packages/core/src/config/telemetryConfigCopy.behavior.test.ts
  • packages/core/src/config/telemetrySettingsCopy.behavior.test.ts
  • packages/core/src/index.ts
  • packages/core/src/perf/perfPhaseObserver.ts
  • packages/core/src/utils/stdio.observer.behavior.test.ts
  • packages/core/src/utils/stdio.ts
  • packages/providers/src/__tests__/attemptRecorder.perf.behavior.test.ts
  • packages/providers/src/logging/attemptRecorder.ts
  • packages/telemetry/index.ts
  • packages/telemetry/package.json
  • packages/telemetry/src/perf/PerfSink.ts
  • packages/telemetry/src/perf/index.ts
  • packages/telemetry/src/perf/perfArtifacts.ts
  • packages/telemetry/src/perf/perfConsumer.behavior.test.ts
  • packages/telemetry/src/perf/perfConsumer.ts
  • packages/telemetry/src/perf/perfDelete.behavior.test.ts
  • packages/telemetry/src/perf/perfDelete.ts
  • packages/telemetry/src/perf/perfInspect.behavior.test.ts
  • packages/telemetry/src/perf/perfInspect.ts
  • packages/telemetry/src/perf/perfPhaseObserver.behavior.test.ts
  • packages/telemetry/src/perf/perfPhaseObserver.ts
  • packages/telemetry/src/perf/perfReader.join.behavior.test.ts
  • packages/telemetry/src/perf/perfReader.streaming.behavior.test.ts
  • packages/telemetry/src/perf/perfReader.tolerant.behavior.test.ts
  • packages/telemetry/src/perf/perfRecordSize.bench.ts
  • packages/telemetry/src/perf/perfRecords.behavior.test.ts
  • packages/telemetry/src/perf/perfRecords.ts
  • packages/telemetry/src/perf/perfRecords.v0.behavior.test.ts
  • packages/telemetry/src/perf/perfRecordsStream.ts
  • packages/telemetry/src/perf/perfReport.behavior.test.ts
  • packages/telemetry/src/perf/perfReport.tokenJoin.behavior.test.ts
  • packages/telemetry/src/perf/perfReport.ts
  • packages/telemetry/src/perf/perfSchema.boundary.behavior.test.ts
  • packages/telemetry/src/perf/perfSelfHealth.behavior.test.ts
  • packages/telemetry/src/perf/perfSelfHealth.model.behavior.test.ts
  • packages/telemetry/src/perf/perfSink.failopen.behavior.test.ts
  • packages/telemetry/src/perf/perfSink.retention.behavior.test.ts
  • packages/telemetry/src/perf/perfSink.roundtrip.behavior.test.ts
  • packages/telemetry/src/perf/perfSlopeBridge.ts
  • packages/telemetry/src/perf/retention.capSelection.behavior.test.ts
  • packages/telemetry/src/perf/retention.claim.behavior.test.ts
  • packages/telemetry/src/perf/retention.eviction.behavior.test.ts
  • packages/telemetry/src/perf/retention.faults.behavior.test.ts
  • packages/telemetry/src/perf/retention.lifecycle.behavior.test.ts
  • packages/telemetry/src/perf/retention.multiowner.behavior.test.ts
  • packages/telemetry/src/perf/retention.scheduling.behavior.test.ts
  • packages/telemetry/src/perf/retention.ts
  • packages/telemetry/src/perf/retention.validation.behavior.test.ts
  • packages/telemetry/src/perf/tokenUsageReader.behavior.test.ts
  • packages/telemetry/src/perf/tokenUsageReader.ts
  • packages/telemetry/src/telemetry/events/tool-events.ts
  • packages/telemetry/src/telemetry/events/toolEvents.boundaries.behavior.test.ts
  • packages/telemetry/src/telemetry/index.ts
  • packages/telemetry/src/telemetry/intervalUnion.behavior.test.ts
  • packages/telemetry/src/telemetry/intervalUnion.ts
  • packages/telemetry/src/telemetry/loggers.perf.behavior.test.ts
  • packages/telemetry/src/telemetry/loggers.ts
  • packages/telemetry/src/telemetry/sessionMetricsAggregator.ts
  • schemas/settings.schema.json

Comment thread packages/cli/src/ui/hooks/agentStream/submitQueryTurnLifecycle.ts
Comment thread packages/cli/src/ui/hooks/memoryTrend/memoryTelemetry.ts
Comment thread packages/cli/src/ui/hooks/perf/interactivePerfRuntime.startup.behavior.test.ts Outdated
Comment thread packages/telemetry/src/perf/PerfSink.ts
Comment thread packages/telemetry/src/perf/PerfSink.ts
Comment thread packages/telemetry/src/perf/retention.claim.behavior.test.ts Outdated
Comment thread packages/telemetry/src/perf/retention.faults.behavior.test.ts
Comment thread packages/telemetry/src/perf/retention.lifecycle.behavior.test.ts
@coderabbitai

coderabbitai Bot commented Aug 10, 2026

Copy link
Copy Markdown
Contributor

Note

GitHub couldn't provide a complete incremental comparison for this pull request, so CodeRabbit is performing a full review instead. This review may take a little longer.

@github-actions

github-actions Bot commented Aug 10, 2026

Copy link
Copy Markdown
Contributor

Walkthrough

Before this PR, the CLI had no built-in mechanism to capture, retain, or inspect local performance telemetry; operation timing, memory trends, and token usage were either unobserved or only surfaced indirectly. After this PR, users can opt into local performance telemetry through settings, with a new perf sink/reader/consumer pipeline that retains records, tracks operation lifecycles, monitors memory trends, and exposes everything through a dedicated /perf command and runtime hooks.

Release Notes

New Features

  • Add opt-in local performance telemetry with configurable retention
  • Introduce operation lifecycle tracking and memory trend monitoring
  • Add a new /perf command for inspecting performance records

Tests

  • Add extensive behavioral and integration tests covering perf ingestion, retention, lifecycle, memory trends, CLI commands, stdio observation, fault tolerance, and fail-open behavior

Documentation

  • Add planning documents, pseudocode, acceptance criteria, and user-facing telemetry privacy docs for the new performance telemetry system

Refactor

  • Refactor telemetry package exports
  • Extract IntervalUnion from SessionMetricsAggregator into a shared module

Changes

Layer File(s) Summary
docs project-plans/issue3167/plan/03-interval-union.md, project-plans/issue3167/plan/06-operation-lifecycle-identity.md, project-plans/issue3167/plan/12-integration-overhead-harness.md, project-plans/issue3167/plan/10-memory-trend.md, project-plans/issue3167/analysis/pseudocode/01-schema-and-reader.md, project-plans/issue3167/plan/13-final-verification.md, project-plans/issue3167/analysis/pseudocode/02-perfsink-and-interval-union.md, project-plans/issue3167/plan/05-stdout-observer-onrender.md, project-plans/issue3167/analysis/pseudocode/08-consumer-and-perf-command.md, project-plans/issue3167/plan/11-reader-consumer-perf-command.md, project-plans/issue3167/analysis/pseudocode/04-operation-lifecycle.md, project-plans/issue3167/analysis/pseudocode/05-client-phases.md, project-plans/issue3167/plan/09-settings.md, project-plans/issue3167/plan/02-analysis-pseudocode.md, project-plans/issue3167/analysis/pseudocode/06-retention.md, project-plans/issue3167/acceptance-criteria.md, project-plans/issue3167/analysis/pseudocode/09-overhead-harness.md, project-plans/issue3167/plan/04-schema-perfsink-reader.md, project-plans/issue3167/plan/00-overview.md, project-plans/issue3167/analysis/pseudocode/03-stdout-observer.md, project-plans/issue3167/analysis/pseudocode/07-memory-trend.md, project-plans/issue3167/plan/08-retention.md, project-plans/issue3167/plan/07-client-phases.md, project-plans/issue3167/.completed/P13.md, project-plans/issue3167/execution-tracker.md, docs/telemetry-privacy.md Planning, pseudocode, acceptance criteria, and user-facing docs for opt-in local performance telemetry.
tests packages/telemetry/src/perf/retention.capSelection.behavior.test.ts, packages/cli/src/ui/hooks/agentStream/tests/lifecyclePerfFixtures.ts, packages/telemetry/src/perf/perfConsumer.behavior.test.ts, packages/cli/src/ui/commands/perfCommand.wiring.behavior.test.ts, packages/cli/src/ui/hooks/agentStream/tests/useAgentEventStream.defaultoff.p07.bun.tsx, packages/cli/src/session/interactiveUI.startup.transaction.behavior.test.ts, packages/telemetry/src/perf/perfReader.tolerant.behavior.test.ts, packages/core/src/config/configPerfGetters.behavior.test.ts, packages/telemetry/src/perf/perfSink.failopen.behavior.test.ts, packages/cli/src/ui/hooks/agentStream/tests/useSubmitQuery.lifecycle.cancellation.bun.tsx, packages/cli/src/ui/hooks/perf/interactiveLifecycle.behavior.test.ts, packages/telemetry/src/perf/perfInspect.behavior.test.ts, packages/telemetry/src/perf/perfSink.roundtrip.behavior.test.ts, packages/cli/src/config/perfSettingsMerge.behavior.test.ts, packages/cli/src/ui/hooks/agentStream/operationLifecycle.p07.behavior.test.ts, packages/telemetry/src/perf/retention.claim.behavior.test.ts, packages/cli/src/ui/commands/perfCommand.behavior.test.ts, packages/telemetry/src/perf/retention.multiowner.behavior.test.ts, packages/telemetry/src/perf/perfSink.retention.behavior.test.ts, packages/cli/src/ui/hooks/agentStream/operationLifecycle.snapshot.behavior.test.ts, packages/cli/src/ui/hooks/memoryTrend/useMemoryMonitor.behavior.test.ts, packages/cli/src/services/BuiltinCommandLoader.test.ts, packages/cli/src/ui/hooks/memoryTrend/memorySlope.behavior.test.ts, packages/telemetry/src/perf/perfRecords.behavior.test.ts, packages/telemetry/src/perf/retention.faults.behavior.test.ts, packages/cli/src/ui/hooks/memoryTrend/memoryTelemetry.behavior.test.ts, packages/cli/src/ui/hooks/agentStream/tests/overheadHarness.useSubmitQuery.test.tsx, packages/telemetry/src/perf/perfSchema.boundary.behavior.test.ts, packages/cli/src/ui/hooks/memoryTrend/memoryRing.behavior.test.ts, packages/cli/src/ui/hooks/perf/interactivePerfRuntime.behavior.test.ts, packages/telemetry/src/perf/retention.scheduling.behavior.test.ts, packages/cli/src/ui/hooks/agentStream/tests/useSubmitQuery.lifecycle.failfast.bun.tsx, packages/telemetry/src/perf/perfSelfHealth.model.behavior.test.ts, packages/telemetry/src/perf/perfRecords.v0.behavior.test.ts, packages/cli/src/ui/hooks/perf/interactivePerfRuntime.startup.behavior.test.ts, packages/core/src/config/perfSettings.behavior.test.ts, packages/telemetry/src/perf/perfReport.behavior.test.ts, packages/telemetry/src/perf/perfReader.join.behavior.test.ts, packages/cli/src/ui/inkRenderOptions.observer.behavior.test.ts, packages/telemetry/src/perf/perfSelfHealth.behavior.test.ts, packages/cli/src/ui/hooks/agentStream/operationLifecycle.p07.contract.behavior.test.ts, packages/telemetry/src/telemetry/events/toolEvents.boundaries.behavior.test.ts, packages/telemetry/src/perf/perfDelete.behavior.test.ts, packages/telemetry/src/perf/perfReader.streaming.behavior.test.ts, packages/core/src/config/telemetrySettingsCopy.behavior.test.ts, packages/core/src/utils/stdio.observer.behavior.test.ts, packages/telemetry/src/perf/perfReport.tokenJoin.behavior.test.ts, packages/cli/src/session/interactiveUI.startup.transaction.behavior.test.ts, packages/telemetry/src/telemetry/loggers.perf.behavior.test.ts, packages/cli/src/ui/hooks/agentStream/operationLifecycle.p10.memory.behavior.test.ts, packages/telemetry/src/perf/tokenUsageReader.behavior.test.ts, packages/telemetry/src/perf/retention.lifecycle.behavior.test.ts, packages/telemetry/src/perf/retention.eviction.behavior.test.ts, packages/providers/src/tests/attemptRecorder.perf.behavior.test.ts, packages/cli/src/ui/hooks/agentStream/useSubmitQuery.lifecycle.test.tsx, packages/cli/src/ui/hooks/agentStream/operationLifecycle.behavior.test.ts, packages/cli/src/cli.startInteractiveUI.test.tsx, packages/cli/src/cli.provider-init.test.ts, packages/cli/src/cli.test.tsx, packages/cli/src/tests/cliSessionDispatch.characterization.test.tsx, packages/cli/src/services/commandApiMapCompleteness.test.ts Behavioral and integration tests covering perf telemetry ingestion, retention, lifecycle, memory trends, CLI commands, and stdio observation.
feature packages/core/src/config/configTypes.ts, packages/telemetry/src/perf/tokenUsageReader.ts, packages/cli/src/ui/hooks/agentStream/useAgentEventStream.ts, packages/telemetry/src/perf/perfPhaseObserver.ts, packages/core/src/config/config.ts, packages/telemetry/src/telemetry/loggers.ts, packages/providers/src/logging/attemptRecorder.ts, packages/cli/src/ui/containers/AppContainer/hooks/useAppInput.ts, packages/core/src/utils/stdio.ts, packages/cli/src/ui/containers/AppContainer/hooks/useAppBootstrap.ts, packages/telemetry/src/perf/perfSlopeBridge.ts, packages/cli/src/ui/hooks/agentStream/useAgentStreamOrchestration.ts, packages/cli/src/ui/hooks/agentStream/useSubmitQuery.ts, packages/core/src/config/configBaseCore.ts, packages/telemetry/src/perf/perfDelete.ts, packages/cli/src/ui/hooks/memoryTrend/memorySlope.ts, packages/telemetry/src/telemetry/events/tool-events.ts, packages/cli/src/ui/hooks/memoryTrend/memoryRing.ts, packages/cli/src/ui/hooks/agentStream/submitQueryTurnLifecycle.ts, packages/cli/src/ui/hooks/agentStream/operationLifecycle.ts, packages/cli/src/ui/cliUiRuntime.ts, packages/telemetry/src/perf/perfConsumer.ts, packages/telemetry/src/perf/perfInspect.ts, packages/telemetry/src/perf/perfSink.ts, packages/telemetry/src/perf/perfRecords.ts, packages/telemetry/src/perf/perfRecordsStream.ts, packages/telemetry/src/perf/perfReport.ts, packages/telemetry/src/perf/perfArtifacts.ts, packages/telemetry/src/perf/index.ts, packages/telemetry/index.ts, packages/cli/src/ui/hooks/memoryTrend/memoryTelemetry.ts, packages/cli/src/ui/hooks/useMemoryMonitor.ts, packages/cli/src/ui/App.tsx, packages/cli/src/ui/AppContainerRuntime.tsx, packages/cli/src/ui/hooks/perf/interactivePerfRuntime.ts, packages/cli/src/ui/commands/perfCommand.ts, packages/cli/src/config/configBuilder.ts, packages/cli/src/services/BuiltinCommandLoader.ts, packages/cli/src/services/cliCommandApiMap.ts, packages/cli/src/ui/hooks/memoryTrend/index.ts, packages/cli/src/ui/inkRenderOptions.ts, packages/cli/src/session/interactiveUiLifecycle.ts, packages/core/src/perf/perfPhaseObserver.ts, packages/core/src/index.ts, packages/core/package.json, packages/telemetry/package.json, schemas/settings.schema.json Core telemetry primitives, CLI wiring, config/schema updates, and new /perf command surface for opt-in local performance telemetry.
refactor packages/telemetry/src/telemetry/index.ts, packages/telemetry/src/telemetry/sessionMetricsAggregator.ts Refactors telemetry package exports and extracts IntervalUnion from SessionMetricsAggregator into a shared module.

Sequence Diagram

sequenceDiagram
  participant User
  participant CLI as CLI/App
  participant Submit as useSubmitQuery
  participant Stream as useAgentEventStream
  participant Agent as Agent
  participant Ink as Ink/onRender
  participant Stdio as StdioProxy
  participant Memory as useMemoryMonitor
  participant Sink as PerfSink
  participant Cmd as /perf Command

  User->>CLI: submit prompt
  CLI->>Submit: initTurn, mint operation_id
  Submit->>Stream: runStream(query, signal, promptId)
  Stream->>Agent: agent.stream()
  Agent-->>Stream: AgentEvent delta stream
  Stream->>Ink: render frame
  Ink-->>Stream: onRender metrics
  Stream->>Stdio: write rendered output
  Stdio-->>Stream: bytes written, write count
  loop every 60s
    Memory->>Memory: process.memoryUsage().rss
    Memory->>Sink: append memory_sample record
  end
  Stream->>Sink: append operation perf record
  Agent-->>Stream: stream complete
  Stream-->>Submit: runStream resolved
  Submit-->>CLI: turn finalized
  User->>Cmd: /perf
  Cmd->>Sink: read JSONL records
  Sink-->>Cmd: perf records
  Cmd-->>User: trend report
Loading

Magnitude

🎯 4 (XL)
34094 additions, 417 deletions, 152 changed files across 4 packages, 0 acceptance criteria

Related


Walkthrough generated by LLxprt PR Review. Planner issue: #2256

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 8

🧹 Nitpick comments (4)
packages/cli/src/session/interactiveUI.tsx (1)

334-349: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Use one mouse-enablement decision for staging and activation.

commitInteractiveStartup computes state.mouseStaged through ports.isMouseEnabled, but setupTerminalExitHandlers calls the module-level isMouseEventsEnabled directly. A test that overrides only the isMouseEnabled port makes staging state and actual activation diverge, which can hide a rollback defect. Pass the computed value into setupTerminal instead.

♻️ Proposed refactor
 function setupTerminalExitHandlers(
   renderOptions: ReturnType<typeof inkRenderOptions>,
-  settings: LoadedSettings,
+  mouseEventsEnabled: boolean,
 ): void {
-  const mouseEventsEnabled = isMouseEventsEnabled(renderOptions, settings);
   if (mouseEventsEnabled) {
     state.mouseStaged = ports.isMouseEnabled(renderOptions, args.settings);
-    ports.setupTerminal(renderOptions, args.settings);
+    ports.setupTerminal(renderOptions, state.mouseStaged);

Also applies to: 536-537

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/cli/src/session/interactiveUI.tsx` around lines 334 - 349, Update
the interactive startup flow so the mouse-enabled decision computed by
commitInteractiveStartup through ports.isMouseEnabled is passed into
setupTerminal and setupTerminalExitHandlers, rather than recomputed with
isMouseEventsEnabled. Use that single value for both staging and activation
while preserving the existing exit-handler registration behavior.
packages/cli/src/ui/hooks/agentStream/__tests__/useSubmitQuery.lifecycle.failfast.bun.tsx (1)

182-193: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Collapse the duplicated AggregateError branches.

The cleanupErrors.length === 1 branch produces the same value as the > 1 branch. [err, ...cleanupErrors] equals [err, cleanupErrors[0]] when the array holds one item. One branch is enough.

♻️ Proposed change
-    if (cleanupErrors.length === 1) {
-      throw new AggregateError(
-        [err, cleanupErrors[0]],
-        'setup cleanup also failed',
-      );
-    }
-    if (cleanupErrors.length > 1) {
+    if (cleanupErrors.length > 0) {
       throw new AggregateError(
         [err, ...cleanupErrors],
         'setup cleanup also failed',
       );
     }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@packages/cli/src/ui/hooks/agentStream/__tests__/useSubmitQuery.lifecycle.failfast.bun.tsx`
around lines 182 - 193, Collapse the two cleanupErrors branches into a single
condition handling any non-empty cleanupErrors array, constructing
AggregateError with [err, ...cleanupErrors] and preserving the existing message
and throw behavior.
packages/telemetry/src/perf/tokenUsageReader.behavior.test.ts (1)

25-33: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Merge the duplicate import and correct the comment.

Lines 25-29 and line 33 import from the same specifier ./tokenUsageReader.js. The comment says the seam is imported "directly from the internal module", but both statements target the same module. Merge them into one import statement to avoid an ESLint no-duplicate-imports failure and to remove the misleading note.

♻️ Proposed change
 import {
   consumeTokenUsageDirectory,
   streamTokenUsageDirectory,
   streamTokenUsageRecords,
+  // Package-private seam: not re-exported from the barrel.
+  streamTokenUsageFromReadable,
 } from './tokenUsageReader.js';
 import type { TokenUsageStreamEntry } from './tokenUsageReader.js';
-// The controlled-readable seam is package-private (not exported from the
-// barrel); same-package tests import it directly from the internal module.
-import { streamTokenUsageFromReadable } from './tokenUsageReader.js';
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/telemetry/src/perf/tokenUsageReader.behavior.test.ts` around lines
25 - 33, Merge the named imports from ./tokenUsageReader.js into a single import
declaration, including streamTokenUsageFromReadable and the existing functions
and type as appropriate. Remove the misleading controlled-readable seam comment
and preserve all imported symbols and their type-only semantics.
packages/telemetry/src/perf/retention.capSelection.behavior.test.ts (1)

87-118: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Correct the comments and strengthen the byte-cap assertion.

Three points in this test are inaccurate:

  • Line 90 says "8 files × ~8 MiB each"; line 91 computes PERF_MAX_BYTES / 7 (~9.14 MiB). Only one value can be right.
  • Line 116 asserts remaining.length < PERF_MAX_FILES. The test creates 8 files, so this holds without any retention work. It does not prove the byte cap bound.
  • The real evidence is line 118 plus the fact that files were removed. Assert the removal directly.
♻️ Proposed adjustment
-    // Create fewer than MAX_FILES files but exceeding MAX_BYTES total.
-    // 8 files × ~8 MiB each = ~64 MiB ≈ MAX_BYTES.
-    const bytesPerFile = Math.ceil(PERF_MAX_BYTES / 7); // ~9.6 MiB each
+    // Create far fewer than MAX_FILES files but exceeding MAX_BYTES total.
+    // 8 files × (MAX_BYTES / 7) ≈ 73 MiB > MAX_BYTES.
+    const bytesPerFile = Math.ceil(PERF_MAX_BYTES / 7);
@@
-    // The byte cap should be enforced (files count < MAX_FILES proves byte cap bound).
-    expect(remaining.length).toBeLessThan(PERF_MAX_FILES);
+    // The byte cap bound: files were deleted even though the count cap (128)
+    // was never reached.
+    expect(remaining.length).toBeLessThan(8);
     // Total bytes should be under the byte cap.
     expect(remainingBytes).toBeLessThanOrEqual(PERF_MAX_BYTES);
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/telemetry/src/perf/retention.capSelection.behavior.test.ts` around
lines 87 - 118, Correct the setup comment in the high-volume retention test to
match the actual bytesPerFile calculation. Replace the ineffective
remaining.length assertion with a direct assertion that retention removed at
least one file, while preserving the remainingBytes <= PERF_MAX_BYTES assertion
as the byte-cap check.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@packages/cli/src/session/interactiveUI.tsx`:
- Around line 221-267: Update buildAndStartPerfOwner around owner.start() to
catch startup failures, dispose the constructed owner, and rethrow the original
error. Preserve successful startup behavior, and first verify whether
InteractivePerfRuntime.start already guarantees cleanup to avoid duplicate
disposal.

In `@packages/cli/src/ui/hooks/agentStream/operationLifecycle.behavior.test.ts`:
- Around line 157-171: Ensure each test setup uses one shared runUuid for both
PerfRetention and PerfSink, matching production wiring. In
packages/cli/src/ui/hooks/agentStream/operationLifecycle.behavior.test.ts#L157-L171,
pass the existing runUuid to PerfSink; add and reuse a shared runUuid in
createRegistry at `#L119-L141` and in inline sink constructions at `#L687-L692`,
`#L760-L765`, `#L791-L796`, and `#L829-L834`. In
packages/cli/src/ui/hooks/agentStream/operationLifecycle.p10.memory.behavior.test.ts#L100-L109,
`#L135-L144`, `#L166-L175`, and `#L209-L218`, replace each pair of random UUID calls
with one shared value.

In `@packages/cli/src/ui/inkRenderOptions.ts`:
- Around line 61-79: Ensure clearing or replacing observers detaches them from
already-built Ink resources: in packages/cli/src/ui/inkRenderOptions.ts:61-79,
update the cached stdio path so writes consult the current observer or otherwise
support detaching instead of capturing a stale observer; in
packages/cli/src/ui/inkRenderOptions.ts:136-145, have the onRender callback read
interactiveRenderObserver at invocation time rather than capturing
renderObserver, while preserving fail-fast behavior for the currently active
observer.

In `@packages/telemetry/src/perf/perfSink.failopen.behavior.test.ts`:
- Around line 285-295: Add an assertion on openAttempts in the fail-open write
test after the second sink.write call, verifying that exclusive open was
attempted again. Keep the existing diagnostics, dispose, and file-count
assertions unchanged.

In `@packages/telemetry/src/perf/perfSink.retention.behavior.test.ts`:
- Around line 278-318: Rename the AC-7 describe block and test so they describe
manual-sweep retention convergence rather than concurrent overshoot, since the
body performs only sequential setup and one retention.maintain call. Preserve
the existing assertions and test behavior; do not introduce concurrency.
- Around line 108-127: Update the PerfSink filename generation used by
sink.write so the perf-<day>-<runUuid>.jsonl day key is derived from the
operation record’s ts, via the existing perfArtifacts.utcDayKey helper, rather
than the wall clock. Ensure all related retention behavior tests use
deterministic record timestamps and preserve the expected 20260808 filename
without depending on the current date.

In `@packages/telemetry/src/perf/retention.eviction.behavior.test.ts`:
- Around line 160-227: Update the tests a fresh claim is never evicted and a
future-mtime claim is protected until it becomes eligible so each created claim
UUID differs from the PerfRetention runUuid, ensuring own-run protection does
not determine the result. Add a separate fresh owner claim only if required by
the sweep setup, while preserving the existing assertions for the non-owner
claims.
- Around line 154-158: Strengthen the assertion after retention.maintain in the
retention test to verify the exact surviving artifact set rather than only its
size. Assert that the expected claim artifact remains, with the intended
filename, and thereby confirm the claim counted toward the cap without being
JSONL-parsed.

---

Nitpick comments:
In `@packages/cli/src/session/interactiveUI.tsx`:
- Around line 334-349: Update the interactive startup flow so the mouse-enabled
decision computed by commitInteractiveStartup through ports.isMouseEnabled is
passed into setupTerminal and setupTerminalExitHandlers, rather than recomputed
with isMouseEventsEnabled. Use that single value for both staging and activation
while preserving the existing exit-handler registration behavior.

In
`@packages/cli/src/ui/hooks/agentStream/__tests__/useSubmitQuery.lifecycle.failfast.bun.tsx`:
- Around line 182-193: Collapse the two cleanupErrors branches into a single
condition handling any non-empty cleanupErrors array, constructing
AggregateError with [err, ...cleanupErrors] and preserving the existing message
and throw behavior.

In `@packages/telemetry/src/perf/retention.capSelection.behavior.test.ts`:
- Around line 87-118: Correct the setup comment in the high-volume retention
test to match the actual bytesPerFile calculation. Replace the ineffective
remaining.length assertion with a direct assertion that retention removed at
least one file, while preserving the remainingBytes <= PERF_MAX_BYTES assertion
as the byte-cap check.

In `@packages/telemetry/src/perf/tokenUsageReader.behavior.test.ts`:
- Around line 25-33: Merge the named imports from ./tokenUsageReader.js into a
single import declaration, including streamTokenUsageFromReadable and the
existing functions and type as appropriate. Remove the misleading
controlled-readable seam comment and preserve all imported symbols and their
type-only semantics.
🪄 Autofix

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: be085559-9bb0-4f14-a049-e1e51990e4b6

📥 Commits

Reviewing files that changed from the base of the PR and between 62f5fcd and 6aa22ed.

⛔ Files ignored due to path filters (27)
  • project-plans/issue3167/.completed/P13.md is excluded by !project-plans/**
  • project-plans/issue3167/acceptance-criteria.md is excluded by !project-plans/**
  • project-plans/issue3167/analysis/domain-model.md is excluded by !project-plans/**
  • project-plans/issue3167/analysis/pseudocode/01-schema-and-reader.md is excluded by !project-plans/**
  • project-plans/issue3167/analysis/pseudocode/02-perfsink-and-interval-union.md is excluded by !project-plans/**
  • project-plans/issue3167/analysis/pseudocode/03-stdout-observer.md is excluded by !project-plans/**
  • project-plans/issue3167/analysis/pseudocode/04-operation-lifecycle.md is excluded by !project-plans/**
  • project-plans/issue3167/analysis/pseudocode/05-client-phases.md is excluded by !project-plans/**
  • project-plans/issue3167/analysis/pseudocode/06-retention.md is excluded by !project-plans/**
  • project-plans/issue3167/analysis/pseudocode/07-memory-trend.md is excluded by !project-plans/**
  • project-plans/issue3167/analysis/pseudocode/08-consumer-and-perf-command.md is excluded by !project-plans/**
  • project-plans/issue3167/analysis/pseudocode/09-overhead-harness.md is excluded by !project-plans/**
  • project-plans/issue3167/execution-tracker.md is excluded by !project-plans/**
  • project-plans/issue3167/plan/00-overview.md is excluded by !project-plans/**
  • project-plans/issue3167/plan/01-preflight-verification.md is excluded by !project-plans/**
  • project-plans/issue3167/plan/02-analysis-pseudocode.md is excluded by !project-plans/**
  • project-plans/issue3167/plan/03-interval-union.md is excluded by !project-plans/**
  • project-plans/issue3167/plan/04-schema-perfsink-reader.md is excluded by !project-plans/**
  • project-plans/issue3167/plan/05-stdout-observer-onrender.md is excluded by !project-plans/**
  • project-plans/issue3167/plan/06-operation-lifecycle-identity.md is excluded by !project-plans/**
  • project-plans/issue3167/plan/07-client-phases.md is excluded by !project-plans/**
  • project-plans/issue3167/plan/08-retention.md is excluded by !project-plans/**
  • project-plans/issue3167/plan/09-settings.md is excluded by !project-plans/**
  • project-plans/issue3167/plan/10-memory-trend.md is excluded by !project-plans/**
  • project-plans/issue3167/plan/11-reader-consumer-perf-command.md is excluded by !project-plans/**
  • project-plans/issue3167/plan/12-integration-overhead-harness.md is excluded by !project-plans/**
  • project-plans/issue3167/plan/13-final-verification.md is excluded by !project-plans/**
📒 Files selected for processing (125)
  • docs/telemetry-privacy.md
  • packages/cli/src/__tests__/cliSessionDispatch.characterization.test.tsx
  • packages/cli/src/cli.provider-init.test.ts
  • packages/cli/src/cli.startInteractiveUI.test.tsx
  • packages/cli/src/cli.test.tsx
  • packages/cli/src/config/configBuilder.ts
  • packages/cli/src/config/perfSettingsMerge.behavior.test.ts
  • packages/cli/src/config/perfSettingsValidation.behavior.test.ts
  • packages/cli/src/config/settingsSchema.ts
  • packages/cli/src/services/BuiltinCommandLoader.test.ts
  • packages/cli/src/services/BuiltinCommandLoader.ts
  • packages/cli/src/services/cliCommandApiMap.ts
  • packages/cli/src/services/commandApiMapCompleteness.test.ts
  • packages/cli/src/session/buildPerfOwner.behavior.test.ts
  • packages/cli/src/session/interactiveUI.startup.transaction.behavior.test.ts
  • packages/cli/src/session/interactiveUI.tsx
  • packages/cli/src/session/interactiveUiLifecycle.ts
  • packages/cli/src/ui/App.tsx
  • packages/cli/src/ui/AppContainerRuntime.tsx
  • packages/cli/src/ui/cliUiRuntime.ts
  • packages/cli/src/ui/commands/perfCommand.behavior.test.ts
  • packages/cli/src/ui/commands/perfCommand.ts
  • packages/cli/src/ui/commands/perfCommand.wiring.behavior.test.ts
  • packages/cli/src/ui/containers/AppContainer/hooks/useAppBootstrap.ts
  • packages/cli/src/ui/containers/AppContainer/hooks/useAppInput.ts
  • packages/cli/src/ui/hooks/agentStream/__tests__/lifecyclePerfFixtures.ts
  • packages/cli/src/ui/hooks/agentStream/__tests__/overheadHarness.useSubmitQuery.test.tsx
  • packages/cli/src/ui/hooks/agentStream/__tests__/useAgentEventStream.defaultoff.p07.bun.tsx
  • packages/cli/src/ui/hooks/agentStream/__tests__/useSubmitQuery.lifecycle.cancellation.bun.tsx
  • packages/cli/src/ui/hooks/agentStream/__tests__/useSubmitQuery.lifecycle.failfast.bun.tsx
  • packages/cli/src/ui/hooks/agentStream/__tests__/useSubmitQuery.lifecycle.test.tsx
  • packages/cli/src/ui/hooks/agentStream/operationLifecycle.behavior.test.ts
  • packages/cli/src/ui/hooks/agentStream/operationLifecycle.p07.behavior.test.ts
  • packages/cli/src/ui/hooks/agentStream/operationLifecycle.p07.contract.behavior.test.ts
  • packages/cli/src/ui/hooks/agentStream/operationLifecycle.p10.memory.behavior.test.ts
  • packages/cli/src/ui/hooks/agentStream/operationLifecycle.snapshot.behavior.test.ts
  • packages/cli/src/ui/hooks/agentStream/operationLifecycle.ts
  • packages/cli/src/ui/hooks/agentStream/submitQueryTurnLifecycle.ts
  • packages/cli/src/ui/hooks/agentStream/useAgentEventStream.ts
  • packages/cli/src/ui/hooks/agentStream/useAgentStream.ts
  • packages/cli/src/ui/hooks/agentStream/useAgentStreamOrchestration.ts
  • packages/cli/src/ui/hooks/agentStream/useSubmitQuery.ts
  • packages/cli/src/ui/hooks/memoryTrend/index.ts
  • packages/cli/src/ui/hooks/memoryTrend/memoryRing.behavior.test.ts
  • packages/cli/src/ui/hooks/memoryTrend/memoryRing.ts
  • packages/cli/src/ui/hooks/memoryTrend/memorySlope.behavior.test.ts
  • packages/cli/src/ui/hooks/memoryTrend/memorySlope.ts
  • packages/cli/src/ui/hooks/memoryTrend/memoryTelemetry.behavior.test.ts
  • packages/cli/src/ui/hooks/memoryTrend/memoryTelemetry.ts
  • packages/cli/src/ui/hooks/memoryTrend/useMemoryMonitor.behavior.test.ts
  • packages/cli/src/ui/hooks/perf/dynamicIdentity.behavior.test.ts
  • packages/cli/src/ui/hooks/perf/interactiveLifecycle.behavior.test.ts
  • packages/cli/src/ui/hooks/perf/interactivePerfRuntime.behavior.test.ts
  • packages/cli/src/ui/hooks/perf/interactivePerfRuntime.startup.behavior.test.ts
  • packages/cli/src/ui/hooks/perf/interactivePerfRuntime.ts
  • packages/cli/src/ui/hooks/useMemoryMonitor.test.tsx
  • packages/cli/src/ui/hooks/useMemoryMonitor.ts
  • packages/cli/src/ui/inkRenderOptions.observer.behavior.test.ts
  • packages/cli/src/ui/inkRenderOptions.ts
  • packages/core/package.json
  • packages/core/src/config/config.ts
  • packages/core/src/config/configBaseCore.ts
  • packages/core/src/config/configConstructor.ts
  • packages/core/src/config/configPerfGetters.behavior.test.ts
  • packages/core/src/config/configTypes.ts
  • packages/core/src/config/perfSettings.behavior.test.ts
  • packages/core/src/config/telemetryConfigCopy.behavior.test.ts
  • packages/core/src/config/telemetrySettingsCopy.behavior.test.ts
  • packages/core/src/index.ts
  • packages/core/src/perf/perfPhaseObserver.ts
  • packages/core/src/utils/stdio.observer.behavior.test.ts
  • packages/core/src/utils/stdio.ts
  • packages/providers/src/__tests__/attemptRecorder.perf.behavior.test.ts
  • packages/providers/src/logging/attemptRecorder.ts
  • packages/telemetry/index.ts
  • packages/telemetry/package.json
  • packages/telemetry/src/perf/PerfSink.ts
  • packages/telemetry/src/perf/index.ts
  • packages/telemetry/src/perf/perfArtifacts.ts
  • packages/telemetry/src/perf/perfConsumer.behavior.test.ts
  • packages/telemetry/src/perf/perfConsumer.ts
  • packages/telemetry/src/perf/perfDelete.behavior.test.ts
  • packages/telemetry/src/perf/perfDelete.ts
  • packages/telemetry/src/perf/perfInspect.behavior.test.ts
  • packages/telemetry/src/perf/perfInspect.ts
  • packages/telemetry/src/perf/perfPhaseObserver.behavior.test.ts
  • packages/telemetry/src/perf/perfPhaseObserver.ts
  • packages/telemetry/src/perf/perfReader.join.behavior.test.ts
  • packages/telemetry/src/perf/perfReader.streaming.behavior.test.ts
  • packages/telemetry/src/perf/perfReader.tolerant.behavior.test.ts
  • packages/telemetry/src/perf/perfRecordSize.bench.ts
  • packages/telemetry/src/perf/perfRecords.behavior.test.ts
  • packages/telemetry/src/perf/perfRecords.ts
  • packages/telemetry/src/perf/perfRecords.v0.behavior.test.ts
  • packages/telemetry/src/perf/perfRecordsStream.ts
  • packages/telemetry/src/perf/perfReport.behavior.test.ts
  • packages/telemetry/src/perf/perfReport.tokenJoin.behavior.test.ts
  • packages/telemetry/src/perf/perfReport.ts
  • packages/telemetry/src/perf/perfSchema.boundary.behavior.test.ts
  • packages/telemetry/src/perf/perfSelfHealth.behavior.test.ts
  • packages/telemetry/src/perf/perfSelfHealth.model.behavior.test.ts
  • packages/telemetry/src/perf/perfSink.failopen.behavior.test.ts
  • packages/telemetry/src/perf/perfSink.retention.behavior.test.ts
  • packages/telemetry/src/perf/perfSink.roundtrip.behavior.test.ts
  • packages/telemetry/src/perf/perfSlopeBridge.ts
  • packages/telemetry/src/perf/retention.capSelection.behavior.test.ts
  • packages/telemetry/src/perf/retention.claim.behavior.test.ts
  • packages/telemetry/src/perf/retention.eviction.behavior.test.ts
  • packages/telemetry/src/perf/retention.faults.behavior.test.ts
  • packages/telemetry/src/perf/retention.lifecycle.behavior.test.ts
  • packages/telemetry/src/perf/retention.multiowner.behavior.test.ts
  • packages/telemetry/src/perf/retention.scheduling.behavior.test.ts
  • packages/telemetry/src/perf/retention.ts
  • packages/telemetry/src/perf/retention.validation.behavior.test.ts
  • packages/telemetry/src/perf/tokenUsageReader.behavior.test.ts
  • packages/telemetry/src/perf/tokenUsageReader.ts
  • packages/telemetry/src/telemetry/events/tool-events.ts
  • packages/telemetry/src/telemetry/events/toolEvents.boundaries.behavior.test.ts
  • packages/telemetry/src/telemetry/index.ts
  • packages/telemetry/src/telemetry/intervalUnion.behavior.test.ts
  • packages/telemetry/src/telemetry/intervalUnion.ts
  • packages/telemetry/src/telemetry/loggers.perf.behavior.test.ts
  • packages/telemetry/src/telemetry/loggers.ts
  • packages/telemetry/src/telemetry/sessionMetricsAggregator.ts
  • schemas/settings.schema.json
🚧 Files skipped from review as they are similar to previous changes (87)
  • packages/cli/src/config/configBuilder.ts
  • packages/cli/src/services/BuiltinCommandLoader.test.ts
  • packages/cli/src/tests/cliSessionDispatch.characterization.test.tsx
  • packages/telemetry/package.json
  • packages/cli/src/services/cliCommandApiMap.ts
  • packages/cli/src/cli.provider-init.test.ts
  • packages/core/package.json
  • packages/cli/src/ui/hooks/useMemoryMonitor.test.tsx
  • packages/cli/src/ui/containers/AppContainer/hooks/useAppInput.ts
  • packages/core/src/config/telemetrySettingsCopy.behavior.test.ts
  • packages/telemetry/index.ts
  • packages/cli/src/ui/inkRenderOptions.observer.behavior.test.ts
  • packages/core/src/config/configPerfGetters.behavior.test.ts
  • packages/cli/src/ui/hooks/agentStream/useAgentStream.ts
  • packages/cli/src/ui/containers/AppContainer/hooks/useAppBootstrap.ts
  • packages/cli/src/cli.startInteractiveUI.test.tsx
  • packages/cli/src/ui/App.tsx
  • packages/core/src/index.ts
  • packages/cli/src/services/commandApiMapCompleteness.test.ts
  • packages/cli/src/services/BuiltinCommandLoader.ts
  • packages/cli/src/ui/hooks/agentStream/useAgentStreamOrchestration.ts
  • packages/cli/src/cli.test.tsx
  • packages/cli/src/ui/hooks/agentStream/tests/overheadHarness.useSubmitQuery.test.tsx
  • packages/cli/src/ui/hooks/agentStream/tests/useAgentEventStream.defaultoff.p07.bun.tsx
  • packages/cli/src/ui/AppContainerRuntime.tsx
  • packages/cli/src/ui/hooks/agentStream/operationLifecycle.p07.contract.behavior.test.ts
  • packages/cli/src/config/settingsSchema.ts
  • packages/cli/src/ui/hooks/perf/interactivePerfRuntime.startup.behavior.test.ts
  • packages/cli/src/ui/hooks/perf/interactivePerfRuntime.behavior.test.ts
  • packages/core/src/perf/perfPhaseObserver.ts
  • packages/cli/src/ui/hooks/agentStream/useAgentEventStream.ts
  • packages/cli/src/ui/hooks/agentStream/operationLifecycle.snapshot.behavior.test.ts
  • packages/core/src/config/configConstructor.ts
  • packages/telemetry/src/telemetry/loggers.ts
  • packages/telemetry/src/telemetry/index.ts
  • packages/cli/src/ui/hooks/agentStream/tests/lifecyclePerfFixtures.ts
  • packages/cli/src/ui/commands/perfCommand.ts
  • packages/cli/src/config/perfSettingsValidation.behavior.test.ts
  • packages/telemetry/src/perf/perfPhaseObserver.behavior.test.ts
  • packages/telemetry/src/perf/perfInspect.ts
  • packages/cli/src/session/buildPerfOwner.behavior.test.ts
  • packages/cli/src/ui/hooks/agentStream/tests/useSubmitQuery.lifecycle.test.tsx
  • packages/core/src/config/config.ts
  • packages/telemetry/src/perf/perfRecordsStream.ts
  • packages/cli/src/ui/hooks/agentStream/useSubmitQuery.ts
  • packages/telemetry/src/perf/perfRecords.behavior.test.ts
  • packages/telemetry/src/perf/perfSink.roundtrip.behavior.test.ts
  • packages/telemetry/src/perf/perfSchema.boundary.behavior.test.ts
  • packages/cli/src/ui/hooks/memoryTrend/useMemoryMonitor.behavior.test.ts
  • packages/cli/src/ui/hooks/memoryTrend/memorySlope.ts
  • packages/cli/src/config/perfSettingsMerge.behavior.test.ts
  • packages/telemetry/src/telemetry/events/toolEvents.boundaries.behavior.test.ts
  • packages/cli/src/ui/hooks/memoryTrend/index.ts
  • packages/telemetry/src/perf/perfReader.join.behavior.test.ts
  • packages/telemetry/src/perf/PerfSink.ts
  • packages/cli/src/ui/hooks/agentStream/operationLifecycle.p07.behavior.test.ts
  • packages/cli/src/ui/hooks/perf/interactivePerfRuntime.ts
  • packages/providers/src/logging/attemptRecorder.ts
  • packages/telemetry/src/perf/perfRecords.ts
  • packages/telemetry/src/perf/perfRecordSize.bench.ts
  • packages/telemetry/src/perf/perfPhaseObserver.ts
  • packages/telemetry/src/telemetry/intervalUnion.behavior.test.ts
  • packages/core/src/config/telemetryConfigCopy.behavior.test.ts
  • packages/core/src/config/configBaseCore.ts
  • packages/cli/src/ui/hooks/useMemoryMonitor.ts
  • packages/cli/src/ui/hooks/agentStream/tests/useSubmitQuery.lifecycle.cancellation.bun.tsx
  • packages/core/src/utils/stdio.ts
  • packages/cli/src/ui/hooks/memoryTrend/memoryRing.ts
  • packages/telemetry/src/perf/perfDelete.ts
  • packages/cli/src/ui/hooks/agentStream/submitQueryTurnLifecycle.ts
  • packages/cli/src/ui/hooks/memoryTrend/memoryTelemetry.ts
  • packages/cli/src/ui/cliUiRuntime.ts
  • packages/telemetry/src/perf/retention.validation.behavior.test.ts
  • packages/cli/src/session/interactiveUiLifecycle.ts
  • packages/telemetry/src/telemetry/intervalUnion.ts
  • docs/telemetry-privacy.md
  • packages/cli/src/ui/hooks/memoryTrend/memoryRing.behavior.test.ts
  • packages/cli/src/ui/hooks/memoryTrend/memorySlope.behavior.test.ts
  • packages/cli/src/ui/hooks/agentStream/operationLifecycle.ts
  • packages/providers/src/tests/attemptRecorder.perf.behavior.test.ts
  • packages/telemetry/src/telemetry/events/tool-events.ts
  • packages/core/src/config/perfSettings.behavior.test.ts
  • packages/telemetry/src/perf/tokenUsageReader.ts
  • packages/telemetry/src/perf/perfReport.ts
  • packages/telemetry/src/perf/perfConsumer.ts
  • packages/core/src/config/configTypes.ts
  • packages/telemetry/src/perf/retention.ts

Comment thread packages/cli/src/session/interactiveUI.tsx
Comment thread packages/cli/src/ui/inkRenderOptions.ts
Comment thread packages/telemetry/src/perf/perfSink.failopen.behavior.test.ts
Comment thread packages/telemetry/src/perf/perfSink.retention.behavior.test.ts
Comment thread packages/telemetry/src/perf/perfSink.retention.behavior.test.ts Outdated
Comment thread packages/telemetry/src/perf/retention.eviction.behavior.test.ts
Comment thread packages/telemetry/src/perf/retention.eviction.behavior.test.ts
@github-actions

github-actions Bot commented Aug 10, 2026

Copy link
Copy Markdown
Contributor

OpenCodeReview — automatic reviews suspended

Automatic OCR reviews are suspended for this PR after 2 of 2 automatic reviews.

To get more reviews you can:

  • Check the box below to re-enable automatic reviews (resets the counter), or

  • Comment /review, /ocr, or /open-code-review to request a single review on demand.

  • Re-enable automatic reviews

@acoliver

Copy link
Copy Markdown
Collaborator Author

CodeRabbit pre-merge summary evaluation

Linked Issues warning: no code change is warranted. The warning is based on the superseded revision in the original issue body. The issue author explicitly states in the issue comments that the committed specification and plan are authoritative and records these corrections:

  • The proposed /stats llxprt-time denominator was rejected because provider, tool, client, and render intervals overlap; forcing them into a partition produces invalid or negative attribution. The accepted design replaces it with directly measured client phases, provider/tool sum and union metrics, and honest unclassified elapsed time in /perf.
  • Shared-file rotation loss is prevented structurally by exclusive per-run UUID files rather than a shared writer file, with real filesystem round-trip and concurrency coverage.
  • Compression and archive overwrite handling were cut from v1; there is no archive overwrite path in this implementation. Backward UTC-day records safely re-adopt only paths created by the same sink, with unknown-path EEXIST preserved as an ownership error.
  • The negative-mtime claim was explicitly withdrawn as a test artifact in the authoritative issue correction. Retention instead protects future-mtime artifacts according to the accepted lease rules, with non-owner behavioral coverage.

Docstring Coverage warning: no broad documentation churn is warranted solely to satisfy CodeRabbit's generated percentage. This repository does not enforce that metric as a merge gate. New public contracts and non-obvious lifecycle/failure semantics are documented, while the extensive Bun behavioral tests provide executable contract evidence. Adding repetitive docstrings to private test fixtures and self-explanatory helpers would reduce signal without addressing issue intent.

@acoliver
acoliver merged commit e4e6aa7 into main Aug 10, 2026
44 of 45 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

maintainer:e2e:ok Trusted contributor; maintainer-approved E2E run

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Measure llxprt's own performance: client-side time accounting, memory trend, and a bounded per-turn perf log

1 participant