@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 +7 -5
- package/src/api/generated-spec-hash.ts +2 -2
- package/src/api/generated.ts +323 -2
- package/src/generated/openapi-routes.ts +4 -0
- package/src/source-sync/README.md +95 -5
- package/src/source-sync/__tests__/attention-evidence.test.ts +96 -0
- package/src/source-sync/__tests__/barrel.test.ts +39 -0
- package/src/source-sync/__tests__/enumeration.test.ts +160 -0
- package/src/source-sync/__tests__/schemas.test.ts +223 -0
- package/src/source-sync/__tests__/vocabulary.test.ts +104 -5
- package/src/source-sync/attention-evidence.ts +93 -0
- package/src/source-sync/candidate-item.ts +25 -0
- package/src/source-sync/enumeration.ts +152 -0
- package/src/source-sync/index.ts +35 -1
- package/src/source-sync/relevance.ts +85 -11
- package/src/source-sync/vocabulary.ts +37 -1
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* WHY a file received T1 attention — the evidence vector, each signal with its
|
|
3
|
+
* PROVENANCE (control ADR index-first-operating-knowledge-discovery;
|
|
4
|
+
* INV-SOURCESYNC-ATTENTION-EVIDENCE-HAS-PROVENANCE).
|
|
5
|
+
*
|
|
6
|
+
* It exists only for a file that crossed T1 attention: pre-attention signals are
|
|
7
|
+
* ephemeral (INV-SOURCESYNC-PRE-ATTENTION-SIGNALS-ARE-EPHEMERAL), so a file that never
|
|
8
|
+
* reached attention carries NULL evidence. Every signal names the producer and version
|
|
9
|
+
* that judged it, so "why did c_S find this?" stays answerable after prompts and
|
|
10
|
+
* lexicons change. `producer` is a documented key string (not a closed set, so a new
|
|
11
|
+
* producer needs no contracts major); the producers in use are `lexicon`,
|
|
12
|
+
* `source-discovery-tree-triage`, `source-discovery-plan` and `metadata-scorer`.
|
|
13
|
+
*
|
|
14
|
+
* TERMS are product- or plan-proposed vocabulary that MATCHED — never a slice of the
|
|
15
|
+
* file's name and never text read from the file. A folder is named by its provider
|
|
16
|
+
* id (`folderId`), never its name (INV-SOURCESYNC-TOPOLOGY-IS-NOT-SOURCE-EVIDENCE).
|
|
17
|
+
*/
|
|
18
|
+
import { z } from "zod";
|
|
19
|
+
import { SHARING_BREADTHS } from "./vocabulary";
|
|
20
|
+
|
|
21
|
+
export const ATTENTION_SIGNAL_KINDS = [
|
|
22
|
+
"tree_prior",
|
|
23
|
+
"path_terms",
|
|
24
|
+
"content_match",
|
|
25
|
+
"breadth",
|
|
26
|
+
"recency",
|
|
27
|
+
"mime_prior",
|
|
28
|
+
] as const;
|
|
29
|
+
export type AttentionSignalKind = (typeof ATTENTION_SIGNAL_KINDS)[number];
|
|
30
|
+
|
|
31
|
+
/** Who judged a signal: a producer key (a prompt key, `lexicon`, a scorer) and its version. */
|
|
32
|
+
export const AttentionSignalSourceSchema = z.object({
|
|
33
|
+
producer: z.string().regex(/^[a-z0-9][a-z0-9.-]{0,63}$/),
|
|
34
|
+
version: z.string().min(1).max(32),
|
|
35
|
+
});
|
|
36
|
+
export type AttentionSignalSource = z.infer<typeof AttentionSignalSourceSchema>;
|
|
37
|
+
|
|
38
|
+
const Score = z.number().min(0).max(1);
|
|
39
|
+
const Terms = z.array(z.string().min(1).max(64)).min(1).max(16);
|
|
40
|
+
|
|
41
|
+
export const AttentionSignalSchema = z.discriminatedUnion("kind", [
|
|
42
|
+
z.object({
|
|
43
|
+
kind: z.literal("tree_prior"),
|
|
44
|
+
score: Score,
|
|
45
|
+
/** The provider id of the folder the tree triage ranked — never its name. */
|
|
46
|
+
folderId: z.string().min(1).max(128),
|
|
47
|
+
source: AttentionSignalSourceSchema,
|
|
48
|
+
}),
|
|
49
|
+
z.object({
|
|
50
|
+
kind: z.literal("path_terms"),
|
|
51
|
+
values: Terms,
|
|
52
|
+
source: AttentionSignalSourceSchema,
|
|
53
|
+
}),
|
|
54
|
+
z.object({
|
|
55
|
+
kind: z.literal("content_match"),
|
|
56
|
+
values: Terms,
|
|
57
|
+
strength: Score,
|
|
58
|
+
/** The structured content probe that matched. */
|
|
59
|
+
probeId: z.string().min(1).max(64),
|
|
60
|
+
source: AttentionSignalSourceSchema,
|
|
61
|
+
}),
|
|
62
|
+
z.object({
|
|
63
|
+
kind: z.literal("breadth"),
|
|
64
|
+
value: z.enum(SHARING_BREADTHS),
|
|
65
|
+
source: AttentionSignalSourceSchema,
|
|
66
|
+
}),
|
|
67
|
+
z.object({
|
|
68
|
+
kind: z.literal("recency"),
|
|
69
|
+
score: Score,
|
|
70
|
+
source: AttentionSignalSourceSchema,
|
|
71
|
+
}),
|
|
72
|
+
z.object({
|
|
73
|
+
kind: z.literal("mime_prior"),
|
|
74
|
+
score: Score,
|
|
75
|
+
source: AttentionSignalSourceSchema,
|
|
76
|
+
}),
|
|
77
|
+
]);
|
|
78
|
+
export type AttentionSignal = z.infer<typeof AttentionSignalSchema>;
|
|
79
|
+
|
|
80
|
+
/** At most one signal of each kind. */
|
|
81
|
+
export const AttentionEvidenceSchema = z
|
|
82
|
+
.array(AttentionSignalSchema)
|
|
83
|
+
.max(ATTENTION_SIGNAL_KINDS.length)
|
|
84
|
+
.superRefine((signals, ctx) => {
|
|
85
|
+
const kinds = signals.map((s) => s.kind);
|
|
86
|
+
if (new Set(kinds).size !== kinds.length) {
|
|
87
|
+
ctx.addIssue({
|
|
88
|
+
code: "custom",
|
|
89
|
+
message: "an attention signal kind appears at most once",
|
|
90
|
+
});
|
|
91
|
+
}
|
|
92
|
+
});
|
|
93
|
+
export type AttentionEvidence = z.infer<typeof AttentionEvidenceSchema>;
|
|
@@ -18,6 +18,7 @@ import { IsoDateTime } from "../api/primitives";
|
|
|
18
18
|
import {
|
|
19
19
|
AUDIENCE_FITS,
|
|
20
20
|
DOCUMENT_TYPES,
|
|
21
|
+
OPERATING_KNOWLEDGE_FAMILIES,
|
|
21
22
|
SHARING_BREADTHS,
|
|
22
23
|
SOURCE_CLASSES,
|
|
23
24
|
SOURCE_SENSITIVITY_CLASSES,
|
|
@@ -34,6 +35,7 @@ import {
|
|
|
34
35
|
SOURCE_RELEVANCE_TIERS,
|
|
35
36
|
TARGET_AUDIENCES,
|
|
36
37
|
} from "./relevance";
|
|
38
|
+
import { AttentionEvidenceSchema } from "./attention-evidence";
|
|
37
39
|
|
|
38
40
|
/** ONE source proposed for ONE target document. */
|
|
39
41
|
export const SourceSyncCandidateItemSchema = z
|
|
@@ -75,6 +77,22 @@ export const SourceSyncCandidateItemSchema = z
|
|
|
75
77
|
documentType: z.enum(DOCUMENT_TYPES).nullable(),
|
|
76
78
|
/** Confidence of the resolved type, 0..1. NULL exactly when documentType is null or `unknown`. */
|
|
77
79
|
documentTypeConfidence: z.number().min(0).max(1).nullable(),
|
|
80
|
+
/**
|
|
81
|
+
* WHAT KIND of operating knowledge the line is — a NAME the backend RESOLVES from the
|
|
82
|
+
* resolved document type through its versioned policy table (control ADR
|
|
83
|
+
* index-first-operating-knowledge-discovery), exactly as it resolves sourceClass. The
|
|
84
|
+
* backend's internal relevance-policy families never travel. NULL when the type is
|
|
85
|
+
* null or unknown, or names no operating knowledge (a legal agreement, a transaction,
|
|
86
|
+
* personal material), and on every line of a run that predates index-first discovery.
|
|
87
|
+
*/
|
|
88
|
+
operatingKnowledgeFamily: z.enum(OPERATING_KNOWLEDGE_FAMILIES).nullable(),
|
|
89
|
+
/**
|
|
90
|
+
* WHERE the source lives in the provider ("Product/Planning/2026") — the Tier-1
|
|
91
|
+
* canonical path the backend resolves from its location graph once the file reached
|
|
92
|
+
* T1 attention (Tier-0 never retains a name or a path). NULL for a file that never
|
|
93
|
+
* reached attention, on a withheld line, and on a run that predates index-first discovery.
|
|
94
|
+
*/
|
|
95
|
+
canonicalPath: z.string().min(1).max(1024).nullable(),
|
|
78
96
|
periodLabel: z.string().max(64).nullable(),
|
|
79
97
|
seriesRole: z.enum(["current", "history"]).nullable(),
|
|
80
98
|
/** The FUSED relevance score (control ADR source-discovery-relevance-funnel), 0..1. */
|
|
@@ -106,6 +124,13 @@ export const SourceSyncCandidateItemSchema = z
|
|
|
106
124
|
targetAudience: z.enum(TARGET_AUDIENCES).nullable(),
|
|
107
125
|
/** Which lane earned each model tier's attention (INV-SOURCESYNC-EXPLORATION-RESERVED). */
|
|
108
126
|
attention: CandidateAttentionSchema,
|
|
127
|
+
/**
|
|
128
|
+
* WHY the line received T1 attention: each signal with its producer and version
|
|
129
|
+
* (INV-SOURCESYNC-ATTENTION-EVIDENCE-HAS-PROVENANCE). NULL for a file that never
|
|
130
|
+
* reached attention, on a withheld line (terms and folder ids identify a source), and
|
|
131
|
+
* on every line of a run that predates index-first discovery.
|
|
132
|
+
*/
|
|
133
|
+
attentionEvidence: AttentionEvidenceSchema.nullable(),
|
|
109
134
|
/**
|
|
110
135
|
* Whether this line is in the set that approval would land — the SERVER's
|
|
111
136
|
* answer, never derived from `use`. A decoy, a series' history or a line a
|
|
@@ -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
|
+
>;
|
package/src/source-sync/index.ts
CHANGED
|
@@ -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
|
|
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
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
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).
|
|
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];
|