Skip to content

Comments

feat(web): ui/ux cleanup#143

Open
skulldogged wants to merge 9 commits intospacedriveapp:mainfrom
skulldogged:ui-ux-improvements
Open

feat(web): ui/ux cleanup#143
skulldogged wants to merge 9 commits intospacedriveapp:mainfrom
skulldogged:ui-ux-improvements

Conversation

@skulldogged
Copy link
Contributor

Summary

Polishes the web UI across channels, skills, and overview pages, and fixes a few backend/API issues that were causing incorrect behavior in pagination and registry data.

Changes

  • Channels

    • Fixed branch/worker cards so collapsed text truncates correctly and expanded text wraps cleanly.
    • Improved branch/worker row alignment.
    • Fixed “load older messages” behavior (debounce + dedupe) and corrected backend timestamp pagination comparison.
  • Skills

    • Improved installed/browse card layout and alignment.
    • Updated installed-state action in browse cards (checkmark -> delete on hover/click).
    • Switched delete controls to neutral outline style.
    • Moved “Install from GitHub” section above the infinite list.
    • Added registry description support and surfaced total registry skill count.
  • Overview

    • Added/fixed edit links for routing, identity cards, memory, and configuration.
    • Improved activity heatmap/card sizing.
    • Updated memory donut card (title/center total) and fixed tooltip layering/tooltip sizing.
  • Shared UI

    • Standardized cortex chat toggle button styling across Channel, Memories, and Cortex pages (neutral style).
  • Backend

    • Fixed compile issues in browser tool URL parsing and API auth middleware extractor.
    • Added registry total passthrough and description enrichment fallback in skills API.

Testing

  • cargo check
  • npm run build

@bilawalriaz
Copy link
Contributor

I already have a PR for "Fixed compile issues in browser tool URL parsing and API auth middleware extractor" here

@coderabbitai
Copy link

coderabbitai bot commented Feb 24, 2026

Walkthrough

Adds optional description/total fields to registry interfaces, enables className on Markdown, deduplicates channel history merges, refactors multiple Agent/Channel UIs, implements cached on-demand GitHub SKILL.md enrichment, and changes SQL timestamp comparison to use datetime().

Changes

Cohort / File(s) Summary
API Interface Extensions
interface/src/api/client.ts
Added optional description?: string to RegistrySkill and total?: number to RegistryBrowseResponse.
Markdown Component
interface/src/components/Markdown.tsx
Added optional className prop and applied it alongside the base "markdown" class on the container.
Channel Live State Hook
interface/src/hooks/useChannelLiveState.ts
Added itemKey() helper; changed history merge to deduplicate incoming timeline items and prepend only new items; hasMore now derived from deduplication results.
Agent Detail UI
interface/src/routes/AgentDetail.tsx
Refactored Memory/Model Routing/Configuration layouts to flex/grid; added per-item Edit links; replaced 7x24 heatmap with CSS grid; MemoryDonut shows total overlay; identity items get per-tab edits and layout adjustments.
Agent Skills UI & State
interface/src/routes/AgentSkills.tsx
Updated InstalledSkill and RegistrySkillCard props/UI (added onRemove, isRemoving); switched installed tracking Set→Map; added GitHub install form; surfaced total registry count; refined install/remove icon/state.
Channel Detail UI & Loading
interface/src/routes/ChannelDetail.tsx
Adjusted branch/worker item truncation/wrapping; preserved whitespace in Markdown; added lastLoadMoreAtRef and 800ms throttling for onLoadMore; strengthened intersection checks.
Backend Skills Enrichment
src/api/skills.rs
Added description: Option<String> to RegistrySkill and total: Option<u64> to RegistryBrowseResponse. Implemented cached async enrichment: fetch SKILL.md from GitHub across branches/paths, parse short descriptions, insert into cache, and populate responses.
Data Layer Timestamp Handling
src/conversation/history.rs
Changed SQL timestamp comparison to datetime(timestamp) < datetime(?3) in load_channel_timeline.

Sequence Diagram(s)

sequenceDiagram
    participant Client as Client
    participant API as Backend API
    participant Cache as Description Cache
    participant GitHub as GitHub Repo

    Client->>API: registry_browse / registry_search
    activate API
    API->>Cache: lookup descriptions for skill keys
    alt cached
        Cache-->>API: cached descriptions
    else not cached
        API->>GitHub: fetch SKILL.md candidates (owner/repo, branches)
        activate GitHub
        GitHub-->>API: SKILL.md content or 404
        deactivate GitHub
        API->>API: parse Markdown -> extract description
        API->>Cache: store description
    end
    API->>API: attach description & total to response
    API-->>Client: RegistryBrowseResponse (with description/total)
    deactivate API
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Poem

🐰 I hopped to parse SKILL.md from far away trees,

Caching the crumbs and tidying UI with breeze.
I dedup the tales and stretch classes with care,
Nibble bugs, stitch layouts, fetch descriptions to share —
Tiny paws, big changes, happy rabbit repairs!

🚥 Pre-merge checks | ✅ 1 | ❌ 2

❌ Failed checks (1 warning, 1 inconclusive)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 42.31% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
Title check ❓ Inconclusive The title is very generic and vague, using 'cleanup' without specifying what was cleaned up or which area is most important. Replace 'cleanup' with more specific details about the main changes, such as 'fix message pagination and improve skills/channel UI' to better convey the primary scope.
✅ Passed checks (1 passed)
Check name Status Explanation
Description check ✅ Passed The description comprehensively covers all major changes in the PR across channels, skills, overview, shared UI, and backend, directly addressing the changeset.

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

✨ Finishing Touches
  • 📝 Generate docstrings (stacked PR)
  • 📝 Generate docstrings (commit on current branch)
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

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.

Copy link

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

🧹 Nitpick comments (3)
interface/src/routes/ChannelDetail.tsx (1)

321-331: Minor indentation inconsistency.

The cortex toggle wrapper <div> at line 321 appears to break from the surrounding indentation level (it's inside the <div className="ml-auto flex items-center gap-3"> but doesn't share its indent). Not a functional issue, but it makes the JSX structure harder to read at a glance.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@interface/src/routes/ChannelDetail.tsx` around lines 321 - 331, Indentation
of the cortex toggle wrapper div in ChannelDetail.tsx is inconsistent; adjust
the JSX so the <div className="flex overflow-hidden rounded-md border
border-app-line bg-app-darkBox"> (the cortex toggle wrapper containing the
Button that calls setCortexOpen and reads cortexOpen) is indented to match the
surrounding <div className="ml-auto flex items-center gap-3"> children, keeping
the Button and its props (onClick, variant, size, className, title) aligned with
the other sibling elements for consistent JSX formatting.
src/api/skills.rs (2)

256-263: Description enrichment adds latency to every browse/search response.

enrich_registry_descriptions runs inline in both registry_browse and registry_search handlers, blocking the response until all GitHub fetches complete (up to 3s timeout × 4 candidate paths per skill, though parallelized). First-time page loads with many uncached skills will be noticeably slow.

Consider enriching descriptions asynchronously (fire-and-forget background task that populates the cache) and returning whatever is cached, or returning partial results and letting the frontend re-fetch.

Also applies to: 306-313

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/api/skills.rs` around lines 256 - 263, The call to
enrich_registry_descriptions is blocking the HTTP response in registry_browse
and registry_search by awaiting GitHub fetches; change to fire-and-forget
background enrichment so the handlers return immediately with whatever
descriptions are already cached. Concretely, replace the await
enrich_registry_descriptions(&client, &mut skills).await with spawning a
background task (e.g., tokio::spawn or equivalent) that clones the needed
client/config and the list of skill identifiers and calls
enrich_registry_descriptions in the background to populate the cache, while the
handler returns Json(RegistryBrowseResponse { skills, ... }) (and do the same
for the RegistrySearchResponse branch). Ensure any shared cache or client is
cloned or Arc-wrapped and handle errors inside the task so the handler never
awaits it.

12-13: Unbounded in-memory cache will grow indefinitely.

REGISTRY_SKILL_DESCRIPTION_CACHE has no eviction policy, TTL, or size limit. Over time, as users browse/search different skills, this HashMap grows without bound. With thousands of registry skills, each storing a description string up to 220 chars, the memory impact is modest per entry but there's no ceiling.

Consider adding a bounded cache (e.g., an LRU with a max capacity) or a simple TTL-based eviction. Even a periodic clear on a timer would suffice.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/api/skills.rs` around lines 12 - 13, REGISTRY_SKILL_DESCRIPTION_CACHE is
an unbounded LazyLock<HashMap<String,String>> and will grow indefinitely;
replace it with a bounded, concurrent cache (e.g., an LRU or TTL cache) or add
periodic eviction: for example swap the LazyLock<RwLock<HashMap<...>>> for a
concurrent cache type (moka::sync::Cache or lru::LruCache behind a
tokio::sync::Mutex) with a configured max_capacity and/or TTL, update all
accesses that read/write REGISTRY_SKILL_DESCRIPTION_CACHE to use the cache's
get/insert/evict APIs (or implement a background task that periodically prunes
old entries if you prefer TTL), and ensure the chosen cache provides safe
async/concurrent usage in place of the current tokio::sync::RwLock wrapper.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@interface/src/routes/AgentSkills.tsx`:
- Around line 353-411: The GitHub form is sharing the global installMutation,
causing its UI states and messages to reflect installs triggered from registry
cards; create a separate mutation for the GitHub form (e.g.,
githubInstallMutation using the same useMutation handler currently used by
installMutation) and replace usages inside the form: onSubmit should call
githubInstallMutation.mutate(spec), the submit button disabled/isPending checks
should use githubInstallMutation.isPending, and the success/error messages
should use githubInstallMutation.isSuccess / githubInstallMutation.isError and
githubInstallMutation.data; leave the existing installMutation in place for
registry card buttons so their feedback remains scoped to registry installs.

In `@src/api/skills.rs`:
- Around line 393-394: The code currently builds raw URLs with a hardcoded
"main" branch (in the loop iterating candidate_paths and building url from
source and path), which fails for repos whose default branch is not main; change
the construction in that loop to resolve the default branch instead — either
call the GitHub REST API (GET /repos/{owner}/{repo}) to read default_branch and
use it when formatting url, or use the raw.githubusercontent.com "HEAD" redirect
by formatting url as "https://raw.githubusercontent.com/{source}/HEAD/{path}" so
the request follows the repo's default branch, and if you prefer robustness add
a small fallback sequence (try HEAD, then "main", then "master") when fetching
each candidate path.
- Around line 316-363: enrich_registry_descriptions is not caching "not found"
results so skills without SKILL.md trigger repeated HTTP calls; change the logic
to store a sentinel (e.g., an empty string or a dedicated marker) in
REGISTRY_SKILL_DESCRIPTION_CACHE when fetch_registry_skill_description returns
None, and when reading from REGISTRY_SKILL_DESCRIPTION_CACHE treat that sentinel
as meaning "looked up but no description" (i.e., set skills[index].description =
None/skip) so failed lookups aren't retried; update the join_set result handling
around fetch_registry_skill_description, the cache.insert call, and the initial
cached read to recognize and handle the sentinel (use registry_skill_key,
REGISTRY_SKILL_DESCRIPTION_CACHE, enrich_registry_descriptions, and
fetch_registry_skill_description to locate changes).

---

Nitpick comments:
In `@interface/src/routes/ChannelDetail.tsx`:
- Around line 321-331: Indentation of the cortex toggle wrapper div in
ChannelDetail.tsx is inconsistent; adjust the JSX so the <div className="flex
overflow-hidden rounded-md border border-app-line bg-app-darkBox"> (the cortex
toggle wrapper containing the Button that calls setCortexOpen and reads
cortexOpen) is indented to match the surrounding <div className="ml-auto flex
items-center gap-3"> children, keeping the Button and its props (onClick,
variant, size, className, title) aligned with the other sibling elements for
consistent JSX formatting.

In `@src/api/skills.rs`:
- Around line 256-263: The call to enrich_registry_descriptions is blocking the
HTTP response in registry_browse and registry_search by awaiting GitHub fetches;
change to fire-and-forget background enrichment so the handlers return
immediately with whatever descriptions are already cached. Concretely, replace
the await enrich_registry_descriptions(&client, &mut skills).await with spawning
a background task (e.g., tokio::spawn or equivalent) that clones the needed
client/config and the list of skill identifiers and calls
enrich_registry_descriptions in the background to populate the cache, while the
handler returns Json(RegistryBrowseResponse { skills, ... }) (and do the same
for the RegistrySearchResponse branch). Ensure any shared cache or client is
cloned or Arc-wrapped and handle errors inside the task so the handler never
awaits it.
- Around line 12-13: REGISTRY_SKILL_DESCRIPTION_CACHE is an unbounded
LazyLock<HashMap<String,String>> and will grow indefinitely; replace it with a
bounded, concurrent cache (e.g., an LRU or TTL cache) or add periodic eviction:
for example swap the LazyLock<RwLock<HashMap<...>>> for a concurrent cache type
(moka::sync::Cache or lru::LruCache behind a tokio::sync::Mutex) with a
configured max_capacity and/or TTL, update all accesses that read/write
REGISTRY_SKILL_DESCRIPTION_CACHE to use the cache's get/insert/evict APIs (or
implement a background task that periodically prunes old entries if you prefer
TTL), and ensure the chosen cache provides safe async/concurrent usage in place
of the current tokio::sync::RwLock wrapper.

ℹ️ Review info

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 7170ca0 and 66c9351.

📒 Files selected for processing (8)
  • interface/src/api/client.ts
  • interface/src/components/Markdown.tsx
  • interface/src/hooks/useChannelLiveState.ts
  • interface/src/routes/AgentDetail.tsx
  • interface/src/routes/AgentSkills.tsx
  • interface/src/routes/ChannelDetail.tsx
  • src/api/skills.rs
  • src/conversation/history.rs

Copy link

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

Caution

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

⚠️ Outside diff range comments (1)
interface/src/routes/ChannelDetail.tsx (1)

153-183: ⚠️ Potential issue | 🔴 Critical

Fix WorkerRunItem JSX + missing expansion state.
expanded is referenced without state and the Link is closed with </button>, which will fail to compile and breaks expansion behavior. Add local state, a toggle, and close the Link correctly (or restructure the wrapper to avoid nested interactive elements).

🛠️ Suggested fix (adds state + toggle and corrects JSX structure)
 function WorkerRunItem({ item, agentId }: { item: TimelineWorkerRun; agentId: string }) {
+	const [expanded, setExpanded] = useState(false);
 	return (
 		<div className="flex gap-3 px-3 py-2">
 			<span className="flex-shrink-0 pt-0.5 text-tiny text-ink-faint">
 				{formatTimestamp(new Date(item.started_at).getTime())}
 			</span>
 			<div className="min-w-0 flex-1">
-				<Link
-					to="/agents/$agentId/workers"
-					params={{ agentId }}
-					search={{ worker: item.id }}
-					className="block w-full rounded-md bg-amber-500/10 px-3 py-2 text-left transition-colors hover:bg-amber-500/15"
-				>
-					<div className="flex min-w-0 items-center gap-2 overflow-hidden">
-						<div className="h-2 w-2 rounded-full bg-amber-400/50" />
-						<span className="text-sm font-medium text-amber-300">Worker</span>
-						<span className="min-w-0 flex-1 truncate text-sm text-ink-dull">{item.task}</span>
-						{item.result && (
-							<span className="flex-shrink-0 self-start text-tiny leading-5 text-ink-faint">
-								{expanded ? "▾" : "▸"}
-							</span>
-						)}
-					</div>
-				</button>
+				<div className="rounded-md bg-amber-500/10 px-3 py-2 text-left transition-colors hover:bg-amber-500/15">
+					<div className="flex min-w-0 items-center gap-2 overflow-hidden">
+						<div className="h-2 w-2 rounded-full bg-amber-400/50" />
+						<span className="text-sm font-medium text-amber-300">Worker</span>
+						<Link
+							to="/agents/$agentId/workers"
+							params={{ agentId }}
+							search={{ worker: item.id }}
+							className="min-w-0 flex-1 truncate text-sm text-ink-dull hover:text-ink"
+						>
+							{item.task}
+						</Link>
+						{item.result && (
+							<button
+								type="button"
+								aria-expanded={expanded}
+								onClick={(e) => {
+									e.stopPropagation();
+									setExpanded(!expanded);
+								}}
+								className="flex-shrink-0 self-start text-tiny leading-5 text-ink-faint"
+							>
+								{expanded ? "▾" : "▸"}
+							</button>
+						)}
+					</div>
+				</div>
 				{expanded && item.result && (
 					<div className="mt-1 rounded-md border border-amber-500/10 bg-amber-500/5 px-3 py-2">
 						<div className="text-sm text-ink-dull">
 							<Markdown className="whitespace-pre-wrap break-words">{item.result}</Markdown>
 						</div>
 					</div>
 				)}
 			</div>
 		</div>
 	);
 }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@interface/src/routes/ChannelDetail.tsx` around lines 153 - 183, The
WorkerRunItem component references expanded without state and incorrectly closes
the Link with </button>, breaking compilation and expansion behavior; add a
local boolean state (e.g., const [expanded, setExpanded] = useState(false>)
inside WorkerRunItem, add a toggle handler (setExpanded(prev => !prev)) wired to
a non-conflicting control (e.g., an onClick on a chevron span or a dedicated
button) and ensure the Link JSX is closed with </Link> (or restructure to avoid
nesting interactive elements) so the expansion conditional (expanded &&
item.result) works and the markup is valid.
♻️ Duplicate comments (1)
src/api/skills.rs (1)

394-405: Negative caching is now correctly implemented — previous concern addressed.

description (Option<String>) is always inserted into REGISTRY_SKILL_DESCRIPTION_CACHE, including None for skills without a SKILL.md. Both read-sites correctly distinguish a cache miss (getNone) from a cached negative (getSome(None)), preventing repeated HTTP storms for missing descriptions.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/api/skills.rs` around lines 394 - 405, Negative-caching is intentionally
used here: the Option<String> named description (including None when SKILL.md is
missing) is always inserted into REGISTRY_SKILL_DESCRIPTION_CACHE, but readers
distinguish a cache miss (None) from a cached negative (Some(None)). Add a brief
clarifying comment immediately above the
REGISTRY_SKILL_DESCRIPTION_CACHE.insert(cache_key, description) call (near
join_set.join_next handling and skills.get_mut usage) stating that inserting
None is intentional for negative caching and that read-sites must check for
Some(None) vs None; no logic change required beyond the comment.
🧹 Nitpick comments (1)
interface/src/routes/AgentSkills.tsx (1)

233-255: githubInstallMutation and installMutation share an identical mutationFn and onSuccess.

Both were kept separate to isolate UI state, which is correct. But the implementation is duplicated. Extract a shared function to keep them DRY:

♻️ Proposed refactor
+    const skillInstallFn = (spec: string) =>
+        api.installSkill({ agent_id: agentId, spec, instance: false });
+    const onInstallSuccess = () =>
+        queryClient.invalidateQueries({ queryKey: ["skills", agentId] });
+
     const installMutation = useMutation({
-        mutationFn: (spec: string) =>
-            api.installSkill({ agent_id: agentId, spec, instance: false }),
-        onSuccess: () => {
-            queryClient.invalidateQueries({ queryKey: ["skills", agentId] });
-        },
+        mutationFn: skillInstallFn,
+        onSuccess: onInstallSuccess,
     });

     const githubInstallMutation = useMutation({
-        mutationFn: (spec: string) =>
-            api.installSkill({ agent_id: agentId, spec, instance: false }),
-        onSuccess: () => {
-            queryClient.invalidateQueries({ queryKey: ["skills", agentId] });
-        },
+        mutationFn: skillInstallFn,
+        onSuccess: onInstallSuccess,
     });
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@interface/src/routes/AgentSkills.tsx` around lines 233 - 255, installMutation
and githubInstallMutation duplicate the same mutationFn and onSuccess; extract a
shared helper (e.g., createInstallMutationOptions or getInstallMutationHandlers)
that returns the mutationFn (calling api.installSkill with agent_id: agentId,
spec, instance: false) and the onSuccess callback (calling
queryClient.invalidateQueries({ queryKey: ["skills", agentId] })), then use that
helper when creating both installMutation and githubInstallMutation to remove
the duplicated implementation while preserving separate useMutation instances
for UI state.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@interface/src/hooks/useChannelLiveState.ts`:
- Around line 586-594: The current code sets hasMore to false when deduplication
yields zero new items (olderItems.length === 0) which can prematurely stop
pagination; change the logic so hasMore reflects the API's data.has_more (do not
override it based on olderItems length), and implement/propagate a cursor from
the response (e.g., response cursor/last_cursor) into the channel state for
subsequent fetches—update the merge block for [channelId] (where existingKeys,
olderItems, itemKey, data.has_more, timeline are used) to store the returned
cursor and use data.has_more directly to decide hasMore so pagination continues
even when dedupe returns no new items.

In `@src/api/skills.rs`:
- Around line 473-480: The nested conditional in the for loop triggers Clippy's
`collapsible_if`; change the logic to an early-continue pattern: inside the for
(index, line) in lines.iter().enumerate() loop, replace the nested if that
checks heading.starts_with('#') && heading.contains("description") with a single
guard that continues when the condition is false, then call
extract_paragraph(&lines[(index + 1)..]) and return Some(description) when it
yields Some. This keeps the same behavior while removing the nested if and
references the existing symbols heading, lines, index, and the
extract_paragraph(...) call.

---

Outside diff comments:
In `@interface/src/routes/ChannelDetail.tsx`:
- Around line 153-183: The WorkerRunItem component references expanded without
state and incorrectly closes the Link with </button>, breaking compilation and
expansion behavior; add a local boolean state (e.g., const [expanded,
setExpanded] = useState(false>) inside WorkerRunItem, add a toggle handler
(setExpanded(prev => !prev)) wired to a non-conflicting control (e.g., an
onClick on a chevron span or a dedicated button) and ensure the Link JSX is
closed with </Link> (or restructure to avoid nesting interactive elements) so
the expansion conditional (expanded && item.result) works and the markup is
valid.

---

Duplicate comments:
In `@src/api/skills.rs`:
- Around line 394-405: Negative-caching is intentionally used here: the
Option<String> named description (including None when SKILL.md is missing) is
always inserted into REGISTRY_SKILL_DESCRIPTION_CACHE, but readers distinguish a
cache miss (None) from a cached negative (Some(None)). Add a brief clarifying
comment immediately above the REGISTRY_SKILL_DESCRIPTION_CACHE.insert(cache_key,
description) call (near join_set.join_next handling and skills.get_mut usage)
stating that inserting None is intentional for negative caching and that
read-sites must check for Some(None) vs None; no logic change required beyond
the comment.

---

Nitpick comments:
In `@interface/src/routes/AgentSkills.tsx`:
- Around line 233-255: installMutation and githubInstallMutation duplicate the
same mutationFn and onSuccess; extract a shared helper (e.g.,
createInstallMutationOptions or getInstallMutationHandlers) that returns the
mutationFn (calling api.installSkill with agent_id: agentId, spec, instance:
false) and the onSuccess callback (calling queryClient.invalidateQueries({
queryKey: ["skills", agentId] })), then use that helper when creating both
installMutation and githubInstallMutation to remove the duplicated
implementation while preserving separate useMutation instances for UI state.

ℹ️ Review info

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 66c9351 and be6c800.

⛔ Files ignored due to path filters (2)
  • Cargo.lock is excluded by !**/*.lock, !**/*.lock
  • Cargo.toml is excluded by !**/*.toml
📒 Files selected for processing (6)
  • interface/src/api/client.ts
  • interface/src/hooks/useChannelLiveState.ts
  • interface/src/routes/AgentSkills.tsx
  • interface/src/routes/ChannelDetail.tsx
  • src/api/skills.rs
  • src/conversation/history.rs
🚧 Files skipped from review as they are similar to previous changes (1)
  • src/conversation/history.rs

Copy link

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

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/api/skills.rs`:
- Around line 394-397: The loop calling join_set.join_next() currently discards
task failures by matching only Ok(...) and continuing on anything else; update
the while let Some(result) = join_set.join_next().await loop to explicitly
handle the Err case (and non-Ok variants) instead of silently continuing:
inspect the Result from join_next(), log the error with context (include
index/cache_key when available) or propagate it up (return Err) so enrichment
failures are visible, and only continue on expected success; change the pattern
matching around the let Ok((index, cache_key, description)) = result else {
continue; } to handle and report the error path for debugging and correctness.
- Around line 438-456: The code in the HTTP fetch loop (using
client.get(&url)...send().await and response.text().await) currently swallows
errors (Err(_) branches) and skips non-success statuses without any logging;
update those branches to log diagnostic details (at debug/trace level) including
the URL, the error returned from the request or text() call, and the
response.status() for non-success cases so failures are observable; locate the
match arms around client.get(...).send().await and the match for
response.text().await in skills.rs and replace the Err(_) and the
!response.status().is_success() paths with calls to your project's logging
facility (e.g., debug!/trace!/error!) that include the url, error, and status to
aid debugging.

ℹ️ Review info

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between be6c800 and 066eaea.

📒 Files selected for processing (1)
  • src/api/skills.rs

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.

2 participants