@company-semantics/contracts 89.1.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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@company-semantics/contracts",
3
- "version": "89.1.0",
3
+ "version": "90.0.0",
4
4
  "private": false,
5
5
  "repository": {
6
6
  "type": "git",
@@ -109,6 +109,7 @@
109
109
  "guard:decisions-deprecation:json": "npx tsx scripts/ci/decisions-deprecation-guard.ts --json",
110
110
  "guard:quick": "sh scripts/ci/run-guard.sh --quick",
111
111
  "ci:local": "pnpm typecheck && pnpm lint:md && pnpm guard:test && pnpm guard",
112
+ "verify": "sh scripts/ci/run-verify.sh",
112
113
  "guard": "sh scripts/ci/run-guard.sh",
113
114
  "guard:test": "NODE_OPTIONS='--max-old-space-size=4096' vitest run scripts/ci/__tests__",
114
115
  "release": "npx tsx scripts/release.ts",
@@ -177,14 +178,15 @@
177
178
  "picomatch@<4.0.4": ">=4.0.4",
178
179
  "esbuild@<0.28.1": ">=0.28.1",
179
180
  "linkify-it@<5.0.2": ">=5.0.2",
180
- "postcss@<8.5.18": ">=8.5.18",
181
- "markdown-it@<14.2.0": ">=14.2.0"
181
+ "postcss@<8.5.23": ">=8.5.23",
182
+ "markdown-it@<14.2.0": ">=14.2.0",
183
+ "@humanfs/node@<0.16.8": ">=0.16.8 <0.17.0"
182
184
  }
183
185
  },
184
186
  "lint-staged": {
185
- "*.ts": "bash -c 'tsc -b --noEmit'",
187
+ "*.ts": "sh scripts/ci/unless-verified.sh tsc 'tsc -b --noEmit'",
186
188
  "*.md": "markdownlint-cli2",
187
189
  "package.json": "node -e \"JSON.parse(require('fs').readFileSync('package.json'))\""
188
190
  },
189
- "securityRequirementsVersion": "bfd57aaebc7d47d195f06ba507ed3fb56da0faa9c40aca3c00f2e63dbd3b617b"
191
+ "securityRequirementsVersion": "8700b9fe36f8c948f12970ec54128e4f1955986dd9b634114f7501fdb1884267"
190
192
  }
@@ -1,3 +1,3 @@
1
1
  // AUTO-GENERATED — do not edit. Run pnpm generate:spec-hash to regenerate.
2
- export const SPEC_HASH = '50b15a02e5ef' as const;
3
- export const SPEC_HASH_FULL = '50b15a02e5efb3a03bd903551f31a4c74693891d2577816c34a030d09909e389' as const;
2
+ export const SPEC_HASH = 'ba43b4881a93' as const;
3
+ export const SPEC_HASH_FULL = 'ba43b4881a9318819726dfe0c0465ecae3802951f7f558fec6786a7171195a91' as const;
@@ -1675,7 +1675,7 @@ export interface paths {
1675
1675
  };
1676
1676
  /**
1677
1677
  * Read one analysis workflow’s generic lifecycle envelope
1678
- * @description The domain-agnostic projection a client polls: identity, registered type, activity-free lifecycle state, start and completion times, the opaque domain progress and result payloads (null until the domain adapter supplies them), a closed failure class on a failed workflow, and the capabilities the surface may offer — canStop from the generic layer, canContinue as the domain declares it. Org-scoped: a workflow in another workspace answers 404.
1678
+ * @description The domain-agnostic projection a client polls: identity, registered type, activity-free lifecycle state, start and completion times, the opaque domain progress and result payloads (null until the domain adapter supplies them), a closed failure class on a failed workflow, the frozen typed outcome of the current execution epoch once it settles (null until then), and the capabilities the surface may offer — canStop from the generic layer, canContinue as the domain declares it. Org-scoped: a workflow in another workspace answers 404.
1679
1679
  */
1680
1680
  get: operations["getAnalysisWorkflow"];
1681
1681
  put?: never;
@@ -1706,6 +1706,26 @@ export interface paths {
1706
1706
  patch?: never;
1707
1707
  trace?: never;
1708
1708
  };
1709
+ "/api/analysis-workflows/{id}/settlement": {
1710
+ parameters: {
1711
+ query?: never;
1712
+ header?: never;
1713
+ path?: never;
1714
+ cookie?: never;
1715
+ };
1716
+ get?: never;
1717
+ put?: never;
1718
+ /**
1719
+ * Deliver a settled analysis workflow’s outcome into its launching conversation now
1720
+ * @description An ACCELERATOR, never required for correctness (control ADR-CTRL-540): every terminal transition records a settlement that the server delivers on its own — immediately after settling, and on the reconciler sweep. The app calls this when its poll sees the current execution epoch settle, so the settlement turn appears without waiting. Idempotent: a second call for the same epoch answers skipped or delivered and writes nothing new. Answers not_settled while the epoch is still moving. Org-scoped: a workflow in another workspace answers 404.
1721
+ */
1722
+ post: operations["deliverAnalysisWorkflowSettlement"];
1723
+ delete?: never;
1724
+ options?: never;
1725
+ head?: never;
1726
+ patch?: never;
1727
+ trace?: never;
1728
+ };
1709
1729
  "/api/company-md/tree": {
1710
1730
  parameters: {
1711
1731
  query?: never;
@@ -4200,6 +4220,57 @@ export interface paths {
4200
4220
  patch?: never;
4201
4221
  trace?: never;
4202
4222
  };
4223
+ "/api/internal-admin/quality/graders": {
4224
+ parameters: {
4225
+ query?: never;
4226
+ header?: never;
4227
+ path?: never;
4228
+ cookie?: never;
4229
+ };
4230
+ /** Every grader version with its calibration and the severities it may gate */
4231
+ get: operations["listQualityGraders"];
4232
+ put?: never;
4233
+ post?: never;
4234
+ delete?: never;
4235
+ options?: never;
4236
+ head?: never;
4237
+ patch?: never;
4238
+ trace?: never;
4239
+ };
4240
+ "/api/internal-admin/quality/graders/{id}/label-queue": {
4241
+ parameters: {
4242
+ query?: never;
4243
+ header?: never;
4244
+ path?: never;
4245
+ cookie?: never;
4246
+ };
4247
+ /** A judge's unlabeled samples with their case content; the judge's verdict is hidden */
4248
+ get: operations["getQualityGraderLabelQueue"];
4249
+ put?: never;
4250
+ post?: never;
4251
+ delete?: never;
4252
+ options?: never;
4253
+ head?: never;
4254
+ patch?: never;
4255
+ trace?: never;
4256
+ };
4257
+ "/api/internal-admin/quality/graders/{id}/labels": {
4258
+ parameters: {
4259
+ query?: never;
4260
+ header?: never;
4261
+ path?: never;
4262
+ cookie?: never;
4263
+ };
4264
+ get?: never;
4265
+ put?: never;
4266
+ /** Label a judged sample, then recalibrate the judge; reveals the judge's verdict */
4267
+ post: operations["submitQualityGraderLabel"];
4268
+ delete?: never;
4269
+ options?: never;
4270
+ head?: never;
4271
+ patch?: never;
4272
+ trace?: never;
4273
+ };
4203
4274
  "/api/comments": {
4204
4275
  parameters: {
4205
4276
  query?: never;
@@ -6915,6 +6986,7 @@ export interface components {
6915
6986
  completedAt: string | null;
6916
6987
  progress: unknown | null;
6917
6988
  result: unknown | null;
6989
+ outcome: unknown | null;
6918
6990
  error?: string;
6919
6991
  capabilities: {
6920
6992
  canStop: boolean;
@@ -6934,6 +7006,10 @@ export interface components {
6934
7006
  /** @constant */
6935
7007
  reason: "not_running";
6936
7008
  };
7009
+ DeliverWorkflowSettlementResponse: {
7010
+ /** @enum {string} */
7011
+ result: "delivered" | "deferred" | "busy" | "skipped" | "not_settled";
7012
+ };
6937
7013
  CompanyMdListResponse: {
6938
7014
  items: {
6939
7015
  id: string;
@@ -9252,8 +9328,10 @@ export interface components {
9252
9328
  /** @enum {string} */
9253
9329
  classificationState: "unclassified" | "classified";
9254
9330
  sourceClass: ("board_deck" | "board_minutes" | "investor_update" | "financial_brief" | "budget_forecast" | "okr_plan" | "annual_plan" | "all_hands" | "unit_strategy" | "roadmap" | "qbr" | "other" | "decoy") | null;
9255
- documentType: ("board_deck" | "board_minutes" | "strategy_memo" | "planning_doc" | "okr_doc" | "roadmap" | "operating_review" | "all_hands_deck" | "offsite_notes" | "investor_update" | "pitch_deck" | "product_spec" | "research_analysis" | "general_meeting_notes" | "financial_plan" | "financial_actuals" | "equity_admin_record" | "corporate_formation_record" | "corporate_governance_record" | "government_filing" | "equity_agreement" | "commercial_agreement" | "employment_agreement" | "transaction_record" | "personal_document" | "social_event_material" | "coursework" | "empty_template" | "image_or_screenshot" | "unknown") | null;
9331
+ documentType: ("board_deck" | "board_minutes" | "strategy_memo" | "planning_doc" | "okr_doc" | "roadmap" | "operating_review" | "all_hands_deck" | "offsite_notes" | "investor_update" | "pitch_deck" | "product_spec" | "research_analysis" | "general_meeting_notes" | "financial_plan" | "financial_actuals" | "equity_admin_record" | "corporate_formation_record" | "corporate_governance_record" | "government_filing" | "equity_agreement" | "commercial_agreement" | "employment_agreement" | "transaction_record" | "personal_document" | "social_event_material" | "coursework" | "empty_template" | "image_or_screenshot" | "team_charter" | "process_playbook" | "policy_handbook" | "onboarding_guide" | "values_culture" | "positioning_messaging" | "decision_log" | "postmortem" | "metrics_definition" | "operating_cadence" | "unknown") | null;
9256
9332
  documentTypeConfidence: number | null;
9333
+ operatingKnowledgeFamily: ("direction" | "operating_model" | "practices" | "identity_market" | "decisions_learning") | null;
9334
+ canonicalPath: string | null;
9257
9335
  periodLabel: string | null;
9258
9336
  seriesRole: ("current" | "history") | null;
9259
9337
  relevance: number;
@@ -9299,6 +9377,59 @@ export interface components {
9299
9377
  t1: ("exploit" | "explore") | null;
9300
9378
  t2: ("exploit" | "explore") | null;
9301
9379
  };
9380
+ attentionEvidence: ({
9381
+ /** @constant */
9382
+ kind: "tree_prior";
9383
+ score: number;
9384
+ folderId: string;
9385
+ source: {
9386
+ producer: string;
9387
+ version: string;
9388
+ };
9389
+ } | {
9390
+ /** @constant */
9391
+ kind: "path_terms";
9392
+ values: string[];
9393
+ source: {
9394
+ producer: string;
9395
+ version: string;
9396
+ };
9397
+ } | {
9398
+ /** @constant */
9399
+ kind: "content_match";
9400
+ values: string[];
9401
+ strength: number;
9402
+ probeId: string;
9403
+ source: {
9404
+ producer: string;
9405
+ version: string;
9406
+ };
9407
+ } | {
9408
+ /** @constant */
9409
+ kind: "breadth";
9410
+ /** @enum {string} */
9411
+ value: "restricted_few" | "group" | "domain" | "anyone" | "unknown";
9412
+ source: {
9413
+ producer: string;
9414
+ version: string;
9415
+ };
9416
+ } | {
9417
+ /** @constant */
9418
+ kind: "recency";
9419
+ score: number;
9420
+ source: {
9421
+ producer: string;
9422
+ version: string;
9423
+ };
9424
+ } | {
9425
+ /** @constant */
9426
+ kind: "mime_prior";
9427
+ score: number;
9428
+ source: {
9429
+ producer: string;
9430
+ version: string;
9431
+ };
9432
+ })[] | null;
9302
9433
  included: boolean;
9303
9434
  treatmentUnresolved: boolean;
9304
9435
  matchReason: string;
@@ -9389,6 +9520,33 @@ export interface components {
9389
9520
  coveragePercent: number;
9390
9521
  /** @enum {string} */
9391
9522
  scanOutcome: "complete" | "budget_exhausted" | "file_ceiling" | "stopped";
9523
+ enumerationCompleteness: ("complete" | "local_budget_truncated" | "provider_incomplete" | "both") | null;
9524
+ rootCoverage: {
9525
+ /** @enum {string} */
9526
+ rootKind: "user" | "shared_drive" | "domain";
9527
+ rootId: string | null;
9528
+ /** @enum {string} */
9529
+ status: "complete" | "local_budget_truncated" | "provider_incomplete" | "both";
9530
+ enumeratedCount: number;
9531
+ observedOldestModifiedTime: string | null;
9532
+ observedNewestModifiedTime: string | null;
9533
+ skippedRanges: {
9534
+ /** Format: date-time */
9535
+ from: string;
9536
+ /** Format: date-time */
9537
+ to: string;
9538
+ }[];
9539
+ }[] | null;
9540
+ stageCounters: {
9541
+ filesEnumerated: number;
9542
+ filesMetadataEligible: number;
9543
+ filesT0Scored: number;
9544
+ filesT1Attended: number;
9545
+ filesPeeked: number;
9546
+ filesClassified: number;
9547
+ filesShown: number;
9548
+ filesRetained: number;
9549
+ } | null;
9392
9550
  } | null;
9393
9551
  };
9394
9552
  SourceSyncApproveResponse: {
@@ -13002,6 +13160,42 @@ export interface operations {
13002
13160
  };
13003
13161
  };
13004
13162
  };
13163
+ deliverAnalysisWorkflowSettlement: {
13164
+ parameters: {
13165
+ query?: never;
13166
+ header?: never;
13167
+ path: {
13168
+ id: string;
13169
+ };
13170
+ cookie?: never;
13171
+ };
13172
+ requestBody?: never;
13173
+ responses: {
13174
+ /** @description What the delivery attempt did */
13175
+ 200: {
13176
+ headers: {
13177
+ [name: string]: unknown;
13178
+ };
13179
+ content: {
13180
+ "application/json": components["schemas"]["DeliverWorkflowSettlementResponse"];
13181
+ };
13182
+ };
13183
+ /** @description The workflow id is not a uuid */
13184
+ 400: {
13185
+ headers: {
13186
+ [name: string]: unknown;
13187
+ };
13188
+ content?: never;
13189
+ };
13190
+ /** @description No such workflow in this workspace */
13191
+ 404: {
13192
+ headers: {
13193
+ [name: string]: unknown;
13194
+ };
13195
+ content?: never;
13196
+ };
13197
+ };
13198
+ };
13005
13199
  getCompanyMdTree: {
13006
13200
  parameters: {
13007
13201
  query?: {
@@ -17743,6 +17937,133 @@ export interface operations {
17743
17937
  };
17744
17938
  };
17745
17939
  };
17940
+ listQualityGraders: {
17941
+ parameters: {
17942
+ query?: never;
17943
+ header?: never;
17944
+ path?: never;
17945
+ cookie?: never;
17946
+ };
17947
+ requestBody?: never;
17948
+ responses: {
17949
+ /** @description Grader versions with confusion matrices by severity and failure kind */
17950
+ 200: {
17951
+ headers: {
17952
+ [name: string]: unknown;
17953
+ };
17954
+ content?: never;
17955
+ };
17956
+ /** @description Not an internal admin, or missing internal.manage_quality */
17957
+ 403: {
17958
+ headers: {
17959
+ [name: string]: unknown;
17960
+ };
17961
+ content?: never;
17962
+ };
17963
+ };
17964
+ };
17965
+ getQualityGraderLabelQueue: {
17966
+ parameters: {
17967
+ query?: {
17968
+ limit?: number;
17969
+ };
17970
+ header?: never;
17971
+ path: {
17972
+ id: string;
17973
+ };
17974
+ cookie?: never;
17975
+ };
17976
+ requestBody?: never;
17977
+ responses: {
17978
+ /** @description Samples waiting for a label, newest run first, without verdicts */
17979
+ 200: {
17980
+ headers: {
17981
+ [name: string]: unknown;
17982
+ };
17983
+ content?: never;
17984
+ };
17985
+ /** @description Validation error */
17986
+ 400: {
17987
+ headers: {
17988
+ [name: string]: unknown;
17989
+ };
17990
+ content?: never;
17991
+ };
17992
+ /** @description Not an internal admin, or missing internal.manage_quality */
17993
+ 403: {
17994
+ headers: {
17995
+ [name: string]: unknown;
17996
+ };
17997
+ content?: never;
17998
+ };
17999
+ /** @description No such judge */
18000
+ 404: {
18001
+ headers: {
18002
+ [name: string]: unknown;
18003
+ };
18004
+ content?: never;
18005
+ };
18006
+ };
18007
+ };
18008
+ submitQualityGraderLabel: {
18009
+ parameters: {
18010
+ query?: never;
18011
+ header?: never;
18012
+ path: {
18013
+ id: string;
18014
+ };
18015
+ cookie?: never;
18016
+ };
18017
+ requestBody: {
18018
+ content: {
18019
+ "application/json": {
18020
+ /** Format: uuid */
18021
+ runId: string;
18022
+ caseId: string;
18023
+ sample: number;
18024
+ humanPassed: boolean;
18025
+ humanFailureKind?: string | null;
18026
+ };
18027
+ };
18028
+ };
18029
+ responses: {
18030
+ /** @description The label, the judge's verdict and the recomputed calibration */
18031
+ 200: {
18032
+ headers: {
18033
+ [name: string]: unknown;
18034
+ };
18035
+ content?: never;
18036
+ };
18037
+ /** @description Validation error */
18038
+ 400: {
18039
+ headers: {
18040
+ [name: string]: unknown;
18041
+ };
18042
+ content?: never;
18043
+ };
18044
+ /** @description Not an internal admin, missing internal.manage_quality, or no CSRF token */
18045
+ 403: {
18046
+ headers: {
18047
+ [name: string]: unknown;
18048
+ };
18049
+ content?: never;
18050
+ };
18051
+ /** @description No such judge, or the judge graded no such sample */
18052
+ 404: {
18053
+ headers: {
18054
+ [name: string]: unknown;
18055
+ };
18056
+ content?: never;
18057
+ };
18058
+ /** @description The sample is already labeled, or the judge gave no valid verdict */
18059
+ 409: {
18060
+ headers: {
18061
+ [name: string]: unknown;
18062
+ };
18063
+ content?: never;
18064
+ };
18065
+ };
18066
+ };
17746
18067
  listCommentThreads: {
17747
18068
  parameters: {
17748
18069
  query: {
@@ -10,6 +10,7 @@ export const openApiRoutes = {
10
10
  '/api/account/sessions': ['GET'],
11
11
  '/api/account/sessions/{sessionId}': ['DELETE'],
12
12
  '/api/analysis-workflows/{id}': ['GET'],
13
+ '/api/analysis-workflows/{id}/settlement': ['POST'],
13
14
  '/api/analysis-workflows/{id}/stop': ['POST'],
14
15
  '/api/auth/consent/grant': ['POST'],
15
16
  '/api/billing/price-list': ['GET'],
@@ -111,6 +112,9 @@ export const openApiRoutes = {
111
112
  '/api/internal-admin/quality/changes/manual': ['POST'],
112
113
  '/api/internal-admin/quality/escalations': ['GET'],
113
114
  '/api/internal-admin/quality/escalations/{id}/resolve': ['POST'],
115
+ '/api/internal-admin/quality/graders': ['GET'],
116
+ '/api/internal-admin/quality/graders/{id}/label-queue': ['GET'],
117
+ '/api/internal-admin/quality/graders/{id}/labels': ['POST'],
114
118
  '/api/internal-admin/quality/plans/{planId}/status': ['GET'],
115
119
  '/api/internal-admin/quality/references/floor': ['PUT'],
116
120
  '/api/internal-admin/quality/spend': ['GET'],
@@ -165,7 +165,15 @@ product policy in the backend decides what that means.
165
165
  what a file IS — `corporate_formation_record`, never `irrelevant_doc` —
166
166
  never how relevant it is. A medium is not a meaning: `image_or_screenshot`
167
167
  is graded on its content. `unknown` is a real answer (the model could not
168
- tell, or two reads disagreed) and is always last.
168
+ tell, or two reads disagreed) and is always last. There are forty: the ten
169
+ operating-knowledge types (`team_charter` … `operating_cadence`, control ADR
170
+ `index-first-operating-knowledge-discovery`) sit just before `unknown`.
171
+ - **Operating-knowledge family is a derived name.**
172
+ `OPERATING_KNOWLEDGE_FAMILIES` (`direction`, `operating_model`, `practices`,
173
+ `identity_market`, `decisions_learning`) is resolved by backend policy from
174
+ the resolved type, like `sourceClass`, and travels as the item's
175
+ `operatingKnowledgeFamily`, which needs a known type. It is not the backend's
176
+ relevance-policy family, which never travels.
169
177
  - **The resolved type travels, nothing else.** The candidate item's
170
178
  `documentType` and `documentTypeConfidence` (0..1) carry the RESOLVED type,
171
179
  after the backend reconciles the T1 and T2 observations. Both are null on an
@@ -191,6 +199,63 @@ product policy in the backend decides what that means.
191
199
  is uncertainty, never irrelevance. `peek_confirms` / `peek_contradicts`
192
200
  stay for v1-pinned rows.
193
201
 
202
+ ## Enumeration
203
+
204
+ Doctrine: control ADR `index-first-operating-knowledge-discovery`. Vocabulary:
205
+ ADR `operating-knowledge-discovery-vocabulary`. The sets live in
206
+ `enumeration.ts`.
207
+
208
+ - **A partial enumeration is never presented as an index of the corpus.**
209
+ `ENUMERATION_COMPLETENESS` has four values, never a boolean: `complete`,
210
+ `local_budget_truncated` (our ceiling), `provider_incomplete` (the
211
+ provider's incomplete search) and `both`. Only `complete` may be called a
212
+ full index. Any other value means absence is unknown, not negative evidence
213
+ (INV-SOURCESYNC-PARTIAL-ENUMERATION-IS-UNKNOWN).
214
+ - **Per root, never by name.** `RootCoverage` covers one root
215
+ (`ENUMERATION_ROOT_KINDS`: `user`, `shared_drive`, `domain`). It holds the
216
+ shared drive's provider id (null for the other kinds), the root's status and
217
+ count, the observed modified-time bounds (present exactly when it enumerated
218
+ a file) and the skipped ranges (none when it is complete). It never holds a
219
+ drive, folder or file name.
220
+ - **The run's completeness is the combination of its roots.**
221
+ `combineEnumerationCompleteness` is pure: our ceiling anywhere and the
222
+ provider's incompleteness anywhere make `both`, and the run is `complete`
223
+ only when every root is.
224
+ - **Counters name what they did.** `DiscoveryStageCounters` holds
225
+ `filesEnumerated`, `filesMetadataEligible`, `filesT0Scored`,
226
+ `filesT1Attended`, `filesPeeked`, `filesClassified`, `filesShown` and
227
+ `filesRetained`, and the counts narrow from enumerated to shown. A metadata
228
+ count never reads as a content read: there is no `filesIndexed`.
229
+ - **On the discovery summary, all or none.** `enumerationCompleteness`,
230
+ `rootCoverage` (1..64) and `stageCounters` are required and nullable. Either
231
+ all three are present, or all three are null, which means the run predates
232
+ index-first discovery.
233
+
234
+ ## Attention evidence
235
+
236
+ Vocabulary: ADR `operating-knowledge-discovery-vocabulary`. The schemas live
237
+ in `attention-evidence.ts`.
238
+
239
+ - **Why a file got T1 attention.** `AttentionEvidence` is an array of at most
240
+ six signals with at most one per `ATTENTION_SIGNAL_KINDS` kind: `tree_prior`
241
+ (a score and the folder's provider `folderId`), `path_terms` (matched
242
+ product terms), `content_match` (matched plan terms, a strength and the
243
+ `probeId`), `breadth` (a `SHARING_BREADTHS` member, so there is no new
244
+ breadth vocabulary), `recency` and `mime_prior` (a score each).
245
+ - **Every signal has provenance.** Each signal carries
246
+ `source: { producer, version }`
247
+ (INV-SOURCESYNC-ATTENTION-EVIDENCE-HAS-PROVENANCE). `producer` is a
248
+ documented lowercase key, not a closed set. The documented keys are
249
+ `lexicon`, `source-discovery-tree-triage`, `source-discovery-plan` and
250
+ `metadata-scorer`.
251
+ - **Terms are vocabulary, never content.** A term is product or plan
252
+ vocabulary that matched. It is never text read from the file and never a
253
+ slice of its name. A folder is named only by its provider id.
254
+ - **Only after T1 attention, never when withheld.** The item's
255
+ `attentionEvidence` and `canonicalPath` (the Tier-1 path) are null when the
256
+ line never received T1 attention, when it is withheld, and for runs before
257
+ index-first discovery.
258
+
194
259
  ## Public API
195
260
 
196
261
  - `SOURCE_CLASSES` / `SourceClass` — what kind of document a source is.
@@ -210,12 +275,34 @@ product policy in the backend decides what that means.
210
275
  `org_admin` or `unit_head`.
211
276
  - `DOCUMENT_TYPES` / `DocumentType` / `KnownDocumentType` — what a source is,
212
277
  structurally; `KnownDocumentType` excludes `unknown` (see Document type).
278
+ - `OPERATING_KNOWLEDGE_FAMILIES` / `OperatingKnowledgeFamily` — what kind of
279
+ operating knowledge a line is, resolved by the backend (see Document type).
280
+ - `ENUMERATION_COMPLETENESS` / `EnumerationCompleteness` — how completely a
281
+ run or root was enumerated; only `complete` is a full index (see
282
+ Enumeration).
283
+ - `ENUMERATION_ROOT_KINDS` / `EnumerationRootKind` — `user`, `shared_drive` or
284
+ `domain`.
285
+ - `combineEnumerationCompleteness(statuses)` — the pure fold of per-root
286
+ statuses into one.
287
+ - `EnumerationSkippedRangeSchema` — a modified-time range left unenumerated.
288
+ - `RootCoverageSchema` / `RootCoverage` — how far one root's enumeration got.
289
+ - `DiscoveryStageCountersSchema` / `DiscoveryStageCounters` — what each stage
290
+ did, by count, narrowing from enumerated to shown.
291
+ - `ATTENTION_SIGNAL_KINDS` / `AttentionSignalKind` — the kinds of attention
292
+ signal (see Attention evidence).
293
+ - `AttentionSignalSourceSchema` / `AttentionSignalSource` — a signal's
294
+ producer and version.
295
+ - `AttentionSignalSchema` / `AttentionSignal` — one signal: a discriminated
296
+ union on `kind`.
297
+ - `AttentionEvidenceSchema` / `AttentionEvidence` — why a line got T1
298
+ attention: at most one signal per kind.
213
299
  - `SourceSyncDiscoveryRunResponseSchema` / `SourceSyncDiscoveryRunResponse` —
214
300
  the discovery run started, and the workflow narrating it.
215
301
  - `SourceSyncCandidateItemSchema` / `SourceSyncCandidateItem` — one source
216
302
  proposed for one target document, with the server's `included` and the
217
303
  resolved `documentType` / `documentTypeConfidence` (null when unclassified
218
- or v1-pinned).
304
+ or v1-pinned), `operatingKnowledgeFamily`, `canonicalPath` and
305
+ `attentionEvidence` (null before index-first discovery).
219
306
  - `SourceSyncCandidateSetResponseSchema` / `SourceSyncCandidateSetResponse` —
220
307
  the draft set under review: its revision, items, presented questions,
221
308
  distinct-file `totals` and cost estimate.
@@ -307,7 +394,10 @@ product policy in the backend decides what that means.
307
394
 
308
395
  `rules.ts` imports only `zod`, `../api/primitives` and `./vocabulary`.
309
396
  `upkeep.ts` and `reach.ts` import only `zod` and `../api/primitives`.
310
- `candidate-item.ts` imports only `zod`, `../api/primitives`, `./vocabulary`
311
- and `./relevance`.
312
- `relevance.ts` imports only `zod`.
397
+ `candidate-item.ts` imports only `zod`, `../api/primitives`, `./vocabulary`,
398
+ `./relevance` and `./attention-evidence`.
399
+ `relevance.ts` imports only `zod` and `./enumeration`.
400
+ `enumeration.ts` imports only `zod` and `../api/primitives` (never
401
+ `./relevance`, so there is no cycle).
402
+ `attention-evidence.ts` imports only `zod` and `./vocabulary`.
313
403
  `vocabulary.ts` itself has no imports.
@@ -0,0 +1,96 @@
1
+ /**
2
+ * Attention evidence tests.
3
+ *
4
+ * INVARIANTS TESTED:
5
+ * - Every attention signal kind is accepted with its provenance; empty evidence
6
+ * is accepted
7
+ * - An attention signal kind appears at most once
8
+ * - A tree prior names its folder by provider id; a content match names its probe
9
+ * - Every signal carries a lowercase producer key and a version
10
+ * - Breadth reuses the sharing breadth vocabulary
11
+ */
12
+
13
+ import { describe, it, expect } from "vitest";
14
+
15
+ import { AttentionEvidenceSchema } from "../attention-evidence";
16
+
17
+ const source = { producer: "lexicon", version: "3" };
18
+ const everyKind = [
19
+ {
20
+ kind: "tree_prior",
21
+ score: 0.72,
22
+ folderId: "0AbcFolder",
23
+ source: { producer: "source-discovery-tree-triage", version: "1" },
24
+ },
25
+ { kind: "path_terms", values: ["planning", "qbr"], source },
26
+ {
27
+ kind: "content_match",
28
+ values: ["operating review"],
29
+ strength: 0.8,
30
+ probeId: "practices_1",
31
+ source: { producer: "source-discovery-plan", version: "2" },
32
+ },
33
+ {
34
+ kind: "breadth",
35
+ value: "domain",
36
+ source: { producer: "metadata-scorer", version: "4" },
37
+ },
38
+ {
39
+ kind: "recency",
40
+ score: 0.31,
41
+ source: { producer: "metadata-scorer", version: "4" },
42
+ },
43
+ {
44
+ kind: "mime_prior",
45
+ score: 0.6,
46
+ source: { producer: "metadata-scorer", version: "4" },
47
+ },
48
+ ] as const;
49
+
50
+ describe("AttentionEvidenceSchema", () => {
51
+ it("accepts one entry of every attention signal kind with its provenance", () => {
52
+ expect(AttentionEvidenceSchema.safeParse(everyKind).success).toBe(true);
53
+ expect(AttentionEvidenceSchema.safeParse([]).success).toBe(true);
54
+ });
55
+
56
+ it("an attention signal kind appears at most once", () => {
57
+ expect(
58
+ AttentionEvidenceSchema.safeParse([everyKind[1], everyKind[1]]).success,
59
+ ).toBe(false);
60
+ });
61
+
62
+ it("a tree prior names the folder and a content match names the probe", () => {
63
+ const { folderId: _f, ...noFolder } = everyKind[0];
64
+ const { probeId: _p, ...noProbe } = everyKind[2];
65
+ expect(AttentionEvidenceSchema.safeParse([noFolder]).success).toBe(false);
66
+ expect(AttentionEvidenceSchema.safeParse([noProbe]).success).toBe(false);
67
+ });
68
+
69
+ it("refuses an attention signal without a producer version", () => {
70
+ expect(
71
+ AttentionEvidenceSchema.safeParse([
72
+ {
73
+ kind: "recency",
74
+ score: 0.3,
75
+ source: { producer: "metadata-scorer" },
76
+ },
77
+ ]).success,
78
+ ).toBe(false);
79
+ expect(
80
+ AttentionEvidenceSchema.safeParse([
81
+ {
82
+ kind: "recency",
83
+ score: 0.3,
84
+ source: { producer: "Has Spaces", version: "1" },
85
+ },
86
+ ]).success,
87
+ ).toBe(false);
88
+ });
89
+
90
+ it("breadth reuses the sharing breadth vocabulary", () => {
91
+ expect(
92
+ AttentionEvidenceSchema.safeParse([{ ...everyKind[3], value: "public" }])
93
+ .success,
94
+ ).toBe(false);
95
+ });
96
+ });