@company-semantics/contracts 27.4.0 → 27.5.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.4.0",
3
+ "version": "27.5.0",
4
4
  "private": false,
5
5
  "repository": {
6
6
  "type": "git",
@@ -123,12 +123,12 @@
123
123
  "zod": "^4.4.3"
124
124
  },
125
125
  "devDependencies": {
126
- "@types/node": "^22.10.0",
126
+ "@types/node": "^26.0.1",
127
127
  "husky": "^9.1.7",
128
- "lint-staged": "^17.0.7",
128
+ "lint-staged": "^17.0.8",
129
129
  "markdownlint-cli2": "^0.22.1",
130
130
  "openapi-typescript": "^7.13.0",
131
- "prettier": "^3.8.4",
131
+ "prettier": "^3.9.4",
132
132
  "tsx": "^4.22.4",
133
133
  "typescript": "^5.8.3",
134
134
  "vitest": "^4.1.9",
@@ -1,3 +1,3 @@
1
1
  // AUTO-GENERATED — do not edit. Run pnpm generate:spec-hash to regenerate.
2
- export const SPEC_HASH = '090fb75c09a2' as const;
3
- export const SPEC_HASH_FULL = '090fb75c09a20a352791bd84163827b0b23d84b41b5f82c216eae20fa5a3909d' as const;
2
+ export const SPEC_HASH = '511ebe19b2ad' as const;
3
+ export const SPEC_HASH_FULL = '511ebe19b2adea947b0bfbd6ad39a192a616cf10e2d30606dde9ef8517389eed' as const;
@@ -753,6 +753,23 @@ export interface paths {
753
753
  patch?: never;
754
754
  trace?: never;
755
755
  };
756
+ "/api/reporting/move/preview": {
757
+ parameters: {
758
+ query?: never;
759
+ header?: never;
760
+ path?: never;
761
+ cookie?: never;
762
+ };
763
+ get?: never;
764
+ put?: never;
765
+ /** Preview an org-chart reporting move as a semantic transform */
766
+ post: operations["previewReportingMove"];
767
+ delete?: never;
768
+ options?: never;
769
+ head?: never;
770
+ patch?: never;
771
+ trace?: never;
772
+ };
756
773
  "/api/chat/interactive-tasks/submit": {
757
774
  parameters: {
758
775
  query?: never;
@@ -3909,6 +3926,18 @@ export interface components {
3909
3926
  };
3910
3927
  idempotencyKey: string;
3911
3928
  };
3929
+ MovePreviewRequest: {
3930
+ /**
3931
+ * Format: uuid
3932
+ * @description Workspace member (users.id) being moved in the chart
3933
+ */
3934
+ memberId: string;
3935
+ /**
3936
+ * Format: uuid
3937
+ * @description The member (users.id) the dragged member should report to after the move
3938
+ */
3939
+ newManagerUserId: string;
3940
+ };
3912
3941
  SubmitInteractiveTaskResponse: {
3913
3942
  executionId: string;
3914
3943
  /** @enum {string} */
@@ -7050,6 +7079,28 @@ export interface operations {
7050
7079
  };
7051
7080
  };
7052
7081
  };
7082
+ previewReportingMove: {
7083
+ parameters: {
7084
+ query?: never;
7085
+ header?: never;
7086
+ path?: never;
7087
+ cookie?: never;
7088
+ };
7089
+ requestBody: {
7090
+ content: {
7091
+ "application/json": components["schemas"]["MovePreviewRequest"];
7092
+ };
7093
+ };
7094
+ responses: {
7095
+ /** @description Transformation preview plus the semantic.transform execution intent (mutations) the app confirms */
7096
+ 200: {
7097
+ headers: {
7098
+ [name: string]: unknown;
7099
+ };
7100
+ content?: never;
7101
+ };
7102
+ };
7103
+ };
7053
7104
  submitInteractiveTask: {
7054
7105
  parameters: {
7055
7106
  query?: never;
package/src/autotune.ts CHANGED
@@ -86,9 +86,6 @@ export type ControllerInput = {
86
86
  export type ControllerOutput<Min extends number, Max extends number> = {
87
87
  readonly value: Bounded<Min, Max>;
88
88
  readonly reason:
89
- | "deadband-hold"
90
- | "window-hold"
91
- | "adjusted"
92
- | "bounded-clamp";
89
+ "deadband-hold" | "window-hold" | "adjusted" | "bounded-clamp";
93
90
  readonly adjustmentWindowId: string;
94
91
  };
@@ -130,11 +130,7 @@ export type CIGuardPlan = {
130
130
  * Note: Transition validation logic lives in CI repo, not here.
131
131
  */
132
132
  export type CIEnvelopeStatus =
133
- | "proposed"
134
- | "validated"
135
- | "blocked"
136
- | "executing"
137
- | "finalized";
133
+ "proposed" | "validated" | "blocked" | "executing" | "finalized";
138
134
 
139
135
  // =============================================================================
140
136
  // Outcome Types
@@ -6,11 +6,7 @@
6
6
  */
7
7
 
8
8
  export type GuardTier =
9
- | "structural"
10
- | "behavioral"
11
- | "invariants"
12
- | "evolution"
13
- | "meta";
9
+ "structural" | "behavioral" | "invariants" | "evolution" | "meta";
14
10
 
15
11
  /**
16
12
  * Minimal structural shape of a guard orchestrator result payload.
@@ -126,6 +126,7 @@ export const openApiRoutes = {
126
126
  '/api/orgs/{orgId}/level-config': ['GET', 'PUT'],
127
127
  '/api/rbac/roles': ['GET'],
128
128
  '/api/reporting/bulk-reparent': ['POST'],
129
+ '/api/reporting/move/preview': ['POST'],
129
130
  '/api/scope/check': ['GET'],
130
131
  '/api/scope/check-batch': ['POST'],
131
132
  '/api/shared/{token}': ['GET'],
@@ -59,11 +59,7 @@ export interface GuardTierResult {
59
59
  * - meta: guards that protect the guard system itself (coverage correctness)
60
60
  */
61
61
  export type GuardTier =
62
- | "structural"
63
- | "behavioral"
64
- | "invariants"
65
- | "evolution"
66
- | "meta";
62
+ "structural" | "behavioral" | "invariants" | "evolution" | "meta";
67
63
 
68
64
  /**
69
65
  * Full guard results, organized by tier.
package/src/index.ts CHANGED
@@ -445,9 +445,13 @@ export {
445
445
  OrgIntentSchema,
446
446
  FactMutationSchema,
447
447
  TransformationOriginSchema,
448
+ ProvenanceSchema,
449
+ ExecutionContextSchema,
448
450
  TransformationRequestSchema,
449
451
  ConstraintViolationSchema,
450
452
  RecommendationSchema,
453
+ FindingSeveritySchema,
454
+ FindingSchema,
451
455
  TransformStrategySchema,
452
456
  SelectionRationaleSchema,
453
457
  TransformationPreviewSchema,
@@ -456,9 +460,13 @@ export type {
456
460
  OrgIntent,
457
461
  FactMutation,
458
462
  TransformationOrigin,
463
+ Provenance,
464
+ ExecutionContext,
459
465
  TransformationRequest,
460
466
  ConstraintViolation,
461
467
  Recommendation,
468
+ FindingSeverity,
469
+ Finding,
462
470
  TransformStrategy,
463
471
  SelectionRationale,
464
472
  TransformationPreview,
package/src/mcp/index.ts CHANGED
@@ -27,11 +27,7 @@ export type { FailureContext, RecoveryAction } from "./failure-context";
27
27
  * 'data' conflates discovery, ingestion, and coverage.
28
28
  */
29
29
  export type ToolCategory =
30
- | "system"
31
- | "data"
32
- | "connections"
33
- | "automation"
34
- | "developer";
30
+ "system" | "data" | "connections" | "automation" | "developer";
35
31
 
36
32
  /**
37
33
  * Tool domain taxonomy.
@@ -30,10 +30,7 @@ export type ConfirmationChoice = "confirm" | "cancel";
30
30
  * 'irreversible' indicates destructive operations that cannot be undone.
31
31
  */
32
32
  export type ConfirmationRiskLevel =
33
- | "low"
34
- | "moderate"
35
- | "high"
36
- | "irreversible";
33
+ "low" | "moderate" | "high" | "irreversible";
37
34
 
38
35
  /**
39
36
  * Human-readable confirmation dialog titles for each execution kind.
@@ -33,8 +33,7 @@ import type { ConfirmationRiskLevel } from "./confirmation";
33
33
  * Mirrors the `{domain}.{verb}` naming of ExecutionKind. Extensible union.
34
34
  */
35
35
  export type InteractiveTaskKind =
36
- | "member.changeManager"
37
- | "companyMd.importContextDoc";
36
+ "member.changeManager" | "companyMd.importContextDoc";
38
37
 
39
38
  /**
40
39
  * How the frontend sources candidate values for an editable field.
@@ -34,9 +34,7 @@ export type StreamPhase = "idle" | "streaming" | "complete";
34
34
  * Lifecycle event type discriminator.
35
35
  */
36
36
  export type MessageLifecycleEventType =
37
- | "message.start"
38
- | "message.delta"
39
- | "message.complete";
37
+ "message.start" | "message.delta" | "message.complete";
40
38
 
41
39
  /**
42
40
  * Base interface for all message lifecycle events.
package/src/org/README.md CHANGED
@@ -95,6 +95,8 @@ Shared type vocabulary for organization ownership, type classification, and tran
95
95
  - `DomainVerificationMethod` _(type)_ — Method used to verify domain ownership. - 'dnstxt': DNS TXT record verification (primary method) - 'email'…
96
96
  - `EffectiveAccess` _(type)_ — Effective access for a user on a document.
97
97
  - `EvaluationStep` _(type)_ — Single step in the access evaluation trace.
98
+ - `ExecutionContext` _(type)_
99
+ - `ExecutionContextSchema` — ExecutionContext — the RUN in which an intent was produced or applied.
98
100
  - `ExecutionScope` _(type)_ — Execution scope determines whose identity is used when executing actions. - 'self': Actions execute under the…
99
101
  - `FACT_SOURCE_TIER_PRECEDENCE` — Truth hierarchy, highest precedence first.
100
102
  - `FactMutation` _(type)_
@@ -107,6 +109,10 @@ Shared type vocabulary for organization ownership, type classification, and tran
107
109
  - `FieldCapabilitySchema` — A mutable field's gate: editable, or locked with attribution (sso vs rbac).
108
110
  - `FillOpenRoleRequest` _(type)_
109
111
  - `FillOpenRoleRequestSchema` — Fill an open role by seating an existing org member (`userId`) or inviting a new person by email.
112
+ - `Finding` _(type)_
113
+ - `FindingSchema` — A single diagnostic the system emits about the org graph — the GENERAL model of which {@link…
114
+ - `FindingSeverity` _(type)_
115
+ - `FindingSeveritySchema` — Severity of a {@link FindingSchema}.
110
116
  - `HomeAssignment` _(type)_
111
117
  - `HomeAssignmentSchema` — A person's single structural home unit — at most one per person per org.
112
118
  - `HomeUnitRole` _(type)_
@@ -246,6 +252,8 @@ Shared type vocabulary for organization ownership, type classification, and tran
246
252
  - `PositionSchema`
247
253
  - `PositionStatus` _(type)_
248
254
  - `PositionStatusSchema` — Lifecycle of a seat's EXISTENCE — distinct from a hiring pipeline.
255
+ - `Provenance` _(type)_
256
+ - `ProvenanceSchema` — Provenance — WHO/WHAT generated an intent.
249
257
  - `ProviderStatus` _(type)_ — Provider-level configuration lifecycle.
250
258
  - `ProviderSuggestion` _(type)_ — MX-based provider suggestion for SSO setup.
251
259
  - `Recommendation` _(type)_
@@ -3,9 +3,12 @@ import {
3
3
  OrgIntentSchema,
4
4
  FactMutationSchema,
5
5
  TransformationOriginSchema,
6
+ ProvenanceSchema,
7
+ ExecutionContextSchema,
6
8
  TransformationRequestSchema,
7
9
  ConstraintViolationSchema,
8
10
  RecommendationSchema,
11
+ FindingSchema,
9
12
  TransformStrategySchema,
10
13
  SelectionRationaleSchema,
11
14
  TransformationPreviewSchema,
@@ -13,6 +16,9 @@ import {
13
16
 
14
17
  const REPORT = "33333333-3333-4333-8333-333333333333";
15
18
  const MANAGER = "44444444-4444-4444-8444-444444444444";
19
+ const UNIT = "55555555-5555-4555-8555-555555555555";
20
+ const PARENT_UNIT = "66666666-6666-4666-8666-666666666666";
21
+ const POSITION = "77777777-7777-4777-8777-777777777777";
16
22
 
17
23
  const setManagerIntent = {
18
24
  kind: "reporting.setManager" as const,
@@ -53,6 +59,53 @@ describe("OrgIntentSchema", () => {
53
59
  }).success,
54
60
  ).toBe(false);
55
61
  });
62
+
63
+ it("accepts a unit.setParent intent with a parent", () => {
64
+ const intent = {
65
+ kind: "unit.setParent" as const,
66
+ unitId: UNIT,
67
+ parentUnitId: PARENT_UNIT,
68
+ };
69
+ expect(OrgIntentSchema.parse(intent)).toEqual(intent);
70
+ });
71
+
72
+ it("accepts a unit.setParent intent with a null parent (unit becomes a root)", () => {
73
+ const intent = {
74
+ kind: "unit.setParent" as const,
75
+ unitId: UNIT,
76
+ parentUnitId: null,
77
+ };
78
+ expect(OrgIntentSchema.parse(intent)).toEqual(intent);
79
+ });
80
+
81
+ it("rejects a non-uuid parentUnitId", () => {
82
+ expect(
83
+ OrgIntentSchema.safeParse({
84
+ kind: "unit.setParent",
85
+ unitId: UNIT,
86
+ parentUnitId: "x",
87
+ }).success,
88
+ ).toBe(false);
89
+ });
90
+
91
+ it("accepts a position.setTitle intent", () => {
92
+ const intent = {
93
+ kind: "position.setTitle" as const,
94
+ positionId: POSITION,
95
+ title: "Staff Engineer",
96
+ };
97
+ expect(OrgIntentSchema.parse(intent)).toEqual(intent);
98
+ });
99
+
100
+ it("rejects a position.setTitle intent with an empty title", () => {
101
+ expect(
102
+ OrgIntentSchema.safeParse({
103
+ kind: "position.setTitle",
104
+ positionId: POSITION,
105
+ title: "",
106
+ }).success,
107
+ ).toBe(false);
108
+ });
56
109
  });
57
110
 
58
111
  describe("FactMutationSchema", () => {
@@ -77,6 +130,34 @@ describe("FactMutationSchema", () => {
77
130
  .success,
78
131
  ).toBe(false);
79
132
  });
133
+
134
+ it("accepts a unit.setParent mutation with a parent", () => {
135
+ const mutation = {
136
+ kind: "unit.setParent" as const,
137
+ unitId: UNIT,
138
+ parentUnitId: PARENT_UNIT,
139
+ };
140
+ expect(FactMutationSchema.parse(mutation)).toEqual(mutation);
141
+ });
142
+
143
+ it("accepts a unit.setParent mutation with a null parent (unit becomes a root)", () => {
144
+ expect(
145
+ FactMutationSchema.safeParse({
146
+ kind: "unit.setParent",
147
+ unitId: UNIT,
148
+ parentUnitId: null,
149
+ }).success,
150
+ ).toBe(true);
151
+ });
152
+
153
+ it("accepts a position.setTitle mutation", () => {
154
+ const mutation = {
155
+ kind: "position.setTitle" as const,
156
+ positionId: POSITION,
157
+ title: "Principal Engineer",
158
+ };
159
+ expect(FactMutationSchema.parse(mutation)).toEqual(mutation);
160
+ });
80
161
  });
81
162
 
82
163
  describe("TransformationOriginSchema", () => {
@@ -126,6 +207,49 @@ describe("TransformationRequestSchema", () => {
126
207
  .success,
127
208
  ).toBe(false);
128
209
  });
210
+
211
+ it("accepts an optional executionContext", () => {
212
+ const withCtx = {
213
+ ...request,
214
+ executionContext: { runId: "run-42", trigger: "scheduled_sync" },
215
+ };
216
+ expect(TransformationRequestSchema.parse(withCtx)).toEqual(withCtx);
217
+ });
218
+ });
219
+
220
+ describe("ProvenanceSchema", () => {
221
+ it("accepts an origin plus optional sourceSystem", () => {
222
+ const provenance = {
223
+ origin: "business_event" as const,
224
+ sourceSystem: "hris",
225
+ };
226
+ expect(ProvenanceSchema.parse(provenance)).toEqual(provenance);
227
+ });
228
+
229
+ it("accepts an origin without a sourceSystem", () => {
230
+ expect(ProvenanceSchema.safeParse({ origin: "user_intent" }).success).toBe(
231
+ true,
232
+ );
233
+ });
234
+
235
+ it("rejects an unknown origin", () => {
236
+ expect(ProvenanceSchema.safeParse({ origin: "robot" }).success).toBe(false);
237
+ });
238
+ });
239
+
240
+ describe("ExecutionContextSchema", () => {
241
+ it("accepts runId and trigger", () => {
242
+ const ctx = { runId: "run-1", trigger: "webhook" };
243
+ expect(ExecutionContextSchema.parse(ctx)).toEqual(ctx);
244
+ });
245
+
246
+ it("accepts an empty context (both fields optional)", () => {
247
+ expect(ExecutionContextSchema.safeParse({}).success).toBe(true);
248
+ });
249
+
250
+ it("rejects a non-string runId", () => {
251
+ expect(ExecutionContextSchema.safeParse({ runId: 42 }).success).toBe(false);
252
+ });
129
253
  });
130
254
 
131
255
  describe("ConstraintViolationSchema", () => {
@@ -156,6 +280,48 @@ describe("ConstraintViolationSchema", () => {
156
280
  });
157
281
  });
158
282
 
283
+ describe("FindingSchema", () => {
284
+ it("accepts each severity level", () => {
285
+ for (const severity of ["error", "warning", "info"] as const) {
286
+ expect(
287
+ FindingSchema.safeParse({ code: "topology.ownerless", severity })
288
+ .success,
289
+ ).toBe(true);
290
+ }
291
+ });
292
+
293
+ it("accepts optional detail and recommendation", () => {
294
+ const finding = {
295
+ code: "reporting.cycle",
296
+ severity: "info" as const,
297
+ subject: REPORT,
298
+ detail: "This seat participates in a reporting loop.",
299
+ recommendation: {
300
+ label: "Move report to new manager",
301
+ mutations: [setManagerMutation],
302
+ cost: 1,
303
+ explanation: ["Breaks the cycle by re-parenting the report."],
304
+ },
305
+ };
306
+ expect(FindingSchema.parse(finding)).toEqual(finding);
307
+ });
308
+
309
+ it("rejects an unknown severity", () => {
310
+ expect(
311
+ FindingSchema.safeParse({ code: "c", severity: "fatal" }).success,
312
+ ).toBe(false);
313
+ });
314
+
315
+ it("parses every ConstraintViolation value (structural superset)", () => {
316
+ const violation = ConstraintViolationSchema.parse({
317
+ code: "reporting.cycle",
318
+ severity: "error",
319
+ subject: REPORT,
320
+ });
321
+ expect(FindingSchema.parse(violation)).toEqual(violation);
322
+ });
323
+ });
324
+
159
325
  describe("RecommendationSchema / TransformStrategySchema", () => {
160
326
  const recommendation = {
161
327
  label: "Reassign report",
@@ -61,10 +61,7 @@ export type CompanyMdDiscoverabilityPolicy = "org" | "owners";
61
61
  * not an omission.
62
62
  */
63
63
  export type CompanyMdClassification =
64
- | "public"
65
- | "internal"
66
- | "confidential"
67
- | "highly_confidential";
64
+ "public" | "internal" | "confidential" | "highly_confidential";
68
65
 
69
66
  /**
70
67
  * @deprecated Since 1.12.0. `CompanyMdDocLevel` is superseded by `CompanyMdDocKind` + `depth`.
package/src/org/index.ts CHANGED
@@ -157,9 +157,13 @@ export {
157
157
  OrgIntentSchema,
158
158
  FactMutationSchema,
159
159
  TransformationOriginSchema,
160
+ ProvenanceSchema,
161
+ ExecutionContextSchema,
160
162
  TransformationRequestSchema,
161
163
  ConstraintViolationSchema,
162
164
  RecommendationSchema,
165
+ FindingSeveritySchema,
166
+ FindingSchema,
163
167
  TransformStrategySchema,
164
168
  SelectionRationaleSchema,
165
169
  TransformationPreviewSchema,
@@ -168,9 +172,13 @@ export type {
168
172
  OrgIntent,
169
173
  FactMutation,
170
174
  TransformationOrigin,
175
+ Provenance,
176
+ ExecutionContext,
171
177
  TransformationRequest,
172
178
  ConstraintViolation,
173
179
  Recommendation,
180
+ FindingSeverity,
181
+ Finding,
174
182
  TransformStrategy,
175
183
  SelectionRationale,
176
184
  TransformationPreview,
@@ -23,25 +23,18 @@
23
23
 
24
24
  /** Semantic classification — projection-only, never drives traversal. */
25
25
  export type OrgUnitClassification =
26
- | "execution_unit"
27
- | "org_container"
28
- | "custom";
26
+ "execution_unit" | "org_container" | "custom";
29
27
 
30
28
  /** External directory reconciliation policy for a unit. */
31
29
  export type OrgUnitSyncMode =
32
- | "manual_only"
33
- | "synced_readonly"
34
- | "synced_with_overrides";
30
+ "manual_only" | "synced_readonly" | "synced_with_overrides";
35
31
 
36
32
  /** Default visibility inherited by new docs under this unit. */
37
33
  export type OrgUnitVisibility = "public" | "private" | "restricted";
38
34
 
39
35
  /** Edge classification for `org_unit_relationships`. Graph-only; never structural. */
40
36
  export type OrgUnitRelationshipType =
41
- | "collaborates_with"
42
- | "reports_to"
43
- | "depends_on"
44
- | "custom";
37
+ "collaborates_with" | "reports_to" | "depends_on" | "custom";
45
38
 
46
39
  /** Edge role — refines the collaboration nature on a graph edge. */
47
40
  export type OrgUnitRelationshipRole = "owner" | "participant" | "supporting";
@@ -51,10 +44,7 @@ export type OrgUnitMembershipStatus = "active" | "pending" | "removed";
51
44
 
52
45
  /** External directory origin of a membership row. */
53
46
  export type OrgUnitMembershipSource =
54
- | "manual"
55
- | "google_groups"
56
- | "scim"
57
- | "hris";
47
+ "manual" | "google_groups" | "scim" | "hris";
58
48
 
59
49
  /**
60
50
  * Hiring lifecycle of an open role — a persisted placeholder for the person
@@ -12,10 +12,7 @@ export type AccessLevel = "viewer" | "commenter" | "editor";
12
12
  * - org_edit: All org members can edit (does NOT imply canShare)
13
13
  */
14
14
  export type SharePolicy =
15
- | "restricted"
16
- | "org_read"
17
- | "org_comment"
18
- | "org_edit";
15
+ "restricted" | "org_read" | "org_comment" | "org_edit";
19
16
 
20
17
  export interface AclEntry {
21
18
  readonly principalType: "user" | "unit";
@@ -11,9 +11,12 @@
11
11
  *
12
12
  * Layers, smallest → largest:
13
13
  * - {@link OrgIntentSchema} — WHAT the operator wants, as a discriminated union
14
- * on `kind`. Today only `reporting.setManager`; shaped so future kinds
14
+ * on `kind`: `reporting.setManager` (point a seat at a manager),
15
+ * `unit.setParent` (re-parent an org unit, null parent → root), and
16
+ * `position.setTitle` (relabel a seat). Shaped so further kinds
15
17
  * (home.setUnit, unit.move, position.open, position.close) append without
16
- * breaking existing consumers.
18
+ * breaking existing consumers. Any consumer that exhaustively switches on
19
+ * `kind` (an `assertNever` default arm) MUST add an arm for each new kind.
17
20
  * - {@link FactMutationSchema} — the typed ATOMIC fact change the engine applies
18
21
  * to the live graph. Distinct from intent: an intent may expand into several
19
22
  * mutations, and a mutation can express un-setting (a null target) that an
@@ -47,12 +50,30 @@ import { z } from "zod";
47
50
 
48
51
  /**
49
52
  * A requested org-graph change expressed as intent. Discriminated on `kind`, a
50
- * dotted string naming the affected relation and verb. Today the only variant
51
- * is `reporting.setManager` — point a reporting seat at a (non-null) manager
52
- * seat. Future kinds (`home.setUnit`, `unit.move`, `position.open`,
53
- * `position.close`) append as new union members without breaking consumers.
53
+ * dotted string naming the affected relation and verb:
54
+ * - `reporting.setManager` — point a reporting seat at a (non-null) manager seat.
55
+ * - `unit.setParent` re-parent an org unit; `parentUnitId` null means the unit
56
+ * becomes a root (level-1) unit, which is a meaningful intent, not an un-set.
57
+ * - `position.setTitle` — relabel a seat's human-facing title.
58
+ *
59
+ * These are the intents the reasoning pipeline's NestOrgUnit (→ `unit.setParent`)
60
+ * and NormalizeRole (→ `position.setTitle`) inferences resolve into, after
61
+ * external→canonical id resolution, so every id here is a canonical uuid.
62
+ *
63
+ * Further kinds (`home.setUnit`, `unit.move`, `position.open`, `position.close`)
64
+ * append as new union members without breaking consumers. Appending is
65
+ * backward-compatible for producers and for consumers that do not exhaustively
66
+ * switch on `kind`; any consumer that DOES exhaustively switch (an `assertNever`
67
+ * default arm on `OrgIntent.kind`) MUST add an arm for each new kind.
54
68
  */
55
69
  export const OrgIntentSchema = z.discriminatedUnion("kind", [
70
+ // reporting.setManager — UNCHANGED from the shipped PRD-00823 vocabulary. This
71
+ // PRD (00827) is purely additive: it appends unit.setParent and
72
+ // position.setTitle below and touches neither this arm's fields nor its types.
73
+ // Its `managerPositionId` is intentionally NON-NULLABLE here (an intent points
74
+ // a seat AT a manager); the corresponding FactMutation arm keeps it nullable so
75
+ // a mutation can un-set the edge. That Intent-vs-Mutation asymmetry is the
76
+ // original by-design shape, not something this PRD introduced or altered.
56
77
  z.object({
57
78
  kind: z.literal("reporting.setManager"),
58
79
  /** The reporting (subordinate) seat whose manager is being set. */
@@ -60,6 +81,20 @@ export const OrgIntentSchema = z.discriminatedUnion("kind", [
60
81
  /** The manager seat to report to. Non-null on an intent (un-setting is a mutation concern). */
61
82
  managerPositionId: z.string().uuid(),
62
83
  }),
84
+ z.object({
85
+ kind: z.literal("unit.setParent"),
86
+ /** The org unit being re-parented. */
87
+ unitId: z.string().uuid(),
88
+ /** The new parent unit, or `null` to make the unit a root (level-1) unit. */
89
+ parentUnitId: z.string().uuid().nullable(),
90
+ }),
91
+ z.object({
92
+ kind: z.literal("position.setTitle"),
93
+ /** The seat whose title is being set. */
94
+ positionId: z.string().uuid(),
95
+ /** The new human-facing title for the seat (non-empty, matching PositionSchema.title). */
96
+ title: z.string().min(1),
97
+ }),
63
98
  ]);
64
99
  export type OrgIntent = z.infer<typeof OrgIntentSchema>;
65
100
 
@@ -72,8 +107,19 @@ export type OrgIntent = z.infer<typeof OrgIntentSchema>;
72
107
  * Discriminated on `kind`, mirroring {@link OrgIntentSchema}. Unlike an intent,
73
108
  * a mutation can express UN-SETTING: `reporting.setManager` with a null
74
109
  * `managerPositionId` clears the seat's manager edge entirely.
110
+ *
111
+ * `unit.setParent` and `position.setTitle` mirror their intent counterparts.
112
+ * `unit.setParent.parentUnitId` is nullable here for the SAME reason it is on the
113
+ * intent — null means "root", a meaningful target — not because a mutation
114
+ * un-sets it. Any consumer that exhaustively switches on `FactMutation.kind`
115
+ * (an `assertNever` default arm) MUST add an arm for each new kind.
75
116
  */
76
117
  export const FactMutationSchema = z.discriminatedUnion("kind", [
118
+ // reporting.setManager — UNCHANGED from the shipped PRD-00823 vocabulary; this
119
+ // PRD (00827) only appends unit.setParent and position.setTitle below. Its
120
+ // `managerPositionId` stays NULLABLE (a mutation can un-set the edge), which is
121
+ // deliberately broader than the OrgIntent arm's non-nullable field — the
122
+ // original, intentional Intent-vs-Mutation asymmetry, preserved as-is.
77
123
  z.object({
78
124
  kind: z.literal("reporting.setManager"),
79
125
  /** The reporting (subordinate) seat whose manager edge is being written. */
@@ -81,6 +127,20 @@ export const FactMutationSchema = z.discriminatedUnion("kind", [
81
127
  /** The new manager seat, or `null` to un-set (clear) the manager edge. */
82
128
  managerPositionId: z.string().uuid().nullable(),
83
129
  }),
130
+ z.object({
131
+ kind: z.literal("unit.setParent"),
132
+ /** The org unit whose parent edge is being written. */
133
+ unitId: z.string().uuid(),
134
+ /** The new parent unit, or `null` to make the unit a root (level-1) unit. */
135
+ parentUnitId: z.string().uuid().nullable(),
136
+ }),
137
+ z.object({
138
+ kind: z.literal("position.setTitle"),
139
+ /** The seat whose title is being written. */
140
+ positionId: z.string().uuid(),
141
+ /** The new human-facing title for the seat (non-empty, matching PositionSchema.title). */
142
+ title: z.string().min(1),
143
+ }),
84
144
  ]);
85
145
  export type FactMutation = z.infer<typeof FactMutationSchema>;
86
146
 
@@ -100,6 +160,37 @@ export const TransformationOriginSchema = z.enum([
100
160
  ]);
101
161
  export type TransformationOrigin = z.infer<typeof TransformationOriginSchema>;
102
162
 
163
+ /**
164
+ * Provenance — WHO/WHAT generated an intent. Distinct from motivation (the
165
+ * free-text {@link TransformationRequestSchema} `reason`, which records *why*)
166
+ * and from {@link ExecutionContextSchema} (the *run* the intent happened in).
167
+ * `origin` reuses the closed {@link TransformationOriginSchema} enum; the
168
+ * optional `sourceSystem` names the concrete system or actor that emitted it
169
+ * (e.g. an HRIS connector id) for machine-originated intents.
170
+ */
171
+ export const ProvenanceSchema = z.object({
172
+ /** Provenance class of the intent — reuses the closed origin enum. */
173
+ origin: TransformationOriginSchema,
174
+ /** Optional concrete system/actor that emitted the intent. */
175
+ sourceSystem: z.string().optional(),
176
+ });
177
+ export type Provenance = z.infer<typeof ProvenanceSchema>;
178
+
179
+ /**
180
+ * ExecutionContext — the RUN in which an intent was produced or applied. Kept
181
+ * separate from {@link ProvenanceSchema} (who/what generated it) and from
182
+ * motivation ({@link TransformationRequestSchema} `reason`). Both fields are
183
+ * optional: `runId` correlates the intent to a batch/job, `trigger` names what
184
+ * kicked off that run (e.g. a webhook or scheduled sync).
185
+ */
186
+ export const ExecutionContextSchema = z.object({
187
+ /** Optional id correlating the intent to a batch/job run. */
188
+ runId: z.string().optional(),
189
+ /** Optional label for what kicked off the run (e.g. "scheduled_sync"). */
190
+ trigger: z.string().optional(),
191
+ });
192
+ export type ExecutionContext = z.infer<typeof ExecutionContextSchema>;
193
+
103
194
  export const TransformationRequestSchema = z.object({
104
195
  /** Provenance class of the request — drives how much scrutiny it gets. */
105
196
  origin: TransformationOriginSchema,
@@ -112,6 +203,12 @@ export const TransformationRequestSchema = z.object({
112
203
  * change was requested, NOT who may make it (ADR-CTRL-180).
113
204
  */
114
205
  reason: z.string().max(2000).nullable().optional(),
206
+ /**
207
+ * Optional {@link ExecutionContextSchema} — the run this intent was produced
208
+ * in. Additive and distinct from `origin` (provenance class) and `reason`
209
+ * (motivation); absent for ad-hoc requests. (ADR-CONTRACTS-074)
210
+ */
211
+ executionContext: ExecutionContextSchema.optional(),
115
212
  });
116
213
  export type TransformationRequest = z.infer<typeof TransformationRequestSchema>;
117
214
 
@@ -153,6 +250,52 @@ export const RecommendationSchema = z.object({
153
250
  });
154
251
  export type Recommendation = z.infer<typeof RecommendationSchema>;
155
252
 
253
+ // ---------------------------------------------------------------------------
254
+ // Finding — the general diagnostic model (superset of ConstraintViolation)
255
+ // ---------------------------------------------------------------------------
256
+
257
+ /**
258
+ * Severity of a {@link FindingSchema}. Broader than
259
+ * {@link ConstraintViolationSchema}'s `error | warning`: `info` is the added
260
+ * non-blocking, non-cautionary level a diagnostic-only (Observation) producer
261
+ * emits. `error` blocks a change; `warning` allows it but flags a caution;
262
+ * `info` is purely descriptive.
263
+ */
264
+ export const FindingSeveritySchema = z.enum(["error", "warning", "info"]);
265
+ export type FindingSeverity = z.infer<typeof FindingSeveritySchema>;
266
+
267
+ /**
268
+ * A single diagnostic the system emits about the org graph — the GENERAL model
269
+ * of which {@link ConstraintViolationSchema} is the error-severity subtype.
270
+ *
271
+ * Finding is a structural SUPERSET of ConstraintViolation: it carries the same
272
+ * `code` / `severity` / `subject` fields, widens `severity` to include `info`
273
+ * (via {@link FindingSeveritySchema}), and adds an optional human-readable
274
+ * `detail` and an optional {@link RecommendationSchema} pointing at a suggested
275
+ * fix. Every ConstraintViolation value therefore parses as a Finding.
276
+ *
277
+ * The Constraint Engine emits the error-severity Findings today, modeled as
278
+ * {@link ConstraintViolationSchema} (which stays unchanged). A future
279
+ * Observation Engine produces the broader `info`/`warning` Findings — advisory
280
+ * diagnostics that describe the graph without blocking a change.
281
+ *
282
+ * @see ConstraintViolationSchema — the unchanged error-severity subtype
283
+ * @see ADR-CONTRACTS-074 for the org mutation / diagnostic vocabulary decision
284
+ */
285
+ export const FindingSchema = z.object({
286
+ /** Open string naming the finding (e.g. "reporting.cycle", "span.wide"). */
287
+ code: z.string().min(1),
288
+ /** `error` blocks; `warning` cautions; `info` is purely descriptive. */
289
+ severity: FindingSeveritySchema,
290
+ /** Optional id of the entity the finding concerns. */
291
+ subject: z.string().optional(),
292
+ /** Optional human-readable elaboration of the finding. */
293
+ detail: z.string().optional(),
294
+ /** Optional suggested fix for the finding. */
295
+ recommendation: RecommendationSchema.optional(),
296
+ });
297
+ export type Finding = z.infer<typeof FindingSchema>;
298
+
156
299
  // ---------------------------------------------------------------------------
157
300
  // TransformStrategy — a scored set of recommendations
158
301
  // ---------------------------------------------------------------------------
package/src/org/types.ts CHANGED
@@ -292,10 +292,7 @@ export interface OidcValidationResult {
292
292
  authorizationEndpoint?: string;
293
293
  error?: string;
294
294
  errorCode?:
295
- | "UNREACHABLE"
296
- | "INVALID_DOCUMENT"
297
- | "MISSING_FIELDS"
298
- | "SSRF_BLOCKED";
295
+ "UNREACHABLE" | "INVALID_DOCUMENT" | "MISSING_FIELDS" | "SSRF_BLOCKED";
299
296
  }
300
297
 
301
298
  /** Initiation payload for a test SSO login attempt. */
package/src/ralph/prd.ts CHANGED
@@ -48,11 +48,7 @@ export type AllowedRepo = (typeof REPO_PRECEDENCE)[number];
48
48
  * Used for prioritization and verification strategy selection.
49
49
  */
50
50
  export type RalphPRDCategory =
51
- | "functional"
52
- | "architectural"
53
- | "integration"
54
- | "polish"
55
- | "validation";
51
+ "functional" | "architectural" | "integration" | "polish" | "validation";
56
52
 
57
53
  /**
58
54
  * Priority level for feature ordering.
@@ -26,13 +26,7 @@ import type { DiagramNode, FlowStage } from "./index";
26
26
  * Add new repos here as the system grows.
27
27
  */
28
28
  export type RepoId =
29
- | "backend"
30
- | "edge"
31
- | "app"
32
- | "site"
33
- | "ci"
34
- | "contracts"
35
- | "control";
29
+ "backend" | "edge" | "app" | "site" | "ci" | "contracts" | "control";
36
30
 
37
31
  /**
38
32
  * Semantic invariants for a capability.
@@ -59,12 +59,7 @@ export type DiagramNodeKind = "repo" | "group" | "feature" | "artifact";
59
59
  * - feedback: Bidirectional loop
60
60
  */
61
61
  export type DiagramEdgeRelation =
62
- | "invokes"
63
- | "emits"
64
- | "authorizes"
65
- | "reads"
66
- | "depends_on"
67
- | "feedback";
62
+ "invokes" | "emits" | "authorizes" | "reads" | "depends_on" | "feedback";
68
63
 
69
64
  /**
70
65
  * Edge direction semantics.