Skip to content

moooon

Motir

Vibe your whole project. Bring an idea — Motir's three AI layers plan it, track it, and ship it, end to end. You're looking at Motir, built in Motir.

  • Vibe Project
  • Open Source
  • AI Agent
  • AI Loop
1
requests
0
upvotes
145
planned
1,361
shipped

Motir · Work items

MOTIR-2241Done

11.7.7 The ACTIVITY read over `/api/v1` — the change trail beside the comments, and its opaque composite cursor

The last uncovered read: a work item's change trail, and the merged view that interleaves it with the discussion. GET /api/v1/work-items/{key}/comments already ships and covers one of the three views; the other two have no v1 path.

What to build

The activity read in its three views, adapting activityService.listHistory (changes), commentsService.listComments (comments) and activityService.listAll (both, interleaved). Scope: read. Path and the relationship to the shipped comments endpoint per 11.7.1 Q1 — the comments endpoint is published API under §8 and cannot be withdrawn, whichever shape that decision chose.

The all cursor is the hard part

The merged view pages over two sources, so its cursor is an OPAQUE COMPOSITE carrying both positions. Three properties, all of which a naive implementation breaks:

  • A client must never construct, parse, split or merge one. It is echoed back exactly as received.
  • It is collection-scoped — a cursor issued for the all view, handed to the changes view, is a 422, not a silent reset onto a meaningless position. v1's signed envelope already gives this; the card's job is not to defeat it.
  • Interleaving must be stable across pages. Two sources merged by timestamp with equal timestamps and no tiebreak will duplicate or drop rows at a page boundary — the failure that only appears on real data, and never in a fixture where every row is a second apart.

Views are not a second endpoint each

Whatever 11.7.1 pinned, the three views share one representation of an entry and one paging contract. An entry's shape stays LOOSE in the way the shipped DTO is loose — a part kind or value type a client has never seen must render as its generic form rather than break the read, because a published client meets newer servers.

Scope BOUNDARY

Ends at the activity read. It does NOT change, move or deprecate the shipped comments endpoint. It does NOT add comment WRITE, edit or delete — writing a comment already ships, and edit/delete are moderation-gated operations the ADR's scope table does not map. It does NOT change activityService or what an entry records. It does NOT touch the MCP tool.

Acceptance criteria

  • The endpoint serves all three views, gated on read, declared in the registry and present in the emitted spec.
  • The shipped …/comments endpoint still exists and behaves identically — asserted, since §8 forbids withdrawing it.
  • The all cursor round-trips as an opaque value; a cursor from another collection or another view is refused with the mapped 422, never silently reset.
  • Paging the all view over rows with IDENTICAL timestamps returns every row exactly once — asserted with a fixture built to collide, because a stable-order bug is invisible on any fixture where timestamps differ.
  • An entry whose part kind or value type the schema does not recognise is returned in its generic form rather than failing validation.
  • Empty, single-page and multi-page cases each behave, including an item with comments but no changes and the reverse.
  • The payload matches the MCP tool's for the same item and view, field for field.
  • Cross-tenant keys return 404; a token without read is refused 403.
  • The per-file coverage floor (≥90%) holds on every new file.

Context refs

  • 11.7.1 Q1 — the path decision and how it sits beside the shipped comments endpoint.
  • 11.7.3 — the schema module and the activity-entry shape.
  • lib/services/activityService.tslistAll and listHistory; lib/services/commentsService.tslistComments.
  • lib/mcp/tools/getWorkItemActivity.ts — the three views, the opaque composite cursor, and the loose-entry contract to mirror. Read, not imported.
  • lib/dto/activity.ts + lib/dto/comments.ts — the shapes mapped FROM, including the open part-kind union.
  • app/api/v1/work-items/[key]/comments/route.ts — the shipped sibling this must not disturb.
  • lib/api/v1/pagination.ts — the signed collection-scoped cursor envelope the composite rides inside.
  • tests/mcp/get-work-item-activity.test.ts — the shipped behaviour to agree with.
  • Story: 11.7.