@company-semantics/contracts 62.4.0 → 62.6.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@company-semantics/contracts",
3
- "version": "62.4.0",
3
+ "version": "62.6.0",
4
4
  "private": false,
5
5
  "repository": {
6
6
  "type": "git",
@@ -139,8 +139,8 @@
139
139
  "node": "22.x"
140
140
  },
141
141
  "dependencies": {
142
- "@slack/types": "^3.0.0",
143
- "zod": "^4.4.3"
142
+ "@slack/types": "^3.1.0",
143
+ "zod": "^4.5.2"
144
144
  },
145
145
  "devDependencies": {
146
146
  "@eslint/js": "^10.0.1",
@@ -149,13 +149,13 @@
149
149
  "culori": "^4.0.2",
150
150
  "eslint": "^10.9.0",
151
151
  "husky": "^9.1.7",
152
- "lint-staged": "^17.3.0",
152
+ "lint-staged": "^17.4.1",
153
153
  "markdownlint-cli2": "^0.23.2",
154
154
  "openapi-typescript": "^7.13.0",
155
155
  "prettier": "^3.9.6",
156
156
  "tsx": "^4.23.12",
157
157
  "typescript": "^5.8.3",
158
- "typescript-eslint": "^8.67.0",
158
+ "typescript-eslint": "^8.68.0",
159
159
  "vite": "^8.2.2",
160
160
  "vitest": "^4.1.11",
161
161
  "yaml": "^2.9.0"
@@ -0,0 +1,71 @@
1
+ import { describe, expect, it } from "vitest";
2
+ import { z } from "zod";
3
+
4
+ import {
5
+ ISO_DATETIME_OFFSET_PATTERN,
6
+ ISO_DATETIME_PATTERN,
7
+ IsoDateTime,
8
+ IsoDateTimeWithOffset,
9
+ } from "../api/primitives";
10
+
11
+ /**
12
+ * These are drift alarms, not unit tests.
13
+ *
14
+ * `IsoDateTime` delegates to zod's built-in RFC 3339 regex, and zod is advanced
15
+ * automatically by Dependabot inside a grouped `minor`/`patch` bump. That is
16
+ * exactly how the seconds-optional -> seconds-mandatory tightening reached
17
+ * production undocumented (backend #520). If zod moves this regex again, the
18
+ * documented format of every `format: date-time` field on the public API moves
19
+ * with it — so it fails here first and gets ratified deliberately.
20
+ *
21
+ * If one of these fails: do not update the constant to make it green. Work out
22
+ * what zod changed, decide whether we want it, and record the decision.
23
+ */
24
+ describe("the RFC 3339 wire contract is pinned, not inherited", () => {
25
+ it("emits the pinned pattern for the Z-only form", () => {
26
+ expect(z.toJSONSchema(IsoDateTime, { io: "input" }).pattern).toBe(
27
+ ISO_DATETIME_PATTERN,
28
+ );
29
+ });
30
+
31
+ it("emits the pinned pattern for the offset-allowing form", () => {
32
+ expect(z.toJSONSchema(IsoDateTimeWithOffset, { io: "input" }).pattern).toBe(
33
+ ISO_DATETIME_OFFSET_PATTERN,
34
+ );
35
+ });
36
+
37
+ it("still emits format: date-time, which the pattern alone would not carry", () => {
38
+ expect(z.toJSONSchema(IsoDateTime, { io: "input" }).format).toBe(
39
+ "date-time",
40
+ );
41
+ });
42
+ });
43
+
44
+ describe("seconds are mandatory (ADR-CONTRACTS-156)", () => {
45
+ it.each([
46
+ "2026-09-01T12:00:00Z",
47
+ "2026-09-01T12:00:00.000Z",
48
+ "2024-02-29T23:59:59Z",
49
+ ])("accepts %s", (value) => {
50
+ expect(IsoDateTime.safeParse(value).success).toBe(true);
51
+ });
52
+
53
+ it.each(["2026-09-01T12:00Z", "2026-09-01T12Z", "2026-09-01"])(
54
+ "rejects %s",
55
+ (value) => {
56
+ expect(IsoDateTime.safeParse(value).success).toBe(false);
57
+ },
58
+ );
59
+
60
+ it("rejects a numeric offset on the Z-only form", () => {
61
+ expect(IsoDateTime.safeParse("2026-09-01T12:00:00+02:00").success).toBe(
62
+ false,
63
+ );
64
+ });
65
+
66
+ it("accepts a numeric offset on the offset-allowing form", () => {
67
+ expect(
68
+ IsoDateTimeWithOffset.safeParse("2026-09-01T12:00:00+02:00").success,
69
+ ).toBe(true);
70
+ });
71
+ });
@@ -7,12 +7,13 @@
7
7
  * direct-grant-audit-trail and the `direct-grant-audit` CI guard).
8
8
  */
9
9
  import { z } from "zod";
10
+ import { IsoDateTime } from "../api/primitives";
10
11
 
11
12
  export const DirectGrantCreate = z.object({
12
13
  subject_id: z.string().uuid(),
13
14
  scope_pattern: z.string(),
14
15
  resource_filter: z.record(z.string(), z.unknown()).optional(),
15
- expires_at: z.string().datetime().optional(),
16
+ expires_at: IsoDateTime.optional(),
16
17
  });
17
18
 
18
19
  export type DirectGrantCreate = z.infer<typeof DirectGrantCreate>;
@@ -24,8 +25,8 @@ export const DirectGrant = z.object({
24
25
  scope_pattern: z.string(),
25
26
  source: z.literal("direct"),
26
27
  granted_by: z.string().uuid(),
27
- granted_at: z.string().datetime(),
28
- expires_at: z.string().datetime().nullable(),
28
+ granted_at: IsoDateTime,
29
+ expires_at: IsoDateTime.nullable(),
29
30
  });
30
31
 
31
32
  export type DirectGrant = z.infer<typeof DirectGrant>;
@@ -1,3 +1,3 @@
1
1
  // AUTO-GENERATED — do not edit. Run pnpm generate:spec-hash to regenerate.
2
- export const SPEC_HASH = 'bd8b060aedb2' as const;
3
- export const SPEC_HASH_FULL = 'bd8b060aedb2cd35af45ee8c8b0e1dc97da92305f985757cd19a57fbd44866e3' as const;
2
+ export const SPEC_HASH = '52d89a463432' as const;
3
+ export const SPEC_HASH_FULL = '52d89a4634328ae80200c221d6aa25d8c34c9d09208a2c460d82a523ec79144d' as const;
@@ -4293,6 +4293,12 @@ export interface components {
4293
4293
  };
4294
4294
  /** @enum {string} */
4295
4295
  origin?: "user" | "proactive";
4296
+ launch?: {
4297
+ /** @constant */
4298
+ kind: "structure-review";
4299
+ /** Format: uuid */
4300
+ proposalId: string;
4301
+ };
4296
4302
  } | {
4297
4303
  /** @constant */
4298
4304
  trigger: "regenerate-message";
@@ -4315,6 +4321,12 @@ export interface components {
4315
4321
  };
4316
4322
  /** @enum {string} */
4317
4323
  origin?: "user" | "proactive";
4324
+ launch?: {
4325
+ /** @constant */
4326
+ kind: "structure-review";
4327
+ /** Format: uuid */
4328
+ proposalId: string;
4329
+ };
4318
4330
  };
4319
4331
  ProactiveChatResolution: {
4320
4332
  /** @constant */
@@ -6783,9 +6795,8 @@ export interface components {
6783
6795
  p95ContextTokens: number;
6784
6796
  medianReadsAfterFirstEdit: number;
6785
6797
  }[];
6786
- bySource: ({
6798
+ bySource: {
6787
6799
  source: string;
6788
- } & {
6789
6800
  p95Reads?: number;
6790
6801
  p50Reads?: number;
6791
6802
  p95ContextTokens?: number;
@@ -6797,7 +6808,7 @@ export interface components {
6797
6808
  navMapUsageBySource?: {
6798
6809
  [key: string]: number;
6799
6810
  };
6800
- })[];
6811
+ }[];
6801
6812
  trend: {
6802
6813
  recent: {
6803
6814
  p95Reads?: number;
@@ -25,3 +25,47 @@ export type CursorPage<T> = {
25
25
  hasMore: boolean;
26
26
  };
27
27
  export type ErrorResponse = z.infer<typeof ErrorResponseSchema>;
28
+
29
+ // =============================================================================
30
+ // The RFC 3339 timestamp contract
31
+ // =============================================================================
32
+ //
33
+ // Every `format: date-time` field on the public API surface is built from one of
34
+ // the two schemas below, so the wire contract has exactly one definition site.
35
+ //
36
+ // The validators delegate to zod's built-in `.datetime()` rather than
37
+ // reimplementing RFC 3339 by hand: that keeps `format: date-time` in the emitted
38
+ // OpenAPI document and avoids a hand-rolled regex drifting from the runtime
39
+ // check. The regex zod produces is therefore an *input* to our published
40
+ // contract, and zod is a transitive dependency that Dependabot advances
41
+ // automatically — so the emitted pattern is pinned below and asserted in
42
+ // `src/__tests__/iso-datetime.test.ts`.
43
+ //
44
+ // That assertion is the point. A zod release that changes this regex changes the
45
+ // documented format of 52 public fields; it must fail a test and be ratified,
46
+ // not land silently inside a grouped `minor`/`patch` bump. See ADR-CONTRACTS-156.
47
+
48
+ /** Emitted `pattern` for {@link IsoDateTime}. Pinned — see the note above. */
49
+ export const ISO_DATETIME_PATTERN =
50
+ "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z))$";
51
+
52
+ /** Emitted `pattern` for {@link IsoDateTimeWithOffset}. Pinned — see the note above. */
53
+ export const ISO_DATETIME_OFFSET_PATTERN =
54
+ "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$";
55
+
56
+ /**
57
+ * A UTC RFC 3339 timestamp — `2026-09-01T12:00:00.000Z`.
58
+ *
59
+ * Seconds are mandatory. `2026-09-01T12:00Z` is rejected: it is not valid
60
+ * RFC 3339, and the backend has rejected it since zod 4.5.x.
61
+ */
62
+ export const IsoDateTime = z.string().datetime();
63
+
64
+ /**
65
+ * An RFC 3339 timestamp that may carry a numeric UTC offset instead of `Z` —
66
+ * `2026-09-01T12:00:00.000+02:00`.
67
+ *
68
+ * Use only where a client legitimately reports local wall-clock time with its
69
+ * offset. Prefer {@link IsoDateTime} for anything the server mints.
70
+ */
71
+ export const IsoDateTimeWithOffset = z.string().datetime({ offset: true });
@@ -89,7 +89,6 @@ Shared types for chat persistence, sharing, real-time events, and runtime profil
89
89
  - `InvalidateChatListEventSchema`
90
90
  - `InvalidationReason` _(type)_ — Reason for cache invalidation.
91
91
  - `InvalidationReasonSchema`
92
- - `IsoDateString` — ISO 8601 datetime string — the runtime safety net for date serialization.
93
92
  - `ListSharesResponse` _(type)_ — Response for GET /api/chats/:chatId/shares
94
93
  - `ListSharesResponseSchema` — Response for GET /api/chats/:chatId/shares
95
94
  - `ProactiveChatResolution` _(type)_ — Resolving an occurrence id to the CURRENT reader's chat — one of the four bounded states `ready` (with…
@@ -117,6 +116,7 @@ Shared types for chat persistence, sharing, real-time events, and runtime profil
117
116
 
118
117
  **Internal domains:**
119
118
 
119
+ - `api`
120
120
  - `proactive`
121
121
 
122
122
  **External packages:**
package/src/chat/index.ts CHANGED
@@ -61,7 +61,6 @@ export type {
61
61
  // =============================================================================
62
62
 
63
63
  export {
64
- IsoDateString,
65
64
  ChatVisibilitySchema,
66
65
  TitleSourceSchema,
67
66
  InvalidationReasonSchema,
@@ -1,11 +1,5 @@
1
1
  import { z } from "zod";
2
-
3
- // =============================================================================
4
- // Reusable Primitives
5
- // =============================================================================
6
-
7
- /** ISO 8601 datetime string — the runtime safety net for date serialization. */
8
- export const IsoDateString = z.string().datetime();
2
+ import { IsoDateTime } from "../api/primitives";
9
3
 
10
4
  // =============================================================================
11
5
  // Enums
@@ -78,15 +72,15 @@ export const ChatSummarySchema = z.object({
78
72
  id: z.string(),
79
73
  title: z.string().nullable(),
80
74
  messageCount: z.number().int(),
81
- createdAt: IsoDateString,
82
- updatedAt: IsoDateString,
75
+ createdAt: IsoDateTime,
76
+ updatedAt: IsoDateTime,
83
77
  isShared: z.boolean(),
84
78
  });
85
79
 
86
80
  export const ChatSummaryExtendedSchema = ChatSummarySchema.extend({
87
- pinnedAt: IsoDateString.nullable(),
81
+ pinnedAt: IsoDateTime.nullable(),
88
82
  titleSource: TitleSourceSchema.nullable(),
89
- titleGeneratedAt: IsoDateString.nullable(),
83
+ titleGeneratedAt: IsoDateTime.nullable(),
90
84
  ...proactiveChatFields,
91
85
  });
92
86
 
@@ -129,7 +123,7 @@ export const ChatShareInfoSchema = z.object({
129
123
  shareUrl: z.string().optional(),
130
124
  messageCountAtShare: z.number().int(),
131
125
  titleAtShare: z.string(),
132
- createdAt: IsoDateString,
126
+ createdAt: IsoDateTime,
133
127
  createdByName: z.string(),
134
128
  isRevoked: z.boolean(),
135
129
  });
@@ -137,7 +131,7 @@ export const ChatShareInfoSchema = z.object({
137
131
  export const SharedChatViewSchema = z.object({
138
132
  title: z.string(),
139
133
  messages: z.array(SharedChatMessageSchema),
140
- sharedAt: IsoDateString,
134
+ sharedAt: IsoDateTime,
141
135
  sharedByName: z.string(),
142
136
  visibility: ChatVisibilitySchema,
143
137
  });
@@ -148,7 +142,7 @@ export const SharedChatViewSchema = z.object({
148
142
 
149
143
  export const BaseEventSchema = z.object({
150
144
  v: z.literal(1),
151
- timestamp: IsoDateString,
145
+ timestamp: IsoDateTime,
152
146
  eventId: z.string().optional(),
153
147
  });
154
148
 
@@ -167,10 +161,10 @@ export const ChatCreatedEventSchema = BaseEventSchema.extend({
167
161
  interactionId: z.string().optional(),
168
162
  title: z.string(),
169
163
  titleSource: TitleSourceSchema,
170
- pinnedAt: IsoDateString.nullable(),
164
+ pinnedAt: IsoDateTime.nullable(),
171
165
  messageCount: z.number().int(),
172
- createdAt: IsoDateString,
173
- updatedAt: IsoDateString,
166
+ createdAt: IsoDateTime,
167
+ updatedAt: IsoDateTime,
174
168
  isShared: z.boolean(),
175
169
  ...proactiveChatFields,
176
170
  }),
@@ -182,11 +176,11 @@ export const ChatUpdatedEventSchema = BaseEventSchema.extend({
182
176
  chatId: z.string(),
183
177
  title: z.string(),
184
178
  titleSource: TitleSourceSchema.nullable(),
185
- titleGeneratedAt: IsoDateString.nullable(),
186
- pinnedAt: IsoDateString.nullable(),
179
+ titleGeneratedAt: IsoDateTime.nullable(),
180
+ pinnedAt: IsoDateTime.nullable(),
187
181
  messageCount: z.number().int(),
188
- createdAt: IsoDateString,
189
- updatedAt: IsoDateString,
182
+ createdAt: IsoDateTime,
183
+ updatedAt: IsoDateTime,
190
184
  isShared: z.boolean(),
191
185
  ...proactiveChatFields,
192
186
  }),
@@ -245,7 +239,7 @@ export const ChatMessageSchema = z.object({
245
239
  content: z.string(),
246
240
  parts: z.array(z.unknown()).optional(),
247
241
  sequenceNumber: z.number().int(),
248
- createdAt: IsoDateString,
242
+ createdAt: IsoDateTime,
249
243
  });
250
244
 
251
245
  // =============================================================================
@@ -325,11 +319,11 @@ export const ChatDetailSchema = z.object({
325
319
  id: z.string(),
326
320
  title: z.string(),
327
321
  interactionId: z.string(),
328
- pinnedAt: IsoDateString.nullable().optional(),
322
+ pinnedAt: IsoDateTime.nullable().optional(),
329
323
  titleSource: TitleSourceSchema.nullable().optional(),
330
- titleGeneratedAt: IsoDateString.nullable().optional(),
331
- createdAt: IsoDateString,
332
- updatedAt: IsoDateString,
324
+ titleGeneratedAt: IsoDateTime.nullable().optional(),
325
+ createdAt: IsoDateTime,
326
+ updatedAt: IsoDateTime,
333
327
  // Opening the detail is what clears `unread`; the detail carries the flag so
334
328
  // the reader can tell a first open from a return visit. `proactiveKind` is
335
329
  // a list concern (the sidebar row) and does not ride the detail.
@@ -102,6 +102,7 @@ TypeScript types and registry for execution kinds and their governance metadata.
102
102
 
103
103
  **Internal domains:**
104
104
 
105
+ - `api`
105
106
  - `message-parts`
106
107
 
107
108
  **External packages:**
@@ -8,6 +8,7 @@
8
8
  */
9
9
 
10
10
  import { z } from "zod";
11
+ import { IsoDateTimeWithOffset } from "../api/primitives";
11
12
 
12
13
  import type { ExecutionResultData } from "../message-parts/execution";
13
14
  import type { ExecutionKind } from "./kinds";
@@ -22,8 +23,8 @@ export const ExecutionSummarySchema = z.object({
22
23
  executionId: z.string().uuid(),
23
24
  kind: z.string(),
24
25
  status: z.enum(["pending", "succeeded", "failed"]),
25
- decidedAt: z.string().datetime({ offset: true }),
26
- completedAt: z.string().datetime({ offset: true }).optional(),
26
+ decidedAt: IsoDateTimeWithOffset,
27
+ completedAt: IsoDateTimeWithOffset.optional(),
27
28
  target: z.object({ type: z.string() }),
28
29
  });
29
30
 
@@ -15,6 +15,7 @@
15
15
  */
16
16
 
17
17
  import { z } from "zod";
18
+ import { IsoDateTimeWithOffset } from "../api/primitives";
18
19
  import { ExecutionEventTypeSchema } from "./events";
19
20
  import { ExecutionStateSchema } from "./status";
20
21
 
@@ -27,7 +28,7 @@ export const ExecutionSseEventSchema = z.object({
27
28
  /** Coarse actor class that produced the event (e.g. `user`, `system`). */
28
29
  actorType: z.string(),
29
30
  /** DB insert time of the event, ISO-8601 with offset. */
30
- createdAt: z.string().datetime({ offset: true }),
31
+ createdAt: IsoDateTimeWithOffset,
31
32
  });
32
33
 
33
34
  export type ExecutionSseEvent = z.infer<typeof ExecutionSseEventSchema>;
@@ -88,6 +88,7 @@ TypeScript types and functions for user identity and display name resolution.
88
88
 
89
89
  **Internal domains:**
90
90
 
91
+ - `api`
91
92
  - `org`
92
93
 
93
94
  **External packages:**
@@ -17,6 +17,7 @@
17
17
  * @see ADR-CONT-032 for identity vocabulary design rationale
18
18
  */
19
19
  import { z } from "zod";
20
+ import { IsoDateTime } from "../api/primitives";
20
21
 
21
22
  // ---------------------------------------------------------------------------
22
23
  // IdentityLink — external system id → Person
@@ -31,8 +32,8 @@ export const IdentityLinkSchema = z.object({
31
32
  externalId: z.string().min(1),
32
33
  // The Person this external identity resolves to (Person.id).
33
34
  personId: z.string().uuid(),
34
- createdAt: z.string().datetime(),
35
- updatedAt: z.string().datetime(),
35
+ createdAt: IsoDateTime,
36
+ updatedAt: IsoDateTime,
36
37
  });
37
38
 
38
39
  export type IdentityLink = z.infer<typeof IdentityLinkSchema>;
@@ -9,6 +9,7 @@
9
9
  */
10
10
 
11
11
  import { z } from "zod";
12
+ import { IsoDateTimeWithOffset } from "../api/primitives";
12
13
 
13
14
  export const ImpersonationSessionSchema = z.object({
14
15
  impersonationSessionId: z.string(),
@@ -16,9 +17,9 @@ export const ImpersonationSessionSchema = z.object({
16
17
  targetUserId: z.string(),
17
18
  reason: z.string(),
18
19
  reasonHash: z.string(),
19
- startedAt: z.string().datetime({ offset: true }),
20
- expiresAt: z.string().datetime({ offset: true }),
21
- endedAt: z.string().datetime({ offset: true }).nullable(),
20
+ startedAt: IsoDateTimeWithOffset,
21
+ expiresAt: IsoDateTimeWithOffset,
22
+ endedAt: IsoDateTimeWithOffset.nullable(),
22
23
  ipAddress: z.string(),
23
24
  userAgent: z.string(),
24
25
  });
@@ -48,9 +49,9 @@ export const ImpersonationSessionWireDtoSchema = z
48
49
  .object({
49
50
  id: z.string(),
50
51
  targetUserId: z.string(),
51
- startedAt: z.string().datetime({ offset: true }),
52
- expiresAt: z.string().datetime({ offset: true }),
53
- endedAt: z.string().datetime({ offset: true }).nullable(),
52
+ startedAt: IsoDateTimeWithOffset,
53
+ expiresAt: IsoDateTimeWithOffset,
54
+ endedAt: IsoDateTimeWithOffset.nullable(),
54
55
  })
55
56
  .strict();
56
57
 
@@ -47,7 +47,7 @@ alongside `Meetings`, `Comms`, and `Docs`.
47
47
 
48
48
  **Internal domains:**
49
49
 
50
- _None._
50
+ - `api`
51
51
 
52
52
  **External packages:**
53
53
 
@@ -16,6 +16,7 @@
16
16
  * stringified JSON blobs on the schema.
17
17
  */
18
18
  import { z } from "zod";
19
+ import { IsoDateTime } from "../api/primitives";
19
20
 
20
21
  // =============================================================================
21
22
  // HR connection status
@@ -87,7 +88,7 @@ export const HrConnectionStatusSchema = z
87
88
  description:
88
89
  "Departments mirrored from the HR provider; null when never synced.",
89
90
  }),
90
- lastSyncAt: z.string().datetime().nullable().meta({
91
+ lastSyncAt: IsoDateTime.nullable().meta({
91
92
  description: "ISO timestamp of the last successful sync; null if never.",
92
93
  }),
93
94
  syncHealth: z.enum(["healthy", "degraded", "error"]).nullable().meta({
@@ -21,6 +21,7 @@
21
21
  * stringified JSON blobs on the schema.
22
22
  */
23
23
  import { z } from "zod";
24
+ import { IsoDateTime } from "../api/primitives";
24
25
 
25
26
  // =============================================================================
26
27
  // Recording identity
@@ -237,7 +238,7 @@ export const TranscriptionSessionGrantSchema = z
237
238
  sessionId: RecordingIdSchema,
238
239
  connectUrl: z.string().url(),
239
240
  authHeaders: z.record(z.string(), z.string()),
240
- expiresAt: z.string().datetime(),
241
+ expiresAt: IsoDateTime,
241
242
  audioConfig: z.object({
242
243
  sampleRateHz: z.literal(16000),
243
244
  channels: z.literal(2),
@@ -279,8 +280,8 @@ export const MeetingMetadataProjectionSchema = z
279
280
  visibility: MeetingVisibilitySchema,
280
281
  status: RecordingStatusSchema,
281
282
  quality: RecordingQualitySchema,
282
- startedAt: z.string().datetime(),
283
- endedAt: z.string().datetime().nullable(),
283
+ startedAt: IsoDateTime,
284
+ endedAt: IsoDateTime.nullable(),
284
285
  durationMs: z.number().int().nonnegative(),
285
286
  participantUserIds: z.array(z.string().uuid()),
286
287
  transcriptAvailable: z.boolean(),
@@ -25,6 +25,7 @@
25
25
  * account.
26
26
  */
27
27
  import { z } from "zod";
28
+ import { IsoDateTime } from "../api/primitives";
28
29
 
29
30
  import { FactProvenanceSchema } from "./structure-facts";
30
31
 
@@ -60,9 +61,9 @@ export const OccupancySchema = z.object({
60
61
  /** Lifecycle of this hold; only `active` makes the position occupied. */
61
62
  status: OccupancyStatusSchema,
62
63
  /** ISO start of the hold; `null`/absent when unknown or open-started. */
63
- startsAt: z.string().datetime().nullable().optional(),
64
+ startsAt: IsoDateTime.nullable().optional(),
64
65
  /** ISO end of the hold; `null`/absent while the hold is still open. */
65
- endsAt: z.string().datetime().nullable().optional(),
66
+ endsAt: IsoDateTime.nullable().optional(),
66
67
  /** Provenance of this occupancy fact (truth hierarchy + supersede model). */
67
68
  provenance: FactProvenanceSchema,
68
69
  });
@@ -20,6 +20,7 @@
20
20
  * `inferenceType` and is not part of the stable contract surface.
21
21
  */
22
22
  import { z } from "zod";
23
+ import { IsoDateTime } from "../api/primitives";
23
24
 
24
25
  // ---------------------------------------------------------------------------
25
26
  // OrgStructureReviewInference — one pending ('review') structure inference
@@ -43,7 +44,7 @@ export const OrgStructureReviewInferenceSchema = z.object({
43
44
  /** Human-meaningful WHY the engine proposed this change. */
44
45
  reason: z.string(),
45
46
  /** ISO timestamp the inference was created. */
46
- createdAt: z.string().datetime(),
47
+ createdAt: IsoDateTime,
47
48
  });
48
49
 
49
50
  export type OrgStructureReviewInference = z.infer<
@@ -39,6 +39,7 @@
39
39
  * remove an entry is the two sides agreeing.
40
40
  */
41
41
  import { z } from "zod";
42
+ import { IsoDateTime } from "../api/primitives";
42
43
 
43
44
  import { FactSourceTierSchema } from "./structure-facts";
44
45
 
@@ -92,7 +93,7 @@ export const OrgDivergenceSchema = z.object({
92
93
  */
93
94
  reason: z.string().max(2000).nullable(),
94
95
  /** ISO timestamp the workspace's current word on this field was recorded. */
95
- recordedAt: z.string().datetime(),
96
+ recordedAt: IsoDateTime,
96
97
  /**
97
98
  * ISO timestamp someone in this workspace ACKNOWLEDGED this entry, or `null`.
98
99
  *
@@ -102,7 +103,7 @@ export const OrgDivergenceSchema = z.object({
102
103
  * the entry — arrives unacknowledged, which is the right default for a
103
104
  * statement nobody has read yet.
104
105
  */
105
- acknowledgedAt: z.string().datetime().nullable(),
106
+ acknowledgedAt: IsoDateTime.nullable(),
106
107
  /** Who acknowledged it. `null` when nobody has, or when that user is gone. */
107
108
  acknowledgedByUserId: z.string().uuid().nullable(),
108
109
  });
@@ -146,7 +147,7 @@ export const ResolveOrgDivergenceResultSchema = z.object({
146
147
  /** How it was resolved. Local decisions only — see the module docblock. */
147
148
  resolution: OrgDivergenceResolutionSchema,
148
149
  /** ISO timestamp the decision was recorded in this workspace. */
149
- resolvedAt: z.string().datetime(),
150
+ resolvedAt: IsoDateTime,
150
151
  /**
151
152
  * Whether anything was written back to the source system. Pinned to the
152
153
  * literal `false`: the HRIS client is read-only and no write scope is
@@ -11,7 +11,7 @@
11
11
  * - scope-check.ts (GET /api/scope/check, POST /api/scope/check-batch)
12
12
  */
13
13
  import { z } from "zod";
14
- import { CursorPageSchema } from "../api/primitives";
14
+ import { CursorPageSchema, IsoDateTime } from "../api/primitives";
15
15
  import { OrgChartRoleSchema } from "../permissions/orgchart-roles";
16
16
  import { PositionRefSchema } from "../identity/position-ref";
17
17
 
@@ -1757,7 +1757,7 @@ export const OwnerAuthoritySchema = z.object({
1757
1757
  * the projection read filters `expires_at > now()` — so a present value is
1758
1758
  * always in the future.
1759
1759
  */
1760
- expiresAt: z.string().datetime().nullable().optional(),
1760
+ expiresAt: IsoDateTime.nullable().optional(),
1761
1761
  });
1762
1762
  export type OwnerAuthority = z.infer<typeof OwnerAuthoritySchema>;
1763
1763
 
@@ -1817,7 +1817,7 @@ export const CreateDelegationRequestSchema = z.object({
1817
1817
  * Optional ISO-8601 expiry. Omitted or `null` ⇒ a permanent delegation. The
1818
1818
  * UI captures a date and sends end-of-day in the user's timezone.
1819
1819
  */
1820
- expiresAt: z.string().datetime().nullable().optional(),
1820
+ expiresAt: IsoDateTime.nullable().optional(),
1821
1821
  /**
1822
1822
  * When true, the granted user is emailed a notification (carrying `note` as
1823
1823
  * the message). Not persisted — it only gates the side-effect email. Omitted
@@ -1836,7 +1836,7 @@ export type CreateDelegationRequest = z.infer<
1836
1836
  * on an existing delegate row.
1837
1837
  */
1838
1838
  export const UpdateDelegationRequestSchema = z.object({
1839
- expiresAt: z.string().datetime().nullable(),
1839
+ expiresAt: IsoDateTime.nullable(),
1840
1840
  });
1841
1841
  export type UpdateDelegationRequest = z.infer<
1842
1842
  typeof UpdateDelegationRequestSchema
@@ -26,6 +26,7 @@
26
26
  * nullable string rather than wrapped in a page envelope.
27
27
  */
28
28
  import { z } from "zod";
29
+ import { IsoDateTime } from "../api/primitives";
29
30
 
30
31
  // ---------------------------------------------------------------------------
31
32
  // SyncRunSummary — the outcome record of one adapter sync run
@@ -35,9 +36,9 @@ export const SyncRunSummarySchema = z.object({
35
36
  /** Unique identifier for this sync run within the org graph. */
36
37
  id: z.string().uuid(),
37
38
  /** ISO start of the run. Always set once a run has begun. */
38
- startedAt: z.string().datetime(),
39
+ startedAt: IsoDateTime,
39
40
  /** ISO end of the run; `null`/absent while the run is still in progress. */
40
- endedAt: z.string().datetime().nullable().optional(),
41
+ endedAt: IsoDateTime.nullable().optional(),
41
42
  /** Count of facts examined by the run (the denominator). */
42
43
  processed: z.number().int().nonnegative(),
43
44
  /** Count of facts that resulted in a real mutation (insert/supersede). */
@@ -18,6 +18,7 @@
18
18
  * permission_decisions; the UI never re-derives.
19
19
  */
20
20
  import { z } from "zod";
21
+ import { IsoDateTime } from "../api/primitives";
21
22
  import { AccessLevelSchema } from "./access-levels";
22
23
  import { AccessSourceSchema } from "./access-source";
23
24
  import { PrincipalSchema } from "./share-api";
@@ -51,7 +52,7 @@ export const RecentDecisionSchema = z.object({
51
52
  allow: z.boolean(),
52
53
  reason: z.string(),
53
54
  source_chain: z.array(AccessSourceSchema),
54
- decided_at: z.string().datetime(),
55
+ decided_at: IsoDateTime,
55
56
  });
56
57
  export type RecentDecision = z.infer<typeof RecentDecisionSchema>;
57
58
 
@@ -21,6 +21,7 @@
21
21
  * changing (ADR-CONTRACTS-093).
22
22
  */
23
23
  import { z } from "zod";
24
+ import { IsoDateTime } from "../api/primitives";
24
25
  import { AccessLevelSchema } from "./access-levels";
25
26
  import { AccessSourceSchema } from "./access-source";
26
27
 
@@ -77,7 +78,7 @@ export const AclGrantResponseSchema = z.object({
77
78
  principal: PrincipalSchema,
78
79
  access_level: GrantableAccessLevelSchema,
79
80
  granted_by_user_id: z.string().uuid(),
80
- granted_at: z.string().datetime(),
81
+ granted_at: IsoDateTime,
81
82
  });
82
83
  export type AclGrantResponse = z.infer<typeof AclGrantResponseSchema>;
83
84
 
@@ -13,6 +13,7 @@
13
13
  * `./secret.ts`) before storage / encryption.
14
14
  */
15
15
  import { z } from "zod";
16
+ import { IsoDateTime } from "../api/primitives";
16
17
 
17
18
  /**
18
19
  * Usage class of an org secret. Drives which resolver layer is allowed to read
@@ -75,10 +76,10 @@ export const OrgSecretSummarySchema = z.object({
75
76
  maskedPrefix: z.string().min(1).max(64),
76
77
  version: z.number().int().positive(),
77
78
  enabled: z.boolean(),
78
- lastUsedAt: z.string().datetime().nullable(),
79
+ lastUsedAt: IsoDateTime.nullable(),
79
80
  usageCount: z.string(),
80
- rotatedAt: z.string().datetime().nullable(),
81
- createdAt: z.string().datetime(),
81
+ rotatedAt: IsoDateTime.nullable(),
82
+ createdAt: IsoDateTime,
82
83
  });
83
84
  export type OrgSecretSummary = z.infer<typeof OrgSecretSummarySchema>;
84
85
 
@@ -29,6 +29,7 @@
29
29
  * stream without first giving it a real commit-ordered log.
30
30
  */
31
31
  import { z } from "zod";
32
+ import { IsoDateTime } from "../api/primitives";
32
33
  import type { ResourceKey } from "../resource-keys";
33
34
  import { isResourceKeyShape } from "../resource-keys";
34
35
 
@@ -36,7 +37,6 @@ import { isResourceKeyShape } from "../resource-keys";
36
37
  * Local, not chat's. `chat/schemas.ts` owns a structurally identical helper;
37
38
  * importing it would make a chat frame change a user-events breaking change.
38
39
  */
39
- const IsoDateString = z.string().datetime();
40
40
 
41
41
  /**
42
42
  * Fields every frame carries.
@@ -47,7 +47,7 @@ const IsoDateString = z.string().datetime();
47
47
  */
48
48
  export const UserEventBaseSchema = z.object({
49
49
  v: z.literal(1),
50
- timestamp: IsoDateString,
50
+ timestamp: IsoDateTime,
51
51
  });
52
52
 
53
53
  // =============================================================================