@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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@company-semantics/contracts",
3
- "version": "99.2.0",
3
+ "version": "100.0.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 = 'bdfe41ff343f' as const;
3
- export const SPEC_HASH_FULL = 'bdfe41ff343f7f914f93bf62144b67f3f1ff2f0cd2c53e6bb0727dff96eb543b' as const;
2
+ export const SPEC_HASH = '7e0143b235a3' as const;
3
+ export const SPEC_HASH_FULL = '7e0143b235a3cc79cc020305bc8e4cf82c7784774cff280504fe70c0e9ee9a81' as const;
@@ -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/WebP/HEIC) up to 5 MB. The caller must be the target user or hold org.manage_users. HEIC/HEIF is converted to JPEG and the bytes are re-hosted via the blob store. Returns the resolved avatar.
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/WebP/HEIC) up to 5 MB. Requires org.settings.write. HEIC/HEIF is converted to JPEG 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.
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],
@@ -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