feat(dashboard): show host provenance for sessions and learnings#129
Conversation
|
Note Reviews pausedIt 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 Use the following commands to manage reviews:
Use the checkboxes below for quick actions:
📝 WalkthroughWalkthroughHost attribution and request lineage now flow through event recording, publishing, session aggregation, and dashboard rendering. Skills and preferences display scopes, application status, and host provenance derived from session request mappings. ChangesHost attribution and lineage
Estimated code review effort: 4 (Complex) | ~60 minutes Sequence Diagram(s)sequenceDiagram
participant Runtime
participant EventHooks
participant SessionReader
participant SkillsPage
Runtime->>EventHooks: attribution_host()
EventHooks->>SessionReader: session JSONL with host and request_id
SessionReader->>SkillsPage: SessionSummary host and request_ids
SkillsPage->>SkillsPage: derive skill scope and host provenance
sequenceDiagram
participant SessionPage
participant SessionReader
participant HostBadge
SessionPage->>SessionReader: load session detail
SessionReader-->>SessionPage: host and learning_interaction_count
SessionPage->>HostBadge: render host metadata
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.
🧹 Nitpick comments (1)
tests/test_events.py (1)
2058-2073: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winTest isolation:
runtime.set_host()leaks global state across tests.
runtime.set_host()mutates the module-level_current_hostglobal and setsos.environ[HOST_ENV]directly (not viamonkeypatch.setenv). Neither is cleaned up after each test. While every host-stamping test currently callsset_host()explicitly, any future test in this file that relies onruntime.host()defaulting toHOST_CLAUDE_CODEwill inherit the last test's host instead.Consider adding an autouse fixture that resets
runtime._current_host = Noneand restoresHOST_ENVafter each test.♻️ Proposed fixture for test isolation
+# Add to conftest.py or the top of test_events.py + +@pytest.fixture(autouse=True) +def _reset_runtime_host(monkeypatch): + """Reset runtime host state after each test to prevent cross-test leakage.""" + import claude_smart.runtime as runtime + monkeypatch.setattr(runtime, "_current_host", None) + yieldAlso applies to: 2076-2090, 2093-2111, 2114-2135
🤖 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 `@tests/test_events.py` around lines 2058 - 2073, Add an autouse fixture in tests/test_events.py that snapshots the current HOST_ENV value and resets runtime._current_host before and after each test, restoring or removing HOST_ENV afterward as appropriate. Apply it to the host-stamping tests so runtime.host() returns the default HOST_CLAUDE_CODE for tests that do not call runtime.set_host().
🤖 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.
Nitpick comments:
In `@tests/test_events.py`:
- Around line 2058-2073: Add an autouse fixture in tests/test_events.py that
snapshots the current HOST_ENV value and resets runtime._current_host before and
after each test, restoring or removing HOST_ENV afterward as appropriate. Apply
it to the host-stamping tests so runtime.host() returns the default
HOST_CLAUDE_CODE for tests that do not call runtime.set_host().
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro Plus
Run ID: 06cf1717-22a2-4964-9ccd-d43f40bf29cb
📒 Files selected for processing (15)
plugin/dashboard/app/dashboard/page.tsxplugin/dashboard/app/sessions/[sessionId]/page.tsxplugin/dashboard/app/sessions/page.tsxplugin/dashboard/components/common/host-badge.tsxplugin/dashboard/components/common/page-header.tsxplugin/dashboard/lib/session-reader.tsplugin/dashboard/lib/types.tsplugin/src/claude_smart/events/post_tool.pyplugin/src/claude_smart/events/session_end.pyplugin/src/claude_smart/events/stop.pyplugin/src/claude_smart/events/user_prompt.pyplugin/src/claude_smart/reflexio_adapter.pyplugin/src/claude_smart/runtime.pytests/test_adapter.pytests/test_events.py
|
Given Claude-smart uses Reflexio User for each "host" (need to validate), does it make sense to reuse and surface User as Host on dashboard? This wouldn't require us to introduce another Host concept and also would help with surfacing playbook's "host" with User |
2cc6d6b to
51240a7
Compare
51240a7 to
64f17ce
Compare
Previous sanitized UI validationSuperseded by the updated placeholder-only screenshots after the value-first UI hierarchy change. |
Return confirmed request lineage with each publish result, preserve raw-client compatibility, and load skill attribution without serial dashboard latency.
Feedback resolutionThe original User vs Host question is resolved in favor of keeping host as a claude-smart-local attribution fact. Claude review disposition:
The CodeRabbit host-state isolation nit is also covered: the affected event tests use Validation on the final commit: 675 Python tests passed, Ruff passed, dashboard ESLint + TypeScript + production build passed, and the six host/install integration paths affected by the publish-result contract passed. The UI screenshots above use placeholder-only fixtures and no private session data. |
Collapse duplicate runtime state, localize one-caller join logic, and remove repeated UI and publish capability decisions without changing attribution behavior.
Simplification passApplied the lean parts of the follow-up review without changing behavior:
I intentionally kept host stamping explicit at the four interaction producers. Adding another record-construction interface would broaden this Phase 1 change and would not reduce the caller contract enough to earn the extra seam. Result: one fewer module, one fewer runtime state variable, a narrower runtime interface, and fewer repeated decisions. Final validation remains 675 tests passed, Ruff/ESLint/TypeScript/dashboard build passed, and all GitHub checks are green on |
Use verified Claude, Codex, and OpenCode product colors with WCAG-AA label contrast across light and dark themes.
Native host colorsUpdated the shared host badge using colors verified from the current official desktop assets:
Accessibility: the small badge labels use dark brown on Claude orange (5.65:1), white on Codex blue (5.39:1), and white on OpenCode black (18.93:1). OpenCode also gets a light boundary in dark mode so the black fill remains visually distinct. The existing sanitized-validation comment now contains freshly uploaded 1440×1000 Sessions and Project Skills screenshots generated from placeholder-only fixtures. No private session or Reflexio data was loaded. Dashboard lint, TypeScript, production build, light/dark visual checks, and all 9 GitHub checks pass on |
Prioritize applied learnings, keep host badges on sessions, and present project-skill host as origin rather than applicability.
Carry applied-learning counts onto session detail, label shared applicability scope explicitly, and centralize host attribution metadata and joins.
Code reviewNo issues found. Checked for bugs and CLAUDE.md compliance. 🤖 Generated with Claude Code |
Code review — additional findings (advisory)5 real findings below the automated confidence threshold (scored 75–78/100). Not false positives, but lower severity or edge-case:
claude-smart/plugin/dashboard/lib/session-reader.ts Lines 29 to 32 in da6366c
claude-smart/plugin/src/claude_smart/runtime.py Lines 25 to 47 in da6366c
claude-smart/plugin/src/claude_smart/events/user_prompt.py Lines 57 to 63 in da6366c
claude-smart/plugin/dashboard/app/skills/page.tsx Lines 148 to 165 in da6366c
🤖 Generated with Claude Code - If this code review was useful, please react with 👍. Otherwise, react with 👎. |
Strip dashboard-only host metadata from Reflexio interactions, ignore malformed host values when a later valid record exists, and let project-skill content render independently of attribution.
|
Follow-up on the five advisory findings against the current head (
Final local checks and the refreshed GitHub matrix are green; CodeRabbit completed with no new actionable thread. |
There was a problem hiding this comment.
Actionable comments posted: 2
🧹 Nitpick comments (1)
plugin/dashboard/lib/host-attribution.ts (1)
25-50: 🩺 Stability & Availability | 🔵 Trivial | ⚡ Quick winAdd a fetch timeout to prevent indefinite hanging.
The
fetch("/api/sessions")call has no timeout. If the endpoint hangs,attributionstaysnullforever — pages never show the "unavailable" banner or host provenance. AnAbortControllerwith a timeout ensures the error path fires and users see the appropriate fallback UI.⏱️ Proposed fix: add AbortController timeout
export function useRequestHostAttribution(): RequestHostAttribution | null { const [attribution, setAttribution] = useState<RequestHostAttribution | null>(null); useEffect(() => { let cancelled = false; + const controller = new AbortController(); + const timeout = setTimeout(() => controller.abort(), 5000); + fetch("/api/sessions", { cache: "no-store" }) + .then(async (response) => { if (!response.ok) throw new Error(`sessions ${response.status}`); const data = await response.json(); return (data.sessions ?? []) as SessionSummary[]; }) .then((sessions) => { if (!cancelled) { setAttribution({ hosts: hostsByRequestId(sessions), unavailable: false, }); } }) .catch(() => { if (!cancelled) { setAttribution({ hosts: new Map(), unavailable: true }); } }) + .finally(() => { + clearTimeout(timeout); + }); return () => { cancelled = true; + controller.abort(); + clearTimeout(timeout); }; }, []); return attribution; }🤖 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 `@plugin/dashboard/lib/host-attribution.ts` around lines 25 - 50, Update the sessions-loading useEffect around fetch("/api/sessions") to create an AbortController and abort the request after a timeout, passing its signal to fetch so hung requests enter the existing catch fallback. Clear the timeout and preserve the existing cancelled cleanup behavior to avoid updates after unmount.
🤖 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 `@plugin/dashboard/app/preferences/`[id]/page.tsx:
- Around line 344-354: Update the “Origin” Meta rendering guard in the
preferences page to require both attribution and
profile.generated_from_request_id. Preserve the existing LearningHostProvenance
props and hide the row whenever the request ID is falsy, matching the skills
detail page behavior.
In `@plugin/dashboard/app/skills/project/`[id]/page.tsx:
- Around line 351-361: Update the Origin Meta render guard in the playbook
details JSX to require both attribution and playbook.request_id, matching the
existing Request CopyMeta behavior; keep the LearningHostProvenance props and
rendering unchanged.
---
Nitpick comments:
In `@plugin/dashboard/lib/host-attribution.ts`:
- Around line 25-50: Update the sessions-loading useEffect around
fetch("/api/sessions") to create an AbortController and abort the request after
a timeout, passing its signal to fetch so hung requests enter the existing catch
fallback. Clear the timeout and preserve the existing cancelled cleanup behavior
to avoid updates after unmount.
🪄 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: defaults
Review profile: CHILL
Plan: Pro Plus
Run ID: bd93b4d3-46df-4880-9940-ca48455f2823
📒 Files selected for processing (7)
plugin/dashboard/app/preferences/[id]/page.tsxplugin/dashboard/app/preferences/page.tsxplugin/dashboard/app/skills/page.tsxplugin/dashboard/app/skills/project/[id]/page.tsxplugin/dashboard/components/common/host-badge.tsxplugin/dashboard/components/common/learning-application-badge.tsxplugin/dashboard/lib/host-attribution.ts
Code review — iteration 2 (host SHA: 8020c62)Validated the claim: preferences, project skills, and sessions all get host provenance; shared skills are correctly excluded (no attribution possible for aggregated-multi-source playbooks). The Three findings (one new, two carry-forward): 1. (New) The fix commit ( Fix: add the same guard as the detail page. // before
{attribution && !attribution.unavailable && (
// after
{attribution && !attribution.unavailable && p.generated_from_request_id && (claude-smart/plugin/dashboard/app/preferences/page.tsx Lines 199 to 207 in 8020c62 2. (Carry-forward) Each of the four pages that calls this hook fires an independent unbounded claude-smart/plugin/dashboard/lib/host-attribution.ts Lines 25 to 50 in 8020c62 3. (Carry-forward)
claude-smart/plugin/src/claude_smart/runtime.py Lines 29 to 47 in 8020c62 🤖 Generated with Claude Code - If this code review was useful, please react with 👍. Otherwise, react with 👎. |
Latest review feedbackAddressed the actionable Claude Code and CodeRabbit findings on
The clean-context simplification pass also removed the tooltip-only Validation: 675 tests passed; Ruff, dashboard ESLint, TypeScript, and production build passed. Screenshots remain current because these changes affect only missing-ID/error behavior and hidden tooltip copy. |






Summary
Show the local agent host that produced a session, then reuse that evidence as quiet generation provenance for project-specific skills and preferences.
Host answers where the recorded request ran. It does not restrict where a learning applies, replace Reflexio lineage, or imply that one host supplied every input to a consolidated learning.
Before / changed / after
Generated via …provenance without implying host-specific applicability.source,user_id,agent_version, and Reflexio lineage keep their existing meanings.UI hierarchy
Generated via …is secondary provenance. Integration identity remains a separate field.Host enrichment is non-blocking. Sessions, skills, and preferences render without waiting for attribution, and an attribution failure does not make any primary content unavailable. Local attribution scans are aborted when their page unmounts and time out after five seconds. Project skills and preferences share the same request-to-host lookup and application-value UI.
Data and compatibility
unknownare accepted. Malformed host values are ignored so a later valid record can establish the session host.hostis stripped from the wire projection and never sent in Reflexio interactions.source="claude-smart"remains integration identity and is sent only when the installed client supports that keyword.user_id; shared-skill filtering continues to useagent_version.Known limitation: learning provenance currently reads all local session summaries to build the request-to-host map. A dedicated index or cache can be added separately if real session volume makes that scan material.
Scope
This is Phase 1, and it is the only phase considered in this PR: local host attribution and dashboard presentation.
Cross-agent/global playbook lineage, changes to retrieval or consolidation, and future changes to how
sourceis set are out of scope and will be discussed separately.Validation
invalid → Codex), applied-learning priority, session detail, project-skill and preference list/detail provenance, shared skills remaining host-free, attribution failure isolation, dark/light themes, and desktop/narrow layouts.View sanitized validation screenshots