Skip to content

fix(telemetry): emit error events at ERROR severity and declutter slash menu - #129

Merged
elkaix merged 4 commits into
mainfrom
fix/telemetry-error-severity
Jun 13, 2026
Merged

fix(telemetry): emit error events at ERROR severity and declutter slash menu#129
elkaix merged 4 commits into
mainfrom
fix/telemetry-error-severity

Conversation

@elkaix

@elkaix elkaix commented Jun 13, 2026

Copy link
Copy Markdown
Member

Description

Two related improvements bundled in this branch:

Telemetry — crash-safe error severity

  • New emit_events_to_otel() centralizes event forwarding: maps event name → OTel severity (error/warning/info), enriches canonical error.* attributes for error-like events, validates schemas, and passes explicit severity to otel.emit_log.
  • flush_sync() now detects buffered events when no sink is attached (e.g. a crash during startup before attach_sink()), backfills device_id/session_id, and emits directly to OTel so the crash event still reaches SigNoz. The queue is only cleared after a successful emit; failures are logged at debug rather than swallowed.

UI — slash command menu

  • Drops redundant [shell]/[command] scope tags, keeping only skill:/flow: kind tags.
  • Adds gap/footer chrome with a persistent navigation legend and a "+N more" overflow indicator, with terminal-adaptive height.

Checklist

  • Tests added for error-severity mapping and crash-safe flush (incl. queue-retained-on-failure).
  • Changelog updated.

elkaix added 3 commits June 13, 2026 13:34
Claude Code does not read AGENTS.md automatically. Add a CLAUDE.md that
imports the tracked repo rules (AGENTS.md) and the machine-local overlay
(AGENTS.local) so Claude Code sessions get the same guidance Pythinker
injects via PYTHINKER_AGENTS_MD.
…tributes

track("error"/"crash"/"api_error") forwarded every event to OTel logs at the
emit_log() default of INFO, so error/crash telemetry was indistinguishable from
product analytics — SigNoz severity filters and the error saved views found
nothing, and the views' error_type filter never matched (call sites flatten it
to property.error_type / property.exc_class).

- Map known error event names to severity in EventSink: error/crash/api_error ->
  ERROR, session_load_failed -> WARN; everything else stays INFO.
- Add canonical error.type/error.site/error.expected/error.kind attributes for
  error-like events so dashboards query one set of keys regardless of whether
  the call site emitted error_type or exc_class. Original property.* preserved.
- Factor OTel emission into emit_events_to_otel() and have flush_sync() drain the
  pre-sink _event_queue, so a startup crash before attach_sink() still reaches
  SigNoz (Bugsink already captured it).
- Docs: correct the stale claim that report_handled_error always calls Sentry;
  expected errors are withheld from Bugsink.

Tests cover severity mapping, canonical attributes, and crash-safe flush.
Add a blank gap line between the input row and the slash command popup,
drop the redundant [command]/[shell] tag (keeping the distinguishing
[skill]/[flow] tags), and add a persistent footer legend set off by its
own separator line. When the list overflows, the footer folds in a
'+N more' count instead of silently hiding entries; the menu height
adapts to the terminal and is capped to leave room for the chrome rows.
@coderabbitai

coderabbitai Bot commented Jun 13, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Warning

Review limit reached

@elkaix, we couldn't start this review because you've reached your PR review rate limit.

More reviews will be available in 56 minutes and 32 seconds. Learn how PR review limits work.

Your organization has used up its prepaid credits, and credit purchases are no longer available. Enable the review add-on in the billing tab to keep reviews running — you're only billed for reviews past your plan's rate limits ($0.25/file).

⌛ How to resolve this issue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans include higher PR review limits than trial, open-source, and free plans. In all cases, reviews become available again over time. During sustained high-volume PR review activity, CodeRabbit may temporarily slow when the next review becomes available.

Please see our Fair Usage Limits Policy for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: c3f4b9db-09b8-4b20-941e-398b22766fc0

📥 Commits

Reviewing files that changed from the base of the PR and between 18cf0f2 and c060c51.

📒 Files selected for processing (4)
  • src/pythinker_code/telemetry/__init__.py
  • src/pythinker_code/telemetry/sink.py
  • tests/telemetry/test_telemetry.py
  • tests/ui_and_conv/test_slash_completer.py
📝 Walkthrough

Walkthrough

This PR enhances telemetry robustness and refines the slash command menu UI. The telemetry changes add crash-safe event flushing to OpenTelemetry when no sink is attached, with severity mapping and canonical error attribute enrichment. The UI changes clean up the completion menu by removing redundant scope tags, adding fixed gap/footer chrome with overflow indicators, and adapting heights to terminal constraints.

Changes

Telemetry Crash-Safe Flush

Layer / File(s) Summary
OTel emission with error severity and canonical attributes
src/pythinker_code/telemetry/sink.py
New emit_events_to_otel() function centralizes event forwarding with event name → OTel severity mapping (error/warning/info), canonical error attribute enrichment for error-like events, schema validation, and explicit severity in otel.emit_log calls. EventSink._emit_to_otel() delegates to this function.
Crash-safe flush when no sink attached
src/pythinker_code/telemetry/__init__.py
flush_sync() detects buffered events with no attached sink, backfills device_id/session_id, and emits directly to OTel before clearing the queue.
Tests for error severity mapping and crash-safe flush
tests/telemetry/test_instrumentation.py, tests/telemetry/test_telemetry.py
Test helpers updated to capture severity in emit_log mocks. New TestErrorSeverity validates event name → severity mapping and canonical error attribute presence. New TestCrashSafeFlush confirms queue draining and OTel emission when sink is absent.

Slash Command Menu UI Layout and Filtering

Layer / File(s) Summary
Metadata tag filtering for slash commands
src/pythinker_code/ui/shell/prompt.py
SlashCommandCompleter now emits kind tags only for skill: and flow: commands, dropping redundant [shell]/[command] scope tags for plain commands.
Layout constants and height calculation
src/pythinker_code/ui/shell/prompt.py
SlashCommandMenuControl introduces gap and footer line constants. preferred_height() computes heights separately for unset vs. set complete_index, accounting for expanded meta lines and clamping by fixed chrome rows.
Content rendering with footer chrome and cursor positioning
src/pythinker_code/ui/shell/prompt.py
create_content() splits available space into item_rows, renders gap line, lays out completions within item_rows, positions cursor accounting for multi-line meta, and appends persistent footer with optional "+N more" overflow count. Window max height increased from 10 to 12.
Tests for menu layout, overflow, and meta filtering
tests/ui_and_conv/test_prompt_tips.py, tests/ui_and_conv/test_slash_completer.py
Tests adjusted for gap line at index 0. New assertions validate that plain commands omit scope tags. Preselect test rewritten for new layout. New tests cover "+N more" footer when list exceeds height and legend-only footer when list fits.

Documentation and Configuration

Layer / File(s) Summary
Changelog and Claude agent guidance
CHANGELOG.md, CLAUDE.md
Changelog entry documents cleaner slash menu with gap spacing, filtered tags, persistent footer, overflow indicator, and terminal-adaptive height. CLAUDE.md specifies agent rules: AGENTS.md is authoritative; AGENTS.local adds detail without weakening rules.

Sequence Diagram

sequenceDiagram
  participant flush_sync as flush_sync()
  participant queue as Event Queue
  participant sink as EventSink
  participant emit_otel as emit_events_to_otel()
  participant otel as otel.emit_log()
  flush_sync->>sink: check if attached
  alt No sink, queued events exist
    flush_sync->>queue: backfill device_id/session_id
    flush_sync->>emit_otel: emit_events_to_otel(events)
    emit_otel->>emit_otel: map event name to severity
    emit_otel->>emit_otel: enrich error.* attributes
    emit_otel->>emit_otel: validate schemas
    emit_otel->>otel: emit_log with severity
    otel-->>emit_otel: ✓
    flush_sync->>queue: clear
  else Sink attached
    flush_sync->>sink: _sink.flush_sync()
    sink-->>flush_sync: ✓
  else Queue empty
    flush_sync-->>flush_sync: no-op
  end
  flush_sync->>flush_sync: sentry.flush / otel.shutdown
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Suggested labels

bug, enhancement

🚥 Pre-merge checks | ✅ 3 | ❌ 2

❌ Failed checks (1 warning, 1 inconclusive)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 28.57% which is insufficient. The required threshold is 70.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
Description check ❓ Inconclusive Description covers both improvements with technical details, but linked issue is missing and checklist items are unchecked despite work being completed. Link the related issue number (e.g., 'Resolve #XYZ') and ensure checklist reflects completed work (mark boxes as done where applicable).
✅ Passed checks (3 passed)
Check name Status Explanation
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Title check ✅ Passed Title follows conventional commits format with valid type and scope; clearly describes the two main changes (telemetry error severity handling and slash menu UI improvements).

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/telemetry-error-severity

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

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

@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: 3

🤖 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 `@src/pythinker_code/telemetry/__init__.py`:
- Around line 209-218: The blanket with suppress(Exception) around the
import/backfill/emit block should be replaced with explicit try/except so errors
aren’t silently swallowed: wrap the import of pythinker_code.telemetry.sink and
the loop that backfills device/session ids plus the call to
emit_events_to_otel(...) in a try block, catch Exception as e, log the exception
(at debug/error level) with context, and only clear _event_queue on successful
emit; also ensure you still perform the backfill of _device_id/_session_id
inside the try before calling emit_events_to_otel and do not lose the original
events on failure (leave the queue intact or persist as intended). Reference
symbols: suppress(Exception) (remove), emit_events_to_otel, _event_queue,
_device_id, _session_id.

In `@src/pythinker_code/telemetry/sink.py`:
- Around line 105-108: The try/except around "from pythinker_code.telemetry
import otel as _otel" silently swallows import failures; before returning, log
the exception with context (e.g., get a module logger via
logging.getLogger(__name__) or use the existing module logger) and emit a debug
or warning message including exc_info=True so the import/init error is recorded,
then return as before.

In `@tests/ui_and_conv/test_slash_completer.py`:
- Around line 407-408: Replace the confusable literal glyph ❯ in the assertions
with an explicit escaped code-point constant (e.g. RIGHT_ARROW = "\u276F" or use
the \N{name} escape) and use that constant in the two asserts that reference
rendered_lines (replace the literal in assert "❯" in rendered_lines[1] and
assert "❯" not in rendered_lines[2] with assert RIGHT_ARROW in rendered_lines[1]
and assert RIGHT_ARROW not in rendered_lines[2]); this keeps the intent via the
rendered_lines variable while satisfying the linter RUF001.
🪄 Autofix (Beta)

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: ASSERTIVE

Plan: Pro Plus

Run ID: 4d4f4501-dd43-4644-ac66-37f0622d2243

📥 Commits

Reviewing files that changed from the base of the PR and between 58c710c and 18cf0f2.

⛔ Files ignored due to path filters (1)
  • docs/en/reference/telemetry.md is excluded by !docs/**
📒 Files selected for processing (9)
  • CHANGELOG.md
  • CLAUDE.md
  • src/pythinker_code/telemetry/__init__.py
  • src/pythinker_code/telemetry/sink.py
  • src/pythinker_code/ui/shell/prompt.py
  • tests/telemetry/test_instrumentation.py
  • tests/telemetry/test_telemetry.py
  • tests/ui_and_conv/test_prompt_tips.py
  • tests/ui_and_conv/test_slash_completer.py

Comment thread src/pythinker_code/telemetry/__init__.py Outdated
Comment thread src/pythinker_code/telemetry/sink.py
Comment thread tests/ui_and_conv/test_slash_completer.py Outdated
@codecov

codecov Bot commented Jun 13, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 78.57143% with 18 lines in your changes missing coverage. Please review.

Files with missing lines Patch % Lines
src/pythinker_code/ui/shell/prompt.py 76.74% 10 Missing ⚠️
src/pythinker_code/telemetry/sink.py 80.48% 6 Missing and 2 partials ⚠️

📢 Thoughts on this report? Let us know!

Address CodeRabbit review feedback on PR #129:

- flush_sync(): replace the blanket `suppress(Exception)` around the
  pre-sink queue drain with an explicit try/except that logs at debug
  and only clears `_event_queue` after a successful emit, so a failed
  hand-off no longer silently drops startup-crash telemetry.
- emit_events_to_otel(): log the OTel import failure (with event count)
  before dropping events instead of returning silently.
- test_slash_completer: build the selection marker via chr(0x276F)
  instead of the literal glyph to satisfy the ambiguous-character lint.
- Add a test asserting the queue is retained when the crash-safe emit
  raises.
@elkaix elkaix changed the title Fix/telemetry error severity fix(telemetry): emit error events at ERROR severity and declutter slash menu Jun 13, 2026
@elkaix
elkaix merged commit c4caa99 into main Jun 13, 2026
33 checks passed
@elkaix
elkaix deleted the fix/telemetry-error-severity branch June 13, 2026 19:48
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant