mirror of
https://github.com/multica-ai/multica.git
synced 2026-07-23 18:17:44 +02:00
agent/lambda/768b92e0
6 Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
c402b91c1d |
fix(properties): harden concurrency and cache coordination from clean-room review
Backend (MUL-4762 F1/F4/F5): - withPropertyLock: pg_advisory_xact_lock helper; definition create/update and value writes now serialize config-vs-value and cap-vs-insert races (workspace-level 'props:' lock + per-definition 'prop:' lock, ordered). - propertySortExpr degrades archived definitions to position sort. Frontend (F2/F3/F6): - onIssuePropertiesChanged invalidates plain assignee-group caches too. - Property value mutations cancel list refetches in onMutate and roll back only the touched key against the current bag (concurrent WS writes to other keys survive a failed write). - useUpdateIssue reconcile drops the stale properties bag from the server snapshot; the property pipeline owns that field. - Surface controller passes persisted property filters/sorts through until the catalog query settles (cold cache no longer strips them). Co-authored-by: multica-agent <github@multica.ai> |
||
|
|
fceb9e90df |
feat(properties): server-side property filtering and sorting on list endpoints
Property filter/sort now execute in the database, so results are correct
across the full issue set — not just the loaded 50-per-status window
(closes MUL-4493 item 1's filter/sort half; requested on MUL-4463).
- New `properties` query param on ListIssues and ListGroupedIssues:
JSON {definitionId: [values]} compiled to an AND-of-ORs containment
check (double NOT EXISTS over jsonb_array_elements). One value expands
to every storage shape it could match — string (select), array element
(multi_select), boolean (checkbox) — so the handler stays type-agnostic.
Guarded at 20 definitions / 50 values.
- `sort=property:<definitionId>` resolves the definition and orders by a
typed expression (numeric CASE cast for number, NULLIF text for
date/text/url); missing values sort last in both directions. Malformed
ids 400; unknown/archived definitions degrade to position order instead
of breaking stale clients.
- Frontend: the property filter and property sort ride the IssueSortParam
window bag, so every surface (workspace + my-issues variants), query
key, and per-status load-more page carries them automatically. The
client-side re-sort layer is gone; applyIssueFilters keeps its property
predicate as an optimistic-update backstop.
- Regression test seeds 55 issues and proves a match at position 55 is
returned by a filtered 50-row page, plus sort order/missing-last,
AND-across-definitions, and the 400/fallback sort paths.
Co-authored-by: multica-agent <github@multica.ai>
|
||
|
|
ea9a470343 |
fix(properties): address MUL-4463 review round 2 — desc sort, option bucketing, archived-state reconciliation
- sort: direction now applies to value comparison only; issues without a value sort last in BOTH directions (the whole-array reverse flipped them to the front on desc). Test covers the desc+missing case. - board: values referencing an option removed from the definition bucket into the No-value column instead of vanishing (unmatched column ids dropped the issue entirely). Defense-in-depth behind the new server-side in-use guard; drag-utils test locks both behaviors. - controller: persisted propertyFilters keyed by archived/deleted definitions are stripped before reaching the filter predicates, and a persisted property sort on a non-active definition degrades to manual order — previously both kept silently applying while the header claimed otherwise. The filter badge counts only active-definition filters. Co-authored-by: multica-agent <github@multica.ai> |
||
|
|
784d9dd08a |
feat(web): custom-property list surfaces — filter, cards, sort, board grouping (MUL-4463 M2)
Brings custom properties to the issue list surfaces on top of the M1 definitions/values core: - Filter: per-definition sections in the Filter dropdown (select / multi_select options with color dots and counts; checkbox as Yes/No pseudo-options). OR within a definition, AND across definitions; client-side in applyIssueFilters, mirrored into filterAssigneeGroups for the assignee-grouped board. Included in active-filter count and Clear all. - Cards: per-property Display toggles (cardPropertyIds) render value chips on board cards and list rows via CustomPropertyValueDisplay. - Sort: SortField gains property:<id> for number/date definitions. Server keeps position order (fixed sort enum); the surface controller re-sorts client-side, swimlane/gantt reuse the same comparator. Date-only strings compare lexically; missing values sort last. - Board grouping: IssueGrouping gains property:<id> for select definitions — one column per option (definition order) plus a trailing No-value column, option-colored headings. Drag-drop moves position via UpdateIssue and applies the value through useSetIssueProperty/useUnsetIssueProperty (properties are not part of UpdateIssueRequest). Stale persisted property groupings fall back to status columns. View-store: propertyFilters + cardPropertyIds persisted via the partialize allowlist; clearFilters resets property filters; new fields deep-merge cleanly into pre-existing persisted snapshots. Co-authored-by: multica-agent <github@multica.ai> |
||
|
|
6077f1a9b0 |
feat(issues): surface cancelled issues via status filter (MUL-4261) (#5099)
* feat(issues): surface cancelled issues via status filter (MUL-4261) Cancelled issues were never visible in the web/desktop issue surface: `PAGINATED_STATUSES`/`BOARD_STATUSES` excluded `cancelled`, so the list/ board/swimlane never fetched or rendered it, and the status filter offered a "Cancelled" checkbox that resolved to an empty list. Implement plan A (fetch-always, hide-by-default): - `PAGINATED_STATUSES` now includes `cancelled`, so it is always fetched into the byStatus cache and rebuckets correctly when an issue is cancelled (previously the card was dropped). `BOARD_STATUSES` stays the default *visible* column set. - The surface gates the flattened list on the status filter: cancelled issues are excluded from `surfaceIssues` (and therefore list/board/ swimlane columns, header facet counts, batch selection, and isEmpty) unless the filter explicitly selects "cancelled". Then a Cancelled section appears, sorted last. - `hiddenStatuses` stays board-only, so cancelled is never offered as a hideable/persistent board column. Dragging a card into the Cancelled column (visible only when filtered) sets status=cancelled through the existing generic column DnD — no new entry point or copy added. Non-goals (unchanged): mobile, member/agent archive surfaces, an always-on cancelled column. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Co-authored-by: multica-agent <github@multica.ai> * fix(issues): swimlane must keep the cancelled column when filtered (MUL-4261) The swimlane derived its status columns as `BOARD_STATUSES.filter(s => visibleStatuses.includes(s))`, re-imposing canonical order by intersecting with BOARD_STATUSES. Since BOARD_STATUSES omits `cancelled`, a filter-selected Cancelled column was silently dropped even though the controller's `visibleStatuses` included it — the surface fetched and gated cancelled correctly, but swimlane never rendered it. Filter against ALL_STATUSES instead: same canonical ordering, but a selected `cancelled` column now survives. `hiddenStatuses` stays board-only, so cancelled is still never a hideable/persistent column. Regression tests: - swimlane renders a Cancelled column + its cards when cancelled is in visibleStatuses, and omits it otherwise (verified failing pre-fix); - controller asserts hiddenStatuses never contains cancelled. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Co-authored-by: multica-agent <github@multica.ai> --------- Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com> Co-authored-by: multica-agent <github@multica.ai> |
||
|
|
ade6b34e5f |
MUL-3903: Extract shared issue surfaces (#4774)
* MUL-3903 refactor project issue surface state Co-authored-by: multica-agent <github@multica.ai> * Refactor project issue surface ownership Co-authored-by: multica-agent <github@multica.ai> * Extract shared issue surface entrypoints Co-authored-by: multica-agent <github@multica.ai> * Fix issue surface create defaults and selection reset Co-authored-by: multica-agent <github@multica.ai> * test(editor): add missing AbortSignal to suggestion items() calls The suggestion items() contract gained a required signal param; the mention/slash test call sites were never updated, breaking pnpm typecheck for @multica/views. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * feat(issues): server-side assignee_types filter on ListIssues ListGroupedIssues has taken assignee_types since squads shipped, but ListIssues never did — so the workspace Members/Agents tabs had to fetch the unfiltered workspace list and post-filter loaded pages client-side, which made column totals and load-more pagination reflect the unfiltered counts. Add the same parse + WHERE clause to ListIssues (count query shares the WHERE, so totals agree), thread the param through the TS client, and widen MyIssuesFilter so scoped list caches can carry it. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * refactor(issues): route issue cache writes through a membership-aware coordinator useUpdateIssue, useBatchUpdateIssues, and the WS issue:updated handler each maintained their own similar-but-diverging patch/invalidate rules. Consolidate them into cache-coordinator.ts (applyIssueChange / rollbackIssueChange / invalidateIssueDerivatives) so local writes and remote echoes follow one rules table by construction. The coordinator is membership-aware via surface/membership.ts (true | false | unknown against each list cache's own filter contract): - a change that moves an issue off a filtered surface removes the card surgically (bucket total decremented) — fixes assignee changes leaving stale cards on My Assigned with no local safety net (previously only the WS echo recovered it), and replaces the blanket invalidate-myAll net for project moves (MUL-3669) with per-key precision - possible entry into a loaded list marks that key stale — never hard-insert; page/slot is server knowledge - stale keys flush on settle for mutations (a mid-flight refetch would stomp the optimistic state) and immediately for WS - batch updates now patch detail + inbox like single updates; the off-screen bucket-count recovery previously exclusive to the WS path now covers local mutations too Preserved invariants: synchronous optimistic patches (dnd-kit), MUL-3375 control-field stripping, and no refetch of surgically reconciled lists (the drag-flicker fix). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * refactor(issues): resolve surfaces via core query plan/repository with window-keyed remount Read-path convergence and the loading/empty semantics that fall out of it: - scope -> API params moves from scope.ts helpers into surface/query-plan.ts; workspace members/agents become server-filtered scoped plans (assignee_types) and the client postFilter machinery is deleted — tab counts and load-more are now exact - query selection moves behind surface/repository.ts; the views data hook no longer branches on workspace-vs-scoped plumbing - IssueSurfaceContent remounts on data-window change (wsId + scope): keepPreviousData placeholders keep sort/filter changes flicker-free within one window but must never let project A's (or workspace A's) cards impersonate B's with no loading state — cold window shows the skeleton, warm window hits cache instantly - isEmpty is only asserted from full-window data; the gantt scheduled-only projection can't prove the window is empty, so GanttView's own "no scheduled issues" empty state renders instead of the generic create-issue one - per-card project lookups hoist into a surface-level projectMap (drops a per-card useQuery), create-defaults typing tightens to IssueCreateDefaults Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * perf(issues): count-only arithmetic for off-window status/membership changes An issue beyond a list's loaded page window used to force a full first-page refetch just to fix two column counts. When the change is CERTAIN (base entity known, membership definitive) the coordinator now does the arithmetic locally: - stayed a member + status changed: move one unit of total between the two buckets (loaded arrays untouched; hasMore stays consistent) - left the list (reassigned / re-projected): old status bucket total -1 - member-to-member reassignment: counts unaffected, not even a stale key Entering a list and any uncertainty (no base, unknown membership) still refetch — the right page/slot is server knowledge. Branches on membership OUTCOMES, not on which field changed, so future dimensions (team) join automatically. Biggest win is the WS path: agents flipping off-screen statuses no longer trigger refetch storms. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * feat(issues): deferred view-refresh indicator during placeholder revalidation Sort/date changes (and any grouped-board filter change) revalidate behind the previous snapshot — correct, but on a slow network the click felt dead: content stays put and isLoading never fires. Surface the state as isRefreshing (isPlaceholderData of the active query) and render a shared ViewRefreshIndicator in every issues header: a fixed-width slot (zero layout shift) whose spinner fades in after 300ms, so sub-second responses show nothing (NN/g) while slow ones get a working signal. Bound to the revalidation STATE, not to any particular control — any current or future server-side view change lights it automatically. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> --------- Co-authored-by: multica-agent <github@multica.ai> Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |