@company-semantics/contracts 99.2.0 → 100.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 +269 -5
- package/src/generated/openapi-routes.ts +3 -0
- package/src/source-sync/__tests__/scope-projection.test.ts +24 -0
- package/src/source-sync/index.ts +2 -0
- package/src/source-sync/schemas.ts +10 -0
- package/src/source-sync/vocabulary.ts +10 -0
- package/src/workflows/deferred-route.ts +2 -0
- package/src/workflows/outcome-reasons.ts +5 -0
package/package.json
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
1
|
// AUTO-GENERATED — do not edit. Run pnpm generate:spec-hash to regenerate.
|
|
2
|
-
export const SPEC_HASH = '
|
|
3
|
-
export const SPEC_HASH_FULL = '
|
|
2
|
+
export const SPEC_HASH = '7e0143b235a3' as const;
|
|
3
|
+
export const SPEC_HASH_FULL = '7e0143b235a3cc79cc020305bc8e4cf82c7784774cff280504fe70c0e9ee9a81' as const;
|
package/src/api/generated.ts
CHANGED
|
@@ -3026,7 +3026,7 @@ export interface paths {
|
|
|
3026
3026
|
get?: never;
|
|
3027
3027
|
/**
|
|
3028
3028
|
* Upload (replace) a user's avatar photo from an image file
|
|
3029
|
-
* @description Accepts a multipart image upload (PNG/JPEG/
|
|
3029
|
+
* @description Accepts a multipart image upload (PNG/JPEG/WEBP/GIF/HEIC/HEIF) up to 5 MB. The caller must be the target user or hold org.manage_users. HEIC/HEIF is converted to JPEG (GIF to its first frame as PNG) and the bytes are re-hosted via the blob store. Returns the resolved avatar.
|
|
3030
3030
|
*/
|
|
3031
3031
|
put: operations["uploadUserAvatar"];
|
|
3032
3032
|
post?: never;
|
|
@@ -3050,7 +3050,7 @@ export interface paths {
|
|
|
3050
3050
|
get?: never;
|
|
3051
3051
|
/**
|
|
3052
3052
|
* Upload (replace) one of the org's two marks from an image file
|
|
3053
|
-
* @description Accepts a multipart image upload (PNG/JPEG/
|
|
3053
|
+
* @description Accepts a multipart image upload (PNG/JPEG/WEBP/GIF/HEIC/HEIF) up to 5 MB. Requires org.settings.write. HEIC/HEIF is converted to JPEG (GIF to its first frame as PNG) and the bytes are re-hosted via the blob store, never hot-linked. The slot to write: `app_logo` (the square mark, also the slot the Slack workspace icon fills) or `logo` (the wordmark, which is upload-only). Returns both slots as signed URLs.
|
|
3054
3054
|
*/
|
|
3055
3055
|
put: operations["uploadOrgLogo"];
|
|
3056
3056
|
post?: never;
|
|
@@ -4116,6 +4116,57 @@ export interface paths {
|
|
|
4116
4116
|
patch?: never;
|
|
4117
4117
|
trace?: never;
|
|
4118
4118
|
};
|
|
4119
|
+
"/api/schedules/tick": {
|
|
4120
|
+
parameters: {
|
|
4121
|
+
query?: never;
|
|
4122
|
+
header?: never;
|
|
4123
|
+
path?: never;
|
|
4124
|
+
cookie?: never;
|
|
4125
|
+
};
|
|
4126
|
+
get?: never;
|
|
4127
|
+
put?: never;
|
|
4128
|
+
/** Run one schedule clock pass for the edge tick source */
|
|
4129
|
+
post: operations["scheduleTick"];
|
|
4130
|
+
delete?: never;
|
|
4131
|
+
options?: never;
|
|
4132
|
+
head?: never;
|
|
4133
|
+
patch?: never;
|
|
4134
|
+
trace?: never;
|
|
4135
|
+
};
|
|
4136
|
+
"/api/schedules/executions/start": {
|
|
4137
|
+
parameters: {
|
|
4138
|
+
query?: never;
|
|
4139
|
+
header?: never;
|
|
4140
|
+
path?: never;
|
|
4141
|
+
cookie?: never;
|
|
4142
|
+
};
|
|
4143
|
+
get?: never;
|
|
4144
|
+
put?: never;
|
|
4145
|
+
/** Claim a scheduled execution (or admit a legacy run) for the calling workflow's run */
|
|
4146
|
+
post: operations["scheduleExecutionStart"];
|
|
4147
|
+
delete?: never;
|
|
4148
|
+
options?: never;
|
|
4149
|
+
head?: never;
|
|
4150
|
+
patch?: never;
|
|
4151
|
+
trace?: never;
|
|
4152
|
+
};
|
|
4153
|
+
"/api/schedules/executions/finish": {
|
|
4154
|
+
parameters: {
|
|
4155
|
+
query?: never;
|
|
4156
|
+
header?: never;
|
|
4157
|
+
path?: never;
|
|
4158
|
+
cookie?: never;
|
|
4159
|
+
};
|
|
4160
|
+
get?: never;
|
|
4161
|
+
put?: never;
|
|
4162
|
+
/** Report a claimed execution's conclusion, fenced by its claim epoch */
|
|
4163
|
+
post: operations["scheduleExecutionFinish"];
|
|
4164
|
+
delete?: never;
|
|
4165
|
+
options?: never;
|
|
4166
|
+
head?: never;
|
|
4167
|
+
patch?: never;
|
|
4168
|
+
trace?: never;
|
|
4169
|
+
};
|
|
4119
4170
|
"/api/internal-admin/impersonate/start": {
|
|
4120
4171
|
parameters: {
|
|
4121
4172
|
query?: never;
|
|
@@ -7418,10 +7469,11 @@ export interface components {
|
|
|
7418
7469
|
/** @enum {string} */
|
|
7419
7470
|
kind: "produced" | "empty" | "partial" | "stopped" | "failed" | "abandoned" | "unknown";
|
|
7420
7471
|
/** @enum {string} */
|
|
7421
|
-
reason: "updates_ready" | "updates_applied" | "stopped_by_user" | "paused_billing" | "run_ceiling_reached" | "pricing_unconfigured" | "synthesis_incomplete" | "approval_mismatch" | "landing_refused" | "standing_order_refused" | "no_connection" | "source_sync_off" | "internal_error" | "abandoned" | "unrecognized_terminal_state";
|
|
7472
|
+
reason: "updates_ready" | "updates_ready_partial" | "updates_applied" | "stopped_by_user" | "paused_billing" | "run_ceiling_reached" | "pricing_unconfigured" | "synthesis_incomplete" | "approval_mismatch" | "landing_refused" | "standing_order_refused" | "no_connection" | "source_sync_off" | "internal_error" | "abandoned" | "unrecognized_terminal_state";
|
|
7422
7473
|
facts: {
|
|
7423
7474
|
scopeCount: number;
|
|
7424
7475
|
violationCount: number;
|
|
7476
|
+
notLandedCount: number;
|
|
7425
7477
|
} | null;
|
|
7426
7478
|
disposition: {
|
|
7427
7479
|
/** @constant */
|
|
@@ -9966,6 +10018,68 @@ export interface components {
|
|
|
9966
10018
|
};
|
|
9967
10019
|
}[];
|
|
9968
10020
|
};
|
|
10021
|
+
ScheduleTickResponse: {
|
|
10022
|
+
/** @enum {string} */
|
|
10023
|
+
source: "edge" | "jobqueue";
|
|
10024
|
+
/** Format: date-time */
|
|
10025
|
+
tickedAt: string;
|
|
10026
|
+
reconcileOk: boolean;
|
|
10027
|
+
registryVersion: string | null;
|
|
10028
|
+
dispatched: number;
|
|
10029
|
+
incidentsOpened: number;
|
|
10030
|
+
incidentsResolved: number;
|
|
10031
|
+
};
|
|
10032
|
+
ScheduleTickRequest: {
|
|
10033
|
+
/** @enum {string} */
|
|
10034
|
+
source: "edge" | "jobqueue";
|
|
10035
|
+
/** Format: date-time */
|
|
10036
|
+
scheduledFor: string;
|
|
10037
|
+
cron: string;
|
|
10038
|
+
};
|
|
10039
|
+
ScheduleClaimResponse: {
|
|
10040
|
+
/** @constant */
|
|
10041
|
+
outcome: "claimed";
|
|
10042
|
+
executionId: string;
|
|
10043
|
+
claimEpoch: number;
|
|
10044
|
+
/** Format: date-time */
|
|
10045
|
+
leaseUntil: string;
|
|
10046
|
+
/** @enum {string} */
|
|
10047
|
+
mode: "off" | "shadow" | "live";
|
|
10048
|
+
} | {
|
|
10049
|
+
/** @constant */
|
|
10050
|
+
outcome: "legacy";
|
|
10051
|
+
scheduleId: string;
|
|
10052
|
+
/** @enum {string} */
|
|
10053
|
+
mode: "off" | "shadow" | "live";
|
|
10054
|
+
} | {
|
|
10055
|
+
/** @constant */
|
|
10056
|
+
outcome: "refused";
|
|
10057
|
+
/** @enum {string} */
|
|
10058
|
+
reason: "already_completed" | "lease_held" | "legacy_run_not_admitted" | "not_dispatched" | "binding_mismatch" | "schedule_off";
|
|
10059
|
+
};
|
|
10060
|
+
ScheduleStartRequest: {
|
|
10061
|
+
executionId: string | null;
|
|
10062
|
+
attempt: number | null;
|
|
10063
|
+
};
|
|
10064
|
+
ScheduleFinishResponse: {
|
|
10065
|
+
/** @constant */
|
|
10066
|
+
recorded: true;
|
|
10067
|
+
executionId: string;
|
|
10068
|
+
/** @enum {string} */
|
|
10069
|
+
state: "due" | "dispatch_requested" | "started" | "completed" | "output_verified" | "timed_out" | "superseded" | "missed" | "acknowledged_loss";
|
|
10070
|
+
} | {
|
|
10071
|
+
/** @constant */
|
|
10072
|
+
recorded: false;
|
|
10073
|
+
/** @enum {string} */
|
|
10074
|
+
reason: "stale_epoch" | "binding_mismatch" | "unknown_execution";
|
|
10075
|
+
};
|
|
10076
|
+
ScheduleFinishRequest: {
|
|
10077
|
+
executionId: string;
|
|
10078
|
+
attempt: number;
|
|
10079
|
+
claimEpoch: number;
|
|
10080
|
+
/** @enum {string} */
|
|
10081
|
+
conclusion: "success" | "failure" | "cancelled";
|
|
10082
|
+
};
|
|
9969
10083
|
ImpersonationSessionResponse: {
|
|
9970
10084
|
session: {
|
|
9971
10085
|
impersonationSessionId: string;
|
|
@@ -10735,6 +10849,8 @@ export interface components {
|
|
|
10735
10849
|
label: string;
|
|
10736
10850
|
} | null;
|
|
10737
10851
|
approvedAt: string | null;
|
|
10852
|
+
approvable: boolean;
|
|
10853
|
+
blockedReason: "needs_doc_access" | null;
|
|
10738
10854
|
}[];
|
|
10739
10855
|
discovery: {
|
|
10740
10856
|
foundThisRun: number;
|
|
@@ -10799,6 +10915,8 @@ export interface components {
|
|
|
10799
10915
|
label: string;
|
|
10800
10916
|
} | null;
|
|
10801
10917
|
approvedAt: string | null;
|
|
10918
|
+
approvable: boolean;
|
|
10919
|
+
blockedReason: "needs_doc_access" | null;
|
|
10802
10920
|
}[];
|
|
10803
10921
|
/** Format: uuid */
|
|
10804
10922
|
workflowId: string;
|
|
@@ -10926,6 +11044,8 @@ export interface components {
|
|
|
10926
11044
|
label: string;
|
|
10927
11045
|
} | null;
|
|
10928
11046
|
approvedAt: string | null;
|
|
11047
|
+
approvable: boolean;
|
|
11048
|
+
blockedReason: "needs_doc_access" | null;
|
|
10929
11049
|
}[];
|
|
10930
11050
|
};
|
|
10931
11051
|
SourceSyncApplyResponse: {
|
|
@@ -10985,6 +11105,8 @@ export interface components {
|
|
|
10985
11105
|
label: string;
|
|
10986
11106
|
} | null;
|
|
10987
11107
|
approvedAt: string | null;
|
|
11108
|
+
approvable: boolean;
|
|
11109
|
+
blockedReason: "needs_doc_access" | null;
|
|
10988
11110
|
}[];
|
|
10989
11111
|
};
|
|
10990
11112
|
SourceSyncRulesResponse: {
|
|
@@ -16916,7 +17038,7 @@ export interface operations {
|
|
|
16916
17038
|
};
|
|
16917
17039
|
content?: never;
|
|
16918
17040
|
};
|
|
16919
|
-
/** @description Unsupported image type or bytes are not a valid image */
|
|
17041
|
+
/** @description Unsupported image type (accepted: PNG/JPEG/WEBP/GIF/HEIC/HEIF) or bytes are not a valid image */
|
|
16920
17042
|
415: {
|
|
16921
17043
|
headers: {
|
|
16922
17044
|
[name: string]: unknown;
|
|
@@ -17006,7 +17128,7 @@ export interface operations {
|
|
|
17006
17128
|
};
|
|
17007
17129
|
content?: never;
|
|
17008
17130
|
};
|
|
17009
|
-
/** @description Unsupported image type or bytes are not a valid image */
|
|
17131
|
+
/** @description Unsupported image type (accepted: PNG/JPEG/WEBP/GIF/HEIC/HEIF) or bytes are not a valid image */
|
|
17010
17132
|
415: {
|
|
17011
17133
|
headers: {
|
|
17012
17134
|
[name: string]: unknown;
|
|
@@ -18885,6 +19007,148 @@ export interface operations {
|
|
|
18885
19007
|
};
|
|
18886
19008
|
};
|
|
18887
19009
|
};
|
|
19010
|
+
scheduleTick: {
|
|
19011
|
+
parameters: {
|
|
19012
|
+
query?: never;
|
|
19013
|
+
header?: never;
|
|
19014
|
+
path?: never;
|
|
19015
|
+
cookie?: never;
|
|
19016
|
+
};
|
|
19017
|
+
requestBody: {
|
|
19018
|
+
content: {
|
|
19019
|
+
"application/json": components["schemas"]["ScheduleTickRequest"];
|
|
19020
|
+
};
|
|
19021
|
+
};
|
|
19022
|
+
responses: {
|
|
19023
|
+
/** @description The pass ran (reconcileOk false when it could not conclude) */
|
|
19024
|
+
200: {
|
|
19025
|
+
headers: {
|
|
19026
|
+
[name: string]: unknown;
|
|
19027
|
+
};
|
|
19028
|
+
content: {
|
|
19029
|
+
"application/json": components["schemas"]["ScheduleTickResponse"];
|
|
19030
|
+
};
|
|
19031
|
+
};
|
|
19032
|
+
/** @description Payload invalid, or a source other than edge */
|
|
19033
|
+
400: {
|
|
19034
|
+
headers: {
|
|
19035
|
+
[name: string]: unknown;
|
|
19036
|
+
};
|
|
19037
|
+
content?: never;
|
|
19038
|
+
};
|
|
19039
|
+
/** @description Missing or invalid edge bearer token */
|
|
19040
|
+
401: {
|
|
19041
|
+
headers: {
|
|
19042
|
+
[name: string]: unknown;
|
|
19043
|
+
};
|
|
19044
|
+
content?: never;
|
|
19045
|
+
};
|
|
19046
|
+
/** @description The pass did not finish within its deadline */
|
|
19047
|
+
503: {
|
|
19048
|
+
headers: {
|
|
19049
|
+
[name: string]: unknown;
|
|
19050
|
+
};
|
|
19051
|
+
content?: never;
|
|
19052
|
+
};
|
|
19053
|
+
};
|
|
19054
|
+
};
|
|
19055
|
+
scheduleExecutionStart: {
|
|
19056
|
+
parameters: {
|
|
19057
|
+
query?: never;
|
|
19058
|
+
header?: never;
|
|
19059
|
+
path?: never;
|
|
19060
|
+
cookie?: never;
|
|
19061
|
+
};
|
|
19062
|
+
requestBody: {
|
|
19063
|
+
content: {
|
|
19064
|
+
"application/json": components["schemas"]["ScheduleStartRequest"];
|
|
19065
|
+
};
|
|
19066
|
+
};
|
|
19067
|
+
responses: {
|
|
19068
|
+
/** @description claimed, legacy (runs, counts toward no slot) or refused (skip the work) */
|
|
19069
|
+
200: {
|
|
19070
|
+
headers: {
|
|
19071
|
+
[name: string]: unknown;
|
|
19072
|
+
};
|
|
19073
|
+
content: {
|
|
19074
|
+
"application/json": components["schemas"]["ScheduleClaimResponse"];
|
|
19075
|
+
};
|
|
19076
|
+
};
|
|
19077
|
+
/** @description Payload invalid */
|
|
19078
|
+
400: {
|
|
19079
|
+
headers: {
|
|
19080
|
+
[name: string]: unknown;
|
|
19081
|
+
};
|
|
19082
|
+
content?: never;
|
|
19083
|
+
};
|
|
19084
|
+
/** @description Missing or rejected GitHub Actions OIDC token */
|
|
19085
|
+
401: {
|
|
19086
|
+
headers: {
|
|
19087
|
+
[name: string]: unknown;
|
|
19088
|
+
};
|
|
19089
|
+
content?: never;
|
|
19090
|
+
};
|
|
19091
|
+
/** @description The workflow is not bound to a registered schedule */
|
|
19092
|
+
403: {
|
|
19093
|
+
headers: {
|
|
19094
|
+
[name: string]: unknown;
|
|
19095
|
+
};
|
|
19096
|
+
content?: never;
|
|
19097
|
+
};
|
|
19098
|
+
/** @description GitHub could not confirm the run; nothing was claimed */
|
|
19099
|
+
503: {
|
|
19100
|
+
headers: {
|
|
19101
|
+
[name: string]: unknown;
|
|
19102
|
+
};
|
|
19103
|
+
content?: never;
|
|
19104
|
+
};
|
|
19105
|
+
};
|
|
19106
|
+
};
|
|
19107
|
+
scheduleExecutionFinish: {
|
|
19108
|
+
parameters: {
|
|
19109
|
+
query?: never;
|
|
19110
|
+
header?: never;
|
|
19111
|
+
path?: never;
|
|
19112
|
+
cookie?: never;
|
|
19113
|
+
};
|
|
19114
|
+
requestBody: {
|
|
19115
|
+
content: {
|
|
19116
|
+
"application/json": components["schemas"]["ScheduleFinishRequest"];
|
|
19117
|
+
};
|
|
19118
|
+
};
|
|
19119
|
+
responses: {
|
|
19120
|
+
/** @description recorded, or not recorded with the reason (a stale epoch is not an error) */
|
|
19121
|
+
200: {
|
|
19122
|
+
headers: {
|
|
19123
|
+
[name: string]: unknown;
|
|
19124
|
+
};
|
|
19125
|
+
content: {
|
|
19126
|
+
"application/json": components["schemas"]["ScheduleFinishResponse"];
|
|
19127
|
+
};
|
|
19128
|
+
};
|
|
19129
|
+
/** @description Payload invalid */
|
|
19130
|
+
400: {
|
|
19131
|
+
headers: {
|
|
19132
|
+
[name: string]: unknown;
|
|
19133
|
+
};
|
|
19134
|
+
content?: never;
|
|
19135
|
+
};
|
|
19136
|
+
/** @description Missing or rejected GitHub Actions OIDC token */
|
|
19137
|
+
401: {
|
|
19138
|
+
headers: {
|
|
19139
|
+
[name: string]: unknown;
|
|
19140
|
+
};
|
|
19141
|
+
content?: never;
|
|
19142
|
+
};
|
|
19143
|
+
/** @description The workflow is not bound to this schedule */
|
|
19144
|
+
403: {
|
|
19145
|
+
headers: {
|
|
19146
|
+
[name: string]: unknown;
|
|
19147
|
+
};
|
|
19148
|
+
content?: never;
|
|
19149
|
+
};
|
|
19150
|
+
};
|
|
19151
|
+
};
|
|
18888
19152
|
startImpersonation: {
|
|
18889
19153
|
parameters: {
|
|
18890
19154
|
query?: never;
|
|
@@ -218,6 +218,9 @@ export const openApiRoutes = {
|
|
|
218
218
|
'/api/reporting/move/apply': ['POST'],
|
|
219
219
|
'/api/reporting/move/preview': ['POST'],
|
|
220
220
|
'/api/reporting/positions/{positionId}/manager': ['PUT'],
|
|
221
|
+
'/api/schedules/executions/finish': ['POST'],
|
|
222
|
+
'/api/schedules/executions/start': ['POST'],
|
|
223
|
+
'/api/schedules/tick': ['POST'],
|
|
221
224
|
'/api/scope/check': ['GET'],
|
|
222
225
|
'/api/scope/check-batch': ['POST'],
|
|
223
226
|
'/api/source-sync/candidate-sets/current': ['GET'],
|
|
@@ -34,6 +34,8 @@ function scope(overrides: Record<string, unknown> = {}) {
|
|
|
34
34
|
vacantHead: false,
|
|
35
35
|
approvedBy: null,
|
|
36
36
|
approvedAt: null,
|
|
37
|
+
approvable: true,
|
|
38
|
+
blockedReason: null,
|
|
37
39
|
...overrides,
|
|
38
40
|
};
|
|
39
41
|
}
|
|
@@ -100,6 +102,28 @@ describe("SourceSyncScopeApprovalProjectionSchema", () => {
|
|
|
100
102
|
});
|
|
101
103
|
});
|
|
102
104
|
|
|
105
|
+
describe("approvable and blockedReason", () => {
|
|
106
|
+
it("accepts a visible scope the caller may not approve, with its reason", () => {
|
|
107
|
+
const parsed = SourceSyncScopeApprovalProjectionSchema.safeParse(
|
|
108
|
+
scope({ approvable: false, blockedReason: "needs_doc_access" }),
|
|
109
|
+
);
|
|
110
|
+
expect(parsed.success).toBe(true);
|
|
111
|
+
});
|
|
112
|
+
|
|
113
|
+
it("refuses a reason outside the vocabulary and a missing approvable", () => {
|
|
114
|
+
expect(
|
|
115
|
+
SourceSyncScopeApprovalProjectionSchema.safeParse(
|
|
116
|
+
scope({ approvable: false, blockedReason: "not_admin" }),
|
|
117
|
+
).success,
|
|
118
|
+
).toBe(false);
|
|
119
|
+
const { approvable: _omit, ...withoutApprovable } = scope();
|
|
120
|
+
expect(
|
|
121
|
+
SourceSyncScopeApprovalProjectionSchema.safeParse(withoutApprovable)
|
|
122
|
+
.success,
|
|
123
|
+
).toBe(false);
|
|
124
|
+
});
|
|
125
|
+
});
|
|
126
|
+
|
|
103
127
|
describe("scopes is required on every checkpoint response", () => {
|
|
104
128
|
const schemas = [
|
|
105
129
|
["candidate set", SourceSyncCandidateSetResponseSchema],
|
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";
|
|
@@ -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,13 @@ 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 the caller's own upload and
|
|
51
|
+
* edit access on the scope's document, because landing and apply act as the
|
|
52
|
+
* approver. `blockedReason` is non-null exactly when a pending scope is not
|
|
53
|
+
* approvable; an approved scope is never approvable and carries null. At the
|
|
54
|
+
* synthesis checkpoint a pending scope is approvable: apply is decided per
|
|
55
|
+
* document by the edit policy, which approving the candidate set required.
|
|
48
56
|
*/
|
|
49
57
|
export const SourceSyncScopeApprovalProjectionSchema = z.object({
|
|
50
58
|
/** 'company' or 'unit:<uuid>'. */
|
|
@@ -62,6 +70,8 @@ export const SourceSyncScopeApprovalProjectionSchema = z.object({
|
|
|
62
70
|
/** Non-null exactly when a person's approval is recorded. */
|
|
63
71
|
approvedBy: z.object({ label: PersonLabel }).nullable(),
|
|
64
72
|
approvedAt: IsoDateTime.nullable(),
|
|
73
|
+
approvable: z.boolean(),
|
|
74
|
+
blockedReason: z.enum(SOURCE_SYNC_SCOPE_BLOCKED_REASONS).nullable(),
|
|
65
75
|
});
|
|
66
76
|
|
|
67
77
|
/** The discovery run a person started, or that upkeep started on a schedule. */
|
|
@@ -111,6 +111,16 @@ 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. `needs_doc_access`: the
|
|
116
|
+
* landing and the apply act as the approver, so approving needs the caller's own
|
|
117
|
+
* upload and edit access on the scope's document (an admin holds only metadata
|
|
118
|
+
* access to company.md content).
|
|
119
|
+
*/
|
|
120
|
+
export const SOURCE_SYNC_SCOPE_BLOCKED_REASONS = ["needs_doc_access"] as const;
|
|
121
|
+
export type SourceSyncScopeBlockedReason =
|
|
122
|
+
(typeof SOURCE_SYNC_SCOPE_BLOCKED_REASONS)[number];
|
|
123
|
+
|
|
114
124
|
/**
|
|
115
125
|
* What a source IS — a STRUCTURAL name the model observes, never a relevance verdict
|
|
116
126
|
* (control ADR source-discovery-observation-vs-policy). The set names operating
|
|
@@ -147,6 +147,8 @@ 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" },
|
|
151
153
|
stopped_by_user: STOPPED,
|
|
152
154
|
paused_billing: EXPLAINED,
|
|
@@ -120,6 +120,8 @@ 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",
|
|
123
125
|
updates_applied: "produced",
|
|
124
126
|
stopped_by_user: "stopped",
|
|
125
127
|
paused_billing: "failed",
|
|
@@ -142,6 +144,7 @@ export type SynthesizeOutcomeReason =
|
|
|
142
144
|
/** What each synthesis ending offers. TOTAL; an empty list is explicit. */
|
|
143
145
|
export const SYNTHESIZE_AFFORDANCES_OF_REASON = {
|
|
144
146
|
updates_ready: [],
|
|
147
|
+
updates_ready_partial: [],
|
|
145
148
|
updates_applied: [],
|
|
146
149
|
stopped_by_user: [],
|
|
147
150
|
paused_billing: [OPEN_BILLING],
|
|
@@ -168,6 +171,8 @@ export const SynthesizeOutcomeFactsSchema = z
|
|
|
168
171
|
scopeCount: Count,
|
|
169
172
|
/** Authority or size violations that left the batch incomplete. */
|
|
170
173
|
violationCount: Count,
|
|
174
|
+
/** Approved files to attach that did not land (refused, unreadable, gone). */
|
|
175
|
+
notLandedCount: Count,
|
|
171
176
|
})
|
|
172
177
|
.strict();
|
|
173
178
|
|