@company-semantics/contracts 58.0.0 → 58.2.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.
Files changed (55) hide show
  1. package/package.json +4 -4
  2. package/src/__tests__/resource-keys.test.ts +30 -0
  3. package/src/api/generated-spec-hash.ts +2 -2
  4. package/src/api/generated.ts +33 -1
  5. package/src/chat/README.md +15 -4
  6. package/src/chat/__tests__/proactive-kind.test.ts +51 -0
  7. package/src/chat/index.ts +9 -0
  8. package/src/chat/proactive-kind.ts +51 -0
  9. package/src/chat/schemas.ts +92 -1
  10. package/src/chat/types.ts +19 -1
  11. package/src/index.ts +110 -0
  12. package/src/message-parts/README.md +5 -0
  13. package/src/message-parts/__tests__/suggested-replies.test.ts +52 -0
  14. package/src/message-parts/__tests__/wire.test.ts +48 -0
  15. package/src/message-parts/index.ts +8 -0
  16. package/src/message-parts/suggested-replies.ts +48 -0
  17. package/src/message-parts/types.ts +7 -1
  18. package/src/message-parts/wire.ts +26 -0
  19. package/src/notifications/__tests__/__snapshots__/monospace-budget.test.ts.snap +1 -0
  20. package/src/notifications/__tests__/__snapshots__/registry.test.ts.snap +1 -0
  21. package/src/notifications/__tests__/__snapshots__/render-snapshot.test.ts.snap +207 -0
  22. package/src/notifications/__tests__/fixtures.ts +9 -0
  23. package/src/notifications/__tests__/org-invite.test.ts +75 -0
  24. package/src/notifications/__tests__/render-snapshot.test.ts +8 -0
  25. package/src/notifications/kinds/org-invite.ts +27 -12
  26. package/src/notifications/payloads.ts +7 -0
  27. package/src/org/README.md +38 -0
  28. package/src/org/__tests__/canonical-facts.test.ts +118 -0
  29. package/src/org/__tests__/structure-inference.test.ts +392 -0
  30. package/src/org/__tests__/structure-provenance.test.ts +187 -0
  31. package/src/org/canonical-facts.ts +94 -1
  32. package/src/org/index.ts +54 -0
  33. package/src/org/schemas.ts +23 -0
  34. package/src/org/structure-inference.ts +521 -0
  35. package/src/proactive/README.md +125 -0
  36. package/src/proactive/__tests__/README.md +56 -0
  37. package/src/proactive/__tests__/chat-templates.test.ts +167 -0
  38. package/src/proactive/__tests__/compile-fixtures.ts +110 -0
  39. package/src/proactive/__tests__/vocabulary.test.ts +279 -0
  40. package/src/proactive/classes.ts +125 -0
  41. package/src/proactive/composer.ts +104 -0
  42. package/src/proactive/facts.ts +87 -0
  43. package/src/proactive/index.ts +52 -0
  44. package/src/proactive/kinds.ts +127 -0
  45. package/src/proactive/plan.ts +79 -0
  46. package/src/proactive/registry.ts +71 -0
  47. package/src/proactive/surfaces.ts +59 -0
  48. package/src/proactive/templates/README.md +58 -0
  49. package/src/proactive/templates/index.ts +32 -0
  50. package/src/proactive/templates/morning-brief.ts +77 -0
  51. package/src/proactive/templates/org-became-shared.ts +54 -0
  52. package/src/resource-key-types.ts +9 -0
  53. package/src/resource-keys.ts +2 -0
  54. package/src/user-notifications/README.md +10 -0
  55. package/src/user-notifications/kinds.ts +28 -0
@@ -7,7 +7,9 @@
7
7
  * source said" and "what the org graph becomes": adapters translate their
8
8
  * proprietary payloads into this vocabulary, and the reconciler consumes it to
9
9
  * create/supersede internal Person / OrgUnit / Position / Occupancy / reporting
10
- * records. See contracts ADR-CONT-085 and control ADR-CTRL-183.
10
+ * records. See contracts ADR-CONT-085 and control ADR-CTRL-183; ADR-CONTRACTS-140
11
+ * amends ADR-CONT-085 with the sixth fact kind (`unresolvedManagers`), the
12
+ * required `status` on a position, and the major-bump consequence.
11
13
  *
12
14
  * Two properties make these facts distinct from the in-graph schemas they mirror
13
15
  * ({@link PersonSchema}, {@link PositionSchema}, {@link OccupancySchema}, …):
@@ -81,6 +83,34 @@ export const CanonicalOrgUnitSchema = z.object({
81
83
  });
82
84
  export type CanonicalOrgUnit = z.infer<typeof CanonicalOrgUnitSchema>;
83
85
 
86
+ // ---------------------------------------------------------------------------
87
+ // CanonicalPositionStatus — seat existence AS REPORTED by the source
88
+ // ---------------------------------------------------------------------------
89
+
90
+ /**
91
+ * Seat-existence as the SOURCE reported it. `filled` is the ordinary case; an
92
+ * adapter that cannot say must say `filled` rather than omit the field.
93
+ *
94
+ * This is a REPORTED claim at the ingest seam, deliberately narrower than the
95
+ * in-graph {@link PositionStatusSchema} lifecycle (which also has `closed`): a
96
+ * source describing its current workforce reports seats that exist, and seat
97
+ * retirement is a reconciliation outcome, not something a directory states.
98
+ *
99
+ * It does NOT contradict the occupancy invariant that in-graph VACANCY is
100
+ * derived from the absence of an active {@link OccupancySchema} (see
101
+ * `occupancy.ts`). This field records what the source SAID about the seat; the
102
+ * reconciler still derives vacancy from occupancy facts and never reads a
103
+ * `vacant` flag off a Position.
104
+ */
105
+ export const CanonicalPositionStatusSchema = z.enum([
106
+ "planned",
107
+ "open",
108
+ "filled",
109
+ ]);
110
+ export type CanonicalPositionStatus = z.infer<
111
+ typeof CanonicalPositionStatusSchema
112
+ >;
113
+
84
114
  // ---------------------------------------------------------------------------
85
115
  // CanonicalPosition — a normalized seat reported by an external source
86
116
  // ---------------------------------------------------------------------------
@@ -99,6 +129,11 @@ export const CanonicalPositionSchema = z.object({
99
129
  title: z.string().min(1),
100
130
  /** External id of the org unit this seat lives in. */
101
131
  unitExternalSourceId: z.string().min(1),
132
+ /**
133
+ * Seat existence as the source reported it. REQUIRED — pre-launch we ship no
134
+ * back-compat optionality; an adapter that cannot say must say `filled`.
135
+ */
136
+ status: CanonicalPositionStatusSchema,
102
137
  /** Provenance of this fact (truth hierarchy + supersede model). */
103
138
  provenance: FactProvenanceSchema,
104
139
  });
@@ -156,6 +191,58 @@ export type CanonicalReportingEdge = z.infer<
156
191
  typeof CanonicalReportingEdgeSchema
157
192
  >;
158
193
 
194
+ // ---------------------------------------------------------------------------
195
+ // CanonicalUnresolvedManager — a supervisory reference that resolved to nothing
196
+ // ---------------------------------------------------------------------------
197
+
198
+ /**
199
+ * Why a supervisory reference could not be resolved to a seat in this batch.
200
+ *
201
+ * `unknown` is the default and asserts NOTHING beyond "we could not resolve
202
+ * it". Only `vacant_position` claims the company has an unfilled seat, and only
203
+ * when the source actually says so — "we could not resolve this reference" and
204
+ * "the company has an open requisition" are different organizational claims,
205
+ * and their causes (permissions, termination, stale data, scope limits, a
206
+ * hidden record, a real vacancy) are not interchangeable.
207
+ */
208
+ export const CanonicalUnresolvedManagerResolutionSchema = z.enum([
209
+ "unknown",
210
+ "vacant_position",
211
+ "inactive_employee",
212
+ "not_visible",
213
+ ]);
214
+ export type CanonicalUnresolvedManagerResolution = z.infer<
215
+ typeof CanonicalUnresolvedManagerResolutionSchema
216
+ >;
217
+
218
+ /**
219
+ * A supervisory reference the source reported that we could NOT resolve to a
220
+ * seat in this batch.
221
+ *
222
+ * Emitting this fact is what keeps an unresolvable supervisor from becoming a
223
+ * SILENT drop: the report seats that pointed at it stay traceable instead of
224
+ * surfacing as unexplained roots in the org graph. It never mints a seat by
225
+ * itself — see {@link CanonicalUnresolvedManagerResolutionSchema} for why only
226
+ * `vacant_position` licenses that.
227
+ */
228
+ export const CanonicalUnresolvedManagerSchema = z.object({
229
+ /** Stable id for this unresolved reference in the origin system. */
230
+ externalSourceId: z.string().min(1),
231
+ /** Origin system naming this id — OPEN string. */
232
+ externalSourceSystem: z.string().min(1),
233
+ /** The supervisor identifier the source gave us and we could not resolve. */
234
+ externalManagerId: z.string().min(1),
235
+ /** External ids of the report seats pointing at this unresolved manager. */
236
+ reportPositionExternalSourceIds: z.array(z.string().min(1)),
237
+ /** Why it did not resolve; defaults to the non-committal `unknown`. */
238
+ resolution: CanonicalUnresolvedManagerResolutionSchema.default("unknown"),
239
+ /** Provenance of this fact (truth hierarchy + supersede model). */
240
+ provenance: FactProvenanceSchema,
241
+ });
242
+ export type CanonicalUnresolvedManager = z.infer<
243
+ typeof CanonicalUnresolvedManagerSchema
244
+ >;
245
+
159
246
  // ---------------------------------------------------------------------------
160
247
  // CanonicalFacts — the batch envelope an adapter emits for reconciliation
161
248
  // ---------------------------------------------------------------------------
@@ -165,6 +252,10 @@ export type CanonicalReportingEdge = z.infer<
165
252
  * person, org unit, position, occupancy, and reporting edge it observed, each
166
253
  * carrying external identity + provenance. The reconciler consumes this whole
167
254
  * envelope to create/supersede internal org-graph records.
255
+ *
256
+ * `unresolvedManagers` is the batch's honesty channel: a supervisory reference
257
+ * the adapter could not resolve is REPORTED here rather than dropped, so the
258
+ * count of observed seats minus placed edges is fully explained.
168
259
  */
169
260
  export const CanonicalFactsSchema = z.object({
170
261
  /** Every person observed in this batch. */
@@ -177,5 +268,7 @@ export const CanonicalFactsSchema = z.object({
177
268
  occupancies: z.array(CanonicalOccupancySchema),
178
269
  /** Every seat-to-manager-seat reporting edge observed in this batch. */
179
270
  reportingEdges: z.array(CanonicalReportingEdgeSchema),
271
+ /** Every supervisory reference this batch could NOT resolve to a seat. */
272
+ unresolvedManagers: z.array(CanonicalUnresolvedManagerSchema),
180
273
  });
181
274
  export type CanonicalFacts = z.infer<typeof CanonicalFactsSchema>;
package/src/org/index.ts CHANGED
@@ -208,20 +208,74 @@ export type {
208
208
  export {
209
209
  CanonicalPersonSchema,
210
210
  CanonicalOrgUnitSchema,
211
+ CanonicalPositionStatusSchema,
211
212
  CanonicalPositionSchema,
212
213
  CanonicalOccupancySchema,
213
214
  CanonicalReportingEdgeSchema,
215
+ CanonicalUnresolvedManagerResolutionSchema,
216
+ CanonicalUnresolvedManagerSchema,
214
217
  CanonicalFactsSchema,
215
218
  } from "./canonical-facts";
216
219
  export type {
217
220
  CanonicalPerson,
218
221
  CanonicalOrgUnit,
222
+ CanonicalPositionStatus,
219
223
  CanonicalPosition,
220
224
  CanonicalOccupancy,
221
225
  CanonicalReportingEdge,
226
+ CanonicalUnresolvedManagerResolution,
227
+ CanonicalUnresolvedManager,
222
228
  CanonicalFacts,
223
229
  } from "./canonical-facts";
224
230
 
231
+ // Org-structure inference: the snapshot the engine reasons over and the
232
+ // proposed desired state it returns. Evidence is TYPED so the validator can
233
+ // enforce boundary and naming rules mechanically; a proposed unit carries
234
+ // targetUnitId so a rename preserves durable identity; every person in the
235
+ // snapshot receives exactly one outcome. (PRD-00955)
236
+ //
237
+ // Accepted structural facts additionally carry StructureProvenance: three
238
+ // ORTHOGONAL axes — proposalOrigin, acceptedBy and authority — so that clicking
239
+ // Apply on an AI proposal makes it human_confirmed without making its origin
240
+ // manual. Precedence runs on `authority` alone, via mayOverrideAuthority.
241
+ // (PRD-00956)
242
+ export {
243
+ StructureEvidenceSchema,
244
+ TOPOLOGY_ONLY_EVIDENCE_KINDS,
245
+ StructureUnitAuthoritySchema,
246
+ StructureProposalOriginSchema,
247
+ StructureAcceptedBySchema,
248
+ StructureAuthoritySchema,
249
+ StructureProvenanceSchema,
250
+ mayOverrideAuthority,
251
+ ProposedOrgUnitSchema,
252
+ PersonStructureOutcomeSchema,
253
+ StructureReviewItemSchema,
254
+ StructureProposalSchema,
255
+ StructurePersonFactSchema,
256
+ StructureReportingFactSchema,
257
+ ExistingUnitFactSchema,
258
+ ExistingPlacementFactSchema,
259
+ StructureInferenceSnapshotSchema,
260
+ } from "./structure-inference";
261
+ export type {
262
+ StructureEvidence,
263
+ StructureUnitAuthority,
264
+ StructureProposalOrigin,
265
+ StructureAcceptedBy,
266
+ StructureAuthority,
267
+ StructureProvenance,
268
+ ProposedOrgUnit,
269
+ PersonStructureOutcome,
270
+ StructureReviewItem,
271
+ StructureProposal,
272
+ StructurePersonFact,
273
+ StructureReportingFact,
274
+ ExistingUnitFact,
275
+ ExistingPlacementFact,
276
+ StructureInferenceSnapshot,
277
+ } from "./structure-inference";
278
+
225
279
  // Canonical OrgUnit tree ordering (PRD-00506)
226
280
  export type { TreeOrderableNode } from "./tree-ordering";
227
281
  export { orderTreeNodes } from "./tree-ordering";
@@ -1192,6 +1192,29 @@ export type UpdateInviteBatchRequest = z.infer<
1192
1192
  */
1193
1193
  export const SendInviteBatchRequestSchema = z.object({
1194
1194
  expectedRevision: z.number().int().nonnegative(),
1195
+ /**
1196
+ * The reviewer's optional note, carried into every invitation this send mails
1197
+ * and attributed to the inviter (`NotificationPayloads['org.invite'].message`).
1198
+ *
1199
+ * It rides the SEND rather than the batch on purpose: it authorizes nothing,
1200
+ * so it has no business bumping a revision that every open review reads as
1201
+ * "read this again". Not persisted — the invitation is the only thing that
1202
+ * ever carries it.
1203
+ */
1204
+ message: z.string().max(2000).optional(),
1205
+ /**
1206
+ * Per-person role overrides, keyed by `personId` — the reviewer set a role for
1207
+ * ONE recipient rather than for the batch.
1208
+ *
1209
+ * The batch's own `role` remains what everyone else is invited as; a key here
1210
+ * replaces it for that person only. A key naming somebody outside the batch,
1211
+ * or somebody it is no longer sending to, is INERT rather than an error: the
1212
+ * live directory re-check at send time already decides who is mailed, and a
1213
+ * stale override describes a person nobody is inviting.
1214
+ */
1215
+ roleByPersonId: z
1216
+ .record(z.string().uuid(), z.enum(["admin", "member"]))
1217
+ .optional(),
1195
1218
  });
1196
1219
 
1197
1220
  export type SendInviteBatchRequest = z.infer<