@company-semantics/contracts 91.0.0 → 92.0.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 +3 -2
- package/src/api/generated-spec-hash.ts +2 -2
- package/src/api/generated.ts +402 -10
- package/src/generated/openapi-routes.ts +3 -0
- package/src/source-sync/README.md +2 -1
- package/src/source-sync/__tests__/approval-schemas.test.ts +2 -13
- package/src/source-sync/__tests__/current-synthesis.test.ts +1 -0
- package/src/source-sync/__tests__/schemas.test.ts +5 -32
- package/src/source-sync/schemas.ts +6 -15
- package/src/workflows/README.md +7 -5
- package/src/workflows/__tests__/affordances-of-outcome.test.ts +2 -5
- package/src/workflows/__tests__/deferred-route.test.ts +2 -3
- package/src/workflows/__tests__/outcome.test.ts +2 -3
- package/src/workflows/affordances-of-outcome.ts +2 -3
- package/src/workflows/deferred-route.ts +7 -3
- package/src/workflows/index.ts +18 -12
- package/src/workflows/outcome-reasons.ts +248 -0
- package/src/workflows/outcome.ts +16 -227
|
@@ -140,11 +140,6 @@ function candidateSet(overrides: Record<string, unknown> = {}) {
|
|
|
140
140
|
filesPendingClassification: 0,
|
|
141
141
|
},
|
|
142
142
|
scopes: [],
|
|
143
|
-
estimate: {
|
|
144
|
-
lowMicroUsd: "250000",
|
|
145
|
-
highMicroUsd: "1250000",
|
|
146
|
-
capMicroUsd: "2000000",
|
|
147
|
-
},
|
|
148
143
|
discovery: {
|
|
149
144
|
foundThisRun: 12,
|
|
150
145
|
retainedDecisions: 3,
|
|
@@ -264,38 +259,16 @@ describe("SourceSyncCandidateItemSchema", () => {
|
|
|
264
259
|
});
|
|
265
260
|
|
|
266
261
|
describe("SourceSyncCandidateSetResponseSchema", () => {
|
|
267
|
-
it("accepts
|
|
262
|
+
it("accepts the draft set", () => {
|
|
268
263
|
expect(
|
|
269
264
|
SourceSyncCandidateSetResponseSchema.safeParse(candidateSet()).success,
|
|
270
265
|
).toBe(true);
|
|
271
|
-
const decimal = candidateSet({
|
|
272
|
-
estimate: {
|
|
273
|
-
lowMicroUsd: "0.25",
|
|
274
|
-
highMicroUsd: "1250000",
|
|
275
|
-
capMicroUsd: "2000000",
|
|
276
|
-
},
|
|
277
|
-
});
|
|
278
|
-
expect(
|
|
279
|
-
SourceSyncCandidateSetResponseSchema.safeParse(decimal).success,
|
|
280
|
-
).toBe(false);
|
|
281
|
-
const jsonNumber = candidateSet({
|
|
282
|
-
estimate: {
|
|
283
|
-
lowMicroUsd: "250000",
|
|
284
|
-
highMicroUsd: "1250000",
|
|
285
|
-
capMicroUsd: 2000000,
|
|
286
|
-
},
|
|
287
|
-
});
|
|
288
|
-
expect(
|
|
289
|
-
SourceSyncCandidateSetResponseSchema.safeParse(jsonNumber).success,
|
|
290
|
-
).toBe(false);
|
|
291
266
|
});
|
|
292
267
|
|
|
293
|
-
it("
|
|
294
|
-
expect(
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
).success,
|
|
298
|
-
).toBe(true);
|
|
268
|
+
it("carries no cost estimate: the review authorizes files, never a spend", () => {
|
|
269
|
+
expect(SourceSyncCandidateSetResponseSchema.shape).not.toHaveProperty(
|
|
270
|
+
"estimate",
|
|
271
|
+
);
|
|
299
272
|
});
|
|
300
273
|
|
|
301
274
|
it("refuses an unknown set state", () => {
|
|
@@ -30,9 +30,6 @@ import {
|
|
|
30
30
|
import { CandidateDiscoverySummarySchema } from "./relevance";
|
|
31
31
|
import { SourceSyncCandidateItemSchema } from "./candidate-item";
|
|
32
32
|
|
|
33
|
-
/** Integer micro-USD as a string — the billing domain's money convention on the wire. */
|
|
34
|
-
const MicroUsd = z.string().regex(/^[0-9]+$/);
|
|
35
|
-
|
|
36
33
|
/** A person as the review names them: a display label, never an id or an address. */
|
|
37
34
|
const PersonLabel = z.string().min(1).max(255);
|
|
38
35
|
|
|
@@ -109,14 +106,6 @@ export const SourceSyncCandidateSetResponseSchema = z.object({
|
|
|
109
106
|
totals: SourceSyncCandidateTotalsSchema,
|
|
110
107
|
/** Every scope of the set the caller may see, and where its approval stands. */
|
|
111
108
|
scopes: z.array(SourceSyncScopeApprovalProjectionSchema).max(500),
|
|
112
|
-
/** A RANGE, and the cap that is the promise. Null until the estimator has run. */
|
|
113
|
-
estimate: z
|
|
114
|
-
.object({
|
|
115
|
-
lowMicroUsd: MicroUsd,
|
|
116
|
-
highMicroUsd: MicroUsd,
|
|
117
|
-
capMicroUsd: MicroUsd,
|
|
118
|
-
})
|
|
119
|
-
.nullable(),
|
|
120
109
|
/** The last completed discovery run's counts; null before any run completed. */
|
|
121
110
|
discovery: z.nullable(CandidateDiscoverySummarySchema),
|
|
122
111
|
});
|
|
@@ -134,13 +123,14 @@ export const SourceSyncAddCandidateRequestSchema = z
|
|
|
134
123
|
|
|
135
124
|
/**
|
|
136
125
|
* Approve the set AS REVIEWED. `revision` is the revision the person was looking at; the server
|
|
137
|
-
* refuses a stale one, freezes the items, and hashes them PER SCOPE
|
|
126
|
+
* refuses a stale one, freezes the items, and hashes them PER SCOPE.
|
|
127
|
+
*
|
|
128
|
+
* An approval authorizes the files, never a spend: there is no cap and no estimate here. Spend is
|
|
129
|
+
* bounded by the org's standing spending controls and a server-derived run ceiling.
|
|
138
130
|
*/
|
|
139
131
|
export const SourceSyncApproveRequestSchema = z
|
|
140
132
|
.object({
|
|
141
133
|
revision: z.number().int().min(0),
|
|
142
|
-
/** The promise: synthesis stops at this. Integer micro-USD. */
|
|
143
|
-
spendCapMicroUsd: MicroUsd,
|
|
144
134
|
/** Absent: every scope the caller may approve (all of them for the single v1 approver). */
|
|
145
135
|
scopeKeys: z.array(z.string().min(1).max(64)).min(1).max(500).optional(),
|
|
146
136
|
})
|
|
@@ -151,7 +141,7 @@ export const SourceSyncScopeApprovalSchema = z.object({
|
|
|
151
141
|
/** 'company' or 'unit:<uuid>'. */
|
|
152
142
|
scopeKey: z.string().min(1).max(64),
|
|
153
143
|
approvalId: z.string().uuid(),
|
|
154
|
-
/** Hash over THIS scope's ordered (source, target, use, observation) tuples
|
|
144
|
+
/** Hash over THIS scope's ordered (source, target, use, observation) tuples. */
|
|
155
145
|
candidateSetHash: z.string().regex(/^[0-9a-f]{64}$/),
|
|
156
146
|
});
|
|
157
147
|
|
|
@@ -285,6 +275,7 @@ export const SOURCE_SYNC_SYNTHESIS_PHASES = [
|
|
|
285
275
|
"drafting",
|
|
286
276
|
"waiting_for_review",
|
|
287
277
|
"paused_billing",
|
|
278
|
+
"paused_ceiling",
|
|
288
279
|
"applied",
|
|
289
280
|
"applied_partial",
|
|
290
281
|
"failed",
|
package/src/workflows/README.md
CHANGED
|
@@ -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`,
|
|
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
|
|
72
|
-
|
|
73
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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. */
|
|
@@ -148,6 +150,8 @@ export const SYNTHESIZE_DEFERRED_ROUTE_OF_REASON = {
|
|
|
148
150
|
updates_applied: { route: "covered", by: "sourceSync.docs_updated", fallback: "notice", audience: "creator" },
|
|
149
151
|
stopped_by_user: STOPPED,
|
|
150
152
|
paused_billing: EXPLAINED,
|
|
153
|
+
run_ceiling_reached: NOTICE_ADMINS,
|
|
154
|
+
pricing_unconfigured: NOTICE_ADMINS,
|
|
151
155
|
synthesis_incomplete: NOTICE_ADMINS,
|
|
152
156
|
approval_mismatch: NOTICE_ADMINS,
|
|
153
157
|
landing_refused: NOTICE_ADMINS,
|
package/src/workflows/index.ts
CHANGED
|
@@ -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
|
-
|
|
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,248 @@
|
|
|
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
|
+
run_ceiling_reached: "failed",
|
|
127
|
+
pricing_unconfigured: "failed",
|
|
128
|
+
synthesis_incomplete: "failed",
|
|
129
|
+
approval_mismatch: "failed",
|
|
130
|
+
landing_refused: "failed",
|
|
131
|
+
standing_order_refused: "failed",
|
|
132
|
+
no_connection: "failed",
|
|
133
|
+
source_sync_off: "failed",
|
|
134
|
+
internal_error: "failed",
|
|
135
|
+
abandoned: "abandoned",
|
|
136
|
+
unrecognized_terminal_state: "unknown",
|
|
137
|
+
} as const satisfies Record<string, WorkflowOutcomeKind>;
|
|
138
|
+
|
|
139
|
+
export type SynthesizeOutcomeReason =
|
|
140
|
+
keyof typeof SYNTHESIZE_OUTCOME_KIND_OF_REASON;
|
|
141
|
+
|
|
142
|
+
/** What each synthesis ending offers. TOTAL; an empty list is explicit. */
|
|
143
|
+
export const SYNTHESIZE_AFFORDANCES_OF_REASON = {
|
|
144
|
+
updates_ready: [],
|
|
145
|
+
updates_applied: [],
|
|
146
|
+
stopped_by_user: [],
|
|
147
|
+
paused_billing: [OPEN_BILLING],
|
|
148
|
+
run_ceiling_reached: [],
|
|
149
|
+
pricing_unconfigured: [],
|
|
150
|
+
synthesis_incomplete: [],
|
|
151
|
+
approval_mismatch: [],
|
|
152
|
+
landing_refused: [],
|
|
153
|
+
standing_order_refused: [],
|
|
154
|
+
no_connection: [CONNECT_GOOGLE_DRIVE],
|
|
155
|
+
source_sync_off: [ENABLE_SOURCE_SYNC],
|
|
156
|
+
internal_error: [],
|
|
157
|
+
abandoned: [],
|
|
158
|
+
unrecognized_terminal_state: [],
|
|
159
|
+
} as const satisfies Record<
|
|
160
|
+
SynthesizeOutcomeReason,
|
|
161
|
+
readonly OfferedAffordance[]
|
|
162
|
+
>;
|
|
163
|
+
|
|
164
|
+
/** The counts a synthesis run reports at its end. */
|
|
165
|
+
export const SynthesizeOutcomeFactsSchema = z
|
|
166
|
+
.object({
|
|
167
|
+
/** Approved scopes the run read. */
|
|
168
|
+
scopeCount: Count,
|
|
169
|
+
/** Authority or size violations that left the batch incomplete. */
|
|
170
|
+
violationCount: Count,
|
|
171
|
+
})
|
|
172
|
+
.strict();
|
|
173
|
+
|
|
174
|
+
export type SynthesizeOutcomeFacts = z.infer<
|
|
175
|
+
typeof SynthesizeOutcomeFactsSchema
|
|
176
|
+
>;
|
|
177
|
+
|
|
178
|
+
// ---------------------------------------------------------------------------
|
|
179
|
+
// org-structure/hris-connect and org-structure/reanalyze
|
|
180
|
+
// ---------------------------------------------------------------------------
|
|
181
|
+
|
|
182
|
+
/**
|
|
183
|
+
* Why an org-structure analysis ended the way it did. Shared by both
|
|
184
|
+
* org-structure workflow types: they run the same analysis. The directory-only
|
|
185
|
+
* endings (`directory_synced`, `analysis_already_running`, `sync_failed`) occur
|
|
186
|
+
* only on `org-structure/hris-connect`, which syncs the directory first.
|
|
187
|
+
*/
|
|
188
|
+
export const ORG_STRUCTURE_OUTCOME_KIND_OF_REASON = {
|
|
189
|
+
proposal_ready: "produced",
|
|
190
|
+
directory_synced: "produced",
|
|
191
|
+
analysis_already_running: "produced",
|
|
192
|
+
proposal_blocked: "failed",
|
|
193
|
+
no_sync_run: "empty",
|
|
194
|
+
no_persons: "empty",
|
|
195
|
+
reporting_not_keyed: "empty",
|
|
196
|
+
stopped_by_user: "stopped",
|
|
197
|
+
sync_failed: "failed",
|
|
198
|
+
snapshot_too_large: "failed",
|
|
199
|
+
model_failed: "failed",
|
|
200
|
+
run_too_large: "failed",
|
|
201
|
+
placement_failed: "failed",
|
|
202
|
+
internal_error: "failed",
|
|
203
|
+
abandoned: "abandoned",
|
|
204
|
+
unrecognized_terminal_state: "unknown",
|
|
205
|
+
} as const satisfies Record<string, WorkflowOutcomeKind>;
|
|
206
|
+
|
|
207
|
+
export type OrgStructureOutcomeReason =
|
|
208
|
+
keyof typeof ORG_STRUCTURE_OUTCOME_KIND_OF_REASON;
|
|
209
|
+
|
|
210
|
+
/**
|
|
211
|
+
* What each org-structure ending offers; shared by both org-structure workflow
|
|
212
|
+
* types, like the kind table. TOTAL; an empty list is explicit.
|
|
213
|
+
*/
|
|
214
|
+
export const ORG_STRUCTURE_AFFORDANCES_OF_REASON = {
|
|
215
|
+
proposal_ready: [],
|
|
216
|
+
directory_synced: [],
|
|
217
|
+
analysis_already_running: [],
|
|
218
|
+
proposal_blocked: [],
|
|
219
|
+
no_sync_run: [RESYNC_DIRECTORY],
|
|
220
|
+
no_persons: [],
|
|
221
|
+
reporting_not_keyed: [],
|
|
222
|
+
stopped_by_user: [],
|
|
223
|
+
sync_failed: [RESYNC_DIRECTORY],
|
|
224
|
+
snapshot_too_large: [],
|
|
225
|
+
model_failed: [REANALYZE_STRUCTURE],
|
|
226
|
+
run_too_large: [],
|
|
227
|
+
placement_failed: [REANALYZE_STRUCTURE],
|
|
228
|
+
internal_error: [REANALYZE_STRUCTURE],
|
|
229
|
+
abandoned: [REANALYZE_STRUCTURE],
|
|
230
|
+
unrecognized_terminal_state: [],
|
|
231
|
+
} as const satisfies Record<
|
|
232
|
+
OrgStructureOutcomeReason,
|
|
233
|
+
readonly OfferedAffordance[]
|
|
234
|
+
>;
|
|
235
|
+
|
|
236
|
+
/** The counts a finished org-structure proposal carries. */
|
|
237
|
+
export const OrgStructureOutcomeFactsSchema = z
|
|
238
|
+
.object({
|
|
239
|
+
departmentCount: Count,
|
|
240
|
+
teamCount: Count,
|
|
241
|
+
placedCount: Count,
|
|
242
|
+
reviewItemCount: Count,
|
|
243
|
+
})
|
|
244
|
+
.strict();
|
|
245
|
+
|
|
246
|
+
export type OrgStructureOutcomeFacts = z.infer<
|
|
247
|
+
typeof OrgStructureOutcomeFactsSchema
|
|
248
|
+
>;
|