Skip to content

[luv-296] fix: populate Transcript field across all harnesses#294

Merged
NiveditJain merged 2 commits intomainfrom
luv-296
May 5, 2026
Merged

[luv-296] fix: populate Transcript field across all harnesses#294
NiveditJain merged 2 commits intomainfrom
luv-296

Conversation

@NiveditJain
Copy link
Copy Markdown
Member

@NiveditJain NiveditJain commented May 5, 2026

Summary

  • Activity dashboard's Transcript field rendered for nearly every non-Claude row because only Claude reliably puts transcript_path on the hook stdin. Codex / Copilot / Cursor don't include one, the OpenCode + Pi shims don't forward one, and Gemini coverage is uneven across versions.
  • New src/hooks/resolve-transcript-path.ts mirrors the existing resolve-permission-mode.ts dispatch pattern: trust stdin first, fall back to the per-CLI find*Transcript(sessionId) helper in lib/<cli>-sessions.ts. Runtime typeof guards on both parsed.transcript_path and sessionId so a malformed JSON payload can't propagate a non-string value into downstream string operations. OpenCode (transcripts in SQLite, no on-disk file) gets a synthetic opencode-db://<sessionId> marker so the field is non-empty and distinguishable from a genuine miss; both detail panels render a muted (stored in opencode DB) suffix when the value carries that scheme.
  • Handler-side fallback covers OpenCode + Pi without touching their shims (no duplicated disk-walk; the Pi shim already discovers session IDs from disk via discoverPiSessionId).
CLI Before After
claude path from stdin path from stdin (unchanged)
codex ~/.codex/sessions/<YYYY>/<MM>/<DD>/...
copilot ~/.copilot/session-state/<id>/events.jsonl
cursor ~/.cursor/projects/.../<id>.jsonl
pi ~/.pi/agent/sessions/.../<id>.jsonl
gemini mixed stdin first, then ~/.gemini/tmp/.../<id>.json
opencode opencode-db://<id> + "(stored in opencode DB)"

Test plan

  • bun run test:run — 1483/1483 unit tests pass (was 1457; +26 new in __tests__/hooks/resolve-transcript-path.test.ts covering stdin passthrough × 7 CLIs, missing-sessionId × 7, per-CLI fallback dispatch × 7, stdin-precedence-beats-fallback, plus 3 runtime-type-guard cases)
  • bun run test:e2e — 290/290 e2e tests pass
  • bun run build — clean Next.js + dist build
  • bun run lint — only the pre-existing unrelated <img> warning in app/components/log-viewer/tool-input-output.tsx
  • Open /policies activity tab, expand a Copilot row, confirm Transcript: <real path> appears instead of Transcript: —

🤖 Generated with Claude Code

Summary by CodeRabbit

  • Bug Fixes

    • Fixed transcript path resolution in the Activity dashboard to correctly handle multiple integration types and ensure stdin-provided paths take precedence over fallback discovery.
  • New Features

    • Added visual indicator "(stored in opencode DB)" for transcripts stored in the OpenCode database.
  • Tests

    • Expanded test coverage for transcript resolution across integration types and edge cases.
  • Documentation

    • Updated changelog with the new fix.

…l harnesses

Only Claude reliably puts transcript_path on hook stdin; Codex/Copilot/
Cursor don't, the OpenCode and Pi shims don't forward one, and Gemini
coverage is uneven across versions. The dashboard rendered "Transcript: —"
for nearly every non-Claude row even though every harness except OpenCode
has the transcript on disk and the repo already had per-CLI find*Transcript
helpers in lib/.

New src/hooks/resolve-transcript-path.ts mirrors the existing
resolve-permission-mode.ts dispatch pattern: trust stdin first, fall back
to the per-CLI helper when sessionId is known. OpenCode (DB-backed)
synthesizes opencode-db://<sessionId> so the field is non-empty and
distinguishable from a genuine miss; both detail panels render a muted
"(stored in opencode DB)" suffix for that scheme.

The handler-side fallback covers OpenCode and Pi without touching their
shims (no duplicated disk-walk).

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 5, 2026

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 75f8e4f4-3150-4f04-bbbc-98d1f6e5541c

📥 Commits

Reviewing files that changed from the base of the PR and between 5e87e5f and b66976e.

📒 Files selected for processing (2)
  • __tests__/hooks/resolve-transcript-path.test.ts
  • src/hooks/resolve-transcript-path.ts
🚧 Files skipped from review as they are similar to previous changes (2)
  • tests/hooks/resolve-transcript-path.test.ts
  • src/hooks/resolve-transcript-path.ts

📝 Walkthrough

Walkthrough

Centralizes transcript-path resolution via a new resolveTranscriptPath(integration, parsed, sessionId) helper with stdin precedence, per-CLI discovery fallbacks, and a synthetic opencode-db://<sessionId> marker; handler and UI were updated and a 23-case test matrix was added to verify behavior.

Changes

Transcript Path Resolution & OpenCode Marker Support

Layer / File(s) Summary
Data / Inputs
src/hooks/resolve-transcript-path.ts
Reads parsed.transcript_path and validates its runtime type; requires a non-empty sessionId for fallback discovery.
Core Implementation
src/hooks/resolve-transcript-path.ts
New exported resolveTranscriptPath(integration, parsed, sessionId) that returns stdin path when present; otherwise dispatches by integration to findCodexTranscript/findCopilotTranscript/findCursorTranscript/findPiTranscript/findGeminiTranscript, returns undefined for claude, and synthesizes opencode-db://<sessionId> for opencode.
Handler Integration
src/hooks/handler.ts
Imports and calls resolveTranscriptPath(cli, parsed, sessionId) to set session.transcriptPath instead of using parsed.transcript_path directly.
UI Display Updates
app/components/session-hooks-panel.tsx, app/policies/hooks-client.tsx
Transcript display now conditionally appends (stored in opencode DB) when item.transcriptPath starts with opencode-db:// (null-safe check).
Tests / Changelog
__tests__/hooks/resolve-transcript-path.test.ts, CHANGELOG.md
Adds a Vitest suite covering 23 cases (stdin passthrough, missing sessionId, per-CLI fallback, opencode marker, runtime type-guards, stdin precedence) and documents the fix in CHANGELOG.md.

Sequence Diagram

sequenceDiagram
    participant Handler as Handler
    participant ResolveFunc as resolveTranscriptPath
    participant Stdin as Stdin Input
    participant CliHelper as CLI Helper (e.g., findCopilotTranscript)
    participant UI as UI Components

    Handler->>ResolveFunc: resolveTranscriptPath(cli, parsed, sessionId)
    ResolveFunc->>Stdin: Check parsed.transcript_path
    alt Stdin provides path (string)
        Stdin-->>ResolveFunc: Return path verbatim
    else Stdin missing or non-string
        ResolveFunc->>ResolveFunc: Validate sessionId
        alt sessionId missing/invalid
            ResolveFunc-->>Handler: Return undefined
        else sessionId present
            alt integration == "opencode"
                ResolveFunc-->>Handler: Return opencode-db://<sessionId>
            else integration has disk helper
                ResolveFunc->>CliHelper: find*Transcript(sessionId)
                CliHelper-->>ResolveFunc: Return path or null
                ResolveFunc-->>Handler: Return discovered path or undefined
            end
        end
    end
    Handler->>UI: Provide session.transcriptPath
    UI->>UI: Check for "opencode-db://" prefix
    alt Prefix present
        UI-->>UI: Append "(stored in opencode DB)" note
    end
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~22 minutes

Poem

🐰 I hopped to find the transcript trail,
stdin first, then helpers without fail—
OpenCode hides within a DB mark,
a tiny suffix lights the dark.
Hooray for paths resolved with care!

🚥 Pre-merge checks | ✅ 3 | ❌ 2

❌ Failed checks (1 warning, 1 inconclusive)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 50.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
Description check ❓ Inconclusive The description provides a comprehensive summary of the problem, solution, and test results. However, it lacks formal sections matching the required template (Type of Change and Checklist). Reorganize the description to follow the repository template with explicit 'Type of Change' section (mark Bug fix) and 'Checklist' section documenting which tests passed and which remain pending.
✅ Passed checks (3 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly identifies the main fix: populating the Transcript field across all CLI harnesses, which is the primary objective of this changeset.
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.

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

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch

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

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 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/hooks/resolve-transcript-path.ts`:
- Around line 46-48: The code currently casts parsed.transcript_path to string
and returns it without runtime validation; change this by validating types at
runtime: check that parsed.transcript_path exists and typeof
parsed.transcript_path === "string" (and optionally non-empty after trim) before
assigning/returning stdinPath, and likewise ensure sessionId is a string before
using it (do not rely on TypeScript cast). Update the logic around the stdinPath
variable and the sessionId check in resolve-transcript-path.ts to gate returns
on these runtime checks and handle non-string values by returning undefined or
normalizing to a safe string.
🪄 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: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 576b5c5e-3b82-439a-a022-ee7bc86340b8

📥 Commits

Reviewing files that changed from the base of the PR and between e88fd44 and 5e87e5f.

📒 Files selected for processing (6)
  • CHANGELOG.md
  • __tests__/hooks/resolve-transcript-path.test.ts
  • app/components/session-hooks-panel.tsx
  • app/policies/hooks-client.tsx
  • src/hooks/handler.ts
  • src/hooks/resolve-transcript-path.ts

Comment thread src/hooks/resolve-transcript-path.ts Outdated
Per CodeRabbit review on PR #294: parsed.transcript_path comes from raw
JSON, so a non-string value (e.g. 42, null, an object) would silently
propagate to the activity record and break downstream string operations
(item.transcriptPath?.startsWith("opencode-db://") would throw on a
non-string-or-nullish value).

Tighten the entry guard from `as string | undefined` cast to a runtime
typeof check, and reject empty-string sessionIds explicitly so they
short-circuit before dispatching to per-CLI find*Transcript helpers.

Adds 3 regression tests covering non-string transcript_path passthrough,
empty-string sessionId, and non-string sessionId.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
@NiveditJain NiveditJain merged commit 5ac5312 into main May 5, 2026
9 checks passed
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