@cueloop/schema 0.1.0-alpha.37 → 0.1.0-alpha.39

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cueloop/schema",
3
- "version": "0.1.0-alpha.37",
3
+ "version": "0.1.0-alpha.39",
4
4
  "description": "The ReviewSession primitive: types, markdown block model, quote-primary anchors, feedback serialization",
5
5
  "homepage": "https://github.com/mmurakaru/cueloop#readme",
6
6
  "bugs": {
package/src/index.ts CHANGED
@@ -1,4 +1,5 @@
1
1
  export * from "./types";
2
+ export * from "./participants";
2
3
  export * from "./markdown";
3
4
  export * from "./working-copy";
4
5
  export * from "./diff";
@@ -0,0 +1,58 @@
1
+ import { describe, expect, test } from "bun:test";
2
+ import { registerParticipant } from "./participants";
3
+ import { SCHEMA_VERSION, type ReviewSession } from "./types";
4
+
5
+ function emptySession(): ReviewSession {
6
+ return {
7
+ schemaVersion: SCHEMA_VERSION,
8
+ id: "ses_1",
9
+ workspace: { repoRoot: "/repo", branch: "main" },
10
+ artifact: { type: "plan", content: "# Plan\n", meta: {} },
11
+ revisions: [],
12
+ annotations: [],
13
+ verdict: null,
14
+ status: "pending",
15
+ createdAt: "2026-01-01T00:00:00.000Z",
16
+ };
17
+ }
18
+
19
+ describe("registerParticipant", () => {
20
+ test("adds a new author with a display name", () => {
21
+ // Act
22
+ const next = registerParticipant(emptySession(), "SHA256:ana", "Ana");
23
+
24
+ // Assert
25
+ expect(next.participants).toEqual([{ id: "SHA256:ana", provider: "ssh", name: "Ana" }]);
26
+ });
27
+
28
+ test("records presence without a name (renders anonymous)", () => {
29
+ // Act
30
+ const next = registerParticipant(emptySession(), "SHA256:ana");
31
+
32
+ // Assert
33
+ expect(next.participants).toEqual([{ id: "SHA256:ana", provider: "ssh" }]);
34
+ });
35
+
36
+ test("a nameless revisit never erases a name a past visit set", () => {
37
+ // Arrange
38
+ const named = registerParticipant(emptySession(), "SHA256:ana", "Ana");
39
+
40
+ // Act
41
+ const next = registerParticipant(named, "SHA256:ana");
42
+
43
+ // Assert
44
+ expect(next).toBe(named);
45
+ expect(next.participants).toEqual([{ id: "SHA256:ana", provider: "ssh", name: "Ana" }]);
46
+ });
47
+
48
+ test("a later name updates the existing identity in place", () => {
49
+ // Arrange
50
+ const present = registerParticipant(emptySession(), "SHA256:ana");
51
+
52
+ // Act
53
+ const next = registerParticipant(present, "SHA256:ana", "Ana");
54
+
55
+ // Assert
56
+ expect(next.participants).toEqual([{ id: "SHA256:ana", provider: "ssh", name: "Ana" }]);
57
+ });
58
+ });
@@ -0,0 +1,36 @@
1
+ /**
2
+ * The participant registry: every annotation carries an `author` id, and the
3
+ * rail resolves that id to a display name through `session.participants`. This
4
+ * registers an author in that registry so a name search on "participant" or
5
+ * "author name" lands here.
6
+ */
7
+
8
+ import type { Identity, ReviewSession } from "./types";
9
+
10
+ /**
11
+ * Return the session with `author` present in the participant registry, setting
12
+ * the display name when one is given. A nameless call only records presence (the
13
+ * rail renders anonymous) and never erases a name a past visit set. Immutable:
14
+ * the input session is not mutated.
15
+ */
16
+ export function registerParticipant(
17
+ session: ReviewSession,
18
+ author: string,
19
+ name?: string,
20
+ ): ReviewSession {
21
+ const participants = session.participants ?? [];
22
+ const existing = participants.find((participant) => participant.id === author);
23
+ const trimmed = name?.trim();
24
+ if (existing && !trimmed) return session;
25
+ const next: Identity = {
26
+ id: author,
27
+ provider: "ssh",
28
+ ...(trimmed ? { name: trimmed } : existing?.name ? { name: existing.name } : {}),
29
+ };
30
+ return {
31
+ ...session,
32
+ participants: existing
33
+ ? participants.map((participant) => (participant.id === author ? next : participant))
34
+ : [...participants, next],
35
+ };
36
+ }