@company-semantics/contracts 99.3.0 → 101.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 +1 -1
- package/src/api/generated-spec-hash.ts +2 -2
- package/src/api/generated.ts +26 -3
- package/src/index.ts +6 -72
- package/src/org/capability-schemas.ts +118 -0
- package/src/org/reporting-write-schemas.ts +71 -0
- package/src/org/schemas.ts +14 -548
- package/src/org/user-org-schemas.ts +75 -0
- package/src/org/workspace-member-schemas.ts +221 -0
- package/src/org/workspace-overview-schemas.ts +92 -0
- package/src/root-exports/README.md +4 -0
- package/src/root-exports/ci-governance.ts +73 -0
- package/src/source-sync/README.md +26 -5
- package/src/source-sync/__tests__/README.md +4 -2
- package/src/source-sync/__tests__/barrel.test.ts +8 -0
- package/src/source-sync/__tests__/scope-projection.test.ts +32 -0
- package/src/source-sync/__tests__/upkeep.test.ts +92 -1
- package/src/source-sync/index.ts +6 -0
- package/src/source-sync/schemas.ts +13 -0
- package/src/source-sync/upkeep.ts +61 -2
- package/src/source-sync/vocabulary.ts +24 -0
- package/src/workflows/__tests__/outcome.test.ts +81 -0
- package/src/workflows/deferred-route.ts +6 -0
- package/src/workflows/outcome-reasons.ts +31 -0
package/src/source-sync/index.ts
CHANGED
|
@@ -31,6 +31,7 @@ export type {
|
|
|
31
31
|
SourceSyncDocDisposition,
|
|
32
32
|
SourceSyncScopeApprovalState,
|
|
33
33
|
SourceSyncScopeApproverKind,
|
|
34
|
+
SourceSyncScopeBlockedReason,
|
|
34
35
|
DocumentType,
|
|
35
36
|
KnownDocumentType,
|
|
36
37
|
OperatingKnowledgeFamily,
|
|
@@ -47,6 +48,7 @@ export {
|
|
|
47
48
|
SOURCE_SYNC_DOC_DISPOSITIONS,
|
|
48
49
|
SOURCE_SYNC_SCOPE_APPROVAL_STATES,
|
|
49
50
|
SOURCE_SYNC_SCOPE_APPROVER_KINDS,
|
|
51
|
+
SOURCE_SYNC_SCOPE_BLOCKED_REASONS,
|
|
50
52
|
DOCUMENT_TYPES,
|
|
51
53
|
OPERATING_KNOWLEDGE_FAMILIES,
|
|
52
54
|
} from "./vocabulary";
|
|
@@ -190,7 +192,9 @@ export type {
|
|
|
190
192
|
SourceSyncFindingsResponse,
|
|
191
193
|
SourceSyncDismissFindingRequest,
|
|
192
194
|
SourceSyncAutoApplyCeiling,
|
|
195
|
+
SourceSyncStandingOrderSuspendedReason,
|
|
193
196
|
SourceSyncStandingOrder,
|
|
197
|
+
SourceSyncStandingOrderGrantability,
|
|
194
198
|
SourceSyncStandingOrdersResponse,
|
|
195
199
|
SourceSyncPutStandingOrderRequest,
|
|
196
200
|
} from "./upkeep";
|
|
@@ -201,7 +205,9 @@ export {
|
|
|
201
205
|
SourceSyncFindingsResponseSchema,
|
|
202
206
|
SourceSyncDismissFindingRequestSchema,
|
|
203
207
|
SOURCE_SYNC_AUTO_APPLY_CEILINGS,
|
|
208
|
+
SOURCE_SYNC_STANDING_ORDER_SUSPENDED_REASONS,
|
|
204
209
|
SourceSyncStandingOrderSchema,
|
|
210
|
+
SourceSyncStandingOrderGrantabilitySchema,
|
|
205
211
|
SourceSyncStandingOrdersResponseSchema,
|
|
206
212
|
SourceSyncPutStandingOrderRequestSchema,
|
|
207
213
|
} from "./upkeep";
|
|
@@ -26,6 +26,7 @@ import {
|
|
|
26
26
|
SOURCE_SYNC_DOC_RISKS,
|
|
27
27
|
SOURCE_SYNC_SCOPE_APPROVAL_STATES,
|
|
28
28
|
SOURCE_SYNC_SCOPE_APPROVER_KINDS,
|
|
29
|
+
SOURCE_SYNC_SCOPE_BLOCKED_REASONS,
|
|
29
30
|
} from "./vocabulary";
|
|
30
31
|
import { CandidateDiscoverySummarySchema } from "./relevance";
|
|
31
32
|
import { SourceSyncCandidateItemSchema } from "./candidate-item";
|
|
@@ -45,6 +46,16 @@ const PersonLabel = z.string().min(1).max(255);
|
|
|
45
46
|
* still a `unit_head` scope, `assignedTo` is null, and the org's admins approve it.
|
|
46
47
|
* - An approved scope reports the routing its approval RECORDED; a later head
|
|
47
48
|
* change does not rewrite it. A pending scope reports its routing as of now.
|
|
49
|
+
* - `approvable` is whether THE CALLER may approve this pending scope now. A scope
|
|
50
|
+
* can be visible but not approvable: approving needs approval authority on the
|
|
51
|
+
* scope AND the caller's own upload and edit access on the scope's document,
|
|
52
|
+
* because landing and apply act as the approver. `blockedReason` is non-null
|
|
53
|
+
* exactly when a pending scope is not approvable BY THE CALLER:
|
|
54
|
+
* `needs_approval_authority` (no approval authority: e.g. an org admin, who
|
|
55
|
+
* coordinates and never approves) or `needs_doc_access`. An approved scope is
|
|
56
|
+
* never approvable and carries null. At the
|
|
57
|
+
* synthesis checkpoint a pending scope is approvable: apply is decided per
|
|
58
|
+
* document by the edit policy, which approving the candidate set required.
|
|
48
59
|
*/
|
|
49
60
|
export const SourceSyncScopeApprovalProjectionSchema = z.object({
|
|
50
61
|
/** 'company' or 'unit:<uuid>'. */
|
|
@@ -62,6 +73,8 @@ export const SourceSyncScopeApprovalProjectionSchema = z.object({
|
|
|
62
73
|
/** Non-null exactly when a person's approval is recorded. */
|
|
63
74
|
approvedBy: z.object({ label: PersonLabel }).nullable(),
|
|
64
75
|
approvedAt: IsoDateTime.nullable(),
|
|
76
|
+
approvable: z.boolean(),
|
|
77
|
+
blockedReason: z.enum(SOURCE_SYNC_SCOPE_BLOCKED_REASONS).nullable(),
|
|
65
78
|
});
|
|
66
79
|
|
|
67
80
|
/** The discovery run a person started, or that upkeep started on a schedule. */
|
|
@@ -18,6 +18,7 @@
|
|
|
18
18
|
|
|
19
19
|
import { z } from "zod";
|
|
20
20
|
import { IsoDateTime } from "../api/primitives";
|
|
21
|
+
import { SOURCE_SYNC_SCOPE_BLOCKED_REASONS } from "./vocabulary";
|
|
21
22
|
|
|
22
23
|
export const SOURCE_SYNC_FINDING_KINDS = [
|
|
23
24
|
"source_changed",
|
|
@@ -92,6 +93,29 @@ export type SourceSyncAutoApplyCeiling =
|
|
|
92
93
|
|
|
93
94
|
const MicroUsd = z.string().regex(/^[0-9]+$/);
|
|
94
95
|
|
|
96
|
+
/**
|
|
97
|
+
* Why a standing order stopped acting. Closed; mirrors the backend
|
|
98
|
+
* `source_sync_standing_orders_suspended_reason_valid` CHECK exactly.
|
|
99
|
+
*
|
|
100
|
+
* - `grantor_lost_authority`: the grantor no longer holds approval authority on
|
|
101
|
+
* the scope (they left the org, lost the unit, or were an org admin, who no
|
|
102
|
+
* longer approves).
|
|
103
|
+
* - `grantor_lost_doc_access`: the grantor still holds the authority but can no
|
|
104
|
+
* longer upload to and edit the scope's document, so an unattended landing or
|
|
105
|
+
* apply would be refused as them. Nothing is retried as anyone else.
|
|
106
|
+
* - `spend_cap_reached`: the month's spend met the cap the grantor stated.
|
|
107
|
+
* - `connection_lost`: Google Drive was disconnected; a reconnect re-activates
|
|
108
|
+
* nothing.
|
|
109
|
+
*/
|
|
110
|
+
export const SOURCE_SYNC_STANDING_ORDER_SUSPENDED_REASONS = [
|
|
111
|
+
"grantor_lost_authority",
|
|
112
|
+
"grantor_lost_doc_access",
|
|
113
|
+
"spend_cap_reached",
|
|
114
|
+
"connection_lost",
|
|
115
|
+
] as const;
|
|
116
|
+
export type SourceSyncStandingOrderSuspendedReason =
|
|
117
|
+
(typeof SOURCE_SYNC_STANDING_ORDER_SUSPENDED_REASONS)[number];
|
|
118
|
+
|
|
95
119
|
export const SourceSyncStandingOrderSchema = z.object({
|
|
96
120
|
/** 'company' or 'unit:<uuid>'. */
|
|
97
121
|
scopeKey: z.string().min(1).max(64),
|
|
@@ -101,14 +125,46 @@ export const SourceSyncStandingOrderSchema = z.object({
|
|
|
101
125
|
/** Display-only, resolved at read time. */
|
|
102
126
|
grantedByLabel: z.string().max(255).nullable(),
|
|
103
127
|
grantedAt: IsoDateTime,
|
|
104
|
-
/** Why it stopped acting
|
|
128
|
+
/** Why it stopped acting; null unless suspended. */
|
|
105
129
|
suspendedReason: z
|
|
106
|
-
.enum(
|
|
130
|
+
.enum(SOURCE_SYNC_STANDING_ORDER_SUSPENDED_REASONS)
|
|
107
131
|
.nullable(),
|
|
108
132
|
});
|
|
109
133
|
|
|
134
|
+
/**
|
|
135
|
+
* Whether the CALLER may grant (turn on, change or turn on again) the standing
|
|
136
|
+
* order of one scope, decided by the server with exactly the rule a grant
|
|
137
|
+
* enforces: approval authority on the scope (an account owner, or authority on
|
|
138
|
+
* that unit) AND the caller's own upload and edit access on the scope's
|
|
139
|
+
* document. Authority is checked first: `needs_approval_authority` when the
|
|
140
|
+
* caller holds none (an org admin coordinates, never approves);
|
|
141
|
+
* `needs_doc_access` when they hold it but cannot act on the document.
|
|
142
|
+
* `blockedReason` is non-null exactly when `grantable` is false. Advisory: the
|
|
143
|
+
* grant re-decides and refuses (403) whatever this said.
|
|
144
|
+
*/
|
|
145
|
+
export const SourceSyncStandingOrderGrantabilitySchema = z
|
|
146
|
+
.object({
|
|
147
|
+
/** 'company' or 'unit:<uuid>'. */
|
|
148
|
+
scopeKey: z.string().min(1).max(64),
|
|
149
|
+
grantable: z.boolean(),
|
|
150
|
+
blockedReason: z.enum(SOURCE_SYNC_SCOPE_BLOCKED_REASONS).nullable(),
|
|
151
|
+
})
|
|
152
|
+
.strict()
|
|
153
|
+
.refine((g) => g.grantable === (g.blockedReason === null), {
|
|
154
|
+
message:
|
|
155
|
+
"blockedReason is non-null exactly when the scope is not grantable",
|
|
156
|
+
path: ["blockedReason"],
|
|
157
|
+
});
|
|
158
|
+
|
|
110
159
|
export const SourceSyncStandingOrdersResponseSchema = z.object({
|
|
111
160
|
orders: z.array(SourceSyncStandingOrderSchema).max(500),
|
|
161
|
+
/**
|
|
162
|
+
* The caller's grantability: the company scope first, then each distinct
|
|
163
|
+
* scope of an active or suspended order in `orders`, at most once each. A
|
|
164
|
+
* scope absent here is undecided (the server evaluated a bounded number); the
|
|
165
|
+
* grant still decides.
|
|
166
|
+
*/
|
|
167
|
+
grantability: z.array(SourceSyncStandingOrderGrantabilitySchema).max(500),
|
|
112
168
|
});
|
|
113
169
|
|
|
114
170
|
/**
|
|
@@ -132,6 +188,9 @@ export type SourceSyncDismissFindingRequest = z.infer<
|
|
|
132
188
|
export type SourceSyncStandingOrder = z.infer<
|
|
133
189
|
typeof SourceSyncStandingOrderSchema
|
|
134
190
|
>;
|
|
191
|
+
export type SourceSyncStandingOrderGrantability = z.infer<
|
|
192
|
+
typeof SourceSyncStandingOrderGrantabilitySchema
|
|
193
|
+
>;
|
|
135
194
|
export type SourceSyncStandingOrdersResponse = z.infer<
|
|
136
195
|
typeof SourceSyncStandingOrdersResponseSchema
|
|
137
196
|
>;
|
|
@@ -111,6 +111,30 @@ export const SOURCE_SYNC_SCOPE_APPROVER_KINDS = [
|
|
|
111
111
|
export type SourceSyncScopeApproverKind =
|
|
112
112
|
(typeof SOURCE_SYNC_SCOPE_APPROVER_KINDS)[number];
|
|
113
113
|
|
|
114
|
+
/**
|
|
115
|
+
* Why the CALLER may not approve a scope they can see, or grant its standing
|
|
116
|
+
* order. One vocabulary for both decisions (they are the same rule).
|
|
117
|
+
*
|
|
118
|
+
* - `needs_approval_authority`: the caller may see the scope but holds no
|
|
119
|
+
* approval authority on it. Approval authority is an account owner, or
|
|
120
|
+
* authority on that unit (local, inherited or delegated). An org admin
|
|
121
|
+
* coordinates and never approves: being responsible for work getting done is
|
|
122
|
+
* not authority to do it.
|
|
123
|
+
* - `needs_doc_access`: the caller holds the authority but lacks their own
|
|
124
|
+
* upload and edit access on the scope's document; landing and apply act as
|
|
125
|
+
* the approver (or a standing order's grantor), so approving would land
|
|
126
|
+
* nothing.
|
|
127
|
+
*
|
|
128
|
+
* Authority is checked first, so a caller lacking both reads
|
|
129
|
+
* `needs_approval_authority`.
|
|
130
|
+
*/
|
|
131
|
+
export const SOURCE_SYNC_SCOPE_BLOCKED_REASONS = [
|
|
132
|
+
"needs_approval_authority",
|
|
133
|
+
"needs_doc_access",
|
|
134
|
+
] as const;
|
|
135
|
+
export type SourceSyncScopeBlockedReason =
|
|
136
|
+
(typeof SOURCE_SYNC_SCOPE_BLOCKED_REASONS)[number];
|
|
137
|
+
|
|
114
138
|
/**
|
|
115
139
|
* What a source IS — a STRUCTURAL name the model observes, never a relevance verdict
|
|
116
140
|
* (control ADR source-discovery-observation-vs-policy). The set names operating
|
|
@@ -13,6 +13,7 @@ import {
|
|
|
13
13
|
ORG_STRUCTURE_OUTCOME_KIND_OF_REASON,
|
|
14
14
|
SYNTHESIZE_AFFORDANCES_OF_REASON,
|
|
15
15
|
SYNTHESIZE_OUTCOME_KIND_OF_REASON,
|
|
16
|
+
SynthesizeOutcomeFactsSchema,
|
|
16
17
|
WORKFLOW_OUTCOME_KINDS,
|
|
17
18
|
} from "../outcome-reasons";
|
|
18
19
|
|
|
@@ -193,3 +194,83 @@ describe("WorkflowOutcome", () => {
|
|
|
193
194
|
).toBe(false);
|
|
194
195
|
});
|
|
195
196
|
});
|
|
197
|
+
|
|
198
|
+
describe("source-sync/synthesize unattended endings", () => {
|
|
199
|
+
// The strict member (src/workflows/outcome.ts memberSchema): workflowType,
|
|
200
|
+
// executionEpoch, kind, reason, facts, disposition, affordances. Each test
|
|
201
|
+
// below supplies kind, reason, facts and its `review` disposition.
|
|
202
|
+
const base = {
|
|
203
|
+
workflowType: "source-sync/synthesize",
|
|
204
|
+
executionEpoch: 0,
|
|
205
|
+
affordances: [],
|
|
206
|
+
};
|
|
207
|
+
const unattended = {
|
|
208
|
+
scopeCount: 1,
|
|
209
|
+
violationCount: 0,
|
|
210
|
+
notLandedCount: 0,
|
|
211
|
+
appliedDocCount: 1,
|
|
212
|
+
notAppliedDocCount: 1,
|
|
213
|
+
};
|
|
214
|
+
|
|
215
|
+
it("a partly applied unattended batch is partial and may hand off to review", () => {
|
|
216
|
+
expect(SYNTHESIZE_OUTCOME_KIND_OF_REASON.updates_applied_partial).toBe(
|
|
217
|
+
"partial",
|
|
218
|
+
);
|
|
219
|
+
expect(SYNTHESIZE_OUTCOME_KIND_OF_REASON.apply_refused).toBe("partial");
|
|
220
|
+
expect(
|
|
221
|
+
WorkflowOutcomeSchema.safeParse({
|
|
222
|
+
...base,
|
|
223
|
+
kind: "partial",
|
|
224
|
+
reason: "updates_applied_partial",
|
|
225
|
+
facts: unattended,
|
|
226
|
+
disposition: {
|
|
227
|
+
kind: "review",
|
|
228
|
+
requestType: "source-sync/synthesis",
|
|
229
|
+
requestId: "batch-1",
|
|
230
|
+
},
|
|
231
|
+
}).success,
|
|
232
|
+
).toBe(true);
|
|
233
|
+
});
|
|
234
|
+
|
|
235
|
+
it("an unattended ending names how many documents it applied", () => {
|
|
236
|
+
const { appliedDocCount: _a, ...withoutApplied } = unattended;
|
|
237
|
+
expect(
|
|
238
|
+
WorkflowOutcomeSchema.safeParse({
|
|
239
|
+
...base,
|
|
240
|
+
kind: "partial",
|
|
241
|
+
reason: "apply_refused",
|
|
242
|
+
facts: withoutApplied,
|
|
243
|
+
disposition: {
|
|
244
|
+
kind: "review",
|
|
245
|
+
requestType: "source-sync/synthesis",
|
|
246
|
+
requestId: "batch-1",
|
|
247
|
+
},
|
|
248
|
+
}).success,
|
|
249
|
+
).toBe(false);
|
|
250
|
+
expect(
|
|
251
|
+
WorkflowOutcomeSchema.safeParse({
|
|
252
|
+
...base,
|
|
253
|
+
kind: "partial",
|
|
254
|
+
reason: "apply_refused",
|
|
255
|
+
facts: { ...unattended, appliedDocCount: 0, notAppliedDocCount: 2 },
|
|
256
|
+
disposition: {
|
|
257
|
+
kind: "review",
|
|
258
|
+
requestType: "source-sync/synthesis",
|
|
259
|
+
requestId: "batch-1",
|
|
260
|
+
},
|
|
261
|
+
}).success,
|
|
262
|
+
).toBe(true);
|
|
263
|
+
});
|
|
264
|
+
|
|
265
|
+
it("an attended ending says no automatic apply happened with null counts", () => {
|
|
266
|
+
expect(
|
|
267
|
+
SynthesizeOutcomeFactsSchema.safeParse({
|
|
268
|
+
scopeCount: 1,
|
|
269
|
+
violationCount: 0,
|
|
270
|
+
notLandedCount: 0,
|
|
271
|
+
appliedDocCount: null,
|
|
272
|
+
notAppliedDocCount: null,
|
|
273
|
+
}).success,
|
|
274
|
+
).toBe(true);
|
|
275
|
+
});
|
|
276
|
+
});
|
|
@@ -147,7 +147,13 @@ export const SYNTHESIZE_DEFERRED_ROUTE_OF_REASON = {
|
|
|
147
147
|
// prettier-ignore
|
|
148
148
|
updates_ready: { route: "covered", by: "sourceSync.review_ready", fallback: "notice", audience: "creator" },
|
|
149
149
|
// prettier-ignore
|
|
150
|
+
updates_ready_partial: { route: "covered", by: "sourceSync.review_ready", fallback: "notice", audience: "creator" },
|
|
151
|
+
// prettier-ignore
|
|
150
152
|
updates_applied: { route: "covered", by: "sourceSync.docs_updated", fallback: "notice", audience: "creator" },
|
|
153
|
+
// prettier-ignore
|
|
154
|
+
updates_applied_partial: { route: "covered", by: "sourceSync.review_ready", fallback: "notice", audience: "creator" },
|
|
155
|
+
// prettier-ignore
|
|
156
|
+
apply_refused: { route: "covered", by: "sourceSync.review_ready", fallback: "notice", audience: "creator" },
|
|
151
157
|
stopped_by_user: STOPPED,
|
|
152
158
|
paused_billing: EXPLAINED,
|
|
153
159
|
run_ceiling_reached: NOTICE_ADMINS,
|
|
@@ -120,7 +120,21 @@ export type DiscoverOutcomeFacts = z.infer<typeof DiscoverOutcomeFactsSchema>;
|
|
|
120
120
|
/** Why a synthesis (landing then drafting) run ended the way it did. */
|
|
121
121
|
export const SYNTHESIZE_OUTCOME_KIND_OF_REASON = {
|
|
122
122
|
updates_ready: "produced",
|
|
123
|
+
/** Drafted from what landed; some approved files could not be brought in. */
|
|
124
|
+
updates_ready_partial: "partial",
|
|
125
|
+
/** An unattended batch: the standing order applied EVERY drafted document. */
|
|
123
126
|
updates_applied: "produced",
|
|
127
|
+
/**
|
|
128
|
+
* An unattended batch: the standing order applied some documents; the rest
|
|
129
|
+
* (proposed by the auto-apply gate, or refused when applied) wait for a person.
|
|
130
|
+
*/
|
|
131
|
+
updates_applied_partial: "partial",
|
|
132
|
+
/**
|
|
133
|
+
* An unattended batch: the automatic apply was attempted and refused for every
|
|
134
|
+
* document it tried, so nothing was applied; the drafts wait for review. Never
|
|
135
|
+
* narrated as applied.
|
|
136
|
+
*/
|
|
137
|
+
apply_refused: "partial",
|
|
124
138
|
stopped_by_user: "stopped",
|
|
125
139
|
paused_billing: "failed",
|
|
126
140
|
run_ceiling_reached: "failed",
|
|
@@ -142,7 +156,10 @@ export type SynthesizeOutcomeReason =
|
|
|
142
156
|
/** What each synthesis ending offers. TOTAL; an empty list is explicit. */
|
|
143
157
|
export const SYNTHESIZE_AFFORDANCES_OF_REASON = {
|
|
144
158
|
updates_ready: [],
|
|
159
|
+
updates_ready_partial: [],
|
|
145
160
|
updates_applied: [],
|
|
161
|
+
updates_applied_partial: [],
|
|
162
|
+
apply_refused: [],
|
|
146
163
|
stopped_by_user: [],
|
|
147
164
|
paused_billing: [OPEN_BILLING],
|
|
148
165
|
run_ceiling_reached: [],
|
|
@@ -168,6 +185,20 @@ export const SynthesizeOutcomeFactsSchema = z
|
|
|
168
185
|
scopeCount: Count,
|
|
169
186
|
/** Authority or size violations that left the batch incomplete. */
|
|
170
187
|
violationCount: Count,
|
|
188
|
+
/** Approved files to attach that did not land (refused, unreadable, gone). */
|
|
189
|
+
notLandedCount: Count,
|
|
190
|
+
/**
|
|
191
|
+
* Documents an UNATTENDED batch applied on its own (the standing order's
|
|
192
|
+
* auto-apply). Null when no automatic apply happened at all: an attended
|
|
193
|
+
* run, or an unattended run that ended before its batch validated. Missing
|
|
194
|
+
* evidence is null, never 0.
|
|
195
|
+
*/
|
|
196
|
+
appliedDocCount: Count.nullable(),
|
|
197
|
+
/**
|
|
198
|
+
* Documents of an UNATTENDED batch left for a person (proposed by the gate,
|
|
199
|
+
* or refused when applied). Null exactly when `appliedDocCount` is null.
|
|
200
|
+
*/
|
|
201
|
+
notAppliedDocCount: Count.nullable(),
|
|
171
202
|
})
|
|
172
203
|
.strict();
|
|
173
204
|
|