Commit Graph
3 Commits
Author SHA1 Message Date
4469bac632 fix(issues): duplicate mark follow-ups from #8741 review (MUL-7349) (#8752)
* perf(issues): resolve a page's duplicate originals in one read (MUL-7349)

Filling duplicate_of read each distinct original with its own primary-key
lookup, so a page of duplicates of different issues cost one round trip
per row. Pages now pass the originals their rows point at to
newStatusCategoryFiller, which loads them with one ListIssueRefsInWorkspace
read, the same way labelsByIssue loads a page's labels. List, open issues,
grouped, search and table rows do this; a response built on its own still
resolves its original, so a caller that passes nothing is slower, never
wrong.

A pointer left on a reopened issue is not looked up at all, and an
original that no longer exists is looked up once and left off.

Co-authored-by: multica-agent <[email protected]>

* fix(issues): stop logging stale duplicate pointers as unmarks (MUL-7349)

Servers from before the duplicate column reopened duplicates and deleted
originals without clearing the pointer. After an upgrade, the next write
to such an issue clears it, and the activity log compared raw pointers,
so it logged "removed duplicate mark" for a mark that no longer counted
and dropped the real status row in its place.

Event publishers now send only live marks: a pointer counts while the
issue is cancelled, the same rule reads apply. Separately, the status row
is dropped only when a duplicate row was actually written to stand in for
it; when the original is gone there is no duplicate row, and the reopen
used to leave no activity at all.

Co-authored-by: multica-agent <[email protected]>

* fix(issues): keep duplicate marks in background issue snapshots (MUL-7349)

IssueToMap hardcoded duplicate_of to null on the assumption that no
background publisher renders a marked issue. PublishAttachmentsChanged
does: a channel /issue with media re-reads the issue after a download of
up to 45 seconds, and a mark made in that window was broadcast as null.
Clients patch their cache with that snapshot, so the list and board
marker disappeared until the next refetch.

IssueToMapResolved now resolves duplicate_of the way the HTTP rendering
does; only a cancelled issue with a pointer costs a read.

Co-authored-by: multica-agent <[email protected]>

---------

Co-authored-by: J <[email protected]>
Co-authored-by: multica-agent <[email protected]>
2026-09-23 17:41:27 +08:00
5d5fe64672 MUL-6343: Add semantic issue activity timestamps (#7193)
* feat: add semantic issue activity timestamps

Co-authored-by: multica-agent <[email protected]>

* fix(migrations): register issue activity index cleanup

Co-authored-by: multica-agent <[email protected]>

* fix(issues): address last activity review

Co-authored-by: multica-agent <[email protected]>

* fix(issues): close last activity review gaps

Co-authored-by: multica-agent <[email protected]>

* fix(issues): harden activity rollout safeguards

Co-authored-by: multica-agent <[email protected]>

---------

Co-authored-by: Sol-Boy <[email protected]>
Co-authored-by: multica-agent <[email protected]>
2026-08-19 16:08:40 +08:00
cb04226fd4 MUL-5632: fix(engine): broadcast the full issue payload for chat-created issues (#6278)
* fix(engine): broadcast the full issue payload for chat-created issues

An issue opened with /issue from a chat channel broadcast
{"issue_id": <uuid>} — the minimal fallback IssueService.Create emits
whenever a caller leaves IssueCreateOpts.BroadcastPayload nil, which the
engine did. Every issue:created consumer reads payload["issue"], so the
missing key meant extractIssueFields (cmd/server/subscriber_listeners.go)
failed its map assertion and returned false before it ever reached the
id / creator_id check. The listener returned early and no auto-subscribe
rule ran, so the person who typed /issue was never subscribed to the
issue they had just filed and got no notifications for it. Both channels
that register with the engine — Feishu/Lark and Slack — were affected.

The HTTP handler already supplies a BroadcastPayload and autopilot
publishes its own event through issueToMap; only the engine path was
short. Export issueToMap as IssueToMap and have the engine use it, so a
single builder is the one source of truth for that shape instead of
three descriptions drifting apart. The workspace issue prefix comes from
the GetWorkspace call the /issue path already made for the chat reply's
identifier, hoisted so it is read once and used for both.

No regression — the payload only gains keys, and consumers read named
fields. The activity and notification listeners type-assert
payload["issue"] to handler.IssueResponse and still skip a map, exactly
as they already do for autopilot-created issues. The subscriber listener
accepts either shape. The workspace WS fanout marshals the payload
as-is, so open clients now render a chat-created issue live instead of
waiting for a refetch.

Co-Authored-By: Claude Fable 5 <[email protected]>

* fix(service): complete the issue:created payload contract

IssueToMap omitted project_id, stage, metadata and properties, all of
which handler.IssueResponse — the other rendering of the same event —
always emits. Clients type both as a complete Issue and insert the
object straight into the list cache without runtime validation, so an
issue created by autopilot, quick-create or the chat /issue command
appeared in open clients missing its project and custom properties
until the next refetch. Broadcasting the full issue from the chat path
would otherwise have spread that existing defect to a third entry
point.

Fill in the missing keys and pin the contract with a test that fails if
the two renderings ever drift apart again, so adding a field to
IssueResponse without adding it here is caught in CI rather than in the
UI. metadata and properties render as {} when unset, matching the
"always present" part of the contract.

Also route both identifier renderings through service.IssueIdentifier,
so a workspace lookup that degrades the prefix cannot show "#42" in the
chat reply while the realtime list shows "-42".

Co-authored-by: Bohan-J <[email protected]>
Co-authored-by: Claude Opus 5 <[email protected]>
Co-authored-by: multica-agent <[email protected]>

* docs(service): name the actual IssueToMap call sites

The doc comment and the shape test listed quick-create among the
non-HTTP publishers of the issue payload. It is not one: the three call
sites are autopilot and the channel engine on issue:created, and the
background stuck-issue status reset on issue:updated.

Co-authored-by: Bohan-J <[email protected]>
Co-authored-by: Claude Opus 5 <[email protected]>
Co-authored-by: multica-agent <[email protected]>

---------

Co-authored-by: Claude Fable 5 <[email protected]>
Co-authored-by: Bohan-J <[email protected]>
Co-authored-by: multica-agent <[email protected]>
2026-08-03 14:29:47 +08:00