@company-semantics/contracts 27.7.0 → 27.9.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": "27.7.0",
3
+ "version": "27.9.0",
4
4
  "private": false,
5
5
  "repository": {
6
6
  "type": "git",
@@ -1,3 +1,3 @@
1
1
  // AUTO-GENERATED — do not edit. Run pnpm generate:spec-hash to regenerate.
2
- export const SPEC_HASH = '628fe99ff916' as const;
3
- export const SPEC_HASH_FULL = '628fe99ff9166b9eb6c0b2bab2794f4e730ceabff0785b7b54b8015897e9551d' as const;
2
+ export const SPEC_HASH = '3974d23a4348' as const;
3
+ export const SPEC_HASH_FULL = '3974d23a434834c5a1f81d93ef082b993014143e7b95e97dd775293045809af8' as const;
@@ -736,23 +736,6 @@ export interface paths {
736
736
  patch: operations["setMemberManager"];
737
737
  trace?: never;
738
738
  };
739
- "/api/reporting/bulk-reparent": {
740
- parameters: {
741
- query?: never;
742
- header?: never;
743
- path?: never;
744
- cookie?: never;
745
- };
746
- get?: never;
747
- put?: never;
748
- /** Atomically re-point many members' solid manager to one new manager */
749
- post: operations["bulkReparentReporting"];
750
- delete?: never;
751
- options?: never;
752
- head?: never;
753
- patch?: never;
754
- trace?: never;
755
- };
756
739
  "/api/reporting/move/preview": {
757
740
  parameters: {
758
741
  query?: never;
@@ -3912,37 +3895,6 @@ export interface components {
3912
3895
  openRoleId: string;
3913
3896
  };
3914
3897
  };
3915
- BulkReparentReportingResponse: {
3916
- /** @constant */
3917
- success: true;
3918
- movedCount: number;
3919
- toManager: {
3920
- /** @constant */
3921
- kind: "member";
3922
- /** Format: uuid */
3923
- userId: string;
3924
- } | {
3925
- /** @constant */
3926
- kind: "open-role";
3927
- /** Format: uuid */
3928
- openRoleId: string;
3929
- };
3930
- };
3931
- BulkReparentReportingRequest: {
3932
- memberIds: string[];
3933
- toManager: {
3934
- /** @constant */
3935
- kind: "member";
3936
- /** Format: uuid */
3937
- userId: string;
3938
- } | {
3939
- /** @constant */
3940
- kind: "open-role";
3941
- /** Format: uuid */
3942
- openRoleId: string;
3943
- };
3944
- idempotencyKey: string;
3945
- };
3946
3898
  MovePreviewRequest: {
3947
3899
  /** @constant */
3948
3900
  kind: "reporting.setManager";
@@ -3962,6 +3914,12 @@ export interface components {
3962
3914
  /** Format: uuid */
3963
3915
  positionId: string;
3964
3916
  title: string;
3917
+ } | {
3918
+ /** @constant */
3919
+ kind: "reporting.setManagerBulk";
3920
+ reportPositionIds: string[];
3921
+ /** Format: uuid */
3922
+ managerPositionId: string;
3965
3923
  };
3966
3924
  MoveApplyRequest: {
3967
3925
  mutations: ({
@@ -7103,30 +7061,6 @@ export interface operations {
7103
7061
  };
7104
7062
  };
7105
7063
  };
7106
- bulkReparentReporting: {
7107
- parameters: {
7108
- query?: never;
7109
- header?: never;
7110
- path?: never;
7111
- cookie?: never;
7112
- };
7113
- requestBody: {
7114
- content: {
7115
- "application/json": components["schemas"]["BulkReparentReportingRequest"];
7116
- };
7117
- };
7118
- responses: {
7119
- /** @description Cluster reorg applied atomically */
7120
- 200: {
7121
- headers: {
7122
- [name: string]: unknown;
7123
- };
7124
- content: {
7125
- "application/json": components["schemas"]["BulkReparentReportingResponse"];
7126
- };
7127
- };
7128
- };
7129
- };
7130
7064
  previewReportingMove: {
7131
7065
  parameters: {
7132
7066
  query?: never;
@@ -62,6 +62,24 @@ describe("EMAIL_KINDS golden snapshot", () => {
62
62
  plainTextRequired: true,
63
63
  htmlSupported: true,
64
64
  },
65
+ "companyMd.access_requested": {
66
+ kind: "companyMd.access_requested",
67
+ subject: "Someone requested access to a document",
68
+ plainTextRequired: true,
69
+ htmlSupported: true,
70
+ },
71
+ "companyMd.access_request_approved": {
72
+ kind: "companyMd.access_request_approved",
73
+ subject: "Your access request was approved",
74
+ plainTextRequired: true,
75
+ htmlSupported: true,
76
+ },
77
+ "companyMd.access_request_denied": {
78
+ kind: "companyMd.access_request_denied",
79
+ subject: "Your access request was reviewed",
80
+ plainTextRequired: true,
81
+ htmlSupported: true,
82
+ },
65
83
  });
66
84
  });
67
85
  });
@@ -125,7 +125,6 @@ export const openApiRoutes = {
125
125
  '/api/orgs/{orgId}/budget-config': ['GET', 'PUT'],
126
126
  '/api/orgs/{orgId}/level-config': ['GET', 'PUT'],
127
127
  '/api/rbac/roles': ['GET'],
128
- '/api/reporting/bulk-reparent': ['POST'],
129
128
  '/api/reporting/move/apply': ['POST'],
130
129
  '/api/reporting/move/preview': ['POST'],
131
130
  '/api/scope/check': ['GET'],
@@ -106,6 +106,55 @@ describe("OrgIntentSchema", () => {
106
106
  }).success,
107
107
  ).toBe(false);
108
108
  });
109
+
110
+ it("accepts a reporting.setManagerBulk intent (N seats -> one manager)", () => {
111
+ const intent = {
112
+ kind: "reporting.setManagerBulk" as const,
113
+ reportPositionIds: [REPORT, POSITION],
114
+ managerPositionId: MANAGER,
115
+ };
116
+ expect(OrgIntentSchema.parse(intent)).toEqual(intent);
117
+ });
118
+
119
+ it("rejects an empty bulk reportPositionIds array", () => {
120
+ expect(
121
+ OrgIntentSchema.safeParse({
122
+ kind: "reporting.setManagerBulk",
123
+ reportPositionIds: [],
124
+ managerPositionId: MANAGER,
125
+ }).success,
126
+ ).toBe(false);
127
+ });
128
+
129
+ it("rejects a bulk array beyond the 500-seat transport cap", () => {
130
+ expect(
131
+ OrgIntentSchema.safeParse({
132
+ kind: "reporting.setManagerBulk",
133
+ reportPositionIds: Array.from({ length: 501 }, () => REPORT),
134
+ managerPositionId: MANAGER,
135
+ }).success,
136
+ ).toBe(false);
137
+ });
138
+
139
+ it("rejects a non-uuid entry in the bulk report set", () => {
140
+ expect(
141
+ OrgIntentSchema.safeParse({
142
+ kind: "reporting.setManagerBulk",
143
+ reportPositionIds: [REPORT, "x"],
144
+ managerPositionId: MANAGER,
145
+ }).success,
146
+ ).toBe(false);
147
+ });
148
+
149
+ it("rejects a null bulk managerPositionId (intents always set a manager)", () => {
150
+ expect(
151
+ OrgIntentSchema.safeParse({
152
+ kind: "reporting.setManagerBulk",
153
+ reportPositionIds: [REPORT],
154
+ managerPositionId: null,
155
+ }).success,
156
+ ).toBe(false);
157
+ });
109
158
  });
110
159
 
111
160
  describe("FactMutationSchema", () => {
@@ -436,4 +485,23 @@ describe("TransformationPreviewSchema", () => {
436
485
  false,
437
486
  );
438
487
  });
488
+
489
+ it("accepts a multi-headline preview carrying primaryMutations (bulk intent)", () => {
490
+ const second = { ...setManagerMutation, reportPositionId: POSITION };
491
+ expect(
492
+ TransformationPreviewSchema.safeParse({
493
+ ...preview,
494
+ primaryMutations: [setManagerMutation, second],
495
+ }).success,
496
+ ).toBe(true);
497
+ });
498
+
499
+ it("rejects a single-element primaryMutations (single moves omit the field)", () => {
500
+ expect(
501
+ TransformationPreviewSchema.safeParse({
502
+ ...preview,
503
+ primaryMutations: [setManagerMutation],
504
+ }).success,
505
+ ).toBe(false);
506
+ });
439
507
  });
@@ -95,6 +95,25 @@ export const OrgIntentSchema = z.discriminatedUnion("kind", [
95
95
  /** The new human-facing title for the seat (non-empty, matching PositionSchema.title). */
96
96
  title: z.string().min(1),
97
97
  }),
98
+ // reporting.setManagerBulk — N reporting seats re-pointed at ONE manager as a
99
+ // single atomic intent (the org-chart cluster-pill drop). The engine expands
100
+ // it into N `reporting.setManager` FactMutations — the existing
101
+ // Intent-vs-Mutation asymmetry; no bulk mutation kind exists or is needed.
102
+ // Cross-field rules (manager ∉ reports, dedupe) are enforced at the route: a
103
+ // discriminatedUnion member cannot carry a ZodEffects refinement.
104
+ z.object({
105
+ kind: z.literal("reporting.setManagerBulk"),
106
+ /**
107
+ * The reporting seats being re-pointed together (e.g. a unit cluster's
108
+ * direct members; their subtrees follow implicitly). Order-insensitive;
109
+ * the engine treats the set as one atomic intent. The cap mirrors the
110
+ * bulk-reparent transport bound (ADR-BE-318).
111
+ */
112
+ reportPositionIds: z.array(z.string().uuid()).min(1).max(500),
113
+ /** The ONE manager seat every report re-points to. Non-null on an intent
114
+ * (un-setting is a mutation concern, exactly as on `reporting.setManager`). */
115
+ managerPositionId: z.string().uuid(),
116
+ }),
98
117
  ]);
99
118
  export type OrgIntent = z.infer<typeof OrgIntentSchema>;
100
119
 
@@ -336,6 +355,15 @@ export const TransformationPreviewSchema = z.object({
336
355
  intentLabel: z.string().min(1),
337
356
  /** The headline fact change the preview is centered on. */
338
357
  primary: FactMutationSchema,
358
+ /**
359
+ * Present only when the intent expands into MORE THAN ONE headline mutation
360
+ * (e.g. `reporting.setManagerBulk`): the full primary bundle, of which
361
+ * `primary` is the first element. Absent on single-mutation previews so
362
+ * existing consumers and goldens see byte-identical output. These are the
363
+ * INTENT's own edges — engine repair splices stay in the strategies'
364
+ * recommendations, never here.
365
+ */
366
+ primaryMutations: z.array(FactMutationSchema).min(2).optional(),
339
367
  /** The strategy the engine recommends by default. */
340
368
  defaultStrategy: TransformStrategySchema,
341
369
  /** Other viable strategies the operator may pick instead. */
@@ -62,14 +62,29 @@ export const AclListResponseSchema = z.object({
62
62
  entity_type: z.string(),
63
63
  entity_id: z.string().uuid(),
64
64
  owner_user_id: z.string().uuid().nullable(),
65
+ /** General-access scope: WHO is eligible (the discoverability axis). */
65
66
  visibility: EntityVisibilitySchema,
67
+ /**
68
+ * The ROLE the general-access scope grants its audience: WHAT they may do (the
69
+ * capability axis, ADR-BE-375). Orthogonal to `visibility` — e.g. "Everyone in
70
+ * Product · Commenter". Defaults to `viewer` (the historical view-only band);
71
+ * ignored when `visibility` is `private` (no general grant).
72
+ */
73
+ general_access_role: GrantableAccessLevelSchema.default("viewer"),
66
74
  editors_can_share: z.boolean(),
67
75
  grants: z.array(AclGrantResponseSchema),
68
76
  });
69
77
  export type AclListResponse = z.infer<typeof AclListResponseSchema>;
70
78
 
71
79
  export const VisibilityPatchRequestSchema = z.object({
80
+ /** The general-access scope (WHO is eligible). */
72
81
  tier: EntityVisibilitySchema,
82
+ /**
83
+ * The role granted to that scope (WHAT they may do, ADR-BE-375). Optional and
84
+ * backward-compatible: omitted preserves the current role (or `viewer` on first
85
+ * set); ignored when `tier` is `private`.
86
+ */
87
+ role: GrantableAccessLevelSchema.optional(),
73
88
  });
74
89
  export type VisibilityPatchRequest = z.infer<
75
90
  typeof VisibilityPatchRequestSchema