Skip to content

Commit c6b635f

Browse files
committed
release: merge develop into main for v0.29.1
2 parents 24af125 + f339dca commit c6b635f

14 files changed

Lines changed: 784 additions & 104 deletions

File tree

CHANGELOG.md

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,26 @@ All notable changes to this project will be documented in this file.
55
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
66
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
77

8+
## [0.29.1] - 2026-04-23
9+
10+
Patch iterating on the v0.29.0 thread-areas feature: UI rebrand, navigation polish, and fixes identified by the post-release verification pass.
11+
12+
### Changed
13+
14+
- **Renamed "Issues" → "Topics" across the UI** — the feature evolved from a pure issue tracker into a container for both tasks and persistent chat threads, so the label no longer fit. Page file renamed `Issues.tsx``Topics.tsx`, route moved `/issues``/topics` with a 302 redirect preserving old bookmarks, sidebar nav item updated, breadcrumb `Topics / {title}`, i18n updated across 3 locales: en `Topics`, pt-BR `Tópicos`, es `Temas`. Backend (`tickets` table, `/api/tickets/*` endpoints, `Ticket` model) intentionally unchanged — pure UX rebranding, zero data migration.
15+
16+
### Added
17+
18+
- **Threads sidebar — navigate between chat threads without leaving the conversation** — when viewing a ticket in thread mode, a 280px sidebar now appears on the left listing all threads, grouped by agent (Clawdia, Kai, Flux…), with active/archived split. Active thread is highlighted with a green left border. Toggle button collapses to 48px (persisted in localStorage). Each item shows title + relative time (`há 2h`, `ontem`, `3d`). On mobile (<768px), sidebar becomes a slide-in drawer triggered by a `PanelLeft` icon — 85vw from the left with backdrop, Escape/click-outside/close to dismiss, `role=dialog` accessibility. Desktop and mobile share the same `ThreadsSidebar` component via an `asDrawer` prop; drawer lazy-mounts to avoid double-fetch. Pure CSS transitions, zero new dependencies.
19+
- **Create workspace folders from the Convert to Thread modal**`+ Nova pasta` button inline in the folder dropdown opens an input accepting `[a-z0-9-]+` names (2-50 chars). Pressing Enter or clicking Create fires `POST /api/workspace/subfolders`; new folder appears in the dropdown pre-selected, no page reload. Backend validates name pattern, defends against path traversal, returns 409 if folder exists, 201 with `{name, path, full_path}` on success.
20+
21+
### Fixed
22+
23+
- **`convert-to-thread` is now idempotent** — calling the endpoint on a ticket that is already a thread returns 200 with the current ticket state instead of 409. Workspace path conflict (different path supplied) still returns 409 `workspace_path_conflict` with both paths in the error body. Prevents spurious errors when the UI double-fires the conversion.
24+
- **`turn-completed` is now race-safe monotonic** — uses `UPDATE ... WHERE message_count < :n` with `n = current + 1`, so concurrent calls with the same base value only increment once (second call is a silent no-op). Implements option (a) from the summary-trigger ADR without extra IO.
25+
- **Convert to Thread modal warns about agent immutability** — orange warning banner before the Convert button: "Após converter, o agente desta thread não poderá ser alterado. Crie uma thread nova para trocar de agente." Consistent with the existing `archived` badge style.
26+
- **Archived threads are read-only in the UI** — when a thread's status is `archived`, the `TicketDetail` shows a "📦 Thread arquivada — read-only. [Unarchive]" banner above the chat, disables interaction on the embedded `AgentChat` via `pointer-events-none opacity-60`, and the Unarchive button calls `POST /api/tickets/:id/unarchive-thread` to reactivate. Previously the UI allowed typing and only the backend rejected it.
27+
828
## [0.29.0] - 2026-04-23
929

1030
### Added

cli/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@evoapi/evo-nexus",
3-
"version": "0.29.0",
3+
"version": "0.29.1",
44
"description": "Unofficial open source toolkit for Claude Code — AI-powered business operating system",
55
"keywords": [
66
"claude-code",

dashboard/backend/routes/tickets.py

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -735,6 +735,44 @@ def workspace_subfolders():
735735
return jsonify({"folders": folders})
736736

737737

738+
@bp.route("/api/workspace/subfolders", methods=["POST"])
739+
def create_workspace_subfolder():
740+
"""Create a new immediate subdirectory under workspace/."""
741+
denied = _require("execute")
742+
if denied:
743+
return denied
744+
data = request.get_json(silent=True) or {}
745+
name = (data.get("name") or "").strip()
746+
747+
if not name:
748+
return jsonify({"error": "name_required"}), 400
749+
if not re.fullmatch(r"[a-z0-9-]+", name):
750+
return jsonify({"error": "invalid_name", "message": "Use só letras minúsculas, números e hífen."}), 400
751+
if len(name) < 2 or len(name) > 50:
752+
return jsonify({"error": "invalid_length"}), 400
753+
754+
root = WORKSPACE / "workspace"
755+
target = root / name
756+
# path traversal defense
757+
resolved = target.resolve()
758+
if not str(resolved).startswith(str(root.resolve())):
759+
return jsonify({"error": "path_traversal"}), 400
760+
761+
if target.exists():
762+
return jsonify({"error": "already_exists", "path": f"workspace/{name}"}), 409
763+
764+
try:
765+
target.mkdir(parents=True, exist_ok=False)
766+
except OSError as exc:
767+
return jsonify({"error": "mkdir_failed", "message": str(exc)}), 500
768+
769+
return jsonify({
770+
"name": name,
771+
"path": f"workspace/{name}",
772+
"full_path": str(target),
773+
}), 201
774+
775+
738776
# --------------- Turn completed (Option D — summary trigger) ---------------
739777

740778
@bp.route("/api/tickets/<string:ticket_id>/turn-completed", methods=["POST"])

dashboard/frontend/src/App.tsx

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ import ShareView from './pages/ShareView'
3131
import ShareLinks from './pages/ShareLinks'
3232
import HeartbeatsList, { HeartbeatDetail } from './pages/Heartbeats'
3333
import Goals from './pages/Goals'
34-
import Issues from './pages/Issues'
34+
import Topics from './pages/Topics'
3535
import TicketDetail from './pages/TicketDetail'
3636
import KnowledgeLayout from './pages/Knowledge/KnowledgeLayout'
3737
import ConnectionLayout from './pages/Knowledge/ConnectionLayout'
@@ -130,7 +130,8 @@ function AppContent() {
130130
{hasPermission('users', 'manage') && <Route path="/roles" element={<Roles />} />}
131131
{hasPermission('workspace', 'manage') && <Route path="/shares" element={<ShareLinks />} />}
132132
<Route path="/goals" element={<Goals />} />
133-
{hasPermission('tickets', 'view') && <Route path="/issues" element={<Issues />} />}
133+
{hasPermission('tickets', 'view') && <Route path="/topics" element={<Topics />} />}
134+
{hasPermission('tickets', 'view') && <Route path="/issues" element={<Navigate to="/topics" replace />} />}
134135
{hasPermission('tickets', 'view') && <Route path="/tickets/:id" element={<TicketDetail />} />}
135136
{hasPermission('knowledge', 'view') && (
136137
<>

dashboard/frontend/src/components/Sidebar.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@ const navGroups: NavGroup[] = [
5252
{ to: '/triggers', labelKey: 'triggers', icon: Webhook, resource: 'triggers' },
5353
{ to: '/heartbeats', labelKey: 'heartbeats', icon: Heart, resource: 'heartbeats' },
5454
{ to: '/goals', labelKey: 'goals', icon: Target, resource: 'goals' },
55-
{ to: '/issues', labelKey: 'issues', icon: Ticket, resource: 'tickets' },
55+
{ to: '/topics', labelKey: 'issues', icon: Ticket, resource: 'tickets' },
5656
{ to: '/templates', labelKey: 'templates', icon: Layout, resource: 'templates' },
5757
],
5858
},

0 commit comments

Comments
 (0)