@mattstack/rt-client 0.11.0 → 0.12.0

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/README.md CHANGED
@@ -111,6 +111,10 @@ socket. The verbs and their payloads are specified in repo-tools
111
111
  `docs/superpowers/specs/2026-08-28-rt-chat-delivery-v2-design.md`; the
112
112
  agent-facing rules are `skills/rt-chat/SKILL.md`.
113
113
 
114
+ ## Runs
115
+
116
+ `RunStageRow.status` is one of `running | done | failed | redirected`; `rt runs stage-redirect` writes the fourth when the work engine leaves a stage for another one, so a reader that maps statuses to icons or filters must handle all four.
117
+
114
118
  ## License
115
119
 
116
120
  MIT
package/dist/client.d.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  import type { RtResponse, RtClientOptions } from "./transport.ts";
2
- import type { DemandDecl, ProjectMRsData, DiscussionsData, MrByBranchData, BranchEnrichment, ForgeSlug, ForgeTokenData, RunSummary, RunDetail, WakeMode, ChatMember, ChatMessage, RoomSummary, BuddyStatus, PresenceRow, AgentRecord, Commands } from "./commands.ts";
2
+ import type { DemandDecl, ProjectMRsData, DiscussionsData, MrByBranchData, BranchEnrichment, ForgeSlug, ForgeTokenData, RunSummary, RunDetail, WakeMode, ChatMember, ChatMessage, ChatClaimOutcome, RoomSummary, BuddyStatus, PresenceRow, AgentRecord, Commands } from "./commands.ts";
3
3
  /**
4
4
  * One repo's project open-MR store. A cold repo forces a full paginated sync
5
5
  * on the daemon side when maxAgeMs demands it, which can run tens of seconds
@@ -59,9 +59,29 @@ export declare function chatPost(a: {
59
59
  handle: string;
60
60
  body: string;
61
61
  mentions?: string[];
62
+ quiet?: boolean;
62
63
  }, o?: RtClientOptions): Promise<RtResponse<{
63
64
  id: number;
64
65
  recipients: string[];
66
+ others: number;
67
+ }>>;
68
+ export declare function chatAck(a: {
69
+ id: number;
70
+ handle: string;
71
+ }, o?: RtClientOptions): Promise<RtResponse<{
72
+ author: string;
73
+ room: string;
74
+ already: boolean;
75
+ }>>;
76
+ export declare function chatClaim(a: {
77
+ id: number;
78
+ handle: string;
79
+ }, o?: RtClientOptions): Promise<RtResponse<ChatClaimOutcome>>;
80
+ export declare function chatRelease(a: {
81
+ id: number;
82
+ handle: string;
83
+ }, o?: RtClientOptions): Promise<RtResponse<{
84
+ holder: string;
65
85
  }>>;
66
86
  export declare function chatRead(a: {
67
87
  handle: string;
@@ -22,6 +22,10 @@ export interface ProjectMRsScope {
22
22
  sections?: string[];
23
23
  /** Demanded sections not yet swept for this client. */
24
24
  uncoveredSections?: string[];
25
+ /** Section headers in the default-branch CODEOWNERS at the last deep or
26
+ backfill. `[]` when the project has none. Absent from a pre-knownSections
27
+ daemon or before the first sweep that demanded a section. */
28
+ knownSections?: string[];
25
29
  }
26
30
  export interface ProjectMRsData {
27
31
  mrs: Record<string, {
@@ -113,6 +117,22 @@ export interface ChatMessage {
113
117
  replyTo?: number;
114
118
  postedAt: number;
115
119
  }
120
+ /** `claimed` is the only outcome that woke anyone; `previousHolder` marks a takeover of an expired claim. */
121
+ export type ChatClaimOutcome = {
122
+ outcome: "claimed";
123
+ author: string;
124
+ room: string;
125
+ previousHolder?: string;
126
+ } | {
127
+ outcome: "held";
128
+ author: string;
129
+ room: string;
130
+ } | {
131
+ outcome: "lost";
132
+ holder: string;
133
+ claimedAt: number;
134
+ expiresAt: number;
135
+ };
116
136
  export interface RoomSummary {
117
137
  room: string;
118
138
  memberCount: number;
@@ -347,6 +367,15 @@ export interface StatusData {
347
367
  dormant: false;
348
368
  };
349
369
  }
370
+ /** Duplicated shape on purpose: mirrors lib/worktree/ready-held.ts's ReadyHeldRepo. */
371
+ export interface ReadyHeldRepo {
372
+ /** Serialized repo identity. A key, never displayed. */
373
+ repo: string;
374
+ /** Decoded display name. Never sent back as a key. */
375
+ label: string;
376
+ hash: string;
377
+ approveCommand: string;
378
+ }
350
379
  export interface TrayStatusData {
351
380
  pid: number;
352
381
  uptime: number;
@@ -364,6 +393,8 @@ export interface TrayStatusData {
364
393
  };
365
394
  metrics: HealthMetrics;
366
395
  eventLoop: HealthEventLoop;
396
+ /** Optional because a daemon older than RT-98 does not send it. */
397
+ worktreeReadyHeld?: ReadyHeldRepo[];
367
398
  }
368
399
  /** Duplicated shape on purpose: mirrors lib/port-scanner.ts's PortEntry. */
369
400
  export interface PortEntry {
@@ -700,16 +731,46 @@ export interface Commands {
700
731
  };
701
732
  data: Record<string, never>;
702
733
  };
734
+ /** `others` counts the room's members besides the author, so a caller can tell "woke nobody of 7" from "nobody else is here". */
703
735
  "chat:post": {
704
736
  payload: {
705
737
  room: string;
706
738
  handle: string;
707
739
  body: string;
708
740
  mentions?: string[];
741
+ quiet?: boolean;
709
742
  };
710
743
  data: {
711
744
  id: number;
712
745
  recipients: string[];
746
+ others: number;
747
+ };
748
+ };
749
+ "chat:ack": {
750
+ payload: {
751
+ id: number;
752
+ handle: string;
753
+ };
754
+ data: {
755
+ author: string;
756
+ room: string;
757
+ already: boolean;
758
+ };
759
+ };
760
+ "chat:claim": {
761
+ payload: {
762
+ id: number;
763
+ handle: string;
764
+ };
765
+ data: ChatClaimOutcome;
766
+ };
767
+ "chat:release": {
768
+ payload: {
769
+ id: number;
770
+ handle: string;
771
+ };
772
+ data: {
773
+ holder: string;
713
774
  };
714
775
  };
715
776
  "chat:read": {
package/dist/index.d.ts CHANGED
@@ -1,8 +1,8 @@
1
1
  export { rtCommand, DEFAULT_SOCK } from "./transport.ts";
2
2
  export type { RtResponse, RtClientOptions } from "./transport.ts";
3
- export { readProjectMRs, readDiscussions, readMrsByBranch, readBranchCache, resolveForgeToken, listRuns, getRun, abandonRun, chatJoin, chatLeave, chatPost, chatRead, chatRooms, chatWho, chatMark, chatMessages, chatSignIn, chatSignOut, chatAway, chatBack, chatBuddies, chatDm, chatArchive, chatDmOpen, eventsHead, agentStart, agentResume, agentGet, agentList, paneList, panePeek, paneSpawn, paneAccounts, paneDirectories, chatInvite, paneSend, paneFocus, } from "./client.ts";
3
+ export { readProjectMRs, readDiscussions, readMrsByBranch, readBranchCache, resolveForgeToken, listRuns, getRun, abandonRun, chatJoin, chatLeave, chatAck, chatClaim, chatRelease, chatPost, chatRead, chatRooms, chatWho, chatMark, chatMessages, chatSignIn, chatSignOut, chatAway, chatBack, chatBuddies, chatDm, chatArchive, chatDmOpen, eventsHead, agentStart, agentResume, agentGet, agentList, paneList, panePeek, paneSpawn, paneAccounts, paneDirectories, chatInvite, paneSend, paneFocus, } from "./client.ts";
4
4
  export { COMMAND_NAMES } from "./commands.ts";
5
- export type { Discussion, DemandDecl, ProjectMRsScope, ProjectMRsData, DiscussionsData, MrByBranchEntry, MrByBranchData, BranchEnrichment, Commands, CommandName, ForgeSlug, ForgeTokenData, Attention, RunSummary, RunStageRow, RunFieldRow, RunDecisionRow, RunDetail, WakeMode, ChatMember, ChatMessage, RoomSummary, BuddyStatus, PresenceRow, AgentRecord, AgentSurface, AgentStatus, ChatPane, PaneAccount, PaneDirectory, InviteResult, PaneDelivery, PaneSendResult, PaneFocusResult, } from "./commands.ts";
5
+ export type { Discussion, DemandDecl, ProjectMRsScope, ProjectMRsData, DiscussionsData, MrByBranchEntry, MrByBranchData, BranchEnrichment, Commands, CommandName, ForgeSlug, ForgeTokenData, Attention, RunSummary, RunStageRow, RunFieldRow, RunDecisionRow, RunDetail, WakeMode, ChatMember, ChatMessage, ChatClaimOutcome, RoomSummary, BuddyStatus, PresenceRow, AgentRecord, AgentSurface, AgentStatus, ChatPane, PaneAccount, PaneDirectory, InviteResult, PaneDelivery, PaneSendResult, PaneFocusResult, } from "./commands.ts";
6
6
  export { subscribe, createRelay, DEFAULT_WS_URL } from "./relay.ts";
7
7
  export type { RelayEventType } from "./relay.ts";
8
8
  export { daemonHealth } from "./health.ts";
package/dist/index.js CHANGED
@@ -82,8 +82,19 @@ function chatPost(a, o = {}) {
82
82
  const payload = { room: a.room, handle: a.handle, body: a.body };
83
83
  if (a.mentions !== undefined)
84
84
  payload.mentions = a.mentions;
85
+ if (a.quiet)
86
+ payload.quiet = true;
85
87
  return rtCommand("chat:post", payload, { sockPath: o.sockPath, timeoutMs: o.timeoutMs ?? 1e4 });
86
88
  }
89
+ function chatAck(a, o = {}) {
90
+ return rtCommand("chat:ack", { id: a.id, handle: a.handle }, { sockPath: o.sockPath, timeoutMs: o.timeoutMs ?? 1e4 });
91
+ }
92
+ function chatClaim(a, o = {}) {
93
+ return rtCommand("chat:claim", { id: a.id, handle: a.handle }, { sockPath: o.sockPath, timeoutMs: o.timeoutMs ?? 1e4 });
94
+ }
95
+ function chatRelease(a, o = {}) {
96
+ return rtCommand("chat:release", { id: a.id, handle: a.handle }, { sockPath: o.sockPath, timeoutMs: o.timeoutMs ?? 1e4 });
97
+ }
87
98
  function chatRead(a, o = {}) {
88
99
  const payload = { handle: a.handle };
89
100
  if (a.room !== undefined)
@@ -259,6 +270,9 @@ var COMMAND_NAMES = [
259
270
  "runs:list",
260
271
  "runs:get",
261
272
  "runs:abandon",
273
+ "chat:ack",
274
+ "chat:claim",
275
+ "chat:release",
262
276
  "chat:join",
263
277
  "chat:leave",
264
278
  "chat:post",
@@ -802,6 +816,13 @@ var REGISTRY = [
802
816
  migrated: true,
803
817
  description: "User-confirmed integration hosts (forgeHost, switchboardUrl), written only by an explicit `rt setup <id> connect --host` after that host validates a real credential. The one trusted source a credential is ever sent to — mattstack.integrations' team-declared host is shown to the user but never auto-used for a fetch."
804
818
  },
819
+ {
820
+ key: "mattstack.roster",
821
+ type: "array",
822
+ scopes: ["team"],
823
+ merge: "replace",
824
+ description: "The suite-wide team roster: [{username, name?}] GitLab usernames with optional display names. Any suite app that lists people reads this; hiding someone is the app's own overlay (e.g. boxscore.hiddenMembers)."
825
+ },
805
826
  {
806
827
  key: "claude.marketplaces",
807
828
  type: "array",
@@ -856,7 +877,7 @@ var REGISTRY = [
856
877
  type: "array",
857
878
  scopes: ["team"],
858
879
  merge: "replace",
859
- description: "The authors tab's roster: whose MRs the classic board lists, including hidden-by-default entries. Codeowners tabs list MRs from anyone."
880
+ description: "The authors tab's roster: whose MRs the classic board lists, including hidden-by-default entries. Codeowners tabs list MRs from anyone. The cross-app roster successor is mattstack.roster; this key remains the board's own list until the board adopts it."
860
881
  },
861
882
  {
862
883
  key: "board.title",
@@ -956,13 +977,6 @@ var REGISTRY = [
956
977
  merge: "deep",
957
978
  description: "Local working directories the board's review/respond/doctor panes launch from."
958
979
  },
959
- {
960
- key: "board.rtRepos",
961
- type: "array",
962
- scopes: ["machine"],
963
- merge: "replace",
964
- description: "rt-registered repo names the board resolves MRs against on this machine."
965
- },
966
980
  {
967
981
  key: "board.triageMaxConcurrent",
968
982
  type: "number",
@@ -977,6 +991,62 @@ var REGISTRY = [
977
991
  merge: "replace",
978
992
  description: "Local switchboard URL the board's POST /peer/join writer targets."
979
993
  },
994
+ {
995
+ key: "boxscore.projects",
996
+ type: "array",
997
+ scopes: ["team"],
998
+ merge: "replace",
999
+ description: 'GitLab projects boxscore scores, as full paths ("group/project").'
1000
+ },
1001
+ {
1002
+ key: "boxscore.linearDoneStates",
1003
+ type: "array",
1004
+ scopes: ["team"],
1005
+ merge: "replace",
1006
+ description: "Linear workflow state names that count as done. Empty means the completed and canceled state types."
1007
+ },
1008
+ {
1009
+ key: "boxscore.sizeBand",
1010
+ type: "object",
1011
+ scopes: ["team"],
1012
+ merge: "deep",
1013
+ description: "MR size health band in changed lines: {tooSmall, tooLarge}. At or below tooSmall, or above tooLarge, is outside the healthy band."
1014
+ },
1015
+ {
1016
+ key: "boxscore.excludeFilePatterns",
1017
+ type: "array",
1018
+ scopes: ["team"],
1019
+ merge: "replace",
1020
+ description: 'Glob patterns for files excluded from addition/deletion counts (e.g. "**/*.json").'
1021
+ },
1022
+ {
1023
+ key: "boxscore.ignoredMrs",
1024
+ type: "array",
1025
+ scopes: ["team"],
1026
+ merge: "replace",
1027
+ description: 'MRs excluded from all metrics: "!123" or "group/project!123".'
1028
+ },
1029
+ {
1030
+ key: "boxscore.botPatterns",
1031
+ type: "array",
1032
+ scopes: ["team"],
1033
+ merge: "replace",
1034
+ description: "Extra regex sources treated as bot accounts, beyond boxscore's built-in detection."
1035
+ },
1036
+ {
1037
+ key: "boxscore.hiddenMembers",
1038
+ type: "array",
1039
+ scopes: ["user"],
1040
+ merge: "replace",
1041
+ description: "Usernames from mattstack.roster hidden from this developer's leaderboard."
1042
+ },
1043
+ {
1044
+ key: "boxscore.defaultRange",
1045
+ type: "string",
1046
+ scopes: ["user"],
1047
+ merge: "replace",
1048
+ description: 'Default comparison window for the API and CLI when none is given: "7d", "30d", or "90d".'
1049
+ },
980
1050
  {
981
1051
  key: "gitq.workSlots",
982
1052
  type: "object",
@@ -1018,7 +1088,8 @@ var REGISTRY = [
1018
1088
  type: "string",
1019
1089
  scopes: ["user"],
1020
1090
  merge: "replace",
1021
- description: "Base URL of the chat viewer, no trailing slash. When set, rt chat post and the tail's wake lines end with a link to the room or message."
1091
+ default: "https://chat.mattstack",
1092
+ description: "Base URL of the chat viewer, no trailing slash; rt chat post and the tail's wake lines end with a link to the room or message. Defaults to the viewer deck serves on the mattstack TLD."
1022
1093
  },
1023
1094
  {
1024
1095
  key: "chat.herdrWorkspace",
@@ -1931,6 +2002,7 @@ export {
1931
2002
  chatSignOut,
1932
2003
  chatSignIn,
1933
2004
  chatRooms,
2005
+ chatRelease,
1934
2006
  chatRead,
1935
2007
  chatPost,
1936
2008
  chatMessages,
@@ -1940,10 +2012,12 @@ export {
1940
2012
  chatInvite,
1941
2013
  chatDmOpen,
1942
2014
  chatDm,
2015
+ chatClaim,
1943
2016
  chatBuddies,
1944
2017
  chatBack,
1945
2018
  chatAway,
1946
2019
  chatArchive,
2020
+ chatAck,
1947
2021
  allDefs,
1948
2022
  agentStart,
1949
2023
  agentResume,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mattstack/rt-client",
3
- "version": "0.11.0",
3
+ "version": "0.12.0",
4
4
  "type": "module",
5
5
  "exports": {
6
6
  ".": {
package/src/client.ts CHANGED
@@ -18,6 +18,7 @@ import type {
18
18
  WakeMode,
19
19
  ChatMember,
20
20
  ChatMessage,
21
+ ChatClaimOutcome,
21
22
  RoomSummary,
22
23
  BuddyStatus,
23
24
  PresenceRow,
@@ -166,12 +167,32 @@ export function chatLeave(
166
167
  }
167
168
 
168
169
  export function chatPost(
169
- a: { room: string; handle: string; body: string; mentions?: string[] },
170
+ a: { room: string; handle: string; body: string; mentions?: string[]; quiet?: boolean },
170
171
  o: RtClientOptions = {},
171
- ): Promise<RtResponse<{ id: number; recipients: string[] }>> {
172
+ ): Promise<RtResponse<{ id: number; recipients: string[]; others: number }>> {
172
173
  const payload: Record<string, unknown> = { room: a.room, handle: a.handle, body: a.body };
173
174
  if (a.mentions !== undefined) payload.mentions = a.mentions;
174
- return rtCommand<{ id: number; recipients: string[] }>("chat:post", payload, { sockPath: o.sockPath, timeoutMs: o.timeoutMs ?? 10_000 });
175
+ if (a.quiet) payload.quiet = true;
176
+ return rtCommand<{ id: number; recipients: string[]; others: number }>("chat:post", payload, { sockPath: o.sockPath, timeoutMs: o.timeoutMs ?? 10_000 });
177
+ }
178
+
179
+ export function chatAck(
180
+ a: { id: number; handle: string },
181
+ o: RtClientOptions = {},
182
+ ): Promise<RtResponse<{ author: string; room: string; already: boolean }>> {
183
+ return rtCommand<{ author: string; room: string; already: boolean }>(
184
+ "chat:ack",
185
+ { id: a.id, handle: a.handle },
186
+ { sockPath: o.sockPath, timeoutMs: o.timeoutMs ?? 10_000 },
187
+ );
188
+ }
189
+
190
+ export function chatClaim(a: { id: number; handle: string }, o: RtClientOptions = {}): Promise<RtResponse<ChatClaimOutcome>> {
191
+ return rtCommand<ChatClaimOutcome>("chat:claim", { id: a.id, handle: a.handle }, { sockPath: o.sockPath, timeoutMs: o.timeoutMs ?? 10_000 });
192
+ }
193
+
194
+ export function chatRelease(a: { id: number; handle: string }, o: RtClientOptions = {}): Promise<RtResponse<{ holder: string }>> {
195
+ return rtCommand<{ holder: string }>("chat:release", { id: a.id, handle: a.handle }, { sockPath: o.sockPath, timeoutMs: o.timeoutMs ?? 10_000 });
175
196
  }
176
197
 
177
198
  export function chatRead(
package/src/commands.ts CHANGED
@@ -25,6 +25,10 @@ export interface ProjectMRsScope {
25
25
  sections?: string[];
26
26
  /** Demanded sections not yet swept for this client. */
27
27
  uncoveredSections?: string[];
28
+ /** Section headers in the default-branch CODEOWNERS at the last deep or
29
+ backfill. `[]` when the project has none. Absent from a pre-knownSections
30
+ daemon or before the first sweep that demanded a section. */
31
+ knownSections?: string[];
28
32
  }
29
33
 
30
34
  export interface ProjectMRsData {
@@ -108,6 +112,12 @@ export interface ChatMessage {
108
112
  postedAt: number;
109
113
  }
110
114
 
115
+ /** `claimed` is the only outcome that woke anyone; `previousHolder` marks a takeover of an expired claim. */
116
+ export type ChatClaimOutcome =
117
+ | { outcome: "claimed"; author: string; room: string; previousHolder?: string }
118
+ | { outcome: "held"; author: string; room: string }
119
+ | { outcome: "lost"; holder: string; claimedAt: number; expiresAt: number };
120
+
111
121
  export interface RoomSummary {
112
122
  room: string;
113
123
  memberCount: number;
@@ -254,11 +264,23 @@ export interface StatusData {
254
264
  worktreePool: { dormant: true; repos: string[]; message: string } | { dormant: false };
255
265
  }
256
266
 
267
+ /** Duplicated shape on purpose: mirrors lib/worktree/ready-held.ts's ReadyHeldRepo. */
268
+ export interface ReadyHeldRepo {
269
+ /** Serialized repo identity. A key, never displayed. */
270
+ repo: string;
271
+ /** Decoded display name. Never sent back as a key. */
272
+ label: string;
273
+ hash: string;
274
+ approveCommand: string;
275
+ }
276
+
257
277
  export interface TrayStatusData {
258
278
  pid: number; uptime: number; memoryUsage: number; watchedRepos: number; cacheEntries: number;
259
279
  portsCached: number; portCacheAge: number | null; lastRefresh: number | null;
260
280
  portsByRepo: Record<string, number>; pendingNotifications: number;
261
281
  health: { level: HealthLevel; reasons: string[] }; metrics: HealthMetrics; eventLoop: HealthEventLoop;
282
+ /** Optional because a daemon older than RT-98 does not send it. */
283
+ worktreeReadyHeld?: ReadyHeldRepo[];
262
284
  }
263
285
 
264
286
  /** Duplicated shape on purpose: mirrors lib/port-scanner.ts's PortEntry. */
@@ -405,7 +427,11 @@ export interface Commands {
405
427
  "runs:abandon": { payload: { runId: string; repo?: string; reason?: string }; data: { ok: boolean } };
406
428
  "chat:join": { payload: { room: string; handle: string; wakeOn?: WakeMode; cwd?: string; pane?: string }; data: { handle: string; memberCount: number; unread: number } };
407
429
  "chat:leave": { payload: { room: string; handle: string }; data: Record<string, never> };
408
- "chat:post": { payload: { room: string; handle: string; body: string; mentions?: string[] }; data: { id: number; recipients: string[] } };
430
+ /** `others` counts the room's members besides the author, so a caller can tell "woke nobody of 7" from "nobody else is here". */
431
+ "chat:post": { payload: { room: string; handle: string; body: string; mentions?: string[]; quiet?: boolean }; data: { id: number; recipients: string[]; others: number } };
432
+ "chat:ack": { payload: { id: number; handle: string }; data: { author: string; room: string; already: boolean } };
433
+ "chat:claim": { payload: { id: number; handle: string }; data: ChatClaimOutcome };
434
+ "chat:release": { payload: { id: number; handle: string }; data: { holder: string } };
409
435
  "chat:read": { payload: { handle: string; room?: string; limit?: number; sinceMs?: number }; data: { rooms: { room: string; messages: ChatMessage[] }[] } };
410
436
  "chat:rooms": { payload: { handle: string; includeArchived?: boolean }; data: { rooms: RoomSummary[] } };
411
437
  "chat:who": { payload: { room: string }; data: { members: ChatMember[] } };
@@ -547,6 +573,9 @@ export const COMMAND_NAMES: readonly CommandName[] = [
547
573
  "runs:list",
548
574
  "runs:get",
549
575
  "runs:abandon",
576
+ "chat:ack",
577
+ "chat:claim",
578
+ "chat:release",
550
579
  "chat:join",
551
580
  "chat:leave",
552
581
  "chat:post",
package/src/index.ts CHANGED
@@ -12,6 +12,9 @@ export {
12
12
  abandonRun,
13
13
  chatJoin,
14
14
  chatLeave,
15
+ chatAck,
16
+ chatClaim,
17
+ chatRelease,
15
18
  chatPost,
16
19
  chatRead,
17
20
  chatRooms,
@@ -64,6 +67,7 @@ export type {
64
67
  WakeMode,
65
68
  ChatMember,
66
69
  ChatMessage,
70
+ ChatClaimOutcome,
67
71
  RoomSummary,
68
72
  BuddyStatus,
69
73
  PresenceRow,
@@ -288,6 +288,16 @@ export const REGISTRY: readonly SettingDef[] = [
288
288
  "User-confirmed integration hosts (forgeHost, switchboardUrl), written only by an explicit `rt setup <id> connect --host` after that host validates a real credential. The one trusted source a credential is ever sent to — mattstack.integrations' team-declared host is shown to the user but never auto-used for a fetch.",
289
289
  },
290
290
 
291
+ // --- mattstack (shared team truth) ---------------------------------------
292
+ {
293
+ key: "mattstack.roster",
294
+ type: "array",
295
+ scopes: ["team"],
296
+ merge: "replace",
297
+ description:
298
+ "The suite-wide team roster: [{username, name?}] GitLab usernames with optional display names. Any suite app that lists people reads this; hiding someone is the app's own overlay (e.g. boxscore.hiddenMembers).",
299
+ },
300
+
291
301
  // --- claude (installer-lane) --------------------------------------------
292
302
  {
293
303
  key: "claude.marketplaces",
@@ -351,7 +361,7 @@ export const REGISTRY: readonly SettingDef[] = [
351
361
  type: "array",
352
362
  scopes: ["team"],
353
363
  merge: "replace",
354
- description: "The authors tab's roster: whose MRs the classic board lists, including hidden-by-default entries. Codeowners tabs list MRs from anyone.",
364
+ description: "The authors tab's roster: whose MRs the classic board lists, including hidden-by-default entries. Codeowners tabs list MRs from anyone. The cross-app roster successor is mattstack.roster; this key remains the board's own list until the board adopts it.",
355
365
  },
356
366
  {
357
367
  key: "board.title",
@@ -455,13 +465,6 @@ export const REGISTRY: readonly SettingDef[] = [
455
465
  merge: "deep",
456
466
  description: "Local working directories the board's review/respond/doctor panes launch from.",
457
467
  },
458
- {
459
- key: "board.rtRepos",
460
- type: "array",
461
- scopes: ["machine"],
462
- merge: "replace",
463
- description: "rt-registered repo names the board resolves MRs against on this machine.",
464
- },
465
468
  {
466
469
  key: "board.triageMaxConcurrent",
467
470
  type: "number",
@@ -477,6 +480,66 @@ export const REGISTRY: readonly SettingDef[] = [
477
480
  description: "Local switchboard URL the board's POST /peer/join writer targets.",
478
481
  },
479
482
 
483
+ // --- boxscore -------------------------------------------------------------
484
+ // No `default` on any boxscore row: fallbacks live in boxscore's app-side
485
+ // read (server/config), so an unset key resolves as absent here.
486
+ {
487
+ key: "boxscore.projects",
488
+ type: "array",
489
+ scopes: ["team"],
490
+ merge: "replace",
491
+ description: "GitLab projects boxscore scores, as full paths (\"group/project\").",
492
+ },
493
+ {
494
+ key: "boxscore.linearDoneStates",
495
+ type: "array",
496
+ scopes: ["team"],
497
+ merge: "replace",
498
+ description: "Linear workflow state names that count as done. Empty means the completed and canceled state types.",
499
+ },
500
+ {
501
+ key: "boxscore.sizeBand",
502
+ type: "object",
503
+ scopes: ["team"],
504
+ merge: "deep",
505
+ description: "MR size health band in changed lines: {tooSmall, tooLarge}. At or below tooSmall, or above tooLarge, is outside the healthy band.",
506
+ },
507
+ {
508
+ key: "boxscore.excludeFilePatterns",
509
+ type: "array",
510
+ scopes: ["team"],
511
+ merge: "replace",
512
+ description: "Glob patterns for files excluded from addition/deletion counts (e.g. \"**/*.json\").",
513
+ },
514
+ {
515
+ key: "boxscore.ignoredMrs",
516
+ type: "array",
517
+ scopes: ["team"],
518
+ merge: "replace",
519
+ description: "MRs excluded from all metrics: \"!123\" or \"group/project!123\".",
520
+ },
521
+ {
522
+ key: "boxscore.botPatterns",
523
+ type: "array",
524
+ scopes: ["team"],
525
+ merge: "replace",
526
+ description: "Extra regex sources treated as bot accounts, beyond boxscore's built-in detection.",
527
+ },
528
+ {
529
+ key: "boxscore.hiddenMembers",
530
+ type: "array",
531
+ scopes: ["user"],
532
+ merge: "replace",
533
+ description: "Usernames from mattstack.roster hidden from this developer's leaderboard.",
534
+ },
535
+ {
536
+ key: "boxscore.defaultRange",
537
+ type: "string",
538
+ scopes: ["user"],
539
+ merge: "replace",
540
+ description: "Default comparison window for the API and CLI when none is given: \"7d\", \"30d\", or \"90d\".",
541
+ },
542
+
480
543
  // --- gitq ------------------------------------------------------------------
481
544
  {
482
545
  key: "gitq.workSlots",
@@ -521,7 +584,8 @@ export const REGISTRY: readonly SettingDef[] = [
521
584
  type: "string",
522
585
  scopes: ["user"],
523
586
  merge: "replace",
524
- description: "Base URL of the chat viewer, no trailing slash. When set, rt chat post and the tail's wake lines end with a link to the room or message.",
587
+ default: "https://chat.mattstack",
588
+ description: "Base URL of the chat viewer, no trailing slash; rt chat post and the tail's wake lines end with a link to the room or message. Defaults to the viewer deck serves on the mattstack TLD.",
525
589
  },
526
590
  {
527
591
  key: "chat.herdrWorkspace",