@company-semantics/contracts 91.0.0 → 91.1.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": "91.0.0",
3
+ "version": "91.1.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 = 'b2a6eec5dea4' as const;
3
- export const SPEC_HASH_FULL = 'b2a6eec5dea4f72290740a1cf16608a826ece2e3654ba64d2bc5e3794d571809' as const;
2
+ export const SPEC_HASH = '2d074ecfa53c' as const;
3
+ export const SPEC_HASH_FULL = '2d074ecfa53c0f377dfda06d9a46167a9cfc77eb3878e2cd71b8a04149c700b7' as const;
@@ -1566,6 +1566,23 @@ export interface paths {
1566
1566
  patch?: never;
1567
1567
  trace?: never;
1568
1568
  };
1569
+ "/api/integrations/bamboohr/sync": {
1570
+ parameters: {
1571
+ query?: never;
1572
+ header?: never;
1573
+ path?: never;
1574
+ cookie?: never;
1575
+ };
1576
+ get?: never;
1577
+ put?: never;
1578
+ /** Run a BambooHR sync now (returns the run summary) */
1579
+ post: operations["syncBambooHrNow"];
1580
+ delete?: never;
1581
+ options?: never;
1582
+ head?: never;
1583
+ patch?: never;
1584
+ trace?: never;
1585
+ };
1569
1586
  "/api/org/reconciliation": {
1570
1587
  parameters: {
1571
1588
  query?: never;
@@ -1666,6 +1683,26 @@ export interface paths {
1666
1683
  patch?: never;
1667
1684
  trace?: never;
1668
1685
  };
1686
+ "/api/org/structure-inference/runs": {
1687
+ parameters: {
1688
+ query?: never;
1689
+ header?: never;
1690
+ path?: never;
1691
+ cookie?: never;
1692
+ };
1693
+ get?: never;
1694
+ put?: never;
1695
+ /**
1696
+ * Launch one org-structure reanalyze workflow and its inference run
1697
+ * @description Mints an org-structure/reanalyze analysis workflow, opens its pending run and schedules the job, all in one transaction (ADR-BE-606, ADR-BE-703): the snapshot, the model calls and the deterministic gate execute on the worker, and the outcome is read back by polling GET /api/org/structure-inference/proposal, whose run field carries this run’s id and status, or GET /api/analysis-workflows/{workflowId} for the workflow. At most one inference is in flight per org — a second POST joins the run already under way. Explicitly operator-triggered: there is no schedule and no sync hook.
1698
+ */
1699
+ post: operations["runOrgStructureInference"];
1700
+ delete?: never;
1701
+ options?: never;
1702
+ head?: never;
1703
+ patch?: never;
1704
+ trace?: never;
1705
+ };
1669
1706
  "/api/analysis-workflows/{id}": {
1670
1707
  parameters: {
1671
1708
  query?: never;
@@ -1726,6 +1763,26 @@ export interface paths {
1726
1763
  patch?: never;
1727
1764
  trace?: never;
1728
1765
  };
1766
+ "/api/affordances/state": {
1767
+ parameters: {
1768
+ query?: never;
1769
+ header?: never;
1770
+ path?: never;
1771
+ cookie?: never;
1772
+ };
1773
+ /**
1774
+ * Read the live policy and availability of offered affordances
1775
+ * @description For exactly the affordance ids named in `ids` (the ids a settled outcome offered — never the catalog), whether the viewer is shown each one (`policy`, from the same capability derivation as GET /api/capabilities) and whether the org’s current state permits it (`availability`). Entries come back in request order, deduped. NON-AUTHORITATIVE: this answer is advisory UX state; the route each affordance binds to stays the only enforcement and may still refuse. Org-scoped: requires active membership of the session org, because availability discloses org state.
1776
+ */
1777
+ get: operations["getAffordanceState"];
1778
+ put?: never;
1779
+ post?: never;
1780
+ delete?: never;
1781
+ options?: never;
1782
+ head?: never;
1783
+ patch?: never;
1784
+ trace?: never;
1785
+ };
1729
1786
  "/api/company-md/tree": {
1730
1787
  parameters: {
1731
1788
  query?: never;
@@ -6808,6 +6865,34 @@ export interface components {
6808
6865
  requiredScope?: string;
6809
6866
  };
6810
6867
  };
6868
+ integrations?: {
6869
+ manage: {
6870
+ /** @enum {string} */
6871
+ state: "allowed" | "blocked";
6872
+ requiredScope?: string;
6873
+ };
6874
+ };
6875
+ sourceSync?: {
6876
+ manage: {
6877
+ /** @enum {string} */
6878
+ state: "allowed" | "blocked";
6879
+ requiredScope?: string;
6880
+ };
6881
+ };
6882
+ billing?: {
6883
+ view: {
6884
+ /** @enum {string} */
6885
+ state: "allowed" | "blocked";
6886
+ requiredScope?: string;
6887
+ };
6888
+ };
6889
+ orgStructure?: {
6890
+ analyze: {
6891
+ /** @enum {string} */
6892
+ state: "allowed" | "blocked";
6893
+ requiredScope?: string;
6894
+ };
6895
+ };
6811
6896
  };
6812
6897
  chatMentions: boolean;
6813
6898
  chatMultiParty: boolean;
@@ -6816,6 +6901,17 @@ export interface components {
6816
6901
  reason: ("BILLING_INACTIVE" | "SEAT_SUBSCRIPTION_PAST_DUE" | "AI_BALANCE_EXHAUSTED" | "AI_BUDGET_EXHAUSTED" | "BYO_KEY_INVALID") | null;
6817
6902
  };
6818
6903
  };
6904
+ BambooHrSyncRunResponse: {
6905
+ /** Format: uuid */
6906
+ runId: string;
6907
+ processed: number;
6908
+ changed: number;
6909
+ skipped: number;
6910
+ conflicts: number;
6911
+ failures: number;
6912
+ sinceCursor: string | null;
6913
+ status: string;
6914
+ };
6819
6915
  ResolveOrgDivergenceRequest: {
6820
6916
  /** @constant */
6821
6917
  resolution: "acknowledged";
@@ -6993,6 +7089,23 @@ export interface components {
6993
7089
  fingerprint: string;
6994
7090
  note?: string;
6995
7091
  };
7092
+ RunStructureInferenceRefusal: {
7093
+ /** @constant */
7094
+ ran: false;
7095
+ /** @enum {string} */
7096
+ reason: "no_sync_run" | "no_persons" | "reporting_not_keyed";
7097
+ message: string;
7098
+ };
7099
+ RunStructureInferenceAccepted: {
7100
+ /** @constant */
7101
+ ran: true;
7102
+ /** Format: uuid */
7103
+ runId: string;
7104
+ workflowId: string | null;
7105
+ /** @enum {string} */
7106
+ status: "pending" | "running";
7107
+ alreadyRunning: boolean;
7108
+ };
6996
7109
  AnalysisWorkflowProjectionResponse: {
6997
7110
  /** Format: uuid */
6998
7111
  id: string;
@@ -7003,7 +7116,180 @@ export interface components {
7003
7116
  completedAt: string | null;
7004
7117
  progress: unknown | null;
7005
7118
  result: unknown | null;
7006
- outcome: unknown | null;
7119
+ outcome: ({
7120
+ /** @constant */
7121
+ workflowType: "source-sync/discover";
7122
+ executionEpoch: number;
7123
+ /** @enum {string} */
7124
+ kind: "produced" | "empty" | "partial" | "stopped" | "failed" | "abandoned" | "unknown";
7125
+ /** @enum {string} */
7126
+ reason: "candidates_found" | "nothing_found" | "nothing_relevant" | "budget_exhausted" | "file_ceiling" | "stopped_by_user" | "plan_coverage_refused" | "billing_refused" | "drive_auth" | "drive_unavailable" | "no_connection" | "source_sync_off" | "internal_error" | "abandoned" | "unrecognized_terminal_state";
7127
+ facts: {
7128
+ probesTotal: number;
7129
+ probesDone: number;
7130
+ filesSeen: number;
7131
+ filesShown: number;
7132
+ filesHidden: number;
7133
+ filesDropped: number;
7134
+ coveragePercent: number | null;
7135
+ } | null;
7136
+ disposition: {
7137
+ /** @constant */
7138
+ kind: "review";
7139
+ /** @enum {string} */
7140
+ requestType: "org-structure/review" | "source-sync/candidates" | "source-sync/synthesis";
7141
+ requestId: string;
7142
+ } | {
7143
+ /** @constant */
7144
+ kind: "narrate";
7145
+ };
7146
+ affordances: ({
7147
+ /** @constant */
7148
+ id: "sourceSync.findFiles";
7149
+ } | {
7150
+ /** @constant */
7151
+ id: "integrations.connectGoogleDrive";
7152
+ } | {
7153
+ /** @constant */
7154
+ id: "sourceSync.enable";
7155
+ } | {
7156
+ /** @constant */
7157
+ id: "billing.open";
7158
+ } | {
7159
+ /** @constant */
7160
+ id: "orgStructure.reanalyze";
7161
+ } | {
7162
+ /** @constant */
7163
+ id: "hris.resyncDirectory";
7164
+ })[];
7165
+ } | {
7166
+ /** @constant */
7167
+ workflowType: "source-sync/synthesize";
7168
+ executionEpoch: number;
7169
+ /** @enum {string} */
7170
+ kind: "produced" | "empty" | "partial" | "stopped" | "failed" | "abandoned" | "unknown";
7171
+ /** @enum {string} */
7172
+ reason: "updates_ready" | "updates_applied" | "stopped_by_user" | "paused_billing" | "synthesis_incomplete" | "approval_mismatch" | "landing_refused" | "standing_order_refused" | "no_connection" | "source_sync_off" | "internal_error" | "abandoned" | "unrecognized_terminal_state";
7173
+ facts: {
7174
+ scopeCount: number;
7175
+ violationCount: number;
7176
+ } | null;
7177
+ disposition: {
7178
+ /** @constant */
7179
+ kind: "review";
7180
+ /** @enum {string} */
7181
+ requestType: "org-structure/review" | "source-sync/candidates" | "source-sync/synthesis";
7182
+ requestId: string;
7183
+ } | {
7184
+ /** @constant */
7185
+ kind: "narrate";
7186
+ };
7187
+ affordances: ({
7188
+ /** @constant */
7189
+ id: "sourceSync.findFiles";
7190
+ } | {
7191
+ /** @constant */
7192
+ id: "integrations.connectGoogleDrive";
7193
+ } | {
7194
+ /** @constant */
7195
+ id: "sourceSync.enable";
7196
+ } | {
7197
+ /** @constant */
7198
+ id: "billing.open";
7199
+ } | {
7200
+ /** @constant */
7201
+ id: "orgStructure.reanalyze";
7202
+ } | {
7203
+ /** @constant */
7204
+ id: "hris.resyncDirectory";
7205
+ })[];
7206
+ } | {
7207
+ /** @constant */
7208
+ workflowType: "org-structure/hris-connect";
7209
+ executionEpoch: number;
7210
+ /** @enum {string} */
7211
+ kind: "produced" | "empty" | "partial" | "stopped" | "failed" | "abandoned" | "unknown";
7212
+ /** @enum {string} */
7213
+ reason: "proposal_ready" | "directory_synced" | "analysis_already_running" | "proposal_blocked" | "no_sync_run" | "no_persons" | "reporting_not_keyed" | "stopped_by_user" | "sync_failed" | "snapshot_too_large" | "model_failed" | "run_too_large" | "placement_failed" | "internal_error" | "abandoned" | "unrecognized_terminal_state";
7214
+ facts: {
7215
+ departmentCount: number;
7216
+ teamCount: number;
7217
+ placedCount: number;
7218
+ reviewItemCount: number;
7219
+ } | null;
7220
+ disposition: {
7221
+ /** @constant */
7222
+ kind: "review";
7223
+ /** @enum {string} */
7224
+ requestType: "org-structure/review" | "source-sync/candidates" | "source-sync/synthesis";
7225
+ requestId: string;
7226
+ } | {
7227
+ /** @constant */
7228
+ kind: "narrate";
7229
+ };
7230
+ affordances: ({
7231
+ /** @constant */
7232
+ id: "sourceSync.findFiles";
7233
+ } | {
7234
+ /** @constant */
7235
+ id: "integrations.connectGoogleDrive";
7236
+ } | {
7237
+ /** @constant */
7238
+ id: "sourceSync.enable";
7239
+ } | {
7240
+ /** @constant */
7241
+ id: "billing.open";
7242
+ } | {
7243
+ /** @constant */
7244
+ id: "orgStructure.reanalyze";
7245
+ } | {
7246
+ /** @constant */
7247
+ id: "hris.resyncDirectory";
7248
+ })[];
7249
+ } | {
7250
+ /** @constant */
7251
+ workflowType: "org-structure/reanalyze";
7252
+ executionEpoch: number;
7253
+ /** @enum {string} */
7254
+ kind: "produced" | "empty" | "partial" | "stopped" | "failed" | "abandoned" | "unknown";
7255
+ /** @enum {string} */
7256
+ reason: "proposal_ready" | "directory_synced" | "analysis_already_running" | "proposal_blocked" | "no_sync_run" | "no_persons" | "reporting_not_keyed" | "stopped_by_user" | "sync_failed" | "snapshot_too_large" | "model_failed" | "run_too_large" | "placement_failed" | "internal_error" | "abandoned" | "unrecognized_terminal_state";
7257
+ facts: {
7258
+ departmentCount: number;
7259
+ teamCount: number;
7260
+ placedCount: number;
7261
+ reviewItemCount: number;
7262
+ } | null;
7263
+ disposition: {
7264
+ /** @constant */
7265
+ kind: "review";
7266
+ /** @enum {string} */
7267
+ requestType: "org-structure/review" | "source-sync/candidates" | "source-sync/synthesis";
7268
+ requestId: string;
7269
+ } | {
7270
+ /** @constant */
7271
+ kind: "narrate";
7272
+ };
7273
+ affordances: ({
7274
+ /** @constant */
7275
+ id: "sourceSync.findFiles";
7276
+ } | {
7277
+ /** @constant */
7278
+ id: "integrations.connectGoogleDrive";
7279
+ } | {
7280
+ /** @constant */
7281
+ id: "sourceSync.enable";
7282
+ } | {
7283
+ /** @constant */
7284
+ id: "billing.open";
7285
+ } | {
7286
+ /** @constant */
7287
+ id: "orgStructure.reanalyze";
7288
+ } | {
7289
+ /** @constant */
7290
+ id: "hris.resyncDirectory";
7291
+ })[];
7292
+ }) | null;
7007
7293
  error?: string;
7008
7294
  capabilities: {
7009
7295
  canStop: boolean;
@@ -7027,6 +7313,26 @@ export interface components {
7027
7313
  /** @enum {string} */
7028
7314
  result: "delivered" | "deferred" | "busy" | "skipped" | "not_settled";
7029
7315
  };
7316
+ AffordanceStateResponse: {
7317
+ entries: {
7318
+ /** @enum {string} */
7319
+ id: "sourceSync.findFiles" | "integrations.connectGoogleDrive" | "sourceSync.enable" | "billing.open" | "orgStructure.reanalyze" | "hris.resyncDirectory" | "review.open";
7320
+ /** @enum {string} */
7321
+ policy: "allowed" | "blocked";
7322
+ availability: {
7323
+ /** @constant */
7324
+ state: "available";
7325
+ } | {
7326
+ /** @constant */
7327
+ state: "already_satisfied";
7328
+ } | {
7329
+ /** @constant */
7330
+ state: "unavailable";
7331
+ /** @enum {string} */
7332
+ reason: "conflicting_operation" | "missing_dependency" | "resource_gone" | "temporarily_disabled";
7333
+ };
7334
+ }[];
7335
+ };
7030
7336
  CompanyMdListResponse: {
7031
7337
  items: {
7032
7338
  id: string;
@@ -12949,6 +13255,33 @@ export interface operations {
12949
13255
  };
12950
13256
  };
12951
13257
  };
13258
+ syncBambooHrNow: {
13259
+ parameters: {
13260
+ query?: never;
13261
+ header?: never;
13262
+ path?: never;
13263
+ cookie?: never;
13264
+ };
13265
+ requestBody?: never;
13266
+ responses: {
13267
+ /** @description Sync run summary */
13268
+ 200: {
13269
+ headers: {
13270
+ [name: string]: unknown;
13271
+ };
13272
+ content: {
13273
+ "application/json": components["schemas"]["BambooHrSyncRunResponse"];
13274
+ };
13275
+ };
13276
+ /** @description No active BambooHR connection, or BambooHR no longer recognizes the organization */
13277
+ 400: {
13278
+ headers: {
13279
+ [name: string]: unknown;
13280
+ };
13281
+ content?: never;
13282
+ };
13283
+ };
13284
+ };
12952
13285
  getOrgReconciliation: {
12953
13286
  parameters: {
12954
13287
  query?: never;
@@ -13096,6 +13429,35 @@ export interface operations {
13096
13429
  };
13097
13430
  };
13098
13431
  };
13432
+ runOrgStructureInference: {
13433
+ parameters: {
13434
+ query?: never;
13435
+ header?: never;
13436
+ path?: never;
13437
+ cookie?: never;
13438
+ };
13439
+ requestBody?: never;
13440
+ responses: {
13441
+ /** @description A named refusal with its operator-facing next step: no directory imported yet, a sync that imported nobody, or a credential whose sync could not key reporting edges (reporting_not_keyed) */
13442
+ 200: {
13443
+ headers: {
13444
+ [name: string]: unknown;
13445
+ };
13446
+ content: {
13447
+ "application/json": components["schemas"]["RunStructureInferenceRefusal"];
13448
+ };
13449
+ };
13450
+ /** @description A run is under way — freshly launched, or already in flight (alreadyRunning: true). Carries the runId the poll surface matches against and the workflowId of the org-structure/reanalyze workflow it executes (null only when the joined run predates the workflow launch) */
13451
+ 202: {
13452
+ headers: {
13453
+ [name: string]: unknown;
13454
+ };
13455
+ content: {
13456
+ "application/json": components["schemas"]["RunStructureInferenceAccepted"];
13457
+ };
13458
+ };
13459
+ };
13460
+ };
13099
13461
  getAnalysisWorkflow: {
13100
13462
  parameters: {
13101
13463
  query?: never;
@@ -13213,6 +13575,42 @@ export interface operations {
13213
13575
  };
13214
13576
  };
13215
13577
  };
13578
+ getAffordanceState: {
13579
+ parameters: {
13580
+ query: {
13581
+ ids: string;
13582
+ };
13583
+ header?: never;
13584
+ path?: never;
13585
+ cookie?: never;
13586
+ };
13587
+ requestBody?: never;
13588
+ responses: {
13589
+ /** @description Policy and availability per requested affordance id, in request order */
13590
+ 200: {
13591
+ headers: {
13592
+ [name: string]: unknown;
13593
+ };
13594
+ content: {
13595
+ "application/json": components["schemas"]["AffordanceStateResponse"];
13596
+ };
13597
+ };
13598
+ /** @description An id is not a known affordance, the list is empty, or it is too long */
13599
+ 400: {
13600
+ headers: {
13601
+ [name: string]: unknown;
13602
+ };
13603
+ content?: never;
13604
+ };
13605
+ /** @description The viewer is not an active member of the session org */
13606
+ 403: {
13607
+ headers: {
13608
+ [name: string]: unknown;
13609
+ };
13610
+ content?: never;
13611
+ };
13612
+ };
13613
+ };
13216
13614
  getCompanyMdTree: {
13217
13615
  parameters: {
13218
13616
  query?: {
@@ -9,6 +9,7 @@ export const openApiRoutes = {
9
9
  '/api/account/deletion-eligibility': ['GET'],
10
10
  '/api/account/sessions': ['GET'],
11
11
  '/api/account/sessions/{sessionId}': ['DELETE'],
12
+ '/api/affordances/state': ['GET'],
12
13
  '/api/analysis-workflows/{id}': ['GET'],
13
14
  '/api/analysis-workflows/{id}/settlement': ['POST'],
14
15
  '/api/analysis-workflows/{id}/stop': ['POST'],
@@ -101,6 +102,7 @@ export const openApiRoutes = {
101
102
  '/api/factory/quality/plans/{planId}/decision': ['GET'],
102
103
  '/api/factory/snapshot': ['GET'],
103
104
  '/api/ingestion/operations/{id}': ['GET'],
105
+ '/api/integrations/bamboohr/sync': ['POST'],
104
106
  '/api/internal-admin/impersonate/end': ['POST'],
105
107
  '/api/internal-admin/impersonate/session': ['GET'],
106
108
  '/api/internal-admin/impersonate/start': ['POST'],
@@ -181,6 +183,7 @@ export const openApiRoutes = {
181
183
  '/api/org/structure-findings': ['GET'],
182
184
  '/api/org/structure-findings/{findingId}/dismiss': ['POST'],
183
185
  '/api/org/structure-findings/{findingId}/reopen': ['POST'],
186
+ '/api/org/structure-inference/runs': ['POST'],
184
187
  '/api/org/system-events': ['GET'],
185
188
  '/api/org/system-events/{id}/acknowledge': ['POST'],
186
189
  '/api/org/transfer-eligibility': ['GET'],
@@ -32,7 +32,8 @@ so every producer, validator and renderer agrees on it.
32
32
  - **Source Sync registers two flows.** `source-sync/discover` (Stage 1)
33
33
  retains nothing; `source-sync/synthesize` (Stage 4) runs only after a person
34
34
  approved the exact set it reads.
35
- - **Every type has a typed outcome** (`outcome.ts`, control ADR-CTRL-540). A
35
+ - **Every type has a typed outcome** (`outcome.ts`, tables in
36
+ `outcome-reasons.ts`; control ADR-CTRL-540). A
36
37
  terminal transition is classified once, by the backend, into a per-type
37
38
  member of `WorkflowOutcome`: a closed `reason`, typed count-only `facts`, a
38
39
  `kind` that is a function of the reason (`*_OUTCOME_KIND_OF_REASON`), and an
@@ -68,11 +69,12 @@ so every producer, validator and renderer agrees on it.
68
69
  ## Public API
69
70
 
70
71
  - `ANALYSIS_WORKFLOW_TYPES`, `WorkflowType`, `isWorkflowType` (`types.ts`).
71
- - `WorkflowOutcomeSchema` / `WorkflowOutcome` and the per-type member schemas,
72
- reason tables, fact schemas and `WorkflowOutcomeDispositionSchema`
73
- (`outcome.ts`).
72
+ - `WorkflowOutcomeSchema` / `WorkflowOutcome`, the per-type member schemas and
73
+ `WorkflowOutcomeDispositionSchema` (`outcome.ts`).
74
+ - `WORKFLOW_OUTCOME_KINDS` and the per-type reason tables and fact schemas
75
+ (`outcome-reasons.ts`).
74
76
  - `DISCOVER_AFFORDANCES_OF_REASON`, `SYNTHESIZE_AFFORDANCES_OF_REASON`,
75
- `ORG_STRUCTURE_AFFORDANCES_OF_REASON` (`outcome.ts`).
77
+ `ORG_STRUCTURE_AFFORDANCES_OF_REASON` (`outcome-reasons.ts`).
76
78
  - `WORKFLOW_AFFORDANCES_OF_REASON`, `affordancesOfReason`,
77
79
  `affordancesOfOutcome` (`affordances-of-outcome.ts`).
78
80
  - `SETTLEMENT_AUDIENCE_POLICIES`, `SETTLEMENT_COVERING_KINDS`,
@@ -6,11 +6,8 @@ import {
6
6
  affordancesOfOutcome,
7
7
  affordancesOfReason,
8
8
  } from "../affordances-of-outcome";
9
- import {
10
- ORG_STRUCTURE_AFFORDANCES_OF_REASON,
11
- WorkflowOutcomeSchema,
12
- type WorkflowOutcome,
13
- } from "../outcome";
9
+ import { WorkflowOutcomeSchema, type WorkflowOutcome } from "../outcome";
10
+ import { ORG_STRUCTURE_AFFORDANCES_OF_REASON } from "../outcome-reasons";
14
11
  import { ANALYSIS_WORKFLOW_TYPES } from "../types";
15
12
 
16
13
  const review = {
@@ -16,6 +16,7 @@ import {
16
16
  toExplainedOutcome,
17
17
  type DeferredRoute,
18
18
  } from "../deferred-route";
19
+ import { WorkflowOutcomeSchema, type WorkflowOutcome } from "../outcome";
19
20
  import {
20
21
  DISCOVER_AFFORDANCES_OF_REASON,
21
22
  DISCOVER_OUTCOME_KIND_OF_REASON,
@@ -23,9 +24,7 @@ import {
23
24
  ORG_STRUCTURE_OUTCOME_KIND_OF_REASON,
24
25
  SYNTHESIZE_AFFORDANCES_OF_REASON,
25
26
  SYNTHESIZE_OUTCOME_KIND_OF_REASON,
26
- WorkflowOutcomeSchema,
27
- type WorkflowOutcome,
28
- } from "../outcome";
27
+ } from "../outcome-reasons";
29
28
 
30
29
  // (kind table, offer table, route table) per outcome family. Built here rather
31
30
  // than exported so the rules stay pinned without widening the public surface.
@@ -5,6 +5,7 @@ import {
5
5
  OfferedAffordanceSchema,
6
6
  } from "../../affordances/affordance";
7
7
  import type { WorkflowType } from "../types";
8
+ import { WorkflowOutcomeSchema, type WorkflowOutcome } from "../outcome";
8
9
  import {
9
10
  DISCOVER_AFFORDANCES_OF_REASON,
10
11
  DISCOVER_OUTCOME_KIND_OF_REASON,
@@ -13,9 +14,7 @@ import {
13
14
  SYNTHESIZE_AFFORDANCES_OF_REASON,
14
15
  SYNTHESIZE_OUTCOME_KIND_OF_REASON,
15
16
  WORKFLOW_OUTCOME_KINDS,
16
- WorkflowOutcomeSchema,
17
- type WorkflowOutcome,
18
- } from "../outcome";
17
+ } from "../outcome-reasons";
19
18
 
20
19
  // Compile-time: every registered workflow type has an outcome member, and the
21
20
  // union names no type that is not registered.
@@ -9,13 +9,12 @@
9
9
  * offered next steps are one list built one way.
10
10
  */
11
11
  import type { Affordance, OfferedAffordance } from "../affordances/affordance";
12
+ import type { WorkflowOutcome, WorkflowOutcomeOf } from "./outcome";
12
13
  import {
13
14
  DISCOVER_AFFORDANCES_OF_REASON,
14
15
  ORG_STRUCTURE_AFFORDANCES_OF_REASON,
15
16
  SYNTHESIZE_AFFORDANCES_OF_REASON,
16
- type WorkflowOutcome,
17
- type WorkflowOutcomeOf,
18
- } from "./outcome";
17
+ } from "./outcome-reasons";
19
18
  import type { WorkflowType } from "./types";
20
19
 
21
20
  /** Every workflow type's offer table, total over the type union. */
@@ -29,16 +29,18 @@
29
29
  import { z } from "zod";
30
30
 
31
31
  import type {
32
- DiscoverOutcomeReason,
33
32
  DiscoverWorkflowOutcome,
34
33
  HrisConnectWorkflowOutcome,
35
- OrgStructureOutcomeReason,
36
34
  ReanalyzeWorkflowOutcome,
37
- SynthesizeOutcomeReason,
38
35
  SynthesizeWorkflowOutcome,
39
36
  WorkflowOutcome,
40
37
  WorkflowOutcomeOf,
41
38
  } from "./outcome";
39
+ import type {
40
+ DiscoverOutcomeReason,
41
+ OrgStructureOutcomeReason,
42
+ SynthesizeOutcomeReason,
43
+ } from "./outcome-reasons";
42
44
  import type { WorkflowType } from "./types";
43
45
 
44
46
  /** Who a routed settlement is addressed to; resolved and frozen by the backend. */
@@ -14,35 +14,41 @@ export { ANALYSIS_WORKFLOW_TYPES, isWorkflowType } from "./types";
14
14
  export type {
15
15
  DiscoverOutcomeFacts,
16
16
  DiscoverOutcomeReason,
17
- DiscoverWorkflowOutcome,
18
- HrisConnectWorkflowOutcome,
19
17
  OrgStructureOutcomeFacts,
20
18
  OrgStructureOutcomeReason,
21
- ReanalyzeWorkflowOutcome,
22
19
  SynthesizeOutcomeFacts,
23
20
  SynthesizeOutcomeReason,
24
- SynthesizeWorkflowOutcome,
25
- WorkflowOutcome,
26
- WorkflowOutcomeDisposition,
27
21
  WorkflowOutcomeKind,
28
- WorkflowOutcomeOf,
29
- } from "./outcome";
22
+ } from "./outcome-reasons";
30
23
 
31
24
  export {
32
25
  DISCOVER_AFFORDANCES_OF_REASON,
33
26
  DISCOVER_OUTCOME_KIND_OF_REASON,
34
27
  DiscoverOutcomeFactsSchema,
35
- DiscoverWorkflowOutcomeSchema,
36
- HrisConnectWorkflowOutcomeSchema,
37
28
  ORG_STRUCTURE_AFFORDANCES_OF_REASON,
38
29
  ORG_STRUCTURE_OUTCOME_KIND_OF_REASON,
39
30
  OrgStructureOutcomeFactsSchema,
40
- ReanalyzeWorkflowOutcomeSchema,
41
31
  SYNTHESIZE_AFFORDANCES_OF_REASON,
42
32
  SYNTHESIZE_OUTCOME_KIND_OF_REASON,
43
33
  SynthesizeOutcomeFactsSchema,
44
- SynthesizeWorkflowOutcomeSchema,
45
34
  WORKFLOW_OUTCOME_KINDS,
35
+ } from "./outcome-reasons";
36
+
37
+ export type {
38
+ DiscoverWorkflowOutcome,
39
+ HrisConnectWorkflowOutcome,
40
+ ReanalyzeWorkflowOutcome,
41
+ SynthesizeWorkflowOutcome,
42
+ WorkflowOutcome,
43
+ WorkflowOutcomeDisposition,
44
+ WorkflowOutcomeOf,
45
+ } from "./outcome";
46
+
47
+ export {
48
+ DiscoverWorkflowOutcomeSchema,
49
+ HrisConnectWorkflowOutcomeSchema,
50
+ ReanalyzeWorkflowOutcomeSchema,
51
+ SynthesizeWorkflowOutcomeSchema,
46
52
  WorkflowOutcomeDispositionSchema,
47
53
  WorkflowOutcomeSchema,
48
54
  } from "./outcome";
@@ -0,0 +1,244 @@
1
+ /**
2
+ * The per-type vocabulary of a workflow outcome (control ADR-CTRL-540): the
3
+ * shared `kind` list and, for every workflow type, its closed `reason` union,
4
+ * the `kind` each reason fixes, the affordances each reason offers, and the
5
+ * typed `facts` it reports.
6
+ *
7
+ * Tables only. `./outcome.ts` builds the outcome schemas from them and states
8
+ * the invariants they uphold (per-type and closed, kind and offer as functions
9
+ * of reason, facts are counts).
10
+ *
11
+ * An ending is one row in a `*_OUTCOME_KIND_OF_REASON` table and one in the
12
+ * matching `*_AFFORDANCES_OF_REASON` table, whose `satisfies` refuses a
13
+ * missing row.
14
+ */
15
+ import { z } from "zod";
16
+
17
+ import type { OfferedAffordance } from "../affordances/affordance";
18
+
19
+ /** The shape of an ending, shared by every workflow type. */
20
+ export const WORKFLOW_OUTCOME_KINDS = [
21
+ /** The run produced what it is for. */
22
+ "produced",
23
+ /** The run finished and produced nothing. */
24
+ "empty",
25
+ /** The run finished short of full coverage (a budget or ceiling). */
26
+ "partial",
27
+ /** A person stopped the run; Continue may begin a new execution epoch. */
28
+ "stopped",
29
+ /** The run failed. */
30
+ "failed",
31
+ /** The run went silent and the reconciler closed it. */
32
+ "abandoned",
33
+ /** A terminal transition the classifier did not recognize. */
34
+ "unknown",
35
+ ] as const;
36
+
37
+ export type WorkflowOutcomeKind = (typeof WORKFLOW_OUTCOME_KINDS)[number];
38
+
39
+ const Count = z.number().int().nonnegative();
40
+
41
+ // The affordances an ending may offer, named once for the tables below.
42
+ const FIND_FILES = { id: "sourceSync.findFiles" } as const;
43
+ const CONNECT_GOOGLE_DRIVE = { id: "integrations.connectGoogleDrive" } as const;
44
+ const ENABLE_SOURCE_SYNC = { id: "sourceSync.enable" } as const;
45
+ const OPEN_BILLING = { id: "billing.open" } as const;
46
+ const REANALYZE_STRUCTURE = { id: "orgStructure.reanalyze" } as const;
47
+ const RESYNC_DIRECTORY = { id: "hris.resyncDirectory" } as const;
48
+
49
+ // ---------------------------------------------------------------------------
50
+ // source-sync/discover
51
+ // ---------------------------------------------------------------------------
52
+
53
+ /** Why a Drive discovery run ended the way it did. */
54
+ export const DISCOVER_OUTCOME_KIND_OF_REASON = {
55
+ candidates_found: "produced",
56
+ nothing_found: "empty",
57
+ nothing_relevant: "empty",
58
+ budget_exhausted: "partial",
59
+ file_ceiling: "partial",
60
+ stopped_by_user: "stopped",
61
+ plan_coverage_refused: "failed",
62
+ billing_refused: "failed",
63
+ drive_auth: "failed",
64
+ drive_unavailable: "failed",
65
+ no_connection: "failed",
66
+ source_sync_off: "failed",
67
+ internal_error: "failed",
68
+ abandoned: "abandoned",
69
+ unrecognized_terminal_state: "unknown",
70
+ } as const satisfies Record<string, WorkflowOutcomeKind>;
71
+
72
+ export type DiscoverOutcomeReason =
73
+ keyof typeof DISCOVER_OUTCOME_KIND_OF_REASON;
74
+
75
+ /**
76
+ * What each discovery ending offers (control ADR affordance-system). TOTAL: an
77
+ * empty list is a decision that no grounded action changes that ending — a
78
+ * re-run of the same search is not "broadening" it, and nothing raises a budget.
79
+ */
80
+ export const DISCOVER_AFFORDANCES_OF_REASON = {
81
+ candidates_found: [],
82
+ nothing_found: [],
83
+ nothing_relevant: [],
84
+ budget_exhausted: [],
85
+ file_ceiling: [],
86
+ stopped_by_user: [],
87
+ plan_coverage_refused: [],
88
+ billing_refused: [OPEN_BILLING],
89
+ drive_auth: [CONNECT_GOOGLE_DRIVE],
90
+ drive_unavailable: [FIND_FILES],
91
+ no_connection: [CONNECT_GOOGLE_DRIVE],
92
+ source_sync_off: [ENABLE_SOURCE_SYNC],
93
+ internal_error: [FIND_FILES],
94
+ abandoned: [FIND_FILES],
95
+ unrecognized_terminal_state: [],
96
+ } as const satisfies Record<
97
+ DiscoverOutcomeReason,
98
+ readonly OfferedAffordance[]
99
+ >;
100
+
101
+ /** The counts a discovery run reports at its end. */
102
+ export const DiscoverOutcomeFactsSchema = z
103
+ .object({
104
+ probesTotal: Count,
105
+ probesDone: Count,
106
+ filesSeen: Count,
107
+ filesShown: Count,
108
+ filesHidden: Count,
109
+ filesDropped: Count,
110
+ coveragePercent: z.number().min(0).max(100).nullable(),
111
+ })
112
+ .strict();
113
+
114
+ export type DiscoverOutcomeFacts = z.infer<typeof DiscoverOutcomeFactsSchema>;
115
+
116
+ // ---------------------------------------------------------------------------
117
+ // source-sync/synthesize
118
+ // ---------------------------------------------------------------------------
119
+
120
+ /** Why a synthesis (landing then drafting) run ended the way it did. */
121
+ export const SYNTHESIZE_OUTCOME_KIND_OF_REASON = {
122
+ updates_ready: "produced",
123
+ updates_applied: "produced",
124
+ stopped_by_user: "stopped",
125
+ paused_billing: "failed",
126
+ synthesis_incomplete: "failed",
127
+ approval_mismatch: "failed",
128
+ landing_refused: "failed",
129
+ standing_order_refused: "failed",
130
+ no_connection: "failed",
131
+ source_sync_off: "failed",
132
+ internal_error: "failed",
133
+ abandoned: "abandoned",
134
+ unrecognized_terminal_state: "unknown",
135
+ } as const satisfies Record<string, WorkflowOutcomeKind>;
136
+
137
+ export type SynthesizeOutcomeReason =
138
+ keyof typeof SYNTHESIZE_OUTCOME_KIND_OF_REASON;
139
+
140
+ /** What each synthesis ending offers. TOTAL; an empty list is explicit. */
141
+ export const SYNTHESIZE_AFFORDANCES_OF_REASON = {
142
+ updates_ready: [],
143
+ updates_applied: [],
144
+ stopped_by_user: [],
145
+ paused_billing: [OPEN_BILLING],
146
+ synthesis_incomplete: [],
147
+ approval_mismatch: [],
148
+ landing_refused: [],
149
+ standing_order_refused: [],
150
+ no_connection: [CONNECT_GOOGLE_DRIVE],
151
+ source_sync_off: [ENABLE_SOURCE_SYNC],
152
+ internal_error: [],
153
+ abandoned: [],
154
+ unrecognized_terminal_state: [],
155
+ } as const satisfies Record<
156
+ SynthesizeOutcomeReason,
157
+ readonly OfferedAffordance[]
158
+ >;
159
+
160
+ /** The counts a synthesis run reports at its end. */
161
+ export const SynthesizeOutcomeFactsSchema = z
162
+ .object({
163
+ /** Approved scopes the run read. */
164
+ scopeCount: Count,
165
+ /** Authority or size violations that left the batch incomplete. */
166
+ violationCount: Count,
167
+ })
168
+ .strict();
169
+
170
+ export type SynthesizeOutcomeFacts = z.infer<
171
+ typeof SynthesizeOutcomeFactsSchema
172
+ >;
173
+
174
+ // ---------------------------------------------------------------------------
175
+ // org-structure/hris-connect and org-structure/reanalyze
176
+ // ---------------------------------------------------------------------------
177
+
178
+ /**
179
+ * Why an org-structure analysis ended the way it did. Shared by both
180
+ * org-structure workflow types: they run the same analysis. The directory-only
181
+ * endings (`directory_synced`, `analysis_already_running`, `sync_failed`) occur
182
+ * only on `org-structure/hris-connect`, which syncs the directory first.
183
+ */
184
+ export const ORG_STRUCTURE_OUTCOME_KIND_OF_REASON = {
185
+ proposal_ready: "produced",
186
+ directory_synced: "produced",
187
+ analysis_already_running: "produced",
188
+ proposal_blocked: "failed",
189
+ no_sync_run: "empty",
190
+ no_persons: "empty",
191
+ reporting_not_keyed: "empty",
192
+ stopped_by_user: "stopped",
193
+ sync_failed: "failed",
194
+ snapshot_too_large: "failed",
195
+ model_failed: "failed",
196
+ run_too_large: "failed",
197
+ placement_failed: "failed",
198
+ internal_error: "failed",
199
+ abandoned: "abandoned",
200
+ unrecognized_terminal_state: "unknown",
201
+ } as const satisfies Record<string, WorkflowOutcomeKind>;
202
+
203
+ export type OrgStructureOutcomeReason =
204
+ keyof typeof ORG_STRUCTURE_OUTCOME_KIND_OF_REASON;
205
+
206
+ /**
207
+ * What each org-structure ending offers; shared by both org-structure workflow
208
+ * types, like the kind table. TOTAL; an empty list is explicit.
209
+ */
210
+ export const ORG_STRUCTURE_AFFORDANCES_OF_REASON = {
211
+ proposal_ready: [],
212
+ directory_synced: [],
213
+ analysis_already_running: [],
214
+ proposal_blocked: [],
215
+ no_sync_run: [RESYNC_DIRECTORY],
216
+ no_persons: [],
217
+ reporting_not_keyed: [],
218
+ stopped_by_user: [],
219
+ sync_failed: [RESYNC_DIRECTORY],
220
+ snapshot_too_large: [],
221
+ model_failed: [REANALYZE_STRUCTURE],
222
+ run_too_large: [],
223
+ placement_failed: [REANALYZE_STRUCTURE],
224
+ internal_error: [REANALYZE_STRUCTURE],
225
+ abandoned: [REANALYZE_STRUCTURE],
226
+ unrecognized_terminal_state: [],
227
+ } as const satisfies Record<
228
+ OrgStructureOutcomeReason,
229
+ readonly OfferedAffordance[]
230
+ >;
231
+
232
+ /** The counts a finished org-structure proposal carries. */
233
+ export const OrgStructureOutcomeFactsSchema = z
234
+ .object({
235
+ departmentCount: Count,
236
+ teamCount: Count,
237
+ placedCount: Count,
238
+ reviewItemCount: Count,
239
+ })
240
+ .strict();
241
+
242
+ export type OrgStructureOutcomeFacts = z.infer<
243
+ typeof OrgStructureOutcomeFactsSchema
244
+ >;
@@ -16,8 +16,9 @@
16
16
  * record: a renderer that switches on `reason` is exhaustive, and a new fact is
17
17
  * a schema change, not a new spelling.
18
18
  * - KIND IS A FUNCTION OF REASON. `kind` is carried for consumers that only care
19
- * about the shape of the ending, but it is fixed by the per-type table below
20
- * (`*_OUTCOME_KIND_OF_REASON`) and the schema refuses any other pairing.
19
+ * about the shape of the ending, but it is fixed by the per-type table
20
+ * (`*_OUTCOME_KIND_OF_REASON` in `./outcome-reasons.ts`) and the schema
21
+ * refuses any other pairing.
21
22
  * - DISPOSITION IS EXPLICIT. `review` names the input request a person decides
22
23
  * on; `narrate` means the ending is told, not reviewed. `produced` does not
23
24
  * imply a review surface, and only a `produced` or `partial` outcome may carry
@@ -44,26 +45,19 @@ import {
44
45
  type OfferedAffordance,
45
46
  } from "../affordances/affordance";
46
47
  import { INPUT_REQUEST_TYPES } from "../input-requests/types";
47
-
48
- /** The shape of an ending, shared by every workflow type. */
49
- export const WORKFLOW_OUTCOME_KINDS = [
50
- /** The run produced what it is for. */
51
- "produced",
52
- /** The run finished and produced nothing. */
53
- "empty",
54
- /** The run finished short of full coverage (a budget or ceiling). */
55
- "partial",
56
- /** A person stopped the run; Continue may begin a new execution epoch. */
57
- "stopped",
58
- /** The run failed. */
59
- "failed",
60
- /** The run went silent and the reconciler closed it. */
61
- "abandoned",
62
- /** A terminal transition the classifier did not recognize. */
63
- "unknown",
64
- ] as const;
65
-
66
- export type WorkflowOutcomeKind = (typeof WORKFLOW_OUTCOME_KINDS)[number];
48
+ import {
49
+ DISCOVER_AFFORDANCES_OF_REASON,
50
+ DISCOVER_OUTCOME_KIND_OF_REASON,
51
+ DiscoverOutcomeFactsSchema,
52
+ ORG_STRUCTURE_AFFORDANCES_OF_REASON,
53
+ ORG_STRUCTURE_OUTCOME_KIND_OF_REASON,
54
+ OrgStructureOutcomeFactsSchema,
55
+ SYNTHESIZE_AFFORDANCES_OF_REASON,
56
+ SYNTHESIZE_OUTCOME_KIND_OF_REASON,
57
+ SynthesizeOutcomeFactsSchema,
58
+ WORKFLOW_OUTCOME_KINDS,
59
+ type WorkflowOutcomeKind,
60
+ } from "./outcome-reasons";
67
61
 
68
62
  const KindSchema = z.enum(WORKFLOW_OUTCOME_KINDS);
69
63
 
@@ -85,211 +79,6 @@ export type WorkflowOutcomeDisposition = z.infer<
85
79
 
86
80
  const Count = z.number().int().nonnegative();
87
81
 
88
- // The affordances an ending may offer, named once for the tables below.
89
- const FIND_FILES = { id: "sourceSync.findFiles" } as const;
90
- const CONNECT_GOOGLE_DRIVE = { id: "integrations.connectGoogleDrive" } as const;
91
- const ENABLE_SOURCE_SYNC = { id: "sourceSync.enable" } as const;
92
- const OPEN_BILLING = { id: "billing.open" } as const;
93
- const REANALYZE_STRUCTURE = { id: "orgStructure.reanalyze" } as const;
94
- const RESYNC_DIRECTORY = { id: "hris.resyncDirectory" } as const;
95
-
96
- // ---------------------------------------------------------------------------
97
- // source-sync/discover
98
- // ---------------------------------------------------------------------------
99
-
100
- /** Why a Drive discovery run ended the way it did. */
101
- export const DISCOVER_OUTCOME_KIND_OF_REASON = {
102
- candidates_found: "produced",
103
- nothing_found: "empty",
104
- nothing_relevant: "empty",
105
- budget_exhausted: "partial",
106
- file_ceiling: "partial",
107
- stopped_by_user: "stopped",
108
- plan_coverage_refused: "failed",
109
- billing_refused: "failed",
110
- drive_auth: "failed",
111
- drive_unavailable: "failed",
112
- no_connection: "failed",
113
- source_sync_off: "failed",
114
- internal_error: "failed",
115
- abandoned: "abandoned",
116
- unrecognized_terminal_state: "unknown",
117
- } as const satisfies Record<string, WorkflowOutcomeKind>;
118
-
119
- export type DiscoverOutcomeReason =
120
- keyof typeof DISCOVER_OUTCOME_KIND_OF_REASON;
121
-
122
- /**
123
- * What each discovery ending offers (control ADR affordance-system). TOTAL: an
124
- * empty list is a decision that no grounded action changes that ending — a
125
- * re-run of the same search is not "broadening" it, and nothing raises a budget.
126
- */
127
- export const DISCOVER_AFFORDANCES_OF_REASON = {
128
- candidates_found: [],
129
- nothing_found: [],
130
- nothing_relevant: [],
131
- budget_exhausted: [],
132
- file_ceiling: [],
133
- stopped_by_user: [],
134
- plan_coverage_refused: [],
135
- billing_refused: [OPEN_BILLING],
136
- drive_auth: [CONNECT_GOOGLE_DRIVE],
137
- drive_unavailable: [FIND_FILES],
138
- no_connection: [CONNECT_GOOGLE_DRIVE],
139
- source_sync_off: [ENABLE_SOURCE_SYNC],
140
- internal_error: [FIND_FILES],
141
- abandoned: [FIND_FILES],
142
- unrecognized_terminal_state: [],
143
- } as const satisfies Record<
144
- DiscoverOutcomeReason,
145
- readonly OfferedAffordance[]
146
- >;
147
-
148
- /** The counts a discovery run reports at its end. */
149
- export const DiscoverOutcomeFactsSchema = z
150
- .object({
151
- probesTotal: Count,
152
- probesDone: Count,
153
- filesSeen: Count,
154
- filesShown: Count,
155
- filesHidden: Count,
156
- filesDropped: Count,
157
- coveragePercent: z.number().min(0).max(100).nullable(),
158
- })
159
- .strict();
160
-
161
- export type DiscoverOutcomeFacts = z.infer<typeof DiscoverOutcomeFactsSchema>;
162
-
163
- // ---------------------------------------------------------------------------
164
- // source-sync/synthesize
165
- // ---------------------------------------------------------------------------
166
-
167
- /** Why a synthesis (landing then drafting) run ended the way it did. */
168
- export const SYNTHESIZE_OUTCOME_KIND_OF_REASON = {
169
- updates_ready: "produced",
170
- updates_applied: "produced",
171
- stopped_by_user: "stopped",
172
- paused_billing: "failed",
173
- synthesis_incomplete: "failed",
174
- approval_mismatch: "failed",
175
- landing_refused: "failed",
176
- standing_order_refused: "failed",
177
- no_connection: "failed",
178
- source_sync_off: "failed",
179
- internal_error: "failed",
180
- abandoned: "abandoned",
181
- unrecognized_terminal_state: "unknown",
182
- } as const satisfies Record<string, WorkflowOutcomeKind>;
183
-
184
- export type SynthesizeOutcomeReason =
185
- keyof typeof SYNTHESIZE_OUTCOME_KIND_OF_REASON;
186
-
187
- /** What each synthesis ending offers. TOTAL; an empty list is explicit. */
188
- export const SYNTHESIZE_AFFORDANCES_OF_REASON = {
189
- updates_ready: [],
190
- updates_applied: [],
191
- stopped_by_user: [],
192
- paused_billing: [OPEN_BILLING],
193
- synthesis_incomplete: [],
194
- approval_mismatch: [],
195
- landing_refused: [],
196
- standing_order_refused: [],
197
- no_connection: [CONNECT_GOOGLE_DRIVE],
198
- source_sync_off: [ENABLE_SOURCE_SYNC],
199
- internal_error: [],
200
- abandoned: [],
201
- unrecognized_terminal_state: [],
202
- } as const satisfies Record<
203
- SynthesizeOutcomeReason,
204
- readonly OfferedAffordance[]
205
- >;
206
-
207
- /** The counts a synthesis run reports at its end. */
208
- export const SynthesizeOutcomeFactsSchema = z
209
- .object({
210
- /** Approved scopes the run read. */
211
- scopeCount: Count,
212
- /** Authority or size violations that left the batch incomplete. */
213
- violationCount: Count,
214
- })
215
- .strict();
216
-
217
- export type SynthesizeOutcomeFacts = z.infer<
218
- typeof SynthesizeOutcomeFactsSchema
219
- >;
220
-
221
- // ---------------------------------------------------------------------------
222
- // org-structure/hris-connect and org-structure/reanalyze
223
- // ---------------------------------------------------------------------------
224
-
225
- /**
226
- * Why an org-structure analysis ended the way it did. Shared by both
227
- * org-structure workflow types: they run the same analysis. The directory-only
228
- * endings (`directory_synced`, `analysis_already_running`, `sync_failed`) occur
229
- * only on `org-structure/hris-connect`, which syncs the directory first.
230
- */
231
- export const ORG_STRUCTURE_OUTCOME_KIND_OF_REASON = {
232
- proposal_ready: "produced",
233
- directory_synced: "produced",
234
- analysis_already_running: "produced",
235
- proposal_blocked: "failed",
236
- no_sync_run: "empty",
237
- no_persons: "empty",
238
- reporting_not_keyed: "empty",
239
- stopped_by_user: "stopped",
240
- sync_failed: "failed",
241
- snapshot_too_large: "failed",
242
- model_failed: "failed",
243
- run_too_large: "failed",
244
- placement_failed: "failed",
245
- internal_error: "failed",
246
- abandoned: "abandoned",
247
- unrecognized_terminal_state: "unknown",
248
- } as const satisfies Record<string, WorkflowOutcomeKind>;
249
-
250
- export type OrgStructureOutcomeReason =
251
- keyof typeof ORG_STRUCTURE_OUTCOME_KIND_OF_REASON;
252
-
253
- /**
254
- * What each org-structure ending offers; shared by both org-structure workflow
255
- * types, like the kind table. TOTAL; an empty list is explicit.
256
- */
257
- export const ORG_STRUCTURE_AFFORDANCES_OF_REASON = {
258
- proposal_ready: [],
259
- directory_synced: [],
260
- analysis_already_running: [],
261
- proposal_blocked: [],
262
- no_sync_run: [RESYNC_DIRECTORY],
263
- no_persons: [],
264
- reporting_not_keyed: [],
265
- stopped_by_user: [],
266
- sync_failed: [RESYNC_DIRECTORY],
267
- snapshot_too_large: [],
268
- model_failed: [REANALYZE_STRUCTURE],
269
- run_too_large: [],
270
- placement_failed: [REANALYZE_STRUCTURE],
271
- internal_error: [REANALYZE_STRUCTURE],
272
- abandoned: [REANALYZE_STRUCTURE],
273
- unrecognized_terminal_state: [],
274
- } as const satisfies Record<
275
- OrgStructureOutcomeReason,
276
- readonly OfferedAffordance[]
277
- >;
278
-
279
- /** The counts a finished org-structure proposal carries. */
280
- export const OrgStructureOutcomeFactsSchema = z
281
- .object({
282
- departmentCount: Count,
283
- teamCount: Count,
284
- placedCount: Count,
285
- reviewItemCount: Count,
286
- })
287
- .strict();
288
-
289
- export type OrgStructureOutcomeFacts = z.infer<
290
- typeof OrgStructureOutcomeFactsSchema
291
- >;
292
-
293
82
  // ---------------------------------------------------------------------------
294
83
  // The union
295
84
  // ---------------------------------------------------------------------------