Skip to content

feat: web ui v2#9

Merged
pedronauck merged 13 commits into
mainfrom
pn/ui-v2
Apr 9, 2026
Merged

feat: web ui v2#9
pedronauck merged 13 commits into
mainfrom
pn/ui-v2

Conversation

@pedronauck
Copy link
Copy Markdown
Member

@pedronauck pedronauck commented Apr 9, 2026

Summary by CodeRabbit

  • New Features

    • Skills management API and UI: list skills, view details & content, enable/disable, and marketplace install indicators
    • Knowledge system: browse, search, view, delete, and consolidate memories
  • UI Updates

    • Redesigned collapsible app sidebar and simplified header
    • New Skills and Knowledge two‑pane pages and routes
    • Chat: agent labels, timestamps, and refined message styling
    • Global design token and font refresh (updated colors, spacing, and fonts)

pedronauck and others added 10 commits April 9, 2026 02:05
Replace the entire CSS design token system: swap OKLCH colors for hex
values from DESIGN.md, replace Geist/Bricolage fonts with Inter/JetBrains
Mono, and remove all gradients, textures, and shadows to achieve the flat
depth model. This is the foundation for the Paper design system.

- Rewrite styles.css with hex-based --color-* tokens and shadcn mapping
- Swap font packages: @fontsource-variable/inter + @fontsource/jetbrains-mono
- Update 35+ component files from var(--ds-*) to var(--color-*) references
- Remove .ds-texture-*, .ds-panel*, font-display, box-shadow, color-mix()
- Add design token verification tests (styles.test.ts)
- All tests pass (365/365), lint and typecheck clean

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Replace shadcn Sidebar with custom two-zone layout: 40px icon rail
(workspace circle avatars, app logo) + 220px collapsible panel
(workspace name, search, agent list, Knowledge/Skills nav, system
footer). Add Zustand sidebar store, empty skills/knowledge routes,
and comprehensive tests.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Update chat header to breadcrumb pattern (status dot > agent > session),
message bubbles to right-aligned user bubbles and left-aligned agent
messages with JetBrains Mono labels, tool call cards with bordered card
styling and DONE/RUNNING/ERROR status badges, message composer with
rounded container and circular accent send button, and empty state with
48px terminal icon per Paper spec.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Expose the skills.Registry through the HTTP and UDS API layers:
- GET /api/skills?workspace=:id — list all skills for a workspace
- GET /api/skills/:name — get skill detail by name
- POST /api/skills/:name/enable — enable a skill
- POST /api/skills/:name/disable — disable a skill

Adds SkillPayload/ProvenancePayload contract types, SkillsRegistry
interface, conversion helpers, error mapping, and comprehensive
table-driven tests with 81% coverage.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Complete systems/skill/ module following app-renderer-systems pattern:
Zod schemas, typed API adapter with SkillApiError, query keys/options,
useSkills/useSkill hooks, useEnableSkill/useDisableSkill mutations with
cache invalidation. All tests pass with full coverage.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…e view

Replaces the empty skills route placeholder with a fully functional Skills
page matching the Paper design. Includes INSTALLED tab with grouped skill
list (BUNDLED/WORKSPACE/MARKETPLACE sections), detail panel with source
badges and enable/disable actions, and MARKETPLACE tab with search and
category filter chips. 24 tests with >96% coverage.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…d tests

Create systems/knowledge/ module following app-renderer-systems pattern,
wrapping existing /api/memory backend endpoints with typed adapter functions,
TanStack Query hooks, and 100% test coverage.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…view

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@pedronauck pedronauck self-assigned this Apr 9, 2026
@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Apr 9, 2026

Walkthrough

Adds a new skills subsystem: API DTOs, handlers, and server wiring; a SkillsRegistry interface and registry runtime changes (load content, SetEnabled); integrates skills into daemon and servers; and implements full frontend features (skills + knowledge UIs, hooks, adapters), styling/token migration, and many tests.

Changes

Cohort / File(s) Summary
API contract & conversions
internal/api/contract/contract.go, internal/api/core/conversions.go
Added skill DTOs (SkillPayload, ProvenancePayload, SkillContentResponse, SkillActionResponse) and conversion helpers mapping *skills.Skill → contract payloads.
API errors & handlers
internal/api/core/errors.go, internal/api/core/handlers.go, internal/api/core/interfaces.go, internal/api/core/skills.go, internal/api/core/skills_test.go
New sentinel errors and StatusForSkillError; injected SkillsRegistry into BaseHandlers; added SkillsRegistry interface and handlers ListSkills, GetSkill, GetSkillContent, EnableSkill, DisableSkill plus unit tests.
Server & routing wiring
internal/api/httpapi/server.go, internal/api/udsapi/server.go, internal/api/udsapi/routes.go, internal/api/httpapi/handlers_test.go, internal/api/udsapi/handlers_test.go, internal/api/udsapi/helpers_test.go, internal/api/udsapi/server_test.go
Added WithSkillsRegistry option and server fields; registered /api/skills endpoints on HTTP and UDS; updated route/handler tests and server test setup.
Registry internals
internal/skills/registry.go, internal/skills/registry_test.go, internal/skills/loader.go, internal/skills/loader_test.go, internal/skills/types.go, internal/skills/bundled/bundled_test.go
Registry now supports workspace-specific disabled overlays, SetEnabled, LoadContent, SkillSourceName, and refactored parsing/read-content separation; tests added/updated for SetEnabled and LoadContent.
Daemon integration & boot
internal/daemon/boot.go, internal/daemon/daemon.go
Threaded SkillsRegistry through RuntimeDeps and boot wiring into HTTP/UDS server construction.
API test utilities
internal/api/testutil/apitest.go, internal/api/udsapi/helpers_test.go, internal/api/udsapi/server_test.go
Added StubSkillsRegistry test stub and adjusted test helpers/servers to supply a skills registry in tests.
Frontend: skill system (types, adapters, hooks, components, route, tests)
web/src/systems/skill/types.ts, web/src/systems/skill/adapters/skill-api.ts, web/src/systems/skill/lib/*, web/src/systems/skill/hooks/*, web/src/systems/skill/components/*, web/src/systems/skill/index.ts, web/src/routes/_app/skills.tsx, web/src/routes/_app/-skills.test.tsx
Full frontend skill feature: Zod schemas, SkillApiError adapter, query keys/options, hooks (list/detail/content/enable/disable), UI panels (list/detail, marketplace), route and comprehensive tests.
Frontend: knowledge system (types, adapters, hooks, components, route, tests)
web/src/systems/knowledge/types.ts, web/src/systems/knowledge/adapters/knowledge-api.ts, web/src/systems/knowledge/lib/*, web/src/systems/knowledge/hooks/*, web/src/systems/knowledge/components/*, web/src/systems/knowledge/index.ts, web/src/routes/_app/knowledge.tsx, web/src/routes/_app/-knowledge.test.tsx
Added knowledge/memory system: Zod schemas, KnowledgeApiError adapter, query keys/options, hooks, list/detail UI panels, route and tests.
Frontend: layout, sidebar & state
web/src/components/app-sidebar.tsx, web/src/components/app-sidebar.test.tsx, web/src/stores/sidebar-store.ts, web/src/stores/sidebar-store.test.ts, web/src/routes/_app.tsx, web/src/routes/-_app.test.tsx
Refactored sidebar to controlled component with Zustand store, replaced SidebarProvider layout with explicit layout wiring, and updated tests.
Frontend: design tokens & styles migration
web/src/styles.css, web/src/styles.test.ts, web/src/components/design-system/*, web/src/components/design-system/stories/*
Replaced --ds-* tokens with --color-*, swapped fonts to Inter/JetBrains Mono, adjusted radii and removed legacy component layer; added style enforcement tests and updated many design-system components.
Frontend: chat/session UI updates
web/src/systems/session/components/*
Propagated agentName through ChatView/MessageBubble, added agent label/timestamp, replaced composer Button with native button, updated tool-call card status badges and related tests.
Frontend: misc generated routes & adjustments
web/src/routeTree.gen.ts, web/src/routes/_app/index.tsx, web/src/routes/_app/*
Added generated routes for /skills and /knowledge, simplified index empty state, and added multiple route/page tests.
Misc & CI/test updates
.gitignore, many test files across backend/frontend
Added .firecrawl ignore and numerous new/updated tests across systems to cover new features and migration.

Sequence Diagram(s)

mermaid
sequenceDiagram
participant Client
participant HTTP_Server as HTTP Server
participant Handler as BaseHandlers
participant Workspace as WorkspaceResolver
participant Registry as SkillsRegistry
Client->>HTTP_Server: POST /api/skills/:name/enable?workspace=...
HTTP_Server->>Handler: dispatch -> EnableSkill(c)
Handler->>Workspace: resolve workspace (if provided)
alt workspace-scoped resolution
Workspace-->>Handler: ResolvedWorkspace
Handler->>Registry: ForWorkspace(ctx, resolved)
Registry-->>Handler: []*Skill (workspace list)
else global resolution
Handler->>Registry: Get(name)
Registry-->>Handler: *Skill / not found
end
alt skill not found
Handler-->>HTTP_Server: 404 Not Found
else skill found & already desired state
Handler-->>HTTP_Server: 200 { ok: true }
else skill found & state change required
Handler->>Registry: SetEnabled(name, resolved?, true)
Registry-->>Handler: nil / error
Handler->>Handler: Logger.Info("enabled")
Handler-->>HTTP_Server: 200 { ok: true }
end

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~75 minutes

Possibly related PRs

  • feat: add workspace entity #5 — Modifications to skills/workspace integration and Registry.ForWorkspace signatures; strong overlap with registry and handler changes.
  • feat: add new skill capabilities #8 — Related changes to skills provenance, load-content behavior, and registry logic used by the new DTOs and APIs.
  • compozy/compozy#284 — Implements overlapping skills feature surface (API, UI, registry), likely to conflict or complement the frontend/back-end skill integrations.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch pn/ui-v2

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

Note

Due to the large number of review comments, Critical severity comments were prioritized as inline comments.

Caution

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

⚠️ Outside diff range comments (1)
web/src/systems/session/components/message-bubble.tsx (1)

102-108: ⚠️ Potential issue | 🟠 Major

The memo comparator ignores the new timestamp output.

formatTimestamp(message.timestamp) is now part of the rendered assistant header, but the custom equality function still skips message.timestamp. A timestamp update will leave the old time on screen. Including message.role here is also safer now that the render path branches on it.

Suggested fix
   (prev, next) =>
+    prev.message.role === next.message.role &&
     prev.message.id === next.message.id &&
     prev.message.content === next.message.content &&
     prev.message.thinking === next.message.thinking &&
     prev.message.thinkingComplete === next.message.thinkingComplete &&
+    prev.message.timestamp === next.message.timestamp &&
     prev.message.isStreaming === next.message.isStreaming &&
     prev.agentName === next.agentName
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@web/src/systems/session/components/message-bubble.tsx` around lines 102 -
108, The memo comparator function that currently compares prev.message.id,
content, thinking, thinkingComplete, isStreaming and prev.agentName must also
compare message.timestamp and message.role so timestamp updates and role-based
render branches re-render correctly; update the arrow comparator (the custom
equality used with React.memo) to include prev.message.timestamp ===
next.message.timestamp and prev.message.role === next.message.role (or compare
formatTimestamp(prev.message.timestamp) ===
formatTimestamp(next.message.timestamp) if you prefer comparing the rendered
string) along with the existing checks.
🟠 Major comments (19)
web/src/systems/knowledge/adapters/knowledge-api.ts-63-73 (1)

63-73: ⚠️ Potential issue | 🟠 Major

Add AbortSignal parameter to all mutating adapter functions.

The writeMemory, deleteMemory, and consolidateMemory functions do not accept or pass signal to their fetch calls, breaking cancellation propagation from TanStack Query. Other functions in this adapter already implement proper signal threading (see lines 32, 51), establishing the expected pattern.

Proposed fix
 export async function writeMemory(
   filename: string,
   content: string,
   scope?: string,
-  workspace?: string
+  workspace?: string,
+  signal?: AbortSignal
 ): Promise<MemoryMutationResponse> {
   const res = await fetch(`/api/memory/${encodeURIComponent(filename)}`, {
     method: "PUT",
     headers: { "Content-Type": "application/json" },
     body: JSON.stringify({ content, scope, workspace }),
+    signal,
   });
   if (!res.ok) {
     throw new KnowledgeApiError(`Failed to write memory "${filename}": ${res.status}`, res.status);
   }
   const json = await res.json();
   return memoryMutationResponseSchema.parse(json);
 }

 export async function deleteMemory(
   scope: string,
   filename: string,
-  workspace?: string
+  workspace?: string,
+  signal?: AbortSignal
 ): Promise<MemoryMutationResponse> {
   const params = new URLSearchParams();
   params.set("scope", scope);
   if (workspace) params.set("workspace", workspace);
   const url = `/api/memory/${encodeURIComponent(filename)}?${params.toString()}`;

-  const res = await fetch(url, { method: "DELETE" });
+  const res = await fetch(url, { method: "DELETE", signal });
   if (!res.ok) {
     if (res.status === 404) {
       throw new KnowledgeApiError(`Memory not found: ${filename}`, 404);
     }
     throw new KnowledgeApiError(`Failed to delete memory "${filename}": ${res.status}`, res.status);
   }
   const json = await res.json();
   return memoryMutationResponseSchema.parse(json);
 }

-export async function consolidateMemory(workspace?: string): Promise<MemoryConsolidateResponse> {
+export async function consolidateMemory(
+  workspace?: string,
+  signal?: AbortSignal
+): Promise<MemoryConsolidateResponse> {
   const res = await fetch("/api/memory/consolidate", {
     method: "POST",
     headers: { "Content-Type": "application/json" },
     body: JSON.stringify({ workspace }),
+    signal,
   });
   if (!res.ok) {
     throw new KnowledgeApiError(`Failed to consolidate memory: ${res.status}`, res.status);
   }
   const json = await res.json();
   return memoryConsolidateResponseSchema.parse(json);
 }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@web/src/systems/knowledge/adapters/knowledge-api.ts` around lines 63 - 73,
The mutating adapter functions writeMemory, deleteMemory, and consolidateMemory
need an AbortSignal parameter so cancellation from TanStack Query propagates;
update each function signature to accept an optional signal (e.g., signal?:
AbortSignal), thread that signal into the fetch init (pass signal in the options
object alongside method/headers/body), and ensure the encoded filename usage
(encodeURIComponent(filename)) and JSON body behavior remain unchanged while
returning the same MemoryMutationResponse; locate the fetch calls in
writeMemory, deleteMemory, and consolidateMemory and add the signal property to
their fetch options.
web/src/systems/knowledge/adapters/knowledge-api.ts-37-38 (1)

37-38: ⚠️ Potential issue | 🟠 Major

Normalize schema-validation failures into KnowledgeApiError.

The direct .parse() calls on lines 37, 59, 78, 99, and 112 can throw raw ZodError when the server returns a 200 status with invalid JSON structure, violating the adapter requirement to use only typed error classes.

Proposed fix pattern
+function parseOrThrow<T>(
+  result: { success: true; data: T } | { success: false },
+  message: string,
+  status: number
+): T {
+  if (!result.success) {
+    throw new KnowledgeApiError(message, status);
+  }
+  return result.data;
+}

 export async function listMemories(
   scope?: string,
   workspace?: string,
   signal?: AbortSignal
 ): Promise<MemoryHeader[]> {
@@
   const res = await fetch(url, { signal });
@@
   const json = await res.json();
-  return memoryHeaderSchema.array().parse(json);
+  return parseOrThrow(
+    memoryHeaderSchema.array().safeParse(json),
+    "Invalid memories list response",
+    res.status
+  );
 }

Apply the same safeParse + parseOrThrow pattern to the other four schema parse sites (lines 59, 78, 99, 112).

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

In `@web/src/systems/knowledge/adapters/knowledge-api.ts` around lines 37 - 38,
Replace direct .parse() calls that can throw ZodError (e.g.,
memoryHeaderSchema.array().parse(json)) with the safeParse + parseOrThrow
pattern so validation failures are normalized to KnowledgeApiError; for each
schema usage that currently does .parse(json) (the sites using
memoryHeaderSchema.array(), and the other four schema parses in this file), call
schema.safeParse(json) and pass the result into the existing parseOrThrow helper
(or throw new KnowledgeApiError wrapping the ZodError) so all schema validation
errors are converted into KnowledgeApiError instead of letting ZodError escape.
web/src/systems/knowledge/lib/query-options.test.ts-3-3 (1)

3-3: ⚠️ Potential issue | 🟠 Major

Use the project alias import in this test file.

./query-options should be replaced with the @/* path alias form.

Proposed fix
-import { memoriesListOptions, memoryDetailOptions } from "./query-options";
+import { memoriesListOptions, memoryDetailOptions } from "@/systems/knowledge/lib/query-options";

As per coding guidelines: Use path alias @/* to map to ./src/* for all imports.

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

In `@web/src/systems/knowledge/lib/query-options.test.ts` at line 3, Replace the
relative import in the test so it uses the project path alias: change the import
of memoriesListOptions and memoryDetailOptions from "./query-options" to the
alias-based path that maps to src (i.e., import from
"@/systems/knowledge/lib/query-options") so the test uses the `@/`* alias and
matches project coding guidelines.
web/src/systems/knowledge/hooks/use-knowledge.test.tsx-6-16 (1)

6-16: ⚠️ Potential issue | 🟠 Major

Normalize test imports/mocks to @/* aliases.

Both direct imports and the vi.mock(...) module path should follow the alias rule.

Proposed fix
-import { useMemories, useMemory } from "./use-knowledge";
+import { useMemories, useMemory } from "@/systems/knowledge/hooks/use-knowledge";

-vi.mock("../adapters/knowledge-api", () => ({
+vi.mock("@/systems/knowledge/adapters/knowledge-api", () => ({
   listMemories: vi.fn(),
   readMemory: vi.fn(),
   deleteMemory: vi.fn(),
   writeMemory: vi.fn(),
   consolidateMemory: vi.fn(),
 }));

-import { listMemories, readMemory } from "../adapters/knowledge-api";
+import { listMemories, readMemory } from "@/systems/knowledge/adapters/knowledge-api";

As per coding guidelines: Use path alias @/* to map to ./src/* for all imports.

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

In `@web/src/systems/knowledge/hooks/use-knowledge.test.tsx` around lines 6 - 16,
Update the test to use the project path alias for the adapters module
consistently: change the vi.mock module path and the subsequent named import so
they refer to the aliased module (e.g.,
"@/systems/knowledge/adapters/knowledge-api") instead of the relative
"../adapters/knowledge-api"; ensure both the mock declaration and the imported
symbols (listMemories, readMemory) match the alias, and keep the existing mocked
function names and the test references to useMemories/useMemory unchanged.
web/src/systems/knowledge/hooks/use-knowledge-actions.test.tsx-6-16 (1)

6-16: ⚠️ Potential issue | 🟠 Major

Use alias-based module paths for imports and mocks.

This file should also use @/* paths (including vi.mock(...)) to satisfy the web import convention.

Proposed fix
-import { useConsolidateMemory, useDeleteMemory } from "./use-knowledge-actions";
+import {
+  useConsolidateMemory,
+  useDeleteMemory,
+} from "@/systems/knowledge/hooks/use-knowledge-actions";

-vi.mock("../adapters/knowledge-api", () => ({
+vi.mock("@/systems/knowledge/adapters/knowledge-api", () => ({
   listMemories: vi.fn(),
   readMemory: vi.fn(),
   deleteMemory: vi.fn(),
   writeMemory: vi.fn(),
   consolidateMemory: vi.fn(),
 }));

-import { consolidateMemory, deleteMemory } from "../adapters/knowledge-api";
+import { consolidateMemory, deleteMemory } from "@/systems/knowledge/adapters/knowledge-api";

As per coding guidelines: Use path alias @/* to map to ./src/* for all imports.

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

In `@web/src/systems/knowledge/hooks/use-knowledge-actions.test.tsx` around lines
6 - 16, The tests use relative paths; change both the hook import and the mocked
module to the project alias form so they follow the web convention: import
useConsolidateMemory and useDeleteMemory from the aliased hook module (replace
"./use-knowledge-actions" with
"@/systems/knowledge/hooks/use-knowledge-actions") and update the vi.mock call
to mock the aliased adapter module instead of "../adapters/knowledge-api" (mock
"@/systems/knowledge/adapters/knowledge-api"), keeping the same exported symbols
consolidateMemory and deleteMemory in the mock and import statements.
web/src/systems/knowledge/lib/query-options.ts-3-4 (1)

3-4: ⚠️ Potential issue | 🟠 Major

Replace relative imports with @/* aliases in query options.

Please use @/systems/... paths to match the web import standard.

Proposed fix
-import { listMemories, readMemory } from "../adapters/knowledge-api";
-import { knowledgeKeys } from "./query-keys";
+import { listMemories, readMemory } from "@/systems/knowledge/adapters/knowledge-api";
+import { knowledgeKeys } from "@/systems/knowledge/lib/query-keys";

As per coding guidelines: Use path alias @/* to map to ./src/* for all imports.

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

In `@web/src/systems/knowledge/lib/query-options.ts` around lines 3 - 4, Update
the imports in query-options.ts to use the project path alias instead of
relative paths: replace the import of listMemories and readMemory from
"../adapters/knowledge-api" with "@/systems/knowledge/adapters/knowledge-api"
and replace the import of knowledgeKeys from "./query-keys" with
"@/systems/knowledge/lib/query-keys", keeping the same exported symbol names
(listMemories, readMemory, knowledgeKeys) so existing references in this file
remain unchanged.
web/src/systems/knowledge/hooks/use-knowledge-actions.ts-3-4 (1)

3-4: ⚠️ Potential issue | 🟠 Major

Use @/* imports instead of relative paths in this hook module.

Relative imports here break the repo-wide web import convention; switch these to @/systems/... paths.

Proposed fix
-import { consolidateMemory, deleteMemory } from "../adapters/knowledge-api";
-import { knowledgeKeys } from "../lib/query-keys";
+import { consolidateMemory, deleteMemory } from "@/systems/knowledge/adapters/knowledge-api";
+import { knowledgeKeys } from "@/systems/knowledge/lib/query-keys";

As per coding guidelines: Use path alias @/* to map to ./src/* for all imports.

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

In `@web/src/systems/knowledge/hooks/use-knowledge-actions.ts` around lines 3 - 4,
Switch the two relative imports in this hook (consolidateMemory and deleteMemory
from "../adapters/knowledge-api" and knowledgeKeys from "../lib/query-keys") to
use the repo path alias (e.g., "@/systems/...") so they follow the project
convention; locate the import statements that reference consolidateMemory,
deleteMemory and knowledgeKeys and replace the "../adapters/knowledge-api" and
"../lib/query-keys" paths with the corresponding "@/systems/..." aliased paths.
web/src/systems/knowledge/adapters/knowledge-api.test.ts-3-10 (1)

3-10: ⚠️ Potential issue | 🟠 Major

Switch this test import to the @/* alias.

This relative import violates the project’s web import rule.

Proposed fix
 } from "./knowledge-api";
+} from "@/systems/knowledge/adapters/knowledge-api";

As per coding guidelines: Use path alias @/* to map to ./src/* for all imports.

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

In `@web/src/systems/knowledge/adapters/knowledge-api.test.ts` around lines 3 -
10, Replace the relative import in the test so it uses the path alias: change
the import statement that currently pulls consolidateMemory, deleteMemory,
KnowledgeApiError, listMemories, readMemory, writeMemory from "./knowledge-api"
to import them from "@/systems/knowledge/adapters/knowledge-api" so the test
conforms to the project's alias rule; update the import line referencing those
symbols accordingly.
web/src/systems/knowledge/hooks/use-knowledge.ts-3-3 (1)

3-3: ⚠️ Potential issue | 🟠 Major

Adopt @/* import paths in this hook file.

The relative import should be normalized to the configured alias.

Proposed fix
-import { memoriesListOptions, memoryDetailOptions } from "../lib/query-options";
+import { memoriesListOptions, memoryDetailOptions } from "@/systems/knowledge/lib/query-options";

As per coding guidelines: Use path alias @/* to map to ./src/* for all imports.

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

In `@web/src/systems/knowledge/hooks/use-knowledge.ts` at line 3, Replace the
relative import of memoriesListOptions and memoryDetailOptions in
use-knowledge.ts with the project path-alias import (use the `@/`* alias) so the
hook imports those symbols via the configured alias (e.g., import {
memoriesListOptions, memoryDetailOptions } from
"@/systems/knowledge/lib/query-options") to normalize imports to the configured
alias.
internal/api/udsapi/server.go-250-255 (1)

250-255: ⚠️ Potential issue | 🟠 Major

Require skillsRegistry in New to fail fast.

Line 254/Line 460 wires skillsRegistry into handlers, but New does not validate it. This can defer dependency failures to runtime on skill routes instead of failing server construction deterministically.

✅ Proposed fix
@@
 	if server.workspaces == nil {
 		return nil, errors.New("udsapi: workspace resolver is required")
 	}
+	if server.skillsRegistry == nil {
+		return nil, errors.New("udsapi: skills registry is required")
+	}
 	if strings.TrimSpace(server.config.Daemon.Socket) == "" {
 		server.config.Daemon.Socket = server.homePaths.DaemonSocket
 	}

Based on learnings: "No production users exist — never sacrifice code quality for backward compatibility; never write migration, compat, or defensive code for old state — delete the old thing instead".

Also applies to: 460-460

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

In `@internal/api/udsapi/server.go` around lines 250 - 255, The constructor New
should validate that the required dependency skillsRegistry is non-nil so server
construction fails fast instead of deferring errors to route handling; update
New to check the incoming skillsRegistry parameter and return an error (or
panic) when nil, and ensure this validation occurs before wiring
server.handlers/newHandlers with handlerConfig (which contains sessions,
observer, workspaces, skillsRegistry, memoryStore) so handlers never receive a
nil skillsRegistry.
web/src/systems/skill/hooks/use-skills.ts-3-3 (1)

3-3: 🛠️ Refactor suggestion | 🟠 Major

Use the project alias import here instead of a relative path.

This should use @/* imports to stay consistent with repository conventions.

Proposed fix
-import { skillDetailOptions, skillsListOptions } from "../lib/query-options";
+import { skillDetailOptions, skillsListOptions } from "@/systems/skill/lib/query-options";

As per coding guidelines, "Use path alias @/* to map to ./src/* for all imports".

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

In `@web/src/systems/skill/hooks/use-skills.ts` at line 3, Replace the relative
import of skillDetailOptions and skillsListOptions in use-skills.ts with the
project path-alias form; locate the import that brings in skillDetailOptions,
skillsListOptions from "../lib/query-options" and update it to use the "@/..."
alias that maps to src (e.g., import from the equivalent
"@/systems/skill/lib/query-options") so the module uses the repository's `@/`*
import convention.
web/src/systems/session/components/chat-header.tsx-35-38 (1)

35-38: ⚠️ Potential issue | 🟠 Major

Don't make session state color-only.

Replacing the textual badge with a dot removes the state for screen readers and for anyone who can't reliably distinguish the token colors. Keep the dot if you want, but add an accessible text label (aria-label and/or sr-only content) for session.state.

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

In `@web/src/systems/session/components/chat-header.tsx` around lines 35 - 38, The
status dot in chat-header.tsx currently encodes session.state only via color
(STATE_DOT_COLOR) which is inaccessible; update the element that uses
className={cn("size-2.5 shrink-0 rounded-full", STATE_DOT_COLOR[session.state])}
and data-testid="agent-status-dot" to keep the visual dot but add an accessible
label and/or hidden text so screen readers read the state—e.g., add an
aria-label like `aria-label={`Session state: ${session.state}`}` and/or include
an adjacent span with the project's sr-only class containing {session.state} so
assistive tech can convey the state.
web/src/components/design-system/design-system-showcase.tsx-165-166 (1)

165-166: ⚠️ Potential issue | 🟠 Major

Finish the token migration here too.

These surfaces still hardcode bg-black/10 and border-white/12, which bypasses the design-token layer this PR is otherwise standardizing on. Since this page is acting as a reference surface, it should use semantic --color-* tokens end-to-end.

As per coding guidelines, "Use Tailwind CSS v4 with only defined design tokens from DESIGN.md; implement flat depth model without shadows".

Also applies to: 255-255, 296-300, 314-330

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

In `@web/src/components/design-system/design-system-showcase.tsx` around lines 165
- 166, The showcased thread card currently hardcodes bg-black/10 and
border-white/12 (see the element using key={thread.id} and the other card blocks
in this file); replace those hardcoded colors with the semantic design-token CSS
variables used elsewhere (use the semantic surface token for the background,
e.g. the --color-surface-* token for muted surfaces, and the --color-divider
token for borders) so the component uses the tokenized Tailwind/CSS variable
classes consistently; apply the same token replacements to the other occurrences
in this component (the other card blocks mentioned) to finish the token
migration and conform to the flat depth model.
web/src/components/app-sidebar.tsx-20-29 (1)

20-29: 🛠️ Refactor suggestion | 🟠 Major

Keep AppSidebar presentational and move the orchestration up a level.

This component now owns Zustand state, several data hooks, and a mutation. That makes a shared web/src/components view responsible for app orchestration instead of just rendering props.

As per coding guidelines, "UI components MUST be pure and presentational; orchestration logic lives in pages/routes" and "Components MUST NOT import from stores/ or adapters directly — pass via props or route context."

Also applies to: 213-223, 307-313, 399-400, 419-425

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

In `@web/src/components/app-sidebar.tsx` around lines 20 - 29, AppSidebar is doing
orchestration by importing and using hooks/stores (useSidebarStore, useAgents,
useDaemonHealth, useCreateSession, useSessions, useWorkspaces) and components
tied to state (ConnectionStatus, AgentIcon); refactor by removing all direct
imports/usage of stores and data hooks from AppSidebar and instead accept the
required data and callbacks as props (e.g., sidebarState, agents, daemonHealth,
createSession, sessions, workspaces, onToggle) so AppSidebar becomes purely
presentational, then lift the orchestration to the parent route/page which will
call useSidebarStore, useAgents, useDaemonHealth, useCreateSession, useSessions,
useWorkspaces and pass their results into AppSidebar; apply the same change for
the other mentioned blocks (lines referenced) so no stores/hooks are imported
inside any component under web/src/components.
web/src/components/app-sidebar.tsx-21-29 (1)

21-29: 🛠️ Refactor suggestion | 🟠 Major

Import other systems through their public barrels.

This sidebar now reaches into agent, daemon, and session internals directly from outside those systems. That couples the shell to private module layout and defeats the system boundary you just added for skill.

As per coding guidelines, "Cross-system imports: only through the public barrel (@/systems/<domain>); never reach into another system's internals."

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

In `@web/src/components/app-sidebar.tsx` around lines 21 - 29, The imports in
app-sidebar.tsx are reaching into internal modules (symbols: AgentIcon,
useAgents, AgentPayload, ConnectionStatus, useDaemonHealth, useCreateSession,
useSessions, SessionPayload, SessionState as SessionStateType, useWorkspaces,
WorkspacePayload); update these to import only from each system's public barrel
(e.g., "@/systems/agent", "@/systems/daemon", "@/systems/session",
"@/systems/workspace") so the sidebar consumes public exports only, and adjust
any names if the barrel exports use different identifiers or re-exports them
from each system's index barrel.
web/src/components/app-sidebar.tsx-71-72 (1)

71-72: 🛠️ Refactor suggestion | 🟠 Major

Replace raw #E8572A classes with the accent token.

This PR is migrating the UI to --color-* tokens, but the sidebar reintroduces hard-coded accent hex values for the logo, active workspace ring, and active indicators. That will drift from the theme and forces tests to couple to implementation details.

As per coding guidelines, "Use Tailwind CSS v4 with only defined design tokens from DESIGN.md; implement flat depth model without shadows."

Also applies to: 89-90, 134-134, 286-286

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

In `@web/src/components/app-sidebar.tsx` around lines 71 - 72, Replace hard-coded
hex color usages in the AppSidebar JSX with the design token CSS variable (e.g.,
use var(--color-accent) via Tailwind arbitrary value syntax) so the logo, active
workspace ring, and active indicators follow the theme; locate the instances by
the className strings such as "mb-3 flex size-8 items-center justify-center
rounded-lg bg-[`#E8572A`] text-white" (logo), the workspace item class that
includes "ring-[...]" for the active workspace, and the active indicator class
that uses "bg-[`#E8572A`]"; update those className values to use
bg-[var(--color-accent)] and ring-[var(--color-accent)] (and any similar
occurrences noted) instead of the hex literal.
web/src/components/app-sidebar.tsx-263-266 (1)

263-266: ⚠️ Potential issue | 🟠 Major

Remove the namespace qualifier for ReactNode — import it directly from React.

The file only imports named bindings from React (import { useMemo, useState } from "react"), so React is not in scope. Using React.ReactNode without a namespace import will fail typecheck.

Suggested fix
-import { useMemo, useState } from "react";
+import { useMemo, useState, type ReactNode } from "react";
@@
 interface NavItemProps {
   to: string;
-  icon: React.ReactNode;
+  icon: ReactNode;
   label: string;
 }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@web/src/components/app-sidebar.tsx` around lines 263 - 266, The NavItemProps
interface uses the undefined namespace React.ReactNode; update the module import
to bring ReactNode into scope and adjust the type: add ReactNode to the named
imports from "react" (so the existing import like `import { useMemo, useState }
from "react"` becomes `import { useMemo, useState, ReactNode } from "react"`)
and change the interface type to use ReactNode (NavItemProps.icon: ReactNode),
or alternatively import the default React and keep React.ReactNode—patch the
import and the NavItemProps usage accordingly.
web/src/components/app-sidebar.tsx-370-371 (1)

370-371: ⚠️ Potential issue | 🟠 Major

Fix navigation paths to use valid route targets instead of route IDs.

The to paths should be /knowledge and /skills, not /_app/knowledge and /_app/skills. TanStack Router's file-based routing generates /knowledge and /skills as the valid navigation destinations, while /_app/knowledge and /_app/skills are route IDs used internally. Using incorrect paths breaks the useMatchRoute call, causing the active state detection to fail—users won't see which page they're on.

Current code (lines 370–371)
            <NavItem to="/_app/knowledge" icon={<Book className="size-3.5" />} label="Knowledge" />
            <NavItem to="/_app/skills" icon={<Wrench className="size-3.5" />} label="Skills" />
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@web/src/components/app-sidebar.tsx` around lines 370 - 371, The NavItem
components are using internal route IDs ("/_app/knowledge" and "/_app/skills")
which breaks active-route detection; update the `to` props on the NavItem
instances (the ones rendering Book and Wrench icons) to the actual route paths
"/knowledge" and "/skills" so useMatchRoute and other router utilities match
correctly and the active state displays.
web/src/styles.css-86-88 (1)

86-88: ⚠️ Potential issue | 🟠 Major

Fix stylelint configuration to support Tailwind v4 directives and quote BlinkMacSystemFont.

The .stylelintrc.json lacks configuration for Tailwind v4's @theme and @custom-variant directives, and BlinkMacSystemFont in the font-family list needs to be quoted to pass the value-keyword-case rule.

Required fixes
  1. Add to .stylelintrc.json (inside "rules"):
"at-rule-no-unknown": ["error", { "ignoreAtRules": ["theme", "custom-variant"] }]
  1. Quote BlinkMacSystemFont in web/src/styles.css line 87:
-  --font-sans: "Inter Variable", -apple-system, BlinkMacSystemFont, sans-serif;
+  --font-sans: "Inter Variable", -apple-system, "BlinkMacSystemFont", sans-serif;
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@web/src/styles.css` around lines 86 - 88, Update stylelint rules and quote
the font keyword: add the at-rule exception for Tailwind v4 by adding the rule
"at-rule-no-unknown" with ignoreAtRules including "theme" and "custom-variant"
to your .stylelintrc.json rules, and in web/src/styles.css update the
--font-sans value to quote BlinkMacSystemFont (i.e., "BlinkMacSystemFont") so it
satisfies the value-keyword-case rule; locate the font declaration in the `@theme`
block and the stylelint config rule named at-rule-no-unknown to make these
edits.
🟡 Minor comments (11)
web/src/components/design-system/status-dot.tsx-9-13 (1)

9-13: ⚠️ Potential issue | 🟡 Minor

Map amber tone to warning token, not accent token.

On Line 10, amber is wired to --color-accent (action). For semantic status signaling, amber should use the warning token.

Suggested fix
-      amber: "bg-[color:var(--color-accent)]",
+      amber: "bg-[color:var(--color-warning)]",

As per coding guidelines web/src/**/*.{ts,tsx}: “Follow the signal system for colors: accent #E8572A = action, ... #FFD60A = warning”.

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

In `@web/src/components/design-system/status-dot.tsx` around lines 9 - 13, The
amber tone mapping in the StatusDot color map is using the accent token; update
the amber entry so it uses the warning token instead (change the mapping for
"amber" from "bg-[color:var(--color-accent)]" to
"bg-[color:var(--color-warning)]" in the status-dot component where the color
map defines neutral, amber, green, violet, danger).
web/src/systems/skill/hooks/use-skill-actions.test.tsx-26-51 (1)

26-51: ⚠️ Potential issue | 🟡 Minor

Cover the rejected mutation path too.

Both cases only prove invalidation after a successful mutation. If either hook accidentally switches from onSettled to onSuccess, this suite still passes. Add one rejected-mutation case per hook and keep the invalidateQueries(...) expectation there as well.

As per coding guidelines, "Always invalidate TanStack Query cache after mutations via onSettled".

Also applies to: 63-88

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

In `@web/src/systems/skill/hooks/use-skill-actions.test.tsx` around lines 26 - 51,
Add a test that covers the rejected-mutation path to ensure cache invalidation
still happens in onSettled: in the existing test file add a case for
useEnableSkill where vi.mocked(enableSkill).mockRejectedValue(new
Error("fail")), call result.current.mutate(...) inside act, waitFor the mutation
to be in error (result.current.isError) and assert that the QueryClient
invalidateQueries spy was still called with { queryKey: ["skills", "list",
"ws_123"] }; do the same pattern for the other hook test(s) that mirror this one
(e.g., the disable-skill hook test) so both success and failure paths verify
invalidateQueries is invoked from onSettled.
web/src/systems/session/components/chat-header.tsx-46-50 (1)

46-50: ⚠️ Potential issue | 🟡 Minor

Empty session names will render a blank breadcrumb.

?? only falls back for null/undefined, so name: "" still produces no visible label here. A trimmed falsy fallback is safer for this UI string.

Possible fix
-          {session.name ?? session.id}
+          {session.name?.trim() || session.id}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@web/src/systems/session/components/chat-header.tsx` around lines 46 - 50, The
breadcrumb can render blank when session.name is an empty string because the
code uses the nullish coalescing operator; update the rendering logic in the
chat header component to treat empty/whitespace names as missing by using a
trimmed falsy fallback (e.g., check trimmed session.name and fall back to
session.id) for the element that uses session.name ?? session.id
(data-testid="session-name").
web/src/systems/session/components/tool-call-card.test.tsx-42-45 (1)

42-45: ⚠️ Potential issue | 🟡 Minor

This test never verifies the terminal icon.

tool-call-card is the outer wrapper, so this will still pass if the icon disappears. Assert the icon itself, or add a stable accessible hook for it, otherwise the test name is misleading.

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

In `@web/src/systems/session/components/tool-call-card.test.tsx` around lines 42 -
45, The test in tool-call-card.test.tsx currently only checks the outer wrapper
(getByTestId("tool-call-card")) so it doesn't verify the terminal icon; update
the test for the ToolCallCard component (the it block that renders <ToolCallCard
message={makeToolMessage()} />) to explicitly assert the terminal icon is
present by querying the icon itself (e.g.,
getByTestId("tool-call-terminal-icon") or getByRole/getByAltText with a stable
accessible name) or add a stable data-testid on the icon element inside
ToolCallCard to target in the test and replace the loose wrapper assertion with
a concrete icon assertion.
web/src/systems/skill/adapters/skill-api.test.ts-16-22 (1)

16-22: ⚠️ Potential issue | 🟡 Minor

Add vi.unstubAllGlobals() to clean up global stubs in all afterEach blocks.

vi.restoreAllMocks() only restores vi.spyOn() spies, not global stubs created with vi.stubGlobal(). Omitting vi.unstubAllGlobals() allows the mocked fetch to leak into subsequent test suites, causing unintended test pollution.

Suggested fix
   afterEach(() => {
     vi.restoreAllMocks();
+    vi.unstubAllGlobals();
   });

Also applies to: 85-91, 150-152, 192-194

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

In `@web/src/systems/skill/adapters/skill-api.test.ts` around lines 16 - 22, The
global fetch stub created in the beforeEach (vi.stubGlobal("fetch", ...)) is not
removed by vi.restoreAllMocks(); update the afterEach blocks in
web/src/systems/skill/adapters/skill-api.test.ts to call vi.unstubAllGlobals()
in addition to vi.restoreAllMocks() so the global stub is cleaned up; apply the
same change for all afterEach blocks corresponding to the other beforeEach
usages in this file (the blocks around the other vi.stubGlobal("fetch", ...)
calls).
web/src/systems/knowledge/components/knowledge-detail-panel.tsx-228-234 (1)

228-234: ⚠️ Potential issue | 🟡 Minor

"View in CLI" button has no onClick handler.

Similar to the "View full content" button, this button has no functionality. Add an onClick handler or indicate this is a placeholder.

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

In `@web/src/systems/knowledge/components/knowledge-detail-panel.tsx` around lines
228 - 234, The "View in CLI" button in KnowledgeDetailPanel (the JSX button with
data-testid="view-in-cli-btn" and the ExternalLink icon) lacks an onClick
handler; add a handler similar to the "View full content" button (e.g., a method
like handleViewInCli or reuse an existing view handler) that either performs the
intended action (open CLI modal/route/copy command) or explicitly marks the
button as a placeholder (onClick={() => {/* TODO: implement */}}) and/or
disables it (disabled prop and aria-disabled) so its lack of functionality is
clear to users and tests can target the handler by data-testid.
web/src/systems/skill/components/skill-list-panel.tsx-116-122 (1)

116-122: ⚠️ Potential issue | 🟡 Minor

Potential runtime error if description is undefined.

The filter at line 120 calls .toLowerCase() on s.description directly. If SkillPayload.description can be undefined or null, this will throw a runtime error. Apply nullish coalescing for safety.

🛡️ Proposed defensive fix
   const filtered = useMemo(() => {
     if (!searchQuery) return skills;
     const q = searchQuery.toLowerCase();
     return skills.filter(
-      s => s.name.toLowerCase().includes(q) || s.description.toLowerCase().includes(q)
+      s => s.name.toLowerCase().includes(q) || (s.description ?? "").toLowerCase().includes(q)
     );
   }, [skills, searchQuery]);
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@web/src/systems/skill/components/skill-list-panel.tsx` around lines 116 -
122, The filter in the useMemo for variable filtered calls
s.description.toLowerCase() which can throw if SkillPayload.description is
undefined/null; update the predicate in the useMemo (the skills.filter callback)
to safely handle missing descriptions by coercing description to an empty string
(e.g., use s.description ?? '' or optional chaining) before calling toLowerCase,
keeping the existing checks for s.name; no other logic changes required.
web/src/systems/knowledge/components/knowledge-detail-panel.tsx-63-69 (1)

63-69: ⚠️ Potential issue | 🟡 Minor

"View full content" button has no onClick handler.

The button is rendered but doesn't have any functionality attached. Consider either implementing the handler or adding a TODO comment indicating this is WIP.

💡 Add placeholder or TODO
       {content.length > 300 && (
         <button
-          className="mt-2 text-sm text-[color:var(--color-accent)] hover:text-[color:var(--color-accent-hover)]"
+          onClick={() => {}} // TODO: Implement full content modal/view
+          className="mt-2 text-sm text-[color:var(--color-accent)] hover:text-[color:var(--color-accent-hover)] cursor-pointer"
           data-testid="view-full-content-link"
         >
           View full content &rarr;
         </button>
       )}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@web/src/systems/knowledge/components/knowledge-detail-panel.tsx` around lines
63 - 69, The "View full content" button in the KnowledgeDetailPanel component is
rendered without any onClick handler; add a handler (e.g., create a
handleViewFullContent function inside KnowledgeDetailPanel) and wire it to the
button's onClick to either call a passed-in prop callback (like
onViewFullContent) or trigger the existing modal/navigation logic for showing
full content; if this feature is not yet implemented, add a clear TODO comment
next to the button and attach a placeholder onClick that prevents default and
logs/returns early so the UI is not dead.
web/src/routes/_app/knowledge.tsx-156-162 (1)

156-162: ⚠️ Potential issue | 🟡 Minor

formatDreamStatus() always returns "Dream: never" due to missing argument.

The function is called without the lastConsolidation parameter, so it will always display "Dream: never". If this is placeholder UI, consider adding a TODO. Otherwise, wire it to actual consolidation timestamp data.

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

In `@web/src/routes/_app/knowledge.tsx` around lines 156 - 162,
formatDreamStatus() is being called with no arguments so it always returns the
default "Dream: never"; fix by passing the actual consolidation timestamp into
it (e.g., call formatDreamStatus(lastConsolidation) using the relevant
consolidation variable from this component/props/state such as
conversation.lastConsolidation or a local lastConsolidation value), or if this
UI is a placeholder, add a TODO comment and leave a deliberate placeholder value
instead.
web/src/systems/skill/components/skill-detail-panel.tsx-209-215 (1)

209-215: ⚠️ Potential issue | 🟡 Minor

"View in CLI" button has no click handler.

Similar to "View full content", this button is rendered without functionality. Consider adding an onClick handler or disabling the button until the feature is implemented.

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

In `@web/src/systems/skill/components/skill-detail-panel.tsx` around lines 209 -
215, The "View in CLI" button in the SkillDetailPanel component is missing a
click handler; add an onClick that either calls an existing action to open the
skill in CLI (e.g., a helper like openSkillInCli(skill.id) or a prop method
passed into SkillDetailPanel such as onOpenInCli) or, if the feature isn't
ready, set the button to disabled and add aria-disabled and a tooltip explaining
it's coming soon; ensure you reference the button by its data-testid
"view-in-cli-btn" and update the component's props/state (SkillDetailPanel, the
relevant handler name: onOpenInCli or openSkillInCli) accordingly so the button
performs the action or is clearly disabled.
web/src/systems/skill/components/skill-detail-panel.tsx-64-66 (1)

64-66: ⚠️ Potential issue | 🟡 Minor

"View full content" button lacks click handler.

The button renders but has no onClick handler, making it non-functional. If this is intentional placeholder code, consider disabling it or adding a TODO comment.

Proposed fix
+      {content.length > 300 && (
+        <button
+          onClick={() => {/* TODO: implement full content view */}}
+          className="mt-2 text-sm text-[color:var(--color-accent)] hover:text-[color:var(--color-accent-hover)]"
+        >
          View full content
        </button>
      )}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@web/src/systems/skill/components/skill-detail-panel.tsx` around lines 64 -
66, The "View full content" button in SkillDetailPanel is missing an onClick
handler; either wire it up to the component's expand/show flow (e.g., call a
passed prop like onViewFull or toggle local state such as setIsExpanded /
setShowFullContent inside the SkillDetailPanel component) or explicitly disable
it and add a TODO comment. Update the button element to include onClick={() =>
onViewFull?.()} if you expect a parent callback, or onClick={() =>
setShowFullContent(true)} if using local state, or add disabled aria-disabled
and a // TODO: implement view full content comment to mark it as intentionally
non-functional.
🧹 Nitpick comments (7)
web/src/routes/-_app.test.tsx (1)

37-40: Test name is stronger than what is asserted.

The test says “inside the content area” but currently only checks outlet presence. Consider renaming it or adding a containment assertion.

Suggested minimal rename
-  it("renders outlet inside the content area", () => {
+  it("renders outlet", () => {
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@web/src/routes/-_app.test.tsx` around lines 37 - 40, The test name "renders
outlet inside the content area" is stronger than the assertion; update the test
in the AppLayout.spec by either renaming the it() string to "renders outlet" or
add a containment assertion using the existing outlet lookup
(getByTestId("outlet")) and the content container lookup (e.g.,
getByTestId("content-area") or the actual container test id used in AppLayout)
and assert containment (e.g., expect(contentContainer).toContainElement(outlet))
so the name matches the behavior.
web/src/routes/_app/skills.tsx (1)

160-165: Marketplace install handler is stubbed.

The onInstall={() => {}} and isInstalling={false} are placeholder implementations. If this is intentional WIP, consider adding a TODO comment or disabling the install button in the MarketplaceView to avoid user confusion.

💡 Optional: Add a TODO comment for clarity
       <MarketplaceView
         skills={skills ?? []}
         installedSkillNames={installedSkillNames}
-        onInstall={() => {}}
+        onInstall={() => {}} // TODO: Implement marketplace install flow
         isInstalling={false}
       />
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@web/src/routes/_app/skills.tsx` around lines 160 - 165, The Marketplace
install props are stubbed — replace the no-op with a real install handler and
loading state: add a useState hook (e.g., const [isInstalling, setIsInstalling]
= useState(false)) and implement a handler function (e.g., async function
handleInstallSkill(skillId) { setIsInstalling(true); try { await
installSkill(skillId); /* update installedSkillNames/state */ } finally {
setIsInstalling(false); } }) and pass <MarketplaceView
onInstall={handleInstallSkill} isInstalling={isInstalling} />; alternatively, if
this is intentionally WIP, add a clear TODO comment next to the MarketplaceView
and disable the install button in MarketplaceView until the handler
(handleInstallSkill) and isInstalling state are implemented.
web/src/systems/skill/components/skill-detail-panel.tsx (1)

24-46: Consider extracting badge color map to a shared constant or using CSS variables.

The colorMap hardcodes hex values with opacity suffixes (e.g., #30d15826). While functional, this could drift from design tokens. If these badge colors are reused elsewhere, consider defining them as CSS variables or a shared constant.

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

In `@web/src/systems/skill/components/skill-detail-panel.tsx` around lines 24 -
46, The SourceBadge component currently hardcodes the badge colorMap inside the
function (colorMap with hex+alpha values), which can drift from design tokens;
extract this mapping into a shared constant or CSS variables so it can be reused
and maintained centrally. Move the colorMap out of SourceBadge (e.g., export
const BADGE_COLORS or map to CSS custom properties) and update SourceBadge to
reference that shared constant or use class names/CSS vars instead of inline hex
strings; ensure to preserve the keys (bundled, workspace, marketplace, user,
additional) so existing references to source remain valid.
web/src/routes/_app/-knowledge.test.tsx (1)

272-285: Delete mutation assertion could be more specific.

The assertion uses expect.any(String) for both filename and scope. Consider asserting the actual expected values based on the auto-selected first memory (user_role.md, global).

More specific assertion
    expect(mockDeleteMutate).toHaveBeenCalledWith(
-      expect.objectContaining({
-        filename: expect.any(String),
-        scope: expect.any(String),
-      })
+      {
+        filename: "user_role.md",
+        scope: "global",
+        workspace: "ws_test",
+      }
    );
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@web/src/routes/_app/-knowledge.test.tsx` around lines 272 - 285, The test
"detail panel Delete button calls useDeleteMemory mutation" is too loose—replace
the generic expect.any(String) assertions for filename and scope with the
concrete expected values for the auto-selected first memory: assert that
mockDeleteMutate was called with an objectContaining { filename: "user_role.md",
scope: "global" } after clicking the delete button (targets: mockDeleteMutate,
"delete-memory-btn", KnowledgePage).
web/src/systems/skill/components/marketplace-view.tsx (1)

138-147: Category filter description fallback may produce false positives.

When tags aren't available, the filter falls back to checking if the description contains the category string (e.g., "testing"). This could match unintended skills (e.g., a skill mentioning "testing" in its description but not actually in that category).

Consider either removing the fallback or making it stricter (e.g., word-boundary match).

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

In `@web/src/systems/skill/components/marketplace-view.tsx` around lines 138 -
147, The category filter in marketplace-view.tsx currently falls back from
checking s.metadata?.tags to matching activeCategory against s.description,
which causes false positives; update the filter in the activeCategory branch
(the result.filter callback using activeCategory, s.metadata?.tags and
s.description) to stop using a raw substring fallback — either remove the
description fallback entirely and only accept items with Array.isArray(tags)
that match the category, or replace the fallback with a stricter word-boundary,
case-insensitive match (e.g., build a regex using the lowercased activeCategory
with \b boundaries) so only whole-word category matches are accepted.
internal/api/core/skills_test.go (1)

338-367: Enable/Disable tests verify response but not actual state mutation.

The tests confirm the endpoint returns ok: true, but don't verify that skill.Enabled was actually toggled. If the handler is supposed to persist the state change, consider asserting the skill's enabled state after the call.

Example: verify state change
 	t.Run("returns ok true on success", func(t *testing.T) {
 		t.Parallel()

 		skill := testSkill()
 		skill.Enabled = false
 		registry := &stubSkillsRegistry{
 			GetFn: func(name string) (*skills.Skill, bool) {
 				if name == "test-skill" {
 					return skill, true
 				}
 				return nil, false
 			},
 		}
 		engine := newSkillsHandlerFixture(t, registry, testutil.StubWorkspaceService{})
 		rec := testutil.PerformRequest(t, engine, http.MethodPost, "/api/skills/test-skill/enable?workspace=ws-1", nil)

 		if rec.Code != http.StatusOK {
 			t.Fatalf("status = %d, want %d; body=%s", rec.Code, http.StatusOK, rec.Body.String())
 		}

+		// Verify state was mutated
+		if !skill.Enabled {
+			t.Error("skill.Enabled = false after enable, want true")
+		}
+
 		var resp contract.SkillActionResponse
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@internal/api/core/skills_test.go` around lines 338 - 367, The test
TestEnableSkill only asserts the HTTP response but not that the skill's state
changed; after decoding the response in TestEnableSkill (using the local skill
variable returned by testSkill and the stub registry), add an assertion that
skill.Enabled is true (i.e., verify the handler mutated/persisted the skill
state). Locate the same pattern around TestDisableSkill (if present) and
similarly assert skill.Enabled is false after calling the disable endpoint; use
the existing skill, registry (stubSkillsRegistry), and newSkillsHandlerFixture
references to perform the assertions.
web/src/styles.css (1)

43-79: Map the shadcn aliases to the new palette tokens.

These semantic vars are repeating raw hex values instead of consuming the --color-* tokens defined above, which makes the palette easier to drift the next time someone updates a color.

Suggested cleanup
-  --background: `#121212`;
-  --foreground: `#e5e5e7`;
-  --card: `#1c1c1e`;
-  --card-foreground: `#e5e5e7`;
-  --popover: `#2c2c2e`;
-  --popover-foreground: `#e5e5e7`;
-  --primary: `#e8572a`;
+  --background: var(--color-canvas);
+  --foreground: var(--color-text-primary);
+  --card: var(--color-surface);
+  --card-foreground: var(--color-text-primary);
+  --popover: var(--color-surface-elevated);
+  --popover-foreground: var(--color-text-primary);
+  --primary: var(--color-accent);
   --primary-foreground: `#ffffff`;
   --secondary: transparent;
-  --secondary-foreground: `#e5e5e7`;
-  --muted: `#2c2c2e`;
-  --muted-foreground: `#8e8e93`;
-  --accent: `#2c2c2e`;
-  --accent-foreground: `#e5e5e7`;
-  --destructive: `#ff453a`;
-  --destructive-foreground: `#e5e5e7`;
-  --border: `#3a3a3c`;
-  --input: `#3a3a3c`;
-  --ring: `#e8572a`;
+  --secondary-foreground: var(--color-text-primary);
+  --muted: var(--color-surface-elevated);
+  --muted-foreground: var(--color-text-secondary);
+  --accent: var(--color-surface-elevated);
+  --accent-foreground: var(--color-text-primary);
+  --destructive: var(--color-danger);
+  --destructive-foreground: var(--color-text-primary);
+  --border: var(--color-divider);
+  --input: var(--color-divider);
+  --ring: var(--color-accent);

As per coding guidelines, web/src/**/*.{tsx,css}: Use Tailwind CSS v4 with only defined design tokens from DESIGN.md; implement flat depth model without shadows

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

In `@web/src/styles.css` around lines 43 - 79, Replace the hardcoded hex values in
the shadcn semantic variables (e.g., --background, --foreground, --card,
--primary, --muted, --accent, --destructive, --border, --input, --ring,
--chart-1..-5, --sidebar, --sidebar-foreground, --sidebar-primary,
--sidebar-accent, --sidebar-border, --success, etc.) so they reference the
existing design palette tokens (the --color-* variables) instead of raw hex
literals; update each declaration in styles.css to use var(--color-<semantic>)
(e.g., --background: var(--color-background); --primary: var(--color-primary);
--muted: var(--color-muted); --destructive: var(--color-destructive); --chart-1:
var(--color-chart-1); etc.), ensuring all shadcn aliases consume the normalized
--color-* tokens and follow the flat token model per DESIGN.md.

Comment thread internal/api/core/skills.go
Comment thread internal/api/core/skills.go
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: 4

🧹 Nitpick comments (10)
web/src/systems/session/components/message-bubble.tsx (2)

16-19: Consider defensive handling for invalid timestamps.

If message.timestamp is undefined, NaN, or 0, this will produce unexpected output ("Invalid Date" or epoch time). Consider adding a guard or fallback.

🛡️ Optional defensive fix
 function formatTimestamp(ts: number): string {
+  if (!ts || !Number.isFinite(ts)) return "";
   const d = new Date(ts);
   return d.toLocaleTimeString([], { hour: "2-digit", minute: "2-digit" });
 }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@web/src/systems/session/components/message-bubble.tsx` around lines 16 - 19,
The formatTimestamp function should defensively handle invalid timestamps
(undefined, NaN, 0) to avoid "Invalid Date" or epoch output: in
formatTimestamp(ts: number) validate ts (e.g., check typeof ts === "number" &&
isFinite(ts) && ts > 0) and fall back to a sensible value such as Date.now() or
an empty string; update any call sites that pass message.timestamp (e.g., where
message.timestamp is used) to rely on the sanitized output from formatTimestamp
so UI never displays "Invalid Date" or epoch time.

81-88: Consider extracting duplicate prose styles to a shared constant.

The same prose class configuration appears in both user and assistant bubbles (lines 41-46 and 82-87). Extracting to a constant would reduce duplication.

♻️ Optional DRY refactor
+const proseClasses = cn(
+  "prose prose-sm prose-invert max-w-none",
+  "prose-p:my-1 prose-headings:mb-2 prose-headings:mt-4",
+  "prose-ul:my-1 prose-ol:my-1 prose-li:my-0.5",
+  "prose-pre:my-0 prose-pre:bg-transparent prose-pre:p-0"
+);
+
 export const MessageBubble = memo(

Then use it in both bubbles:

<div className={cn(proseClasses, "text-sm leading-relaxed text-[color:var(--color-text-primary)]")}>

Also applies to: 39-47

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

In `@web/src/systems/session/components/message-bubble.tsx` around lines 81 - 88,
The prose class string is duplicated between the user and assistant message
bubbles in message-bubble.tsx; extract the repeated CN string into a shared
constant (e.g., const proseClasses = "prose prose-sm prose-invert max-w-none
prose-p:my-1 prose-headings:mb-2 prose-headings:mt-4 prose-ul:my-1 prose-ol:my-1
prose-li:my-0.5 prose-pre:my-0 prose-pre:bg-transparent prose-pre:p-0") and
replace both className calls (the divs in the user and assistant branches inside
the MessageBubble component) to use cn(proseClasses, "<per-bubble text
color/size classes>") so only the per-bubble overrides remain; update any nearby
references to cn or imports if necessary.
web/src/systems/session/components/tool-call-card.test.tsx (1)

47-51: Optional: avoid duplicate DOM lookup in the executing-state test.

You can query tool-card-executing once and reuse the element to keep the test slightly cleaner.

Proposed small cleanup
   it("shows tool name in executing state", () => {
     render(<ToolCallCard message={makeToolMessage()} />);
-    expect(screen.getByTestId("tool-card-executing")).toBeInTheDocument();
-    expect(screen.getByTestId("tool-card-executing")).toHaveTextContent("Reading...");
+    const executing = screen.getByTestId("tool-card-executing");
+    expect(executing).toBeInTheDocument();
+    expect(executing).toHaveTextContent("Reading...");
   });
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@web/src/systems/session/components/tool-call-card.test.tsx` around lines 47 -
51, The test "shows tool name in executing state" in ToolCallCard should avoid
duplicate DOM lookups: query the element with test id "tool-card-executing" once
(e.g., store the result of screen.getByTestId("tool-card-executing") in a
variable) and then run both assertions against that variable so you reuse the
element instead of calling getByTestId twice.
web/src/systems/knowledge/adapters/knowledge-api.test.ts (1)

179-247: Consider adding filename encoding test for writeMemory for parity.

The readMemory tests include an explicit test for filename URL encoding (line 166-176), but writeMemory lacks this. Since the implementation does encode the filename, adding a similar test would improve coverage consistency.

💡 Optional test to add
+  it("encodes filename in URL", async () => {
+    vi.mocked(fetch).mockResolvedValue({
+      ok: true,
+      json: () => Promise.resolve({ ok: true }),
+    } as Response);
+
+    await writeMemory("my file.md", "content", "global");
+    expect(fetch).toHaveBeenCalledWith("/api/memory/my%20file.md", expect.any(Object));
+  });
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@web/src/systems/knowledge/adapters/knowledge-api.test.ts` around lines 179 -
247, Add a unit test for writeMemory that verifies filenames are URL-encoded:
call writeMemory with a filename containing spaces/special characters (e.g., "my
file `@1.md`") and a sample content/scope/workspace, mock fetch to return a
successful JSON response, then assert fetch was called with the encoded path
(e.g., "/api/memory/my%20file%20%401.md") and the expected options (method
"PUT", Content-Type header, JSON stringified body, and signal if provided).
Target the existing writeMemory tests (same describe block) and mirror the shape
of the readMemory filename-encoding test to ensure parity.
web/src/systems/knowledge/components/knowledge-detail-panel.tsx (1)

113-118: Move scope orchestration out of the component.

This component derives mutation input (scope) and feeds it into onDelete, which mixes orchestration into a presentational layer. Prefer passing scope (or a pre-bound delete handler) from the route/page.

As per coding guidelines, UI components MUST be pure and presentational; orchestration logic lives in pages/routes.

Also applies to: 179-183, 225-225

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

In `@web/src/systems/knowledge/components/knowledge-detail-panel.tsx` around lines
113 - 118, The component contains orchestration by deriving a deletion scope via
deriveScope(mem: MemoryHeader) and calling onDelete with that computed scope;
pull that logic out of the presentational component (KnowledgeDetailPanel) and
instead accept either a pre-computed scope prop or a pre-bound delete handler
(e.g., onDeleteWithScope) from the route/page that owns orchestration. Remove
calls to deriveScope inside the component (and any uses at the other noted
locations) and update the component props signature to take scope: string or
onDeleteBound: (id: string) => Promise<void>, then update the parent/route to
compute deriveScope(mem) and pass the result or bound handler into the
component.
web/src/systems/skill/components/skill-detail-panel.tsx (2)

3-4: Consider consistent import ordering.

The relative import (../types) comes before the alias import (@/lib/utils). While not breaking, consistent ordering (aliases first or relatives first) improves readability.

♻️ Reorder imports
+import { cn } from "@/lib/utils";
+
 import type { SkillPayload } from "../types";
-import { cn } from "@/lib/utils";
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@web/src/systems/skill/components/skill-detail-panel.tsx` around lines 3 - 4,
The imports in skill-detail-panel.tsx are out of the preferred order (relative
import of SkillPayload before alias import of cn); reorder them so alias imports
come first — e.g., import { cn } from "@/lib/utils" before importing type {
SkillPayload } from "../types" — to keep import ordering consistent and
readable.

67-91: Content preview truncation could use a named constant.

The magic number 300 appears twice. Extracting it improves maintainability.

♻️ Extract constant
+const CONTENT_PREVIEW_MAX_LENGTH = 300;
+
 function ContentPreviewCard({ content }: { content: string }) {
-  const preview = content.length > 300 ? `${content.slice(0, 300)}...` : content;
+  const preview = content.length > CONTENT_PREVIEW_MAX_LENGTH 
+    ? `${content.slice(0, CONTENT_PREVIEW_MAX_LENGTH)}...` 
+    : content;
   // ...
-      {content.length > 300 && (
+      {content.length > CONTENT_PREVIEW_MAX_LENGTH && (
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@web/src/systems/skill/components/skill-detail-panel.tsx` around lines 67 -
91, The magic number 300 in ContentPreviewCard should be extracted to a named
constant (e.g., CONTENT_PREVIEW_LIMIT or PREVIEW_CHAR_LIMIT) and used in place
of the two literal occurrences; update the preview calculation (currently using
content.slice(0, 300)) and the conditional check (content.length > 300) to
reference that constant so future changes only require updating one identifier.
web/src/components/app-sidebar.test.tsx (1)

43-53: Collapsible mock ignores defaultOpen prop.

The mock always renders data-state="open" regardless of the defaultOpen prop value. This could mask bugs where the component relies on collapsed-by-default behavior (e.g., agents with zero sessions should start collapsed per the actual component logic at Line 150 of app-sidebar.tsx).

♻️ Optional: Respect defaultOpen in mock
 vi.mock("@/components/ui/collapsible", () => ({
-  Collapsible: ({ children, className }: { children: ReactNode; className?: string }) => (
-    <div className={className} data-state="open">
+  Collapsible: ({ children, className, defaultOpen = true }: { children: ReactNode; className?: string; defaultOpen?: boolean }) => (
+    <div className={className} data-state={defaultOpen ? "open" : "closed"}>
       {children}
     </div>
   ),
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@web/src/components/app-sidebar.test.tsx` around lines 43 - 53, The
Collapsible mock always forces data-state="open" and ignores the defaultOpen
prop, which hides collapse-by-default behavior used in app-sidebar (e.g., agents
with zero sessions); update the vi.mock for Collapsible to accept a defaultOpen
prop on the Collapsible component and set data-state conditionally (e.g., "open"
when defaultOpen is true, otherwise "closed"), preserving the existing
CollapsibleContent and CollapsibleTrigger shapes so tests reflect real collapsed
vs. open states used by the app-sidebar component.
internal/api/core/skills_test.go (1)

19-55: Share this stub via internal/api/testutil.

This helper is now duplicated with internal/api/udsapi/helpers_test.go Lines 32-65. Moving it into internal/api/testutil will keep core.SkillsRegistry fixture behavior in one place and prevent the copies from drifting on the next interface change.

As per coding guidelines, "Use shared test helpers from internal/testutil and internal/api/testutil."

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

In `@internal/api/core/skills_test.go` around lines 19 - 55, The
stubSkillsRegistry test helper is duplicated; extract the type and its methods
(stubSkillsRegistry, Get, List, ForWorkspace, SetEnabled) into a shared package
internal/api/testutil and replace the duplicate in
internal/api/udsapi/helpers_test.go by importing that package; update tests to
use testutil.stubSkillsRegistry (or the exported name you choose), remove the
duplicate definition from both places, and run tests to ensure imports and
package names are correct so future core.SkillsRegistry interface changes stay
centralized.
internal/skills/registry_test.go (1)

1113-1157: Cover the SetEnabled error branches with subtests.

This only exercises the happy-path toggle. The new contract also rejects blank names and unknown skills, and those are the branches that will drive handler error mapping. Please fold this into t.Run("Should...") cases so the full behavior is pinned down.

As per coding guidelines, "Use table-driven tests with subtests (t.Run) as default" and "MUST use t.Run("Should...") pattern for ALL test cases."

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

In `@internal/skills/registry_test.go` around lines 1113 - 1157, Refactor
TestRegistrySetEnabled into table-driven subtests using t.Run("Should ...") so
it covers not only the happy-path toggle but also the error branches: call
registry.SetEnabled with a blank name and with an unknown skill name and assert
the expected error responses; keep the existing happy-path checks for
globalSkills, wsCache, and registry.cfg.DisabledSkills, and ensure each case
references registry.SetEnabled and inspects registry.globalSkills,
registry.wsCache["ws-1"].skills, and registry.cfg.DisabledSkills to validate
state changes or returned errors.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@internal/api/core/interfaces.go`:
- Around line 51-56: SetEnabled on the SkillsRegistry is underspecified for
workspace-scoped skills and can’t distinguish toggling a workspace override from
the global/shared skill; change the SkillsRegistry interface to carry workspace
identity (e.g., replace SetEnabled(name string, enabled bool) error with
something that includes workspace context or a unique skill id — for example
SetEnabled(name string, workspace *workspacepkg.ResolvedWorkspace, enabled bool)
error or SetEnabledByID(skillID string, enabled bool) error), update the
implementation sites that call SetEnabled (notably the code that uses
ForWorkspace and the tests in registry_test.go), and adjust
internal/api/core/skills.go usage so enable/disable operates on the intended
workspace-scoped skill rather than mutating all same-name copies.

In `@internal/skills/registry.go`:
- Around line 187-229: The DisabledSkills slice is mutated under r.mu in
SetEnabled while applyDisabled (used by loadGlobalSkills/loadWorkspaceSkills and
by ForWorkspace/RefreshGlobal) reads it without locking, causing races; fix by
introducing a locked snapshot or accessor: add a method (e.g.,
getDisabledSkillsSnapshot) that acquires r.mu and returns a shallow copy of
r.cfg.DisabledSkills, then update loadGlobalSkills, loadWorkspaceSkills,
ForWorkspace, RefreshGlobal and any applyDisabled callers to use that snapshot
(or accept the slice as a parameter) instead of reading r.cfg.DisabledSkills
directly so all reads use the copied slice and races are eliminated.

In `@web/src/systems/knowledge/components/knowledge-detail-panel.tsx`:
- Line 5: Change the relative import of MemoryHeader in
knowledge-detail-panel.tsx to use the project path-alias form (start with "@/")
instead of "../types"; update the import that currently references MemoryHeader
to import from the aliased module path (e.g., "@/...") so it follows the "Use
path alias `@/`* to map to ./src/*" guideline and keeps the symbol MemoryHeader
intact.
- Around line 120-131: formatDateTime currently constructs a Date from dateStr
but relies on a try/catch that never fires for invalid dates; update
formatDateTime to create the Date once (e.g., const date = new Date(dateStr)),
check validity with Number.isNaN(date.getTime()) and if invalid return the
original dateStr, otherwise call date.toLocaleString(...) to format; ensure you
reference the formatDateTime function when making the change.

---

Nitpick comments:
In `@internal/api/core/skills_test.go`:
- Around line 19-55: The stubSkillsRegistry test helper is duplicated; extract
the type and its methods (stubSkillsRegistry, Get, List, ForWorkspace,
SetEnabled) into a shared package internal/api/testutil and replace the
duplicate in internal/api/udsapi/helpers_test.go by importing that package;
update tests to use testutil.stubSkillsRegistry (or the exported name you
choose), remove the duplicate definition from both places, and run tests to
ensure imports and package names are correct so future core.SkillsRegistry
interface changes stay centralized.

In `@internal/skills/registry_test.go`:
- Around line 1113-1157: Refactor TestRegistrySetEnabled into table-driven
subtests using t.Run("Should ...") so it covers not only the happy-path toggle
but also the error branches: call registry.SetEnabled with a blank name and with
an unknown skill name and assert the expected error responses; keep the existing
happy-path checks for globalSkills, wsCache, and registry.cfg.DisabledSkills,
and ensure each case references registry.SetEnabled and inspects
registry.globalSkills, registry.wsCache["ws-1"].skills, and
registry.cfg.DisabledSkills to validate state changes or returned errors.

In `@web/src/components/app-sidebar.test.tsx`:
- Around line 43-53: The Collapsible mock always forces data-state="open" and
ignores the defaultOpen prop, which hides collapse-by-default behavior used in
app-sidebar (e.g., agents with zero sessions); update the vi.mock for
Collapsible to accept a defaultOpen prop on the Collapsible component and set
data-state conditionally (e.g., "open" when defaultOpen is true, otherwise
"closed"), preserving the existing CollapsibleContent and CollapsibleTrigger
shapes so tests reflect real collapsed vs. open states used by the app-sidebar
component.

In `@web/src/systems/knowledge/adapters/knowledge-api.test.ts`:
- Around line 179-247: Add a unit test for writeMemory that verifies filenames
are URL-encoded: call writeMemory with a filename containing spaces/special
characters (e.g., "my file `@1.md`") and a sample content/scope/workspace, mock
fetch to return a successful JSON response, then assert fetch was called with
the encoded path (e.g., "/api/memory/my%20file%20%401.md") and the expected
options (method "PUT", Content-Type header, JSON stringified body, and signal if
provided). Target the existing writeMemory tests (same describe block) and
mirror the shape of the readMemory filename-encoding test to ensure parity.

In `@web/src/systems/knowledge/components/knowledge-detail-panel.tsx`:
- Around line 113-118: The component contains orchestration by deriving a
deletion scope via deriveScope(mem: MemoryHeader) and calling onDelete with that
computed scope; pull that logic out of the presentational component
(KnowledgeDetailPanel) and instead accept either a pre-computed scope prop or a
pre-bound delete handler (e.g., onDeleteWithScope) from the route/page that owns
orchestration. Remove calls to deriveScope inside the component (and any uses at
the other noted locations) and update the component props signature to take
scope: string or onDeleteBound: (id: string) => Promise<void>, then update the
parent/route to compute deriveScope(mem) and pass the result or bound handler
into the component.

In `@web/src/systems/session/components/message-bubble.tsx`:
- Around line 16-19: The formatTimestamp function should defensively handle
invalid timestamps (undefined, NaN, 0) to avoid "Invalid Date" or epoch output:
in formatTimestamp(ts: number) validate ts (e.g., check typeof ts === "number"
&& isFinite(ts) && ts > 0) and fall back to a sensible value such as Date.now()
or an empty string; update any call sites that pass message.timestamp (e.g.,
where message.timestamp is used) to rely on the sanitized output from
formatTimestamp so UI never displays "Invalid Date" or epoch time.
- Around line 81-88: The prose class string is duplicated between the user and
assistant message bubbles in message-bubble.tsx; extract the repeated CN string
into a shared constant (e.g., const proseClasses = "prose prose-sm prose-invert
max-w-none prose-p:my-1 prose-headings:mb-2 prose-headings:mt-4 prose-ul:my-1
prose-ol:my-1 prose-li:my-0.5 prose-pre:my-0 prose-pre:bg-transparent
prose-pre:p-0") and replace both className calls (the divs in the user and
assistant branches inside the MessageBubble component) to use cn(proseClasses,
"<per-bubble text color/size classes>") so only the per-bubble overrides remain;
update any nearby references to cn or imports if necessary.

In `@web/src/systems/session/components/tool-call-card.test.tsx`:
- Around line 47-51: The test "shows tool name in executing state" in
ToolCallCard should avoid duplicate DOM lookups: query the element with test id
"tool-card-executing" once (e.g., store the result of
screen.getByTestId("tool-card-executing") in a variable) and then run both
assertions against that variable so you reuse the element instead of calling
getByTestId twice.

In `@web/src/systems/skill/components/skill-detail-panel.tsx`:
- Around line 3-4: The imports in skill-detail-panel.tsx are out of the
preferred order (relative import of SkillPayload before alias import of cn);
reorder them so alias imports come first — e.g., import { cn } from
"@/lib/utils" before importing type { SkillPayload } from "../types" — to keep
import ordering consistent and readable.
- Around line 67-91: The magic number 300 in ContentPreviewCard should be
extracted to a named constant (e.g., CONTENT_PREVIEW_LIMIT or
PREVIEW_CHAR_LIMIT) and used in place of the two literal occurrences; update the
preview calculation (currently using content.slice(0, 300)) and the conditional
check (content.length > 300) to reference that constant so future changes only
require updating one identifier.
🪄 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

Run ID: 7ff968ab-23bc-4190-8667-5ddd3c4818f5

📥 Commits

Reviewing files that changed from the base of the PR and between 0c569ba and 3f17a3d.

📒 Files selected for processing (38)
  • internal/api/core/interfaces.go
  • internal/api/core/skills.go
  • internal/api/core/skills_test.go
  • internal/api/udsapi/helpers_test.go
  • internal/api/udsapi/server.go
  • internal/api/udsapi/server_test.go
  • internal/skills/registry.go
  • internal/skills/registry_test.go
  • web/src/components/app-sidebar.test.tsx
  • web/src/components/app-sidebar.tsx
  • web/src/components/design-system/design-system-showcase.tsx
  • web/src/components/design-system/status-dot.tsx
  • web/src/routes/-_app.test.tsx
  • web/src/routes/_app.tsx
  • web/src/routes/_app/-knowledge.test.tsx
  • web/src/routes/_app/knowledge.tsx
  • web/src/routes/_app/skills.tsx
  • web/src/styles.css
  • web/src/styles.test.ts
  • web/src/systems/knowledge/adapters/knowledge-api.test.ts
  • web/src/systems/knowledge/adapters/knowledge-api.ts
  • web/src/systems/knowledge/components/knowledge-detail-panel.tsx
  • web/src/systems/knowledge/hooks/use-knowledge-actions.test.tsx
  • web/src/systems/knowledge/hooks/use-knowledge-actions.ts
  • web/src/systems/knowledge/hooks/use-knowledge.test.tsx
  • web/src/systems/knowledge/hooks/use-knowledge.ts
  • web/src/systems/knowledge/lib/query-options.test.ts
  • web/src/systems/knowledge/lib/query-options.ts
  • web/src/systems/session/components/chat-header.tsx
  • web/src/systems/session/components/message-bubble.tsx
  • web/src/systems/session/components/tool-call-card.test.tsx
  • web/src/systems/session/components/tool-call-card.tsx
  • web/src/systems/skill/adapters/skill-api.test.ts
  • web/src/systems/skill/components/marketplace-view.tsx
  • web/src/systems/skill/components/skill-detail-panel.tsx
  • web/src/systems/skill/components/skill-list-panel.tsx
  • web/src/systems/skill/hooks/use-skill-actions.test.tsx
  • web/src/systems/skill/hooks/use-skills.ts
✅ Files skipped from review due to trivial changes (7)
  • web/src/components/design-system/status-dot.tsx
  • web/src/systems/skill/hooks/use-skills.ts
  • web/src/systems/knowledge/hooks/use-knowledge.ts
  • web/src/systems/knowledge/lib/query-options.test.ts
  • web/src/systems/knowledge/lib/query-options.ts
  • web/src/routes/_app/-knowledge.test.tsx
  • web/src/systems/knowledge/hooks/use-knowledge-actions.test.tsx
🚧 Files skipped from review as they are similar to previous changes (14)
  • web/src/routes/-_app.test.tsx
  • internal/api/udsapi/server.go
  • web/src/routes/_app.tsx
  • web/src/systems/knowledge/hooks/use-knowledge-actions.ts
  • web/src/components/design-system/design-system-showcase.tsx
  • web/src/systems/knowledge/hooks/use-knowledge.test.tsx
  • web/src/systems/session/components/chat-header.tsx
  • web/src/systems/skill/hooks/use-skill-actions.test.tsx
  • web/src/systems/skill/adapters/skill-api.test.ts
  • web/src/systems/skill/components/skill-list-panel.tsx
  • web/src/styles.test.ts
  • web/src/systems/session/components/tool-call-card.tsx
  • web/src/systems/skill/components/marketplace-view.tsx
  • web/src/routes/_app/knowledge.tsx

Comment thread internal/api/core/interfaces.go Outdated
Comment thread internal/skills/registry.go Outdated
Comment thread web/src/systems/knowledge/components/knowledge-detail-panel.tsx Outdated
Comment thread web/src/systems/knowledge/components/knowledge-detail-panel.tsx
@pedronauck pedronauck merged commit 63f8615 into main Apr 9, 2026
1 check was pending
@pedronauck pedronauck deleted the pn/ui-v2 branch April 9, 2026 17:43
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: 4

🧹 Nitpick comments (3)
web/src/routes/_app/-knowledge.test.tsx (1)

51-55: Make useMemories mock parameter-aware to actually validate tab filtering.

The current mock returns the same dataset regardless of scope/workspace, so the tab tests mainly validate styling, not route-to-hook filtering behavior.

Suggested test-mock refinement
-    useMemories: () => ({
-      data: mockMemories,
+    useMemories: (scope?: string) => ({
+      data: mockMemories.filter(memory => {
+        const isWorkspace = memory.filename.startsWith("workspace/") || memory.filename.startsWith("ws/");
+        if (!scope || scope === "all") return true;
+        if (scope === "workspace") return isWorkspace;
+        if (scope === "global") return !isWorkspace;
+        return true;
+      }),
       isLoading: mockMemoriesLoading,
       error: mockMemoriesError,
     }),

Based on learnings: "Check dependent package APIs before writing integration code or tests".

Also applies to: 167-186

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

In `@web/src/routes/_app/-knowledge.test.tsx` around lines 51 - 55, The
useMemories mock currently returns mockMemories regardless of incoming params,
so update the mock implementation for useMemories to be parameter-aware (inspect
its arguments like scope/workspace) and return a filtered array based on those
params (e.g., filter mockMemories by memory.scope or memory.workspace), then
adjust the tests to call the component with the appropriate route/query params
and assert the filtered results; apply the same change to the other mocked
hook(s) referenced around the later block (lines 167-186) so all tab/filter
tests validate real route->hook behavior rather than only styling.
internal/api/httpapi/server.go (1)

241-249: Consider validating skillsRegistry if it's a required dependency.

The server validates sessions, observer, and workspaces as required but does not validate skillsRegistry. If skills endpoints are called without a registry, this could result in nil pointer dereferences in handlers. If the registry is intentionally optional (for backward compatibility), consider adding nil checks in the handlers.

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

In `@internal/api/httpapi/server.go` around lines 241 - 249, The init validation
currently checks server.sessions, server.observer, and server.workspaces but
omits server.skillsRegistry; add a nil check for skillsRegistry alongside the
existing checks (i.e., return an error like "httpapi: skills registry is
required") if the registry must be present, or if it is intentionally optional,
add guards in the skills-related handlers (the handlers referencing
skillsRegistry) to return a clear error response when skillsRegistry is nil.
Locate the initialization code that checks
server.sessions/server.observer/server.workspaces and either add the
skillsRegistry validation there or add nil checks inside the skills endpoints'
handler functions to avoid nil pointer dereferences.
internal/api/testutil/apitest.go (1)

210-251: Add compile-time interface verification for StubSkillsRegistry.

The file includes compile-time interface checks for other stubs (lines 407-409) but not for StubSkillsRegistry.

♻️ Proposed fix

Add at the end of the file (after line 409):

var _ core.SkillsRegistry = StubSkillsRegistry{}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@internal/api/testutil/apitest.go` around lines 210 - 251, Add a compile-time
interface assertion to ensure StubSkillsRegistry implements core.SkillsRegistry
by adding the line creating the var assertion for these types (var _
core.SkillsRegistry = StubSkillsRegistry{}) to the end of the file; locate the
StubSkillsRegistry type and append that var assertion after its method
definitions so the compiler verifies the implementation.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@web/src/routes/_app/-knowledge.test.tsx`:
- Line 68: The test imports Route via a relative path ("./knowledge"); update
the import to use the project path alias instead (use the `@/`* map to src/*) so
the import reads from the aliased module and keeps the same exported symbol
(Route) — e.g., replace the current "./knowledge" import with the corresponding
"@/routes/_app/knowledge" aliased import and keep the named import "Route"
unchanged.

In `@web/src/routes/_app/knowledge.tsx`:
- Around line 35-47: The function formatDreamStatus can produce "NaN" when given
invalid date strings because new Date(...) doesn't throw; update
formatDreamStatus to validate the parsed date (e.g., const date = new
Date(lastConsolidation); if (isNaN(date.getTime())) return "Dream: unknown";)
before computing diffMs and diffH, so malformed timestamps return the fallback
message rather than producing "NaN" output.

In `@web/src/routes/_app/skills.tsx`:
- Around line 39-40: The code sets activeWorkspaceId = workspaces?.[0]?.id ?? ""
which yields an empty string when no workspace exists and causes downstream
skill API hooks to request with ?workspace=; change this to produce undefined
when no workspace (e.g., const activeWorkspaceId = workspaces?.[0]?.id ??
undefined) and update all places that call the skill API hooks to only pass
activeWorkspaceId when it is truthy or to skip/inhibit the hook calls when
activeWorkspaceId is undefined (for example wrap calls to the skill hooks or
conditionally render components depending on useWorkspaces and
activeWorkspaceId). Ensure the hooks that receive workspace param are invoked
only when activeWorkspaceId is valid.

In `@web/src/systems/skill/adapters/skill-api.test.ts`:
- Around line 154-188: Add tests for error handling in getSkillContent: add one
test that mocks fetch returning { ok: false, status: 404 } and asserts
getSkillContent throws a SkillApiError (or rejects) for not-found, and another
test that mocks fetch returning ok: false with a non-404 status (e.g., 500) and
asserts it throws a SkillApiError for general non-2xx responses; reference the
getSkillContent function and SkillApiError in the assertions and ensure the
fetch call URL encoding behavior is preserved in these error tests.

---

Nitpick comments:
In `@internal/api/httpapi/server.go`:
- Around line 241-249: The init validation currently checks server.sessions,
server.observer, and server.workspaces but omits server.skillsRegistry; add a
nil check for skillsRegistry alongside the existing checks (i.e., return an
error like "httpapi: skills registry is required") if the registry must be
present, or if it is intentionally optional, add guards in the skills-related
handlers (the handlers referencing skillsRegistry) to return a clear error
response when skillsRegistry is nil. Locate the initialization code that checks
server.sessions/server.observer/server.workspaces and either add the
skillsRegistry validation there or add nil checks inside the skills endpoints'
handler functions to avoid nil pointer dereferences.

In `@internal/api/testutil/apitest.go`:
- Around line 210-251: Add a compile-time interface assertion to ensure
StubSkillsRegistry implements core.SkillsRegistry by adding the line creating
the var assertion for these types (var _ core.SkillsRegistry =
StubSkillsRegistry{}) to the end of the file; locate the StubSkillsRegistry type
and append that var assertion after its method definitions so the compiler
verifies the implementation.

In `@web/src/routes/_app/-knowledge.test.tsx`:
- Around line 51-55: The useMemories mock currently returns mockMemories
regardless of incoming params, so update the mock implementation for useMemories
to be parameter-aware (inspect its arguments like scope/workspace) and return a
filtered array based on those params (e.g., filter mockMemories by memory.scope
or memory.workspace), then adjust the tests to call the component with the
appropriate route/query params and assert the filtered results; apply the same
change to the other mocked hook(s) referenced around the later block (lines
167-186) so all tab/filter tests validate real route->hook behavior rather than
only styling.
🪄 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

Run ID: 8ee90373-1cfb-424c-8355-4ce865526b88

📥 Commits

Reviewing files that changed from the base of the PR and between 3f17a3d and b9a917f.

⛔ Files ignored due to path filters (32)
  • .codex/plans/2026-04-09-skill-progressive-disclosure.md is excluded by !**/*.md
  • .compozy/tasks/extensability/analysis.md is excluded by !**/*.md
  • .compozy/tasks/extensability/analysis/analysis_claude_code.md is excluded by !**/*.md
  • .compozy/tasks/extensability/analysis/analysis_cross_cutting.md is excluded by !**/*.md
  • .compozy/tasks/extensability/analysis/analysis_goclaw.md is excluded by !**/*.md
  • .compozy/tasks/extensability/analysis/analysis_hermes.md is excluded by !**/*.md
  • .compozy/tasks/extensability/analysis/analysis_openclaw.md is excluded by !**/*.md
  • .compozy/tasks/extensability/analysis/analysis_openfang.md is excluded by !**/*.md
  • .compozy/tasks/extensability/analysis/analysis_pi_mono.md is excluded by !**/*.md
  • .compozy/tasks/extensability/analysis/analysis_research_extensibility.md is excluded by !**/*.md
  • docs/ideas/from-claude-code/_meta.md is excluded by !**/*.md
  • docs/ideas/from-claude-code/analysis_memory_autonomous.md is excluded by !**/*.md
  • docs/ideas/from-claude-code/analysis_multi_agent.md is excluded by !**/*.md
  • docs/ideas/from-claude-code/analysis_prompt_architecture.md is excluded by !**/*.md
  • docs/ideas/from-claude-code/analysis_query_engine.md is excluded by !**/*.md
  • docs/ideas/from-claude-code/analysis_services_infra.md is excluded by !**/*.md
  • docs/ideas/from-claude-code/analysis_tool_system.md is excluded by !**/*.md
  • docs/ideas/from-claude-code/filtered_recommendations.md is excluded by !**/*.md
  • docs/ideas/from-claude-code/our_system_cli.md is excluded by !**/*.md
  • docs/ideas/from-claude-code/our_system_infra.md is excluded by !**/*.md
  • docs/ideas/from-claude-code/our_system_kernel.md is excluded by !**/*.md
  • docs/ideas/network/agora-council_round1.md is excluded by !**/*.md
  • docs/ideas/network/agora-council_round2.md is excluded by !**/*.md
  • docs/ideas/network/agora-recipe-design.md is excluded by !**/*.md
  • docs/ideas/network/agora-spec-v0.1.md is excluded by !**/*.md
  • docs/ideas/network/agora-spec-v0.2.md is excluded by !**/*.md
  • docs/ideas/network/draft_1.md is excluded by !**/*.md
  • docs/ideas/network/draft_2.md is excluded by !**/*.md
  • docs/ideas/network/draft_3.md is excluded by !**/*.md
  • docs/ideas/network/draft_4.md is excluded by !**/*.md
  • docs/ideas/network/draft_5.md is excluded by !**/*.md
  • docs/ideas/orchestration/multi-agent-patterns-analysis.md is excluded by !**/*.md
📒 Files selected for processing (41)
  • .gitignore
  • internal/api/contract/contract.go
  • internal/api/core/conversions.go
  • internal/api/core/interfaces.go
  • internal/api/core/skills.go
  • internal/api/core/skills_test.go
  • internal/api/httpapi/handlers_test.go
  • internal/api/httpapi/server.go
  • internal/api/testutil/apitest.go
  • internal/api/udsapi/handlers_test.go
  • internal/api/udsapi/helpers_test.go
  • internal/api/udsapi/routes.go
  • internal/cli/skill.go
  • internal/skills/bundled/bundled_test.go
  • internal/skills/loader.go
  • internal/skills/loader_test.go
  • internal/skills/registry.go
  • internal/skills/registry_test.go
  • internal/skills/types.go
  • web/src/components/app-sidebar.test.tsx
  • web/src/routes/_app/-knowledge.test.tsx
  • web/src/routes/_app/-skills.test.tsx
  • web/src/routes/_app/knowledge.tsx
  • web/src/routes/_app/skills.tsx
  • web/src/systems/knowledge/adapters/knowledge-api.test.ts
  • web/src/systems/knowledge/components/knowledge-detail-panel.tsx
  • web/src/systems/session/components/message-bubble.test.tsx
  • web/src/systems/session/components/message-bubble.tsx
  • web/src/systems/session/components/tool-call-card.test.tsx
  • web/src/systems/skill/adapters/skill-api.test.ts
  • web/src/systems/skill/adapters/skill-api.ts
  • web/src/systems/skill/components/skill-detail-panel.tsx
  • web/src/systems/skill/hooks/use-skill-actions.test.tsx
  • web/src/systems/skill/hooks/use-skill-actions.ts
  • web/src/systems/skill/hooks/use-skills.test.tsx
  • web/src/systems/skill/hooks/use-skills.ts
  • web/src/systems/skill/index.ts
  • web/src/systems/skill/lib/query-keys.ts
  • web/src/systems/skill/lib/query-options.ts
  • web/src/systems/skill/types.test.ts
  • web/src/systems/skill/types.ts
✅ Files skipped from review due to trivial changes (10)
  • .gitignore
  • internal/api/udsapi/helpers_test.go
  • internal/api/udsapi/handlers_test.go
  • internal/api/udsapi/routes.go
  • internal/api/contract/contract.go
  • web/src/systems/session/components/tool-call-card.test.tsx
  • web/src/systems/knowledge/adapters/knowledge-api.test.ts
  • web/src/routes/_app/-skills.test.tsx
  • internal/api/core/skills_test.go
  • web/src/systems/skill/types.ts
🚧 Files skipped from review as they are similar to previous changes (16)
  • internal/api/core/conversions.go
  • web/src/systems/skill/hooks/use-skill-actions.ts
  • web/src/systems/session/components/message-bubble.test.tsx
  • web/src/systems/skill/hooks/use-skills.ts
  • web/src/systems/skill/types.test.ts
  • web/src/systems/skill/lib/query-keys.ts
  • web/src/systems/skill/hooks/use-skills.test.tsx
  • web/src/systems/skill/hooks/use-skill-actions.test.tsx
  • internal/api/core/interfaces.go
  • web/src/systems/skill/lib/query-options.ts
  • internal/skills/registry_test.go
  • internal/api/httpapi/handlers_test.go
  • web/src/systems/knowledge/components/knowledge-detail-panel.tsx
  • internal/skills/registry.go
  • web/src/systems/skill/components/skill-detail-panel.tsx
  • internal/api/core/skills.go

};
});

import { Route } from "./knowledge";
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Use @/* alias import instead of relative route import.

Please replace ./knowledge with the configured alias path to keep import style consistent.

As per coding guidelines web/src/**/*.{ts,tsx}: "Use path alias @/* (maps to ./src/*) for all module imports".

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

In `@web/src/routes/_app/-knowledge.test.tsx` at line 68, The test imports Route
via a relative path ("./knowledge"); update the import to use the project path
alias instead (use the `@/`* map to src/*) so the import reads from the aliased
module and keeps the same exported symbol (Route) — e.g., replace the current
"./knowledge" import with the corresponding "@/routes/_app/knowledge" aliased
import and keep the named import "Route" unchanged.

Comment on lines +35 to +47
function formatDreamStatus(lastConsolidation?: string): string {
if (!lastConsolidation) return "Dream: status unavailable";
try {
const date = new Date(lastConsolidation);
const diffMs = Date.now() - date.getTime();
const diffH = Math.floor(diffMs / (1000 * 60 * 60));
if (diffH < 1) return "Dream: <1h ago";
if (diffH < 24) return `Dream: ${diffH}h ago`;
const diffD = Math.floor(diffH / 24);
return `Dream: ${diffD}d ago`;
} catch {
return "Dream: unknown";
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Handle invalid date strings explicitly in formatDreamStatus.

new Date(...) does not throw on invalid input, so the current code can return Dream: NaNd ago (Line 44) for malformed timestamps.

Proposed fix
 function formatDreamStatus(lastConsolidation?: string): string {
   if (!lastConsolidation) return "Dream: status unavailable";
-  try {
-    const date = new Date(lastConsolidation);
-    const diffMs = Date.now() - date.getTime();
-    const diffH = Math.floor(diffMs / (1000 * 60 * 60));
-    if (diffH < 1) return "Dream: <1h ago";
-    if (diffH < 24) return `Dream: ${diffH}h ago`;
-    const diffD = Math.floor(diffH / 24);
-    return `Dream: ${diffD}d ago`;
-  } catch {
+  try {
+    const timestamp = new Date(lastConsolidation).getTime();
+    if (Number.isNaN(timestamp)) return "Dream: unknown";
+    const diffMs = Date.now() - timestamp;
+    if (diffMs < 0) return "Dream: unknown";
+    const diffH = Math.floor(diffMs / (1000 * 60 * 60));
+    if (diffH < 1) return "Dream: <1h ago";
+    if (diffH < 24) return `Dream: ${diffH}h ago`;
+    const diffD = Math.floor(diffH / 24);
+    return `Dream: ${diffD}d ago`;
+  } catch {
     return "Dream: unknown";
   }
 }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@web/src/routes/_app/knowledge.tsx` around lines 35 - 47, The function
formatDreamStatus can produce "NaN" when given invalid date strings because new
Date(...) doesn't throw; update formatDreamStatus to validate the parsed date
(e.g., const date = new Date(lastConsolidation); if (isNaN(date.getTime()))
return "Dream: unknown";) before computing diffMs and diffH, so malformed
timestamps return the fallback message rather than producing "NaN" output.

Comment on lines +39 to +40
const { data: workspaces } = useWorkspaces();
const activeWorkspaceId = workspaces?.[0]?.id ?? "";
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Consider guarding against empty workspace.

When workspaces is undefined or empty, activeWorkspaceId defaults to "". This empty string is then passed to all skill API hooks, which will make requests with ?workspace= (empty). Depending on backend behavior, this could cause errors or return unexpected results.

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

In `@web/src/routes/_app/skills.tsx` around lines 39 - 40, The code sets
activeWorkspaceId = workspaces?.[0]?.id ?? "" which yields an empty string when
no workspace exists and causes downstream skill API hooks to request with
?workspace=; change this to produce undefined when no workspace (e.g., const
activeWorkspaceId = workspaces?.[0]?.id ?? undefined) and update all places that
call the skill API hooks to only pass activeWorkspaceId when it is truthy or to
skip/inhibit the hook calls when activeWorkspaceId is undefined (for example
wrap calls to the skill hooks or conditionally render components depending on
useWorkspaces and activeWorkspaceId). Ensure the hooks that receive workspace
param are invoked only when activeWorkspaceId is valid.

Comment on lines +154 to +188
describe("getSkillContent", () => {
beforeEach(() => {
vi.stubGlobal("fetch", vi.fn());
});

afterEach(() => {
vi.restoreAllMocks();
vi.unstubAllGlobals();
});

it("calls GET /api/skills/:name/content?workspace=:id and returns content string", async () => {
vi.mocked(fetch).mockResolvedValue({
ok: true,
json: () => Promise.resolve({ content: "full skill content" }),
} as Response);

const result = await getSkillContent("test-skill", "ws_123");
expect(result).toBe("full skill content");
expect(fetch).toHaveBeenCalledWith("/api/skills/test-skill/content?workspace=ws_123", {
signal: undefined,
});
});

it("encodes skill name in content URL", async () => {
vi.mocked(fetch).mockResolvedValue({
ok: true,
json: () => Promise.resolve({ content: "full skill content" }),
} as Response);

await getSkillContent("my skill", "ws_123");
expect(fetch).toHaveBeenCalledWith("/api/skills/my%20skill/content?workspace=ws_123", {
signal: undefined,
});
});
});
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Missing error handling tests for getSkillContent.

The getSkillContent implementation handles 404 and other non-2xx responses by throwing SkillApiError, but this test block only covers success cases. Other functions (getSkill, enableSkill, disableSkill) all have tests for 404 and non-2xx error handling.

💡 Suggested tests to add
   it("encodes skill name in content URL", async () => {
     vi.mocked(fetch).mockResolvedValue({
       ok: true,
       json: () => Promise.resolve({ content: "full skill content" }),
     } as Response);

     await getSkillContent("my skill", "ws_123");
     expect(fetch).toHaveBeenCalledWith("/api/skills/my%20skill/content?workspace=ws_123", {
       signal: undefined,
     });
   });
+
+  it("throws SkillApiError with 404 for unknown skill", async () => {
+    vi.mocked(fetch).mockResolvedValue({
+      ok: false,
+      status: 404,
+    } as Response);
+
+    await expect(getSkillContent("unknown", "ws_123")).rejects.toThrow("Skill not found: unknown");
+  });
+
+  it("throws SkillApiError on non-2xx response", async () => {
+    vi.mocked(fetch).mockResolvedValue({
+      ok: false,
+      status: 500,
+    } as Response);
+
+    await expect(getSkillContent("test-skill", "ws_123")).rejects.toThrow(SkillApiError);
+  });
 });
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
describe("getSkillContent", () => {
beforeEach(() => {
vi.stubGlobal("fetch", vi.fn());
});
afterEach(() => {
vi.restoreAllMocks();
vi.unstubAllGlobals();
});
it("calls GET /api/skills/:name/content?workspace=:id and returns content string", async () => {
vi.mocked(fetch).mockResolvedValue({
ok: true,
json: () => Promise.resolve({ content: "full skill content" }),
} as Response);
const result = await getSkillContent("test-skill", "ws_123");
expect(result).toBe("full skill content");
expect(fetch).toHaveBeenCalledWith("/api/skills/test-skill/content?workspace=ws_123", {
signal: undefined,
});
});
it("encodes skill name in content URL", async () => {
vi.mocked(fetch).mockResolvedValue({
ok: true,
json: () => Promise.resolve({ content: "full skill content" }),
} as Response);
await getSkillContent("my skill", "ws_123");
expect(fetch).toHaveBeenCalledWith("/api/skills/my%20skill/content?workspace=ws_123", {
signal: undefined,
});
});
});
describe("getSkillContent", () => {
beforeEach(() => {
vi.stubGlobal("fetch", vi.fn());
});
afterEach(() => {
vi.restoreAllMocks();
vi.unstubAllGlobals();
});
it("calls GET /api/skills/:name/content?workspace=:id and returns content string", async () => {
vi.mocked(fetch).mockResolvedValue({
ok: true,
json: () => Promise.resolve({ content: "full skill content" }),
} as Response);
const result = await getSkillContent("test-skill", "ws_123");
expect(result).toBe("full skill content");
expect(fetch).toHaveBeenCalledWith("/api/skills/test-skill/content?workspace=ws_123", {
signal: undefined,
});
});
it("encodes skill name in content URL", async () => {
vi.mocked(fetch).mockResolvedValue({
ok: true,
json: () => Promise.resolve({ content: "full skill content" }),
} as Response);
await getSkillContent("my skill", "ws_123");
expect(fetch).toHaveBeenCalledWith("/api/skills/my%20skill/content?workspace=ws_123", {
signal: undefined,
});
});
it("throws SkillApiError with 404 for unknown skill", async () => {
vi.mocked(fetch).mockResolvedValue({
ok: false,
status: 404,
} as Response);
await expect(getSkillContent("unknown", "ws_123")).rejects.toThrow("Skill not found: unknown");
});
it("throws SkillApiError on non-2xx response", async () => {
vi.mocked(fetch).mockResolvedValue({
ok: false,
status: 500,
} as Response);
await expect(getSkillContent("test-skill", "ws_123")).rejects.toThrow(SkillApiError);
});
});
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@web/src/systems/skill/adapters/skill-api.test.ts` around lines 154 - 188, Add
tests for error handling in getSkillContent: add one test that mocks fetch
returning { ok: false, status: 404 } and asserts getSkillContent throws a
SkillApiError (or rejects) for not-found, and another test that mocks fetch
returning ok: false with a non-404 status (e.g., 500) and asserts it throws a
SkillApiError for general non-2xx responses; reference the getSkillContent
function and SkillApiError in the assertions and ensure the fetch call URL
encoding behavior is preserved in these error tests.

pedronauck added a commit that referenced this pull request May 26, 2026
## Release v0.0.1

This PR prepares the release of version v0.0.1.

### Changelog

## 0.0.1 - 2026-05-26



### Other Changes

- Lessons learned



### ♻️ Refactoring

- Project structure (#7)
- Kb improvements (#12)
- Rename spaces to channels (#17)
- Add extensions gaps (#21)
- Improve tool calls ui (#22)
- Remove web app header
- Module improvements (#29)
- Memory improvements (#35)
- Storybook for web and ui (#38)
- Enable AGH network by default for new installs (#57)
- Hermes adjustments (#69)
- Badges design (#84)
- Storybook scenario and logos gallery
- Migrate typescript tests (#114)
- Internal go packages (#120)
- Ui patterns (#127)
- Improve e2e tests (#130)
- Ui redesign
- Workspace isolation across runtime surfaces (#145)
- Prod ready applies (#162)
- Tool card ui (#164)
- Alpha on logo
- Prod ready features (#167)
- Thread sheet (#202)



### 🎉 Features

- Implement config foundation packages
- Implement sqlite store package
- Add ACP client package
- Add session lifecycle manager
- Implement observe package
- Add daemon composition root
- Add uds api server
- Implement cli package
- Add http api server
- Add system design
- Add foundation types, schemas, and layout shell for web client
- Add daemon health polling and agent sidebar systems for web client
- Add session system CRUD, streaming core, and session store for web
client
- Add chat view, messages, and composer tests for web client
- Add tool cards and renderers for web client
- Add file-backed memory store core
- Scaffold memory session seams
- Add memory dream consolidation service
- Wire memory assembler into daemon
- Add memory api and cli
- New skills system (#1)
- Add workspace entity (#5)
- Add new skill capabilities (#8)
- Web ui v2 (#9)
- Improve hooks system (#10)
- Session resilience (#11)
- Add extensability (#13)
- Add automation (#16)
- Add channels (#14)
- Add network implementation (#15)
- Add network, bridges and automations web pages (#18)
- Ext registry (#20)
- Add core tasks (#19)
- Bridge adapters (#23)
- Add site (#26)
- Add ext refac and sandbox (#25)
- Settings ui (#37)
- Tasks ui (#36)
- Harness improvements (#44)
- Agent capabilities (#49)
- Redesign ui (#48)
- Unify capability (#53)
- Redesign network workspace (#59)
- Add task deletion and split session delete from stop (#58)
- Session provider selection (#60)
- Production grade adjustments (#66)
- Autonomous system (#75)
- Add agent session route (#80)
- Tools registry (#85)
- Agents soul (#88)
- Add network threads (#105)
- Orchestration improvements (#106)
- Memory v2 (#108)
- Agent categories (#113)
- Providers model (#118)
- Add canonical AGH bundled skill (#143)
- Onboarding and improvements (#198)
- Onboarding and improvements (#201)



### 🐛 Bug Fixes

- Review round
- Review rounds
- Resolve memory extensibility review batch
- Embed web into daemon
- Defaults agents
- Acp integration (#4)
- Lint errors
- Prd folder
- Remove orphan web actions and dead surfaces (#55)
- Qa testing and fixes (#73)
- New review rounds (#82)
- Security audit (#90)
- Release qa round (#95)
- Add missing tools (#141)
- New qa round (#147)
- Advanced qa round (#149)
- Homebrew tap
- Final review round (#151)
- Daemon healthy
- Reasoning models (#158)
- Lint errors (#160)
- Review round (#168)
- Release adjustments (#171)
- Stabilize release ci fixtures
- Stabilize release integration gate
- Stabilize release verify gates
- Stabilize release integration flows
- Stabilize release verify gates
- Stabilize main verify shutdown
- Ignore stale acpmock cancel
- Marketplace search focus and filtering (#193)
- Website video
- Workspace command select



### 📚 Documentation

- Update agents.md
- Update prd
- Update skills
- Update compozy tasks
- Update compozy
- Update compozy
- Add new skills
- Archive prd
- Update prds
- Update rfc
- Update prds
- Update prds
- Add automation prd
- Channels prd
- Update prd
- Update prd
- New prds
- Archive prds
- Bridges adapters prd
- Sandbox prd
- Update
- Archive prd
- Update
- Add new prd
- New design
- Update prd
- Archive prds
- Update prds
- Tasks-ui prd tasks
- Update prd
- Update design docs
- Agent capabilities prd
- Improve site docs
- Remove old design references
- Udpate
- Autonomous prd
- Update skills
- Blog design
- Agent sould prd
- Final qa plan
- Update
- Remove codex ledgers from gitignore
- Remove not needed files
- Udpate ledger
- Update cy-codex-loop skill
- Orchestration improves prd
- Update prds
- Orch improvs prd
- Memv2 prd
- Providers model prd
- Update refacs prd
- New design proposal
- Update rules
- Update skills
- New blog posts (#173)
- Format docs
- Remove old design files
- Remove old
- Skeeper update



### 📦 Build System

- Initial structure
- Commitlint
- Frontend base structure
- Update vscode settings
- Add subagents
- Coderabbit
- Prd and tooling
- Bun lock
- Lint tooling
- Copy.md and tooling adjusts
- Add repoclone rc
- Upgrade skeeper to v0.2.0
- Update go.mod
- Adopt task artifacts into skeeper
- Sync codex plans with skeeper
- Skeeper lock
- Skeeper lock
- New skills
- Skeeper lock
- Skeeper lock
- Skeeper lock
- Update deps and go
- Regenerate daytona sidecar assets for go 1.26.3
- Fix cliff
- Ignore docs on fmt
- Build web assets before goreleaser
- Extend release dry-run timeout



### 🔧 CI/CD

- Lint errors
- Fint release pr
- Fix goreleaser



### 🧪 Testing

- Add e2e tests (#27)
- Qa rounds (#78)
- Improve test suite (#138)
- Harden daemon-served restart reloads
- Harden daemon-served readiness waits
- Stabilize dashboard focus assertion
- Stabilize release integration gates
- Stabilize release e2e markers
- Stabilize release e2e flows

Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
This was referenced May 26, 2026
pedronauck added a commit that referenced this pull request May 26, 2026
## Release v0.0.1

This PR prepares the release of version v0.0.1.

### Changelog

## 0.0.1 - 2026-05-26



### Other Changes

- Lessons learned



### ♻️ Refactoring

- Project structure (#7)
- Kb improvements (#12)
- Rename spaces to channels (#17)
- Add extensions gaps (#21)
- Improve tool calls ui (#22)
- Remove web app header
- Module improvements (#29)
- Memory improvements (#35)
- Storybook for web and ui (#38)
- Enable AGH network by default for new installs (#57)
- Hermes adjustments (#69)
- Badges design (#84)
- Storybook scenario and logos gallery
- Migrate typescript tests (#114)
- Internal go packages (#120)
- Ui patterns (#127)
- Improve e2e tests (#130)
- Ui redesign
- Workspace isolation across runtime surfaces (#145)
- Prod ready applies (#162)
- Tool card ui (#164)
- Alpha on logo
- Prod ready features (#167)
- Thread sheet (#202)



### 🎉 Features

- Implement config foundation packages
- Implement sqlite store package
- Add ACP client package
- Add session lifecycle manager
- Implement observe package
- Add daemon composition root
- Add uds api server
- Implement cli package
- Add http api server
- Add system design
- Add foundation types, schemas, and layout shell for web client
- Add daemon health polling and agent sidebar systems for web client
- Add session system CRUD, streaming core, and session store for web
client
- Add chat view, messages, and composer tests for web client
- Add tool cards and renderers for web client
- Add file-backed memory store core
- Scaffold memory session seams
- Add memory dream consolidation service
- Wire memory assembler into daemon
- Add memory api and cli
- New skills system (#1)
- Add workspace entity (#5)
- Add new skill capabilities (#8)
- Web ui v2 (#9)
- Improve hooks system (#10)
- Session resilience (#11)
- Add extensability (#13)
- Add automation (#16)
- Add channels (#14)
- Add network implementation (#15)
- Add network, bridges and automations web pages (#18)
- Ext registry (#20)
- Add core tasks (#19)
- Bridge adapters (#23)
- Add site (#26)
- Add ext refac and sandbox (#25)
- Settings ui (#37)
- Tasks ui (#36)
- Harness improvements (#44)
- Agent capabilities (#49)
- Redesign ui (#48)
- Unify capability (#53)
- Redesign network workspace (#59)
- Add task deletion and split session delete from stop (#58)
- Session provider selection (#60)
- Production grade adjustments (#66)
- Autonomous system (#75)
- Add agent session route (#80)
- Tools registry (#85)
- Agents soul (#88)
- Add network threads (#105)
- Orchestration improvements (#106)
- Memory v2 (#108)
- Agent categories (#113)
- Providers model (#118)
- Add canonical AGH bundled skill (#143)
- Onboarding and improvements (#198)
- Onboarding and improvements (#201)



### 🐛 Bug Fixes

- Review round
- Review rounds
- Resolve memory extensibility review batch
- Embed web into daemon
- Defaults agents
- Acp integration (#4)
- Lint errors
- Prd folder
- Remove orphan web actions and dead surfaces (#55)
- Qa testing and fixes (#73)
- New review rounds (#82)
- Security audit (#90)
- Release qa round (#95)
- Add missing tools (#141)
- New qa round (#147)
- Advanced qa round (#149)
- Homebrew tap
- Final review round (#151)
- Daemon healthy
- Reasoning models (#158)
- Lint errors (#160)
- Review round (#168)
- Release adjustments (#171)
- Stabilize release ci fixtures
- Stabilize release integration gate
- Stabilize release verify gates
- Stabilize release integration flows
- Stabilize release verify gates
- Stabilize main verify shutdown
- Ignore stale acpmock cancel
- Marketplace search focus and filtering (#193)
- Website video
- Workspace command select



### 📚 Documentation

- Update agents.md
- Update prd
- Update skills
- Update compozy tasks
- Update compozy
- Update compozy
- Add new skills
- Archive prd
- Update prds
- Update rfc
- Update prds
- Update prds
- Add automation prd
- Channels prd
- Update prd
- Update prd
- New prds
- Archive prds
- Bridges adapters prd
- Sandbox prd
- Update
- Archive prd
- Update
- Add new prd
- New design
- Update prd
- Archive prds
- Update prds
- Tasks-ui prd tasks
- Update prd
- Update design docs
- Agent capabilities prd
- Improve site docs
- Remove old design references
- Udpate
- Autonomous prd
- Update skills
- Blog design
- Agent sould prd
- Final qa plan
- Update
- Remove codex ledgers from gitignore
- Remove not needed files
- Udpate ledger
- Update cy-codex-loop skill
- Orchestration improves prd
- Update prds
- Orch improvs prd
- Memv2 prd
- Providers model prd
- Update refacs prd
- New design proposal
- Update rules
- Update skills
- New blog posts (#173)
- Format docs
- Remove old design files
- Remove old
- Skeeper update



### 📦 Build System

- Initial structure
- Commitlint
- Frontend base structure
- Update vscode settings
- Add subagents
- Coderabbit
- Prd and tooling
- Bun lock
- Lint tooling
- Copy.md and tooling adjusts
- Add repoclone rc
- Upgrade skeeper to v0.2.0
- Update go.mod
- Adopt task artifacts into skeeper
- Sync codex plans with skeeper
- Skeeper lock
- Skeeper lock
- New skills
- Skeeper lock
- Skeeper lock
- Skeeper lock
- Update deps and go
- Regenerate daytona sidecar assets for go 1.26.3
- Fix cliff
- Ignore docs on fmt
- Build web assets before goreleaser
- Extend release dry-run timeout



### 🔧 CI/CD

- Lint errors
- Fint release pr
- Fix goreleaser
- Fix release



### 🧪 Testing

- Add e2e tests (#27)
- Qa rounds (#78)
- Improve test suite (#138)
- Harden daemon-served restart reloads
- Harden daemon-served readiness waits
- Stabilize dashboard focus assertion
- Stabilize release integration gates
- Stabilize release e2e markers
- Stabilize release e2e flows

Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
pedronauck added a commit that referenced this pull request May 26, 2026
## Release v0.0.2

This PR prepares the release of version v0.0.2.

### Changelog

## 0.0.2 - 2026-05-26



### Other Changes

- Lessons learned



### ♻️ Refactoring

- Project structure (#7)
- Kb improvements (#12)
- Rename spaces to channels (#17)
- Add extensions gaps (#21)
- Improve tool calls ui (#22)
- Remove web app header
- Module improvements (#29)
- Memory improvements (#35)
- Storybook for web and ui (#38)
- Enable AGH network by default for new installs (#57)
- Hermes adjustments (#69)
- Badges design (#84)
- Storybook scenario and logos gallery
- Migrate typescript tests (#114)
- Internal go packages (#120)
- Ui patterns (#127)
- Improve e2e tests (#130)
- Ui redesign
- Workspace isolation across runtime surfaces (#145)
- Prod ready applies (#162)
- Tool card ui (#164)
- Alpha on logo
- Prod ready features (#167)
- Thread sheet (#202)



### 🎉 Features

- Implement config foundation packages
- Implement sqlite store package
- Add ACP client package
- Add session lifecycle manager
- Implement observe package
- Add daemon composition root
- Add uds api server
- Implement cli package
- Add http api server
- Add system design
- Add foundation types, schemas, and layout shell for web client
- Add daemon health polling and agent sidebar systems for web client
- Add session system CRUD, streaming core, and session store for web
client
- Add chat view, messages, and composer tests for web client
- Add tool cards and renderers for web client
- Add file-backed memory store core
- Scaffold memory session seams
- Add memory dream consolidation service
- Wire memory assembler into daemon
- Add memory api and cli
- New skills system (#1)
- Add workspace entity (#5)
- Add new skill capabilities (#8)
- Web ui v2 (#9)
- Improve hooks system (#10)
- Session resilience (#11)
- Add extensability (#13)
- Add automation (#16)
- Add channels (#14)
- Add network implementation (#15)
- Add network, bridges and automations web pages (#18)
- Ext registry (#20)
- Add core tasks (#19)
- Bridge adapters (#23)
- Add site (#26)
- Add ext refac and sandbox (#25)
- Settings ui (#37)
- Tasks ui (#36)
- Harness improvements (#44)
- Agent capabilities (#49)
- Redesign ui (#48)
- Unify capability (#53)
- Redesign network workspace (#59)
- Add task deletion and split session delete from stop (#58)
- Session provider selection (#60)
- Production grade adjustments (#66)
- Autonomous system (#75)
- Add agent session route (#80)
- Tools registry (#85)
- Agents soul (#88)
- Add network threads (#105)
- Orchestration improvements (#106)
- Memory v2 (#108)
- Agent categories (#113)
- Providers model (#118)
- Add canonical AGH bundled skill (#143)
- Onboarding and improvements (#198)
- Onboarding and improvements (#201)



### 🐛 Bug Fixes

- Review round
- Review rounds
- Resolve memory extensibility review batch
- Embed web into daemon
- Defaults agents
- Acp integration (#4)
- Lint errors
- Prd folder
- Remove orphan web actions and dead surfaces (#55)
- Qa testing and fixes (#73)
- New review rounds (#82)
- Security audit (#90)
- Release qa round (#95)
- Add missing tools (#141)
- New qa round (#147)
- Advanced qa round (#149)
- Homebrew tap
- Final review round (#151)
- Daemon healthy
- Reasoning models (#158)
- Lint errors (#160)
- Review round (#168)
- Release adjustments (#171)
- Stabilize release ci fixtures
- Stabilize release integration gate
- Stabilize release verify gates
- Stabilize release integration flows
- Stabilize release verify gates
- Stabilize main verify shutdown
- Ignore stale acpmock cancel
- Marketplace search focus and filtering (#193)
- Website video
- Workspace command select



### 📚 Documentation

- Update agents.md
- Update prd
- Update skills
- Update compozy tasks
- Update compozy
- Update compozy
- Add new skills
- Archive prd
- Update prds
- Update rfc
- Update prds
- Update prds
- Add automation prd
- Channels prd
- Update prd
- Update prd
- New prds
- Archive prds
- Bridges adapters prd
- Sandbox prd
- Update
- Archive prd
- Update
- Add new prd
- New design
- Update prd
- Archive prds
- Update prds
- Tasks-ui prd tasks
- Update prd
- Update design docs
- Agent capabilities prd
- Improve site docs
- Remove old design references
- Udpate
- Autonomous prd
- Update skills
- Blog design
- Agent sould prd
- Final qa plan
- Update
- Remove codex ledgers from gitignore
- Remove not needed files
- Udpate ledger
- Update cy-codex-loop skill
- Orchestration improves prd
- Update prds
- Orch improvs prd
- Memv2 prd
- Providers model prd
- Update refacs prd
- New design proposal
- Update rules
- Update skills
- New blog posts (#173)
- Format docs
- Remove old design files
- Remove old
- Skeeper update



### 📦 Build System

- Initial structure
- Commitlint
- Frontend base structure
- Update vscode settings
- Add subagents
- Coderabbit
- Prd and tooling
- Bun lock
- Lint tooling
- Copy.md and tooling adjusts
- Add repoclone rc
- Upgrade skeeper to v0.2.0
- Update go.mod
- Adopt task artifacts into skeeper
- Sync codex plans with skeeper
- Skeeper lock
- Skeeper lock
- New skills
- Skeeper lock
- Skeeper lock
- Skeeper lock
- Update deps and go
- Regenerate daytona sidecar assets for go 1.26.3
- Fix cliff
- Ignore docs on fmt
- Build web assets before goreleaser
- Extend release dry-run timeout



### 🔧 CI/CD

- Lint errors
- Fint release pr
- Fix goreleaser
- Fix release
- Fix release process



### 🧪 Testing

- Add e2e tests (#27)
- Qa rounds (#78)
- Improve test suite (#138)
- Harden daemon-served restart reloads
- Harden daemon-served readiness waits
- Stabilize dashboard focus assertion
- Stabilize release integration gates
- Stabilize release e2e markers
- Stabilize release e2e flows
- Improve suite speed

Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
pedronauck added a commit that referenced this pull request May 27, 2026
## Release v0.0.2

This PR prepares the release of version v0.0.2.

### Changelog

## 0.0.2 - 2026-05-26



### Other Changes

- Lessons learned



### ♻️ Refactoring

- Project structure (#7)
- Kb improvements (#12)
- Rename spaces to channels (#17)
- Add extensions gaps (#21)
- Improve tool calls ui (#22)
- Remove web app header
- Module improvements (#29)
- Memory improvements (#35)
- Storybook for web and ui (#38)
- Enable AGH network by default for new installs (#57)
- Hermes adjustments (#69)
- Badges design (#84)
- Storybook scenario and logos gallery
- Migrate typescript tests (#114)
- Internal go packages (#120)
- Ui patterns (#127)
- Improve e2e tests (#130)
- Ui redesign
- Workspace isolation across runtime surfaces (#145)
- Prod ready applies (#162)
- Tool card ui (#164)
- Alpha on logo
- Prod ready features (#167)
- Thread sheet (#202)



### 🎉 Features

- Implement config foundation packages
- Implement sqlite store package
- Add ACP client package
- Add session lifecycle manager
- Implement observe package
- Add daemon composition root
- Add uds api server
- Implement cli package
- Add http api server
- Add system design
- Add foundation types, schemas, and layout shell for web client
- Add daemon health polling and agent sidebar systems for web client
- Add session system CRUD, streaming core, and session store for web
client
- Add chat view, messages, and composer tests for web client
- Add tool cards and renderers for web client
- Add file-backed memory store core
- Scaffold memory session seams
- Add memory dream consolidation service
- Wire memory assembler into daemon
- Add memory api and cli
- New skills system (#1)
- Add workspace entity (#5)
- Add new skill capabilities (#8)
- Web ui v2 (#9)
- Improve hooks system (#10)
- Session resilience (#11)
- Add extensability (#13)
- Add automation (#16)
- Add channels (#14)
- Add network implementation (#15)
- Add network, bridges and automations web pages (#18)
- Ext registry (#20)
- Add core tasks (#19)
- Bridge adapters (#23)
- Add site (#26)
- Add ext refac and sandbox (#25)
- Settings ui (#37)
- Tasks ui (#36)
- Harness improvements (#44)
- Agent capabilities (#49)
- Redesign ui (#48)
- Unify capability (#53)
- Redesign network workspace (#59)
- Add task deletion and split session delete from stop (#58)
- Session provider selection (#60)
- Production grade adjustments (#66)
- Autonomous system (#75)
- Add agent session route (#80)
- Tools registry (#85)
- Agents soul (#88)
- Add network threads (#105)
- Orchestration improvements (#106)
- Memory v2 (#108)
- Agent categories (#113)
- Providers model (#118)
- Add canonical AGH bundled skill (#143)
- Onboarding and improvements (#198)
- Onboarding and improvements (#201)



### 🐛 Bug Fixes

- Review round
- Review rounds
- Resolve memory extensibility review batch
- Embed web into daemon
- Defaults agents
- Acp integration (#4)
- Lint errors
- Prd folder
- Remove orphan web actions and dead surfaces (#55)
- Qa testing and fixes (#73)
- New review rounds (#82)
- Security audit (#90)
- Release qa round (#95)
- Add missing tools (#141)
- New qa round (#147)
- Advanced qa round (#149)
- Homebrew tap
- Final review round (#151)
- Daemon healthy
- Reasoning models (#158)
- Lint errors (#160)
- Review round (#168)
- Release adjustments (#171)
- Stabilize release ci fixtures
- Stabilize release integration gate
- Stabilize release verify gates
- Stabilize release integration flows
- Stabilize release verify gates
- Stabilize main verify shutdown
- Ignore stale acpmock cancel
- Marketplace search focus and filtering (#193)
- Website video
- Workspace command select



### 📚 Documentation

- Update agents.md
- Update prd
- Update skills
- Update compozy tasks
- Update compozy
- Update compozy
- Add new skills
- Archive prd
- Update prds
- Update rfc
- Update prds
- Update prds
- Add automation prd
- Channels prd
- Update prd
- Update prd
- New prds
- Archive prds
- Bridges adapters prd
- Sandbox prd
- Update
- Archive prd
- Update
- Add new prd
- New design
- Update prd
- Archive prds
- Update prds
- Tasks-ui prd tasks
- Update prd
- Update design docs
- Agent capabilities prd
- Improve site docs
- Remove old design references
- Udpate
- Autonomous prd
- Update skills
- Blog design
- Agent sould prd
- Final qa plan
- Update
- Remove codex ledgers from gitignore
- Remove not needed files
- Udpate ledger
- Update cy-codex-loop skill
- Orchestration improves prd
- Update prds
- Orch improvs prd
- Memv2 prd
- Providers model prd
- Update refacs prd
- New design proposal
- Update rules
- Update skills
- New blog posts (#173)
- Format docs
- Remove old design files
- Remove old
- Skeeper update



### 📦 Build System

- Initial structure
- Commitlint
- Frontend base structure
- Update vscode settings
- Add subagents
- Coderabbit
- Prd and tooling
- Bun lock
- Lint tooling
- Copy.md and tooling adjusts
- Add repoclone rc
- Upgrade skeeper to v0.2.0
- Update go.mod
- Adopt task artifacts into skeeper
- Sync codex plans with skeeper
- Skeeper lock
- Skeeper lock
- New skills
- Skeeper lock
- Skeeper lock
- Skeeper lock
- Update deps and go
- Regenerate daytona sidecar assets for go 1.26.3
- Fix cliff
- Ignore docs on fmt
- Build web assets before goreleaser
- Extend release dry-run timeout



### 🔧 CI/CD

- Lint errors
- Fint release pr
- Fix goreleaser
- Fix release
- Fix release process
- Fix release sync
- Decouple release dry-run npm auth
- Persist web assets git auth



### 🧪 Testing

- Add e2e tests (#27)
- Qa rounds (#78)
- Improve test suite (#138)
- Harden daemon-served restart reloads
- Harden daemon-served readiness waits
- Stabilize dashboard focus assertion
- Stabilize release integration gates
- Stabilize release e2e markers
- Stabilize release e2e flows
- Improve suite speed


<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->

## Summary by CodeRabbit

* **Chores**
* Updated web assets dependency to a newer version for improved
stability and performance.

<!-- review_stack_entry_start -->

[![Review Change
Stack](https://storage.googleapis.com/coderabbit_public_assets/review-stack-in-coderabbit-ui.svg)](https://app.coderabbit.ai/change-stack/compozy/agh/pull/211?utm_source=github_walkthrough&utm_medium=github&utm_campaign=change_stack)

<!-- review_stack_entry_end -->

<!-- end of auto-generated comment: release notes by coderabbit.ai -->

Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
pedronauck added a commit that referenced this pull request May 27, 2026
## Release v0.0.2

This PR prepares the release of version v0.0.2.

### Changelog

## 0.0.2 - 2026-05-27



### Other Changes

- Lessons learned



### ♻️ Refactoring

- Project structure (#7)
- Kb improvements (#12)
- Rename spaces to channels (#17)
- Add extensions gaps (#21)
- Improve tool calls ui (#22)
- Remove web app header
- Module improvements (#29)
- Memory improvements (#35)
- Storybook for web and ui (#38)
- Enable AGH network by default for new installs (#57)
- Hermes adjustments (#69)
- Badges design (#84)
- Storybook scenario and logos gallery
- Migrate typescript tests (#114)
- Internal go packages (#120)
- Ui patterns (#127)
- Improve e2e tests (#130)
- Ui redesign
- Workspace isolation across runtime surfaces (#145)
- Prod ready applies (#162)
- Tool card ui (#164)
- Alpha on logo
- Prod ready features (#167)
- Thread sheet (#202)



### 🎉 Features

- Implement config foundation packages
- Implement sqlite store package
- Add ACP client package
- Add session lifecycle manager
- Implement observe package
- Add daemon composition root
- Add uds api server
- Implement cli package
- Add http api server
- Add system design
- Add foundation types, schemas, and layout shell for web client
- Add daemon health polling and agent sidebar systems for web client
- Add session system CRUD, streaming core, and session store for web
client
- Add chat view, messages, and composer tests for web client
- Add tool cards and renderers for web client
- Add file-backed memory store core
- Scaffold memory session seams
- Add memory dream consolidation service
- Wire memory assembler into daemon
- Add memory api and cli
- New skills system (#1)
- Add workspace entity (#5)
- Add new skill capabilities (#8)
- Web ui v2 (#9)
- Improve hooks system (#10)
- Session resilience (#11)
- Add extensability (#13)
- Add automation (#16)
- Add channels (#14)
- Add network implementation (#15)
- Add network, bridges and automations web pages (#18)
- Ext registry (#20)
- Add core tasks (#19)
- Bridge adapters (#23)
- Add site (#26)
- Add ext refac and sandbox (#25)
- Settings ui (#37)
- Tasks ui (#36)
- Harness improvements (#44)
- Agent capabilities (#49)
- Redesign ui (#48)
- Unify capability (#53)
- Redesign network workspace (#59)
- Add task deletion and split session delete from stop (#58)
- Session provider selection (#60)
- Production grade adjustments (#66)
- Autonomous system (#75)
- Add agent session route (#80)
- Tools registry (#85)
- Agents soul (#88)
- Add network threads (#105)
- Orchestration improvements (#106)
- Memory v2 (#108)
- Agent categories (#113)
- Providers model (#118)
- Add canonical AGH bundled skill (#143)
- Onboarding and improvements (#198)
- Onboarding and improvements (#201)



### 🐛 Bug Fixes

- Review round
- Review rounds
- Resolve memory extensibility review batch
- Embed web into daemon
- Defaults agents
- Acp integration (#4)
- Lint errors
- Prd folder
- Remove orphan web actions and dead surfaces (#55)
- Qa testing and fixes (#73)
- New review rounds (#82)
- Security audit (#90)
- Release qa round (#95)
- Add missing tools (#141)
- New qa round (#147)
- Advanced qa round (#149)
- Homebrew tap
- Final review round (#151)
- Daemon healthy
- Reasoning models (#158)
- Lint errors (#160)
- Review round (#168)
- Release adjustments (#171)
- Stabilize release ci fixtures
- Stabilize release integration gate
- Stabilize release verify gates
- Stabilize release integration flows
- Stabilize release verify gates
- Stabilize main verify shutdown
- Ignore stale acpmock cancel
- Marketplace search focus and filtering (#193)
- Website video
- Workspace command select



### 📚 Documentation

- Update agents.md
- Update prd
- Update skills
- Update compozy tasks
- Update compozy
- Update compozy
- Add new skills
- Archive prd
- Update prds
- Update rfc
- Update prds
- Update prds
- Add automation prd
- Channels prd
- Update prd
- Update prd
- New prds
- Archive prds
- Bridges adapters prd
- Sandbox prd
- Update
- Archive prd
- Update
- Add new prd
- New design
- Update prd
- Archive prds
- Update prds
- Tasks-ui prd tasks
- Update prd
- Update design docs
- Agent capabilities prd
- Improve site docs
- Remove old design references
- Udpate
- Autonomous prd
- Update skills
- Blog design
- Agent sould prd
- Final qa plan
- Update
- Remove codex ledgers from gitignore
- Remove not needed files
- Udpate ledger
- Update cy-codex-loop skill
- Orchestration improves prd
- Update prds
- Orch improvs prd
- Memv2 prd
- Providers model prd
- Update refacs prd
- New design proposal
- Update rules
- Update skills
- New blog posts (#173)
- Format docs
- Remove old design files
- Remove old
- Skeeper update



### 📦 Build System

- Initial structure
- Commitlint
- Frontend base structure
- Update vscode settings
- Add subagents
- Coderabbit
- Prd and tooling
- Bun lock
- Lint tooling
- Copy.md and tooling adjusts
- Add repoclone rc
- Upgrade skeeper to v0.2.0
- Update go.mod
- Adopt task artifacts into skeeper
- Sync codex plans with skeeper
- Skeeper lock
- Skeeper lock
- New skills
- Skeeper lock
- Skeeper lock
- Skeeper lock
- Update deps and go
- Regenerate daytona sidecar assets for go 1.26.3
- Fix cliff
- Ignore docs on fmt
- Build web assets before goreleaser
- Extend release dry-run timeout
- Fix release dry-run token contract



### 🔧 CI/CD

- Lint errors
- Fint release pr
- Fix goreleaser
- Fix release
- Fix release process
- Fix release sync
- Decouple release dry-run npm auth
- Persist web assets git auth
- Require npm auth before release merge



### 🧪 Testing

- Add e2e tests (#27)
- Qa rounds (#78)
- Improve test suite (#138)
- Harden daemon-served restart reloads
- Harden daemon-served readiness waits
- Stabilize dashboard focus assertion
- Stabilize release integration gates
- Stabilize release e2e markers
- Stabilize release e2e flows
- Improve suite speed


<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->

## Summary by CodeRabbit

* **Chores**
  * Updated dependencies to latest versions.

<!-- review_stack_entry_start -->

[![Review Change
Stack](https://storage.googleapis.com/coderabbit_public_assets/review-stack-in-coderabbit-ui.svg)](https://app.coderabbit.ai/change-stack/compozy/agh/pull/214?utm_source=github_walkthrough&utm_medium=github&utm_campaign=change_stack)

<!-- review_stack_entry_end -->

<!-- end of auto-generated comment: release notes by coderabbit.ai -->

Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
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