feat(peek): rebuild the session journey in Slack (canvas + Block Kit fallback)#159
Conversation
…in for journey rendering
New read tool `render_session_journey({ sessionId, errorId? })` reuses the exact same
data path as `get_user_action_before_error` (getDb → eventsFor → userActionsBeforeError →
extractDomMutationsInWindow → getNetworkErrorsInWindow → buildCausalChain) and returns the
full CausalChain JSON. When errorId is omitted the session's latest console error is selected
automatically. When the session has no console errors a clear text message is returned (no
throw). Tool count bumped 20→21 in PEEK_MCP_TOOLS, stdio-smoke and server.test.ts.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Signed-off-by: harry-harish <22562634+harry-harish@users.noreply.github.com>
…rney interception Add optional SurfaceAdapter.renderJourney?(conversationId, journey): Promise<string> and extend interceptCallTool to intercept render_session_journey results — valid CausalChain JSON (has timeline + narrative) is handed to the adapter; its confirmation string is returned to the brain instead of the full timeline, keeping LLM context lean. Adapters without renderJourney degrade to a brief text note (no throw). share_session handling is unchanged. All 122 tests pass; strict TS build clean. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> Signed-off-by: harry-harish <22562634+harry-harish@users.noreply.github.com>
…convId degrade tests Fix 1: remove unused feedbackText variable and tautological assertion from test (a) of the render_session_journey suite — the deferred approach was replaced by (a-confirm); (a) now cleanly asserts only that adapter.renderJourney was called with (conversationId, journey). Fix 2: add test (e) — plain-text render_session_journey result (e.g. "no console errors found") passes through interceptCallTool unchanged, adapter.renderJourney NOT called, no throw. Driven through the real SdkBrain+interceptCallTool path. Fix 3: add test (f) — no active conversationId at interception time: interceptCallTool called directly (outside runLoop, #activeConversationId undefined) with a valid CausalChain JSON result → returns the brief degrade note, renderJourney NOT called, no throw. Production code (runtime.ts, surface.ts) byte-for-byte unchanged. 124 tests pass (was 122). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Signed-off-by: harry-harish <22562634+harry-harish@users.noreply.github.com>
… Block Kit fallback Signed-off-by: harry-harish <22562634+harry-harish@users.noreply.github.com>
…rJourney
- Replace canvases.create with conversations.canvases.create({ channel_id, title,
document_content }) so the canvas is channel-linked and visible to members
- Fetch clickable permalink via files.info({ file: canvas_id }) after canvas creation
- Post mrkdwn link <permalink|Session journey> instead of bare canvas ID
- Add console.warn for canvas unavailable + files.info failure (observable errors)
- Fix isJourneyCausalChain to check typeof error.message === 'string'
- Fix misleading MAX_BK_TIMELINE_ENTRIES comment (1 block/entry × 12 + ~4 fixed)
- Update tests: renamed helper to conversationsCanvasesCreate + filesInfo mock,
assert channel_id arg + files.info call + mrkdwn link in text; new test for
canvas_id:undefined → Block Kit fallback; fallback tests use new mock structure
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Signed-off-by: Harish Kumar <22562634+harry-harish@users.noreply.github.com>
…k when no permalink Signed-off-by: Harish Kumar <22562634+harry-harish@users.noreply.github.com>
Add attack-surface #10 and a full egress section for the render_session_journey canvas render path: user-initiated @peek mention as consent, derived CausalChain (not raw bundle) to a channel-linked Slack canvas (canvases:write) with Block Kit fallback, honest residual limits (Slack retention, channel visibility, capture-time masking). Verify @peekdev/mcp minor changeset render-session-journey-tool.md is correct; no connector changeset needed (private packages). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Signed-off-by: Harish Kumar <22562634+harry-harish@users.noreply.github.com>
Whole-branch review minors, fixed pre-PR: - N1: a net-level failure serializes as JSON status:null (peek-mcp emits number|null); the fallback table rendered the literal "null" instead of errorText. Use `!= null` and widen the local type to number|null. - N2: isJourneyCausalChain now validates error.level (dereferenced via .toUpperCase() by both renderers), not just error.message, keeping renderJourney total on malformed input. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Signed-off-by: Harish Kumar <22562634+harry-harish@users.noreply.github.com>
|
Warning Review limit reached
Next review available in: 37 minutes Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available. How can I continue?After more reviews become available, a review can be triggered using the To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews. How do review limits work?CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability. For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window. Please refer docs for additional details. Review details⚙️ Run configurationConfiguration used: Path: .coderabbit.yaml Review profile: CHILL Plan: Pro Plus Run ID: 📒 Files selected for processing (9)
📝 WalkthroughWalkthroughChangesAdds Session journey rendering
Estimated code review effort: 4 (Complex) | ~45 minutes Sequence Diagram(s)sequenceDiagram
participant MCPServer
participant ConnectorRuntime
participant SlackAdapter
participant SlackCanvas
participant SlackThread
MCPServer->>ConnectorRuntime: return CausalChain tool result
ConnectorRuntime->>SlackAdapter: renderJourney(conversationId, journey)
SlackAdapter->>SlackCanvas: create journey markdown canvas
SlackCanvas-->>SlackAdapter: canvas id and permalink
SlackAdapter->>SlackThread: post clickable canvas confirmation
SlackAdapter-->>ConnectorRuntime: return confirmation text
Possibly related PRs
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✨ Finishing Touches🧪 Generate unit tests (beta)
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. Comment |
There was a problem hiding this comment.
Actionable comments posted: 6
🤖 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/connector-core/src/runtime.ts`:
- Around line 236-250: Strengthen `#postProcessRenderJourney` so malformed journey
payloads cannot cause adapter.renderJourney to throw. Wrap the
adapter.renderJourney(conversationId, parsed) call in try/catch, and return the
existing “Session journey is ready, but this surface cannot render it.” fallback
when rendering fails, preserving the no-throw and no-data-leak behavior
documented for this method.
In `@packages/connector-slack/src/journey.ts`:
- Around line 279-294: Strengthen isJourneyCausalChain so malformed timeline and
networkErrors entries cannot reach journeyMarkdown or journeyBlocks and crash
cap(). Either deep-validate every entry’s required fields, including
timeline.summary and networkErrors.url, or make cap() safely handle non-string
values while preserving valid rendering; ensure renderJourney remains total for
malformed input.
- Around line 222-227: Reserve space for the uppercase error-level prefix when
truncating the headline in the header construction near `headline` and
`blocks.push`. Compute the prefix first, cap the error message to the remaining
characters so the combined `${prefix}: ${headline}` stays within Slack’s
150-character limit, and use that bounded value in the header.
In `@packages/connector-slack/src/slack-adapter.ts`:
- Around line 226-253: Guard both result-delivery chat.postMessage calls in
renderJourney: wrap the canvas confirmation post and the Block Kit fallback post
in try/catch, log delivery failures, and return a clean fallback/status string
instead of propagating Slack errors. Preserve the existing canvas-to-summary
degradation flow and ensure failures in the final fallback also resolve without
throwing.
In `@packages/peek-mcp/src/mcp/server.ts`:
- Around line 599-608: Replace the `getConsoleErrors` call in the session error
auto-selection branch with a dedicated descending query that orders console
errors by timestamp and ID, limits to one row, and selects the true latest error
before building the journey. Preserve the no-error response behavior, and add a
regression test covering sessions with more than 200 console errors.
In `@packages/peek-mcp/test/render-session-journey.test.ts`:
- Around line 229-243: Update the test case in “render_session_journey: unknown
session / no DB” to assert the specific not-found response rather than only
checking that the unknown session ID appears. Use an assertion that
distinguishes the handler’s unknown-session message from the
empty-session/no-console-errors response.
🪄 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: CHILL
Plan: Pro Plus
Run ID: 07126359-31ab-43af-9bd8-226733ee300e
📒 Files selected for processing (13)
.changeset/render-session-journey-tool.mddocs/peek/THREATMODEL.mdpackages/connector-core/src/runtime.test.tspackages/connector-core/src/runtime.tspackages/connector-core/src/surface.tspackages/connector-slack/src/journey.test.tspackages/connector-slack/src/journey.tspackages/connector-slack/src/slack-adapter.test.tspackages/connector-slack/src/slack-adapter.tspackages/peek-mcp/src/mcp/server.tspackages/peek-mcp/test/render-session-journey.test.tspackages/peek-mcp/test/server.test.tspackages/peek-mcp/test/stdio-smoke.test.ts
…, total renderers, header budget Signed-off-by: Harish Kumar <22562634+harry-harish@users.noreply.github.com>
|
Thanks — all 6 addressed in
One note on #6: peek-mcp has no session-existence concept — an unknown All suites green: peek-mcp 465, connector-core 125, connector-slack 108; @coderabbitai review |
|
Tip For best results, initiate chat on the files or code changes. 🧩 Analysis chain🏁 Script executed: #!/bin/bash
set -euo pipefail
gh pr diff 159 --repo Cubenest/rrweb-stack > /tmp/pr159.diff
wc -l /tmp/pr159.diff
echo "---commit list---"
gh pr view 159 --repo Cubenest/rrweb-stack --json commits --jq '.commits[].oid' | tail -5Length of output: 610 🏁 Script executed: #!/bin/bash
set -euo pipefail
echo "=== runtime.ts postProcessRenderJourney ==="
rg -n "postProcessRenderJourney" -A30 packages/connector-core/src/runtime.ts | head -80
echo "=== journey.ts isJourneyCausalChain ==="
rg -n "isJourneyCausalChain" -A40 packages/connector-slack/src/journey.ts | head -80
echo "=== journey.ts journeyBlocks header cap ==="
rg -n "cap\(error.message" -B5 -A5 packages/connector-slack/src/journey.ts
echo "=== slack-adapter.ts renderJourney ==="
rg -n "renderJourney" -A80 packages/connector-slack/src/slack-adapter.ts | head -150
echo "=== server.ts getLatestConsoleError ==="
rg -n "getLatestConsoleError|render_session_journey" -B3 -A40 packages/peek-mcp/src/mcp/server.ts | head -150Length of output: 20888 🏁 Script executed: #!/bin/bash
set -euo pipefail
echo "=== isTimelineEntry / isNetworkError ==="
rg -n "function isTimelineEntry|function isNetworkError" -A15 packages/connector-slack/src/journey.ts
echo "=== getLatestConsoleError query ==="
rg -n "getLatestConsoleError" -B3 -A20 packages/peek-mcp/src/mcp/queries.tsLength of output: 2584 All six fixes check out against
Nice work — all fixes are solid, well-guarded, and consistent with the existing "never throw into the tool loop" pattern used elsewhere in ヽ(•‿•)ノ [resolve] |
What
@peek rebuild the journey→ the connector reconstructs the failing session's journey (the ordered path of actions → the failure, plus DOM/network context and the narrative) and renders it as a rich, shared Slack canvas (Block Kit fallback), posting a clickable permalink into the thread.This is the connector's shared-debugging thesis in one surface: today "what failed?" returns a one-line text answer; this puts the whole path in front of the team where they already talk.
Key de-risk
peek already assembles the journey server-side —
buildCausalChain(...)returns{ timeline, narrative, error, ... }. This is primarily a rendering feature, not new data assembly. It reuses the exactget_user_action_before_errordata path.How (mirrors the shipped
share_sessionpattern)render_session_journey(sessionId, errorId?)MCP tool (@peekdev/mcp) — a dedicated read tool that reusesbuildCausalChainand auto-selects the session's latest console error whenerrorIdis omitted. Dedicated (notget_user_action_before_error) so the connector can intercept only journey results for canvas rendering.SurfaceAdapter.renderJourney?(connector-core) — the runtime intercepts an approvedrender_session_journeyresult at the callTool boundary (interceptCallTool— the same seamshare_sessionuses) and returns a short confirmation (the canvas link) to the brain, keeping the full timeline out of LLM context.renderJourney—CausalChain → canvas markdown→conversations.canvases.create({ channel_id })(channel-linked) →files.info→ post a clickable<permalink|Session journey>. On any canvas failure → Block Kit fallback (truncated timeline + narrative, capped at Slack's 50-block limit). Never posts a bare/deadcanvas_id.Egress / honesty
A journey canvas sends more derived session content (timeline, selectors, error tables) to Slack than a one-line text answer — a real widening of what leaves the local-first store, but not a raw bundle upload (that's
share_session). The explicit@peek rebuild the journeytrigger is the consent (user-initiated); no separate card in v1. THREATMODEL egress row #10 + a dedicated section document it plainly, including theshare_session-vs-journey comparison and residual limits.Review
Built subagent-driven (4 tasks, per-task spec+quality review). Whole-branch review (opus) verified 7 cross-task invariants: real inline read-path interception (not a dead branch), brain gets confirmation not raw JSON, canvas is channel-linked + clickable + never-dead-id, read tool has no consent/audit coupling, no behavior change to other tools, contract consistency, changeset + THREATMODEL. Verdict: ready. Two review Minors fixed pre-PR (
9259ba2): null-status net rows rendering literal "null"; guarderror.level.Testing
render-session-journey.test.ts— CausalChain shape, auto-select, no-error/no-DB paths; tool-count snapshots updated).renderJourney→ confirmation-not-JSON; degrade without adapter; passthrough).pnpm -r typecheckclean; build OK.Maintainer (out of code)
Add
canvases:write(+ existingchat:write) to the Slack app and reinstall to make the canvas path live; without it, it degrades to the Block Kit fallback.Changeset
One
@peekdev/mcpminor (render_session_journey). connector-core/connector-slack areprivate:true→ no changeset.🤖 Generated with Claude Code
Summary by CodeRabbit
New Features
render_session_journeytool to reconstruct a session’s timeline, narrative, errors, actions, DOM changes, and network failures.Documentation