mirror of
https://github.com/multica-ai/multica.git
synced 2026-08-06 10:50:54 +02:00
fix/cloud-runtime-create-fields
17 Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
54368fd826 |
feat(projects): scheduled-only Gantt data source + WS reactivity (MUL-1881) (#2856)
* feat(projects): scheduled-only Gantt data source + WS reactivity (MUL-1881) Project Gantt now fetches its own scheduled-only data instead of riding the Board/List pagination cache. The Unscheduled drawer and pagination warning banner are gone, and any WS-driven issue change (create / update / delete) invalidates the new cache so the timeline stays live. - Backend: `GET /api/issues?scheduled=true` adds an `(i.start_date IS NOT NULL OR i.due_date IS NOT NULL)` predicate on both ListIssues and CountIssues. New SQL filter is plumbed through sqlc + handler. - Frontend: new `projectGanttIssuesOptions(wsId, projectId)` issues a single fetch and lives under its own cache key. WS handlers and mutations invalidate the prefix on create/update/delete so the bar reacts to start_date / due_date changes from other tabs and from this tab without waiting on the WS round-trip. - GanttView: drops the Unscheduled section, the pagination warning banner, and the load-all button; renders only scheduled rows. - Removes now-dead `useLoadAllRemaining`, `myIssueListPaginationOptions`, `summarizeIssueListPagination`, and the gantt locale strings that supported the old plumbing. Co-authored-by: multica-agent <github@multica.ai> * fix(projects): page through Gantt fetch and isolate per-view data sources - Walk paginated `scheduled=true` issues until total is reached so projects with more than 500 scheduled bars no longer silently truncate. - Gantt mode disables the bucketed Board/List query and reads its own scheduled cache for the project empty-state check, so the page never short-circuits Gantt with a Board-derived "no issues" CTA. - `onIssueLabelsChanged` patches matching rows in the Project Gantt cache in-place, keeping label filters consistent after attach/detach from other tabs or agents. MUL-1881 Co-authored-by: multica-agent <github@multica.ai> --------- Co-authored-by: multica-agent <github@multica.ai> |
||
|
|
98ef021d1d |
feat(projects): add Project Gantt view (MUL-1881) (#2843)
* feat(projects): add Project Gantt view (MUL-1881) Adds Gantt as a third option in the Project page's view toggle (Board / List / Gantt). Bars span start_date → due_date; issues with only one date render as markers, issues with neither are collapsed into an Unscheduled section. Toolbar exposes day/week/month zoom and a show-completed toggle. The Gantt view shares the existing IssuesHeader filters/sort. Implementation is self-rendered SVG/HTML — no new dependencies. UTC day-aligned date math keeps bars on the right columns regardless of viewer timezone. Co-authored-by: multica-agent <github@multica.ai> * fix(projects): scope Gantt to project surface + warn on hidden pages - IssuesHeader / IssueDisplayControls now take `allowGantt` (default false); only Project Detail opts in. /issues, /my-issues and the actor panel no longer expose a Gantt option that silently fell through to List, and the toggle icon falls back to List when a stored `viewMode === "gantt"` lands on a surface that doesn't render it. - Project Gantt now surfaces a banner with hidden-issue count plus a Load-all action that drains every remaining paginated page into the cache via the new `useLoadAllRemaining` helper. Pagination summary comes from `myIssueListPaginationOptions`, which shares the existing cache key with `myIssueListOptions` so totals stay in sync with Board/List. - ScheduledRow normalizes a `start_date > due_date` anomaly to min/max and outlines the bar with a destructive ring + tooltip note, instead of silently dropping the row. Co-authored-by: multica-agent <github@multica.ai> --------- Co-authored-by: multica-agent <github@multica.ai> |
||
|
|
93153d08b7 |
feat(my-issues): cover squad assignees via involves_user_id (MUL-2397) (#2829)
Re-introduces the `involves_user_id` filter on the issues list / open-list / count / grouped paths, but with the semantics nailed down for the second time around: tab 3 surfaces issues whose assignee is an *indirect* extension of the user (owned agent, or a squad they're a human member of / lead via owned agent / have an owned agent inside) — and explicitly NOT direct member assignment, which is tab 1's meaning. - server/pkg/db/queries/issue.sql: 4-branch filter on ListIssues / ListOpenIssues / CountIssues. Each subquery clamps workspace_id because issue.assignee_id is polymorphic with no FK. Leader resolution reads squad.leader_id directly, not the squad_member copy row (squad.go ignores errors when seeding that copy, so it can be missing). FindActiveDuplicateIssue switched from positional $2/$3/$4 to named sqlc.arg() — pure hygiene so the generated struct field names don't drift when new nargs are added. - server/internal/handler/issue.go: parse involves_user_id and plumb it into the three sqlc params; ListGroupedIssues (hand-written dynamic SQL) gets a mirrored 4-branch fragment, no shortcut. - packages/core: ListIssuesParams / ListGroupedIssuesParams / MyIssuesFilter / api.listIssues / api.listGroupedIssues all carry the new param through. - packages/views/my-issues: tab 3 switches from client-side agent-fanout to involves_user_id=user.id. agentListOptions import and the myAgentIds memo go away. - server/internal/handler/issue_involves_test.go: 13 integration tests cover every branch (positive + cross-workspace negatives) plus the critical ExcludesDirectMemberAssignee negative on BOTH the sqlc and the grouped paths, locking tab 3 ∩ tab 1 = ∅. Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com> Co-authored-by: multica-agent <github@multica.ai> |
||
|
|
5476e7678d |
Revert "feat(my-issues): cover squad assignees via involves_user_id (MUL-2364…" (#2828)
This reverts commit
|
||
|
|
3c510c31ed |
feat(my-issues): cover squad assignees via involves_user_id (MUL-2364) (#2801)
* feat(my-issues): cover squad assignees via involves_user_id (MUL-2364) The "My Agents" tab on /my-issues only resolved agents owned by the caller, so issues assigned to squads (member, leader, or agent-member of mine) never surfaced. This added a UNION-based involves_user_id filter that the backend expands to "me + agents I own + squads I relate to" in a single query. - SQL: ListIssues / ListOpenIssues / CountIssues accept narg involves_user_id and OR a workspace-scoped 3-branch UNION on the squad assignee subquery. Leader is sourced from canonical squad.leader_id (not the best-effort squad_member copy row whose AddSquadMember error is dropped in squad.go:177-188 and :259-263). - Handler: parses involves_user_id via parseUUIDOrBadRequest, plumbs into all three list params, and mirrors the same UNION fragment into the grouped dynamic SQL path. - Frontend: ListIssuesParams / ListGroupedIssuesParams / MyIssuesFilter gain involves_user_id; api client forwards it to the querystring. - My Issues page: "agents" scope now passes involves_user_id instead of fanning out owned-agent IDs client-side. Tab label widens to "我的智能体 / 小队" / "My Agents / Squads". - Tests: Go suite covers all three squad relations including the canonical-leader-without-squad_member-copy variant, cross-workspace isolation for agent / leader / squad_member branches, combination with creator_id, and the malformed-UUID 400 path. Client test pins the involves_user_id querystring wiring for both list endpoints. The FindActiveDuplicateIssue query gets explicit sqlc.arg() names so sqlc regeneration keeps the existing struct field names regardless of the local sqlc version (no behavior change). Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> Co-authored-by: multica-agent <github@multica.ai> * test(my-issues): tighten cross-workspace negatives for involves_user_id UNION Cross-workspace negative tests previously put both the foreign actor and the foreign issue in the foreign workspace, so the outer i.workspace_id = $1 already excluded the row before the UNION branches were exercised. Stripping a.workspace_id = $1 / s.workspace_id = $1 from any of the UNION subqueries would not have failed the tests. Rewrite the three existing negative cases to seed the issue in testWorkspaceID with a polymorphic assignee_id pointing at a foreign-workspace agent or squad (issue.assignee_id has no FK per migrations/001_init.up.sql:61). Now each UNION branch must enforce its own workspace scoping for the issue to stay out of the result. Also add ExcludesOtherWorkspaceSquadAgentMember: the squad_member.agent UNION branch had only positive coverage; this test pins that s.workspace_id = $1 and a.workspace_id = $1 must both hold there too. Verified by mutation: stripping the workspace clause from each branch makes the corresponding test fail. Co-authored-by: multica-agent <github@multica.ai> --------- Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com> Co-authored-by: multica-agent <github@multica.ai> |
||
|
|
8e88156356 | Add assignee grouping for issue boards (#2693) | ||
|
|
352e838b01 |
fix(attachments): re-sign CloudFront download URLs at click time (#2407)
* fix(attachments): re-sign CloudFront download URLs at click time The attachment download buttons opened `download_url` directly from cached timeline/comment payloads. The signed URL is valid for 30 minutes, so a page left open past that window would 403 with `AccessDenied` (MUL-2038 / GitHub #2397). - Add `GET /api/attachments/{id}` client method that re-signs on every call, validated by a stricter `AttachmentResponseSchema` (enforces `url`, `download_url`, `filename` so a malformed response degrades to the EMPTY_ATTACHMENT record instead of opening `undefined`). - Introduce `useDownloadAttachment` hook with two execution shapes: - Web: synchronously open `about:blank` inside the click gesture to keep popup activation, then hydrate `location.href` after the fetch. Cannot pass `noopener` here — HTML spec dom-open step 17 makes that return null. - Desktop: skip the placeholder (Electron's setWindowOpenHandler rejects about:blank) and hand the fresh URL to `openExternal`. - Wire the hook into the standalone attachment buttons (comment-card) and the inline `<img>` / file-card buttons inside `ReadonlyContent`. Inline buttons resolve the attachment id by URL match; external URLs fall back to `openExternal`. Co-authored-by: multica-agent <github@multica.ai> * fix(editor): re-sign downloads from ContentEditor file/image NodeViews The previous commit only wired the click-time fresh-sign through ReadonlyContent + the standalone attachment list. The Tiptap NodeViews inside ContentEditor still opened the raw URL with `window.open(href, "_blank", "noopener,noreferrer")`, leaving two download surfaces on stale signatures: - Issue description (always renders via ContentEditor) - Comment edit mode (transient ContentEditor instance) - Add AttachmentDownloadContext + AttachmentDownloadProvider so NodeViews can resolve markdown URLs to an attachment id and call the existing `useDownloadAttachment` hook. The default fallback (no provider mounted) hands the raw URL to `openExternal`, keeping non-editor mounts unaffected. - ContentEditor accepts `attachments?: Attachment[]` and wraps EditorContent with the provider. - file-card.tsx and image-view.tsx NodeViews swap their `window.open(...)` calls for `openByUrl(href|src)` from the provider. - issue-detail.tsx threads `useQuery(issueAttachmentsOptions(id))` into ContentEditor for the description. - comment-card.tsx passes `entry.attachments` to both edit-mode editors. Co-authored-by: multica-agent <github@multica.ai> --------- Co-authored-by: multica-agent <github@multica.ai> |
||
|
|
3f20999597 |
refactor(timeline): drop server-side comment + timeline pagination (#2322)
* refactor(timeline): drop server-side comment + timeline pagination (MUL-1929) The cursor-paginated /timeline and /comments endpoints were sized for a problem the data shape doesn't have: prod p99 is ~30 comments per issue and the all-time max is ~1.1k. Time-based pagination also splits reply threads across page boundaries (orphan replies), which the frontend was papering over with an "orphan rescue" that promoted disconnected replies to top-level — confusing UX with no real benefit. Replace both endpoints with a single full-issue fetch, capped server-side at 2000 rows as a defensive safety net (never hit in practice). Server - /api/issues/:id/timeline now returns a flat ASC TimelineEntry[] (matches the legacy desktop contract — older Multica.app builds keep working because the wrapped TimelineResponse + cursors are gone, and the raw array shape was always what they consumed). - /api/issues/:id/comments drops limit/offset; only ?since is honoured for the CLI agent-polling flow. - Drop ListCommentsBefore/After/Latest, ListActivitiesBefore/After/Latest and the timelineCursor encoding. - Replace with ListCommentsForIssue / ListCommentsSinceForIssue / ListActivitiesForIssue (capped by argument). CLI - multica issue comment list drops --limit / --offset and the X-Total-Count reporting; --since is preserved for incremental polling. Frontend - Replace useInfiniteQuery with useQuery in useIssueTimeline; drop fetchOlder/Newer, jumpToLatest, isAtLatest, newEntriesBelowCount. - Remove timeline-cache helpers (mapAllEntries / filterAllEntries / prependToLatestPage) and the TimelinePage / TimelinePageParam types. - WS event handlers update the single flat-array cache directly. - Drop the orphan-reply rescue in issue-detail — every reply's parent is now guaranteed to be in the same array. - Strip the "show older / show newer / jump to latest" buttons and their i18n strings. Co-authored-by: multica-agent <github@multica.ai> * fix(timeline): address review feedback on pagination removal Three issues caught in PR #2322 review: 1. /timeline broke for stale clients between #2128 and this PR. They send ?limit/?before/?after/?around and parse with the wrapped TimelinePageSchema; the new flat-array response was failing schema validation and falling back to an empty timeline. Restore the wrapped shape on those query params (DESC entries, null cursors, has_more_*=false), keeping the flat ASC array for bare requests. Around-mode now also fills target_index from the merged slice so legacy clients can still scroll-to-anchor without a follow-up. 2. The agent prompts in runtime_config.go and prompt.go still told agents that `multica issue comment list` accepts --limit/--offset and to use `--limit 30` on truncated output. With those flags removed in this PR, new agent runs would hit "unknown flag" or skip context. Update the prompt copy to "returns all comments, capped at 2000; --since for incremental polling". 3. useCreateComment's onSuccess was a bare append to the timeline cache with no id-dedupe, so a fast comment:created WS event firing before onSuccess produced a transient duplicate. Restore the id guard the old prependToLatestPage helper used to provide. Adds two new boundary tests: - TestListTimeline_LegacyWrappedShape_OnPaginationParams - TestListTimeline_LegacyWrappedShape_AroundFillsTargetIndex Co-authored-by: multica-agent <github@multica.ai> * test(handler): fix timeline test assertions for handler-package isolation The TestListTimeline_* assertions assumed CreateIssue would seed an "issue_created" activity_log row, but the activity listener that publishes those rows is registered in cmd/server/main.go — handler-package tests don't wire it up. CI saw 5 entries (3 comments + 2 activities) where the test expected ≥6. Drop the auto-activity assumption: assert exactly 5 entries in TestListTimeline_MergesCommentsAndActivities, and tighten TestListTimeline_EmptyIssue to assert a fully-empty timeline. Co-authored-by: multica-agent <github@multica.ai> --------- Co-authored-by: multica-agent <github@multica.ai> |
||
|
|
ba147708a6 |
fix(timeline): cursor-paginated timeline to stop long-issue freeze (#1968) (#2128)
* fix(timeline): cursor-paginated timeline to stop long-issue freeze (#1968) Opening an issue from Inbox with thousands of timeline entries used to hard-freeze the browser tab on a synchronous render of every comment + activity. The whole pipeline was unbounded: the API returned every row, TanStack Query cached the full array, and IssueDetail mounted N CommentCards (each running a full react-markdown + lowlight pipeline) in one frame. This swaps the timeline endpoint to keyset cursor pagination and rewires the frontend to useInfiniteQuery so a long issue costs the same as a short one on first paint. API: - GET /issues/:id/timeline now accepts ?before / ?after / ?around (mutex) + ?limit (default 50, max 100); response wraps entries with next/prev cursors and has_more flags. Cursors are opaque base64 (created_at, id). - ?around=<entry_id> anchors a window on the target so Inbox notifications pointing at an old comment never trigger the freeze. - New composite indexes on (issue_id, created_at DESC, id DESC) replace the redundant single-column ones so keyset queries are index-only scans. - /issues/:id/comments default branch now caps at 50 instead of returning every row unbounded; the unbounded ListComments / ListActivities sqlc queries are deleted. Frontend: - useIssueTimeline switches to useInfiniteQuery, exposes fetchOlder/fetchNewer/jumpToLatest + isAtLatest + newEntriesBelowCount. - WS handlers respect the at-latest invariant: comment/activity:created prepends to pages[0] only when the user is reading the live tail; otherwise it just bumps a counter so the UI offers a "Jump to latest" affordance without yanking scroll. - Optimistic mutations adapted to the InfiniteData shape via shared helpers (mapAllEntries / filterAllEntries / prependToLatestPage in core/issues/timeline-cache.ts) and use setQueriesData so all open windows of the same issue stay in sync. - IssueDetail Activity section gets a TimelineSkeleton placeholder during the brief load window plus subtle text-link load-more buttons matching the existing Subscribe affordance (no Button chrome). Top uses a divider for boundary clarity; bottom shows "Jump to latest · N new" weighted slightly heavier when there's unread state. - highlightCommentId now flows into the hook's around parameter so Inbox jumps fetch the surrounding 50 entries directly. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * chore(agent): default comment list to 50 + prompt hint about long issues The CLI's "multica issue comment list" used to default to --limit 0 (meaning "fetch every comment"), which lets an agent on a long issue fill its context window with thousands of rows. The default is now 50; agents that need older history can pass --limit or --since explicitly. The local-coding-agent prompt also gains a single-line note about this in both the comment-triggered and on-assign flows so the agent knows to scope its fetches when issue size is unknown. Autopilot run-only mode is intentionally unchanged — it has no issue context to query. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com> |
||
|
|
f745a3bbbe |
feat(agent): presence v3 + execution log + trigger summary (#1823)
* refactor(views): migrate agent/runtime/skill lists to TanStack DataTable
Replace the per-page CSS Grid + minmax(min, fr) + sticky-first-col + truncate
implementation with a TanStack Table backend rendered through a Dice UI-style
DataTable shell. Column widths are now px-based via column.size, so cells
no longer shrink or auto-truncate as the viewport narrows; when the sum of
columns exceeds the viewport, the container scrolls horizontally instead.
- Add @tanstack/react-table to the catalog (8.21.3) and wire it into
packages/ui (dep) and packages/views (peerDep).
- packages/ui: new DataTable + DataTableColumnHeader + lib/data-table.ts
(getColumnPinningStyle), adapted from Dice UI's registry. The shell
renders <table> directly (skipping shadcn's <Table> wrapper) so its own
outer overflow controls both axes — no nested overflow conflicts.
- packages/views: each list now declares ColumnDef[] with explicit
cell renderers. Row click navigates to detail via onRowClick (instead of
wrapping <tr> in <a>, which is invalid HTML); kebab dropdowns
stopPropagation so they don't trigger the row navigation.
- Drop the previous AGENT_LIST_GRID / GRID_WITH_OWNER / ROW_GRID
templates and the sticky-first-col / subgrid mechanics that came with
them. agent-list-item.tsx is removed; runtime-list.tsx and
skills-page.tsx are trimmed to thin wrappers.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* feat(agent): cap description at 255 chars (db + api + ui)
Symmetric enforcement across DB, server, and UI:
- Migration 060: pre-flight truncate of any oversize rows, then ADD
CONSTRAINT NOT VALID + VALIDATE CONSTRAINT so the new check doesn't
block writes during validation.
- Server handler validates utf8.RuneCountInString on Create/Update and
rejects over-limit input with 400.
- Front-end gets AGENT_DESCRIPTION_MAX_LENGTH in core/agents/constants
(single source of truth shared by the create dialog + edit modal +
test suite) and a CharCounter component that warns at 90% and errors
past the cap.
- Description editor moves from a 288px popover to a roomy modal.
Editor body is mounted only while the dialog is open, so the local
draft state is locked in at mount time and never reset by an external
WS update — the React-recommended replacement for the
useEffect(reset, [value]) anti-pattern.
Counted in code points everywhere (rune count / spread length /
char_length) so multibyte input agrees across all three layers.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* refactor(views): data-table polish across runtime + skill lists
Builds on the DataTable migration in
|
||
|
|
abd69890a8 |
Revert "feat(issues): server-side filters incl. label, fixing pagination drop…" (#1779)
This reverts commit
|
||
|
|
246fcd4ce4 |
feat(issues): server-side filters incl. label, fixing pagination drops (#1776)
* feat(issues): server-side label + filter querying for issue list Extends GET /api/issues with label_ids, priorities, creator_ids, project_ids, include_no_assignee, and include_no_project params, and moves the existing single-value filters onto array-form. Each filter becomes part of the SQL WHERE clause so paginated buckets reflect the user's selection — fixes the bug where client-side filtering hid matches sitting past the first page (#1491). CLI gains a repeatable --label flag; legacy --priority/--assignee/ --project keep working via the single-value compatibility paths. * feat(issues): drive workspace + my-issues filters from the server issueListOptions and myIssueListOptions now key the React Query cache on a normalized filter object, so each filter combination has its own cache entry and a filter change re-fetches with the wire-shape filter applied server-side. Drops the client-side filterIssues step on the issues page, my-issues page, and project detail — that step silently hid matches that lived past the first paginated page (#1491). Adds a Label submenu to the workspace issues filter dropdown, plus labelFilters in the view store. Mutations and ws-updaters fan their optimistic patches across every filter-keyed list cache via qc.setQueriesData on issueKeys.listPrefix(wsId), and the editor's mention-suggestion reads from any matching list cache for instant first paint regardless of which filter is active. * fix(issues): route Members/Agents scope through server-side filter The Members/Agents scope tabs on the workspace issues page were still narrowing client-side via `assignee_type === 'member'`. That hits the exact pagination-blind bug this PR is meant to fix: if the first 50 issues per status don't include the right assignee type, the tab shows "No issues" while later pages have matches. Adds an `assignee_types text[]` filter to ListIssues / ListOpenIssues / CountIssues, threads it through the API client, normalizer and view filter, and maps the scope tab to it. Each scope now keys its own list cache and refetches with the correct first page. Also disables the My Issues "My Agents" query when the user owns no agents — `assignee_ids: []` was getting dropped by both the API client and the query-key normalizer, so the request went out unfiltered and surfaced unrelated issues under "My Agents". |
||
|
|
642844c736 |
feat(issues): paginate every status column, not just done (#1422)
* feat(issues): paginate every status column, not just done
Previously the workspace issues list fetched all non-done/cancelled
issues in a single unbounded `open_only=true` request and only
paginated the done column. In workspaces with many open issues this
ballooned the initial payload and skipped pagination entirely.
Restructure the issue list cache into per-status buckets
(`{ byStatus: { [status]: { issues, total } } }`) fetched in parallel,
generalize `useLoadMoreDoneIssues` into `useLoadMoreByStatus(status,
myIssuesOpts?)`, and render an infinite-scroll sentinel inside every
accordion group and kanban column. Sort and filter stay client-side,
matching the done column's existing behavior.
Backend `ListIssues` already supports per-status pagination, so no
API changes are required.
* fix(issues): handle project / hidden-column / lookup regressions from paginated list cache
After bucketing the issue list cache by status, three consumers that
treated `issueListOptions()` as a complete local index broke:
- `project-detail.tsx` filtered the workspace list by `project_id`
client-side, so projects whose issues sat past the first 50-per-status
page rendered empty. Switch to `myIssueListOptions(wsId,
'project:<id>', { project_id })` so the server returns only this
project's issues; add `project_id` to `ListIssuesParams` /
`MyIssuesFilter` / api client.
- `board-view.tsx` HiddenColumnsPanel read counts from the in-memory
`issues` array — a paginated fragment. Pass `myIssuesOpts` through to
a per-row subcomponent that reads the real per-status total from the
cache.
- `tasks-tab.tsx` and `search-command.tsx` used the list as a global
lookup for task titles / Recent items / current-issue chrome. Switch
both to per-id `issueDetailOptions` via `useQueries` so they're
independent of which page the issue lands on.
Drop the now-redundant `doneTotal` override prop on BoardView/ListView
and the `allIssues` prop on BoardView (only HiddenColumnsPanel consumed
it).
Tests updated: tasks-tab now mocks `api.getIssue`; search-command mocks
`issueDetailOptions` + `useQueries`; project-issue-metrics drops the
`doneColumnCount` assertion.
|
||
|
|
a94c6481dd |
fix: compute sub-issue progress from database instead of paginated client cache (#865)
The sub-issue progress indicator (e.g. "0/2") was undercounting because it was computed from the client-side issue list, which only loads the first 50 done issues. Sub-issues marked as done beyond that page were excluded from both the total and done counts. Added a dedicated backend endpoint (GET /api/issues/child-progress) that aggregates child issue counts directly from the database, ensuring accurate totals regardless of client-side pagination or filtering. Fixes MUL-702 |
||
|
|
b52c048c8e |
fix(my-issues): use server-side filtering instead of client-side (#649)
* fix(my-issues): use server-side filtering instead of client-side My Issues was fetching ALL workspace issues and filtering client-side, causing the Done column to show wrong counts (269 vs user's actual count) and only 2-3 done issues to appear from the first 50-item page. Backend: - Add creator_id and assignee_ids (uuid[]) filters to ListIssues, ListOpenIssues, and CountIssues SQL queries - Parse creator_id and assignee_ids (comma-separated) query params Frontend: - Add myIssueListOptions with per-scope server-filtered queries - Each tab now calls the API with the right filter: Assigned → assignee_id, Created → creator_id, My Agents → assignee_ids - Add useLoadMoreMyDoneIssues for server-filtered done pagination - WS events invalidate My Issues cache via issueKeys.myAll Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * refactor(my-issues): merge duplicate load-more hooks into one Both board-view and list-view were unconditionally calling two hooks (useLoadMoreDoneIssues + useLoadMoreMyDoneIssues) and picking one at runtime. Merged into a single useLoadMoreDoneIssues with an optional myIssues param so only one hook runs per render. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com> |
||
|
|
88982ad23f |
feat(issues): display token usage per issue in detail sidebar (#581)
* feat(issues): display token usage per issue in detail sidebar Add a new "Token usage" section to the issue detail right sidebar that shows aggregated input/output tokens, cache tokens, and run count across all tasks for the issue. Backed by a new SQL query and API endpoint. * fix(db): add index on agent_task_queue(issue_id) for usage queries The GetIssueUsageSummary query joins agent_task_queue filtered by issue_id across all statuses. The existing partial index (migration 022) only covers queued/dispatched rows, so completed tasks require a sequential scan. Add a general index to prevent performance degradation as task volume grows. |
||
|
|
e1e7f68330 |
feat: extract packages/core — Turborepo infrastructure + headless business logic
Phase 1: Monorepo infrastructure - Add Turborepo with turbo.json pipeline (build, dev, typecheck, test) - Update pnpm-workspace.yaml to include packages/* - Create shared TypeScript config (packages/tsconfig) Phase 2: Extract packages/core (zero react-dom, all-platform reuse) - Move domain types, API client, logger, utils → packages/core/ - Move TanStack Query modules (issues, inbox, workspace, runtimes) - Move Zustand stores (auth, workspace, issues, navigation, modals) - Move realtime sync (WSProvider, hooks, ws-updaters) - Refactor auth/workspace stores to factory pattern for DI - Refactor ApiClient with onUnauthorized callback - Refactor useWorkspaceId to React Context (WorkspaceIdProvider) - Refactor WSProvider to accept wsUrl + store props - Create apps/web/platform/ bridge layer (api singleton, store instances) - Update 91 import paths across apps/web/ - Fix 3 test files for new import paths Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> |