@company-semantics/contracts 89.2.0 → 90.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.
@@ -0,0 +1,152 @@
1
+ /**
2
+ * Index-first discovery's ENUMERATION vocabulary (control ADR
3
+ * index-first-operating-knowledge-discovery; INV-SOURCESYNC-PARTIAL-ENUMERATION-IS-UNKNOWN).
4
+ *
5
+ * A discovery run first enumerates every source reachable through the connection,
6
+ * root by root (the person's own corpus, each shared drive, the domain corpus).
7
+ * How completely it did so is a closed answer that separates OUR ceiling from the
8
+ * PROVIDER's incompleteness, so a partial enumeration can never read as an index of
9
+ * the corpus: only `complete` lets a surface say "full index". Anything else means
10
+ * absence is UNKNOWN, never negative evidence.
11
+ *
12
+ * Stage counters name what each count did: enumerated / metadata-eligible / T0-scored
13
+ * are metadata only; attended / peeked / classified involve a model; shown / retained
14
+ * are outcomes. `filesEnumerated = 100000` with `local_budget_truncated` never reads as
15
+ * "indexed every file".
16
+ */
17
+ import { z } from "zod";
18
+ import { IsoDateTime } from "../api/primitives";
19
+
20
+ /** How completely a run (or one root) was enumerated. */
21
+ export const ENUMERATION_COMPLETENESS = [
22
+ "complete",
23
+ "local_budget_truncated",
24
+ "provider_incomplete",
25
+ "both",
26
+ ] as const;
27
+ export type EnumerationCompleteness = (typeof ENUMERATION_COMPLETENESS)[number];
28
+
29
+ /** Which corpus a root is: the connected person's, one shared drive, or the domain. */
30
+ export const ENUMERATION_ROOT_KINDS = [
31
+ "user",
32
+ "shared_drive",
33
+ "domain",
34
+ ] as const;
35
+ export type EnumerationRootKind = (typeof ENUMERATION_ROOT_KINDS)[number];
36
+
37
+ /**
38
+ * PURE: one completeness for many roots. Our ceiling anywhere and the provider's
39
+ * incompleteness anywhere combine to `both`; only every root complete is `complete`.
40
+ */
41
+ export function combineEnumerationCompleteness(
42
+ statuses: readonly EnumerationCompleteness[],
43
+ ): EnumerationCompleteness {
44
+ const local = statuses.some(
45
+ (s) => s === "local_budget_truncated" || s === "both",
46
+ );
47
+ const provider = statuses.some(
48
+ (s) => s === "provider_incomplete" || s === "both",
49
+ );
50
+ if (local && provider) return "both";
51
+ if (local) return "local_budget_truncated";
52
+ if (provider) return "provider_incomplete";
53
+ return "complete";
54
+ }
55
+
56
+ const MS = (iso: string): number => Date.parse(iso);
57
+
58
+ /** A modified-time range the sampler did not enumerate. */
59
+ export const EnumerationSkippedRangeSchema = z
60
+ .object({ from: IsoDateTime, to: IsoDateTime })
61
+ .refine((r) => MS(r.from) <= MS(r.to), {
62
+ message: "a skipped range ends no earlier than it starts",
63
+ });
64
+
65
+ /** How far one root's enumeration got. Counts, times and provider ids only — never a name. */
66
+ export const RootCoverageSchema = z
67
+ .object({
68
+ rootKind: z.enum(ENUMERATION_ROOT_KINDS),
69
+ /** The shared drive's provider id; null for the user and domain corpora. */
70
+ rootId: z.string().min(1).max(128).nullable(),
71
+ status: z.enum(ENUMERATION_COMPLETENESS),
72
+ enumeratedCount: z.number().int().min(0),
73
+ /** Null exactly when nothing was enumerated under the root. */
74
+ observedOldestModifiedTime: IsoDateTime.nullable(),
75
+ observedNewestModifiedTime: IsoDateTime.nullable(),
76
+ skippedRanges: z.array(EnumerationSkippedRangeSchema).max(16),
77
+ })
78
+ .superRefine((root, ctx) => {
79
+ if ((root.rootId !== null) !== (root.rootKind === "shared_drive")) {
80
+ ctx.addIssue({
81
+ code: "custom",
82
+ message: "a root id names exactly a shared drive",
83
+ });
84
+ }
85
+ if (root.status === "complete" && root.skippedRanges.length > 0) {
86
+ ctx.addIssue({
87
+ code: "custom",
88
+ message: "a complete root skips nothing",
89
+ });
90
+ }
91
+ const empty = root.enumeratedCount === 0;
92
+ const timesAbsent =
93
+ root.observedOldestModifiedTime === null &&
94
+ root.observedNewestModifiedTime === null;
95
+ const timesPresent =
96
+ root.observedOldestModifiedTime !== null &&
97
+ root.observedNewestModifiedTime !== null;
98
+ if (empty ? !timesAbsent : !timesPresent) {
99
+ ctx.addIssue({
100
+ code: "custom",
101
+ message:
102
+ "observed modified times are present exactly when the root enumerated a file",
103
+ });
104
+ }
105
+ if (
106
+ timesPresent &&
107
+ MS(root.observedOldestModifiedTime as string) >
108
+ MS(root.observedNewestModifiedTime as string)
109
+ ) {
110
+ ctx.addIssue({
111
+ code: "custom",
112
+ message: "the oldest observed time is not after the newest",
113
+ });
114
+ }
115
+ });
116
+ export type RootCoverage = z.infer<typeof RootCoverageSchema>;
117
+
118
+ /**
119
+ * A run's stage counters. Each narrows the one before it:
120
+ * enumerated >= metadata-eligible >= T0-scored >= T1-attended >= peeked;
121
+ * T0-scored >= classified; T0-scored >= retained >= shown.
122
+ */
123
+ export const DiscoveryStageCountersSchema = z
124
+ .object({
125
+ filesEnumerated: z.number().int().min(0),
126
+ filesMetadataEligible: z.number().int().min(0),
127
+ filesT0Scored: z.number().int().min(0),
128
+ filesT1Attended: z.number().int().min(0),
129
+ filesPeeked: z.number().int().min(0),
130
+ filesClassified: z.number().int().min(0),
131
+ filesShown: z.number().int().min(0),
132
+ filesRetained: z.number().int().min(0),
133
+ })
134
+ .superRefine((c, ctx) => {
135
+ const narrows =
136
+ c.filesMetadataEligible <= c.filesEnumerated &&
137
+ c.filesT0Scored <= c.filesMetadataEligible &&
138
+ c.filesT1Attended <= c.filesT0Scored &&
139
+ c.filesPeeked <= c.filesT1Attended &&
140
+ c.filesClassified <= c.filesT0Scored &&
141
+ c.filesRetained <= c.filesT0Scored &&
142
+ c.filesShown <= c.filesRetained;
143
+ if (!narrows) {
144
+ ctx.addIssue({
145
+ code: "custom",
146
+ message: "stage counters narrow from enumerated to shown",
147
+ });
148
+ }
149
+ });
150
+ export type DiscoveryStageCounters = z.infer<
151
+ typeof DiscoveryStageCountersSchema
152
+ >;
@@ -3,7 +3,9 @@
3
3
  *
4
4
  * Re-exports the closed Source Sync vocabularies (the sets the backend persists
5
5
  * and mirrors as database CHECK constraints, and the app handles exhaustively)
6
- * including the discovery relevance vocabulary, the discovery and candidate-set response schemas, the candidate-edit and
6
+ * including the discovery relevance vocabulary, the index-first enumeration
7
+ * vocabulary (completeness, root coverage, stage counters), the attention
8
+ * evidence schemas, the discovery and candidate-set response schemas, the candidate-edit and
7
9
  * per-scope approval request and response schemas, the per-document change
8
10
  * plan and apply schemas, the source-resolution rule schemas, the staleness
9
11
  * finding and standing-order schemas, and the Drive-reach status and upgrade
@@ -31,6 +33,7 @@ export type {
31
33
  SourceSyncScopeApproverKind,
32
34
  DocumentType,
33
35
  KnownDocumentType,
36
+ OperatingKnowledgeFamily,
34
37
  } from "./vocabulary";
35
38
 
36
39
  export {
@@ -45,6 +48,7 @@ export {
45
48
  SOURCE_SYNC_SCOPE_APPROVAL_STATES,
46
49
  SOURCE_SYNC_SCOPE_APPROVER_KINDS,
47
50
  DOCUMENT_TYPES,
51
+ OPERATING_KNOWLEDGE_FAMILIES,
48
52
  } from "./vocabulary";
49
53
 
50
54
  export type {
@@ -86,6 +90,36 @@ export {
86
90
  refineCandidateRelevance,
87
91
  } from "./relevance";
88
92
 
93
+ export type {
94
+ EnumerationCompleteness,
95
+ EnumerationRootKind,
96
+ RootCoverage,
97
+ DiscoveryStageCounters,
98
+ } from "./enumeration";
99
+
100
+ export {
101
+ ENUMERATION_COMPLETENESS,
102
+ ENUMERATION_ROOT_KINDS,
103
+ combineEnumerationCompleteness,
104
+ EnumerationSkippedRangeSchema,
105
+ RootCoverageSchema,
106
+ DiscoveryStageCountersSchema,
107
+ } from "./enumeration";
108
+
109
+ export type {
110
+ AttentionSignalKind,
111
+ AttentionSignalSource,
112
+ AttentionSignal,
113
+ AttentionEvidence,
114
+ } from "./attention-evidence";
115
+
116
+ export {
117
+ ATTENTION_SIGNAL_KINDS,
118
+ AttentionSignalSourceSchema,
119
+ AttentionSignalSchema,
120
+ AttentionEvidenceSchema,
121
+ } from "./attention-evidence";
122
+
89
123
  export type {
90
124
  SourceSyncCandidateItem,
91
125
  SourceSyncCandidateItemPatch,
@@ -7,6 +7,12 @@
7
7
  */
8
8
 
9
9
  import { z } from "zod";
10
+ import {
11
+ combineEnumerationCompleteness,
12
+ DiscoveryStageCountersSchema,
13
+ ENUMERATION_COMPLETENESS,
14
+ RootCoverageSchema,
15
+ } from "./enumeration";
10
16
 
11
17
  /** The funnel's verdict. `dropped` is persisted (drop ledger / re-scored observation) and never reaches the wire item. */
12
18
  export const SOURCE_RELEVANCE_TIERS = ["dropped", "hidden", "shown"] as const;
@@ -165,17 +171,50 @@ export type CandidateAttention = z.infer<typeof CandidateAttentionSchema>;
165
171
 
166
172
  /**
167
173
  * The last completed discovery run's counts, so no surface shows one ambiguous total.
168
- * The set response carries it NULL before any run has completed.
174
+ * The set response carries it NULL before any run has completed. The three enumeration
175
+ * fields are all present or all absent: NULL on all three = the run predates index-first
176
+ * discovery (control ADR index-first-operating-knowledge-discovery).
169
177
  */
170
- export const CandidateDiscoverySummarySchema = z.object({
171
- foundThisRun: z.number().int().min(0),
172
- retainedDecisions: z.number().int().min(0),
173
- notRediscovered: z.number().int().min(0),
174
- pendingClassification: z.number().int().min(0),
175
- filesDropped: z.number().int().min(0),
176
- coveragePercent: z.number().int().min(0).max(100),
177
- scanOutcome: z.enum(SCAN_OUTCOMES),
178
- });
178
+ export const CandidateDiscoverySummarySchema = z
179
+ .object({
180
+ foundThisRun: z.number().int().min(0),
181
+ retainedDecisions: z.number().int().min(0),
182
+ notRediscovered: z.number().int().min(0),
183
+ pendingClassification: z.number().int().min(0),
184
+ filesDropped: z.number().int().min(0),
185
+ coveragePercent: z.number().int().min(0).max(100),
186
+ scanOutcome: z.enum(SCAN_OUTCOMES),
187
+ /** How completely the run enumerated; only `complete` may be called a full index. */
188
+ enumerationCompleteness: z.enum(ENUMERATION_COMPLETENESS).nullable(),
189
+ /** One entry per enumerated root. */
190
+ rootCoverage: z.array(RootCoverageSchema).min(1).max(64).nullable(),
191
+ /** What each stage did, by count (filesEnumerated is metadata only — never "indexed"). */
192
+ stageCounters: DiscoveryStageCountersSchema.nullable(),
193
+ })
194
+ .superRefine((s, ctx) => {
195
+ const present = [
196
+ s.enumerationCompleteness,
197
+ s.rootCoverage,
198
+ s.stageCounters,
199
+ ].filter((v) => v !== null).length;
200
+ if (present !== 0 && present !== 3) {
201
+ ctx.addIssue({
202
+ code: "custom",
203
+ message: "enumeration fields are all present or all absent",
204
+ });
205
+ }
206
+ if (
207
+ s.rootCoverage !== null &&
208
+ s.enumerationCompleteness !== null &&
209
+ combineEnumerationCompleteness(s.rootCoverage.map((r) => r.status)) !==
210
+ s.enumerationCompleteness
211
+ ) {
212
+ ctx.addIssue({
213
+ code: "custom",
214
+ message: "enumeration completeness is the combination of its roots",
215
+ });
216
+ }
217
+ });
179
218
  export type CandidateDiscoverySummary = z.infer<
180
219
  typeof CandidateDiscoverySummarySchema
181
220
  >;
@@ -191,6 +230,11 @@ type CandidateRelevanceFacts = {
191
230
  selectionSource: SelectionSource | null;
192
231
  documentType: string | null;
193
232
  documentTypeConfidence: number | null;
233
+ withheld: boolean;
234
+ attention: { t1: AttentionLane | null; t2: AttentionLane | null };
235
+ operatingKnowledgeFamily: string | null;
236
+ canonicalPath: string | null;
237
+ attentionEvidence: readonly unknown[] | null;
194
238
  };
195
239
 
196
240
  /**
@@ -198,7 +242,10 @@ type CandidateRelevanceFacts = {
198
242
  * a hidden line is never included; classification decides whether a recommendation and a
199
243
  * class exist; `selectionSource` is present exactly when a treatment is committed. A
200
244
  * document type exists only on a classified line, and carries a confidence exactly when
201
- * it is a known type (control ADR source-discovery-observation-vs-policy).
245
+ * it is a known type (control ADR source-discovery-observation-vs-policy). An
246
+ * operating-knowledge family needs a known type; attention evidence and a canonical path
247
+ * exist only on a line T1 attended and never on a withheld one (control ADR
248
+ * index-first-operating-knowledge-discovery).
202
249
  */
203
250
  export function refineCandidateRelevance(
204
251
  item: CandidateRelevanceFacts,
@@ -248,4 +295,31 @@ export function refineCandidateRelevance(
248
295
  "a known document type carries a confidence; unknown and absent carry none",
249
296
  });
250
297
  }
298
+ if (item.operatingKnowledgeFamily !== null && !knownType) {
299
+ ctx.addIssue({
300
+ code: "custom",
301
+ message: "an operating-knowledge family needs a known document type",
302
+ });
303
+ }
304
+ if (
305
+ item.withheld &&
306
+ (item.attentionEvidence !== null || item.canonicalPath !== null)
307
+ ) {
308
+ ctx.addIssue({
309
+ code: "custom",
310
+ message: "a withheld line carries no attention evidence and no path",
311
+ });
312
+ }
313
+ if (item.attentionEvidence !== null && item.attention.t1 === null) {
314
+ ctx.addIssue({
315
+ code: "custom",
316
+ message: "attention evidence exists only on a line T1 attended",
317
+ });
318
+ }
319
+ if (item.canonicalPath !== null && item.attention.t1 === null) {
320
+ ctx.addIssue({
321
+ code: "custom",
322
+ message: "a canonical path exists only on a line T1 attended",
323
+ });
324
+ }
251
325
  }
@@ -113,7 +113,9 @@ export type SourceSyncScopeApproverKind =
113
113
 
114
114
  /**
115
115
  * What a source IS — a STRUCTURAL name the model observes, never a relevance verdict
116
- * (control ADR source-discovery-observation-vs-policy). Product policy in the backend maps a
116
+ * (control ADR source-discovery-observation-vs-policy). The set names operating
117
+ * knowledge — charters, playbooks, policies, decision logs, postmortems and the like —
118
+ * beside strategy (control ADR index-first-operating-knowledge-discovery). Product policy in the backend maps a
117
119
  * resolved type to a family, a source class, a sensitivity and a relevance policy; none of
118
120
  * that travels on the wire. A medium is not a meaning: `image_or_screenshot` is graded on
119
121
  * content. `unknown` is a real answer (the model could not tell, or two reads disagreed).
@@ -151,7 +153,41 @@ export const DOCUMENT_TYPES = [
151
153
  "coursework",
152
154
  "empty_template",
153
155
  "image_or_screenshot",
156
+ // Operating knowledge (control ADR index-first-operating-knowledge-discovery):
157
+ "team_charter",
158
+ "process_playbook",
159
+ "policy_handbook",
160
+ "onboarding_guide",
161
+ "values_culture",
162
+ "positioning_messaging",
163
+ "decision_log",
164
+ "postmortem",
165
+ "metrics_definition",
166
+ "operating_cadence",
154
167
  "unknown",
155
168
  ] as const;
156
169
  export type DocumentType = (typeof DOCUMENT_TYPES)[number];
157
170
  export type KnownDocumentType = Exclude<DocumentType, "unknown">;
171
+
172
+ /**
173
+ * WHAT KIND of operating knowledge a source is: what the company is trying to do
174
+ * (direction), how it is organized to do it (operating_model), how it runs
175
+ * (practices), who it is and for whom (identity_market), and what it decided and
176
+ * learned (decisions_learning). Control ADR index-first-operating-knowledge-discovery.
177
+ *
178
+ * A NAME the product derives from the resolved document type — the backend's
179
+ * versioned policy table owns the type -> family mapping, exactly as it owns
180
+ * sourceClass. It is NOT the backend's relevance-policy family (strategy,
181
+ * reference, corporate_record, …), which never travels on the wire. Board and
182
+ * financial material are ordinary members of `direction`; no family is required
183
+ * to exist for a discovery to be valid.
184
+ */
185
+ export const OPERATING_KNOWLEDGE_FAMILIES = [
186
+ "direction",
187
+ "operating_model",
188
+ "practices",
189
+ "identity_market",
190
+ "decisions_learning",
191
+ ] as const;
192
+ export type OperatingKnowledgeFamily =
193
+ (typeof OPERATING_KNOWLEDGE_FAMILIES)[number];