@company-semantics/contracts 89.2.0 → 91.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 +4 -3
- package/src/affordances/README.md +65 -0
- package/src/affordances/__tests__/README.md +30 -0
- package/src/affordances/__tests__/affordances.test.ts +251 -0
- package/src/affordances/affordance.ts +124 -0
- package/src/affordances/args.ts +59 -0
- package/src/affordances/availability.ts +78 -0
- package/src/affordances/ids.ts +45 -0
- package/src/affordances/index.ts +39 -0
- package/src/api/generated-spec-hash.ts +2 -2
- package/src/api/generated.ts +142 -2
- package/src/generated/openapi-routes.ts +1 -0
- package/src/proactive/README.md +26 -4
- package/src/proactive/__tests__/README.md +10 -0
- package/src/proactive/__tests__/chat-templates.test.ts +8 -0
- package/src/proactive/__tests__/vocabulary.test.ts +11 -0
- package/src/proactive/__tests__/workflow-attention-fixtures.ts +49 -0
- package/src/proactive/__tests__/workflow-attention.test.ts +146 -0
- package/src/proactive/composer.ts +8 -0
- package/src/proactive/facts.ts +36 -0
- package/src/proactive/index.ts +2 -0
- package/src/proactive/kinds.ts +29 -0
- package/src/proactive/registry.ts +22 -0
- package/src/proactive/surfaces.ts +2 -0
- package/src/proactive/templates/README.md +9 -1
- package/src/proactive/templates/index.ts +2 -0
- package/src/proactive/templates/workflow-needs-attention.ts +35 -0
- package/src/root-exports/README.md +1 -0
- package/src/root-exports/delivery.ts +69 -1
- 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
- package/src/user-notifications/README.md +8 -0
- package/src/user-notifications/kinds.ts +13 -0
- package/src/user-notifications/lifecycle-registry.ts +10 -0
- package/src/workflows/README.md +38 -2
- package/src/workflows/__tests__/README.md +21 -2
- package/src/workflows/__tests__/affordances-of-outcome.test.ts +99 -0
- package/src/workflows/__tests__/deferred-route.test.ts +168 -0
- package/src/workflows/__tests__/outcome.test.ts +66 -0
- package/src/workflows/affordances-of-outcome.ts +65 -0
- package/src/workflows/deferred-route.ts +240 -0
- package/src/workflows/index.ts +34 -0
- package/src/workflows/outcome.ts +114 -1
package/src/proactive/facts.ts
CHANGED
|
@@ -23,7 +23,9 @@
|
|
|
23
23
|
* receive different prose because they were handed different facts.
|
|
24
24
|
*/
|
|
25
25
|
|
|
26
|
+
import type { WorkflowOutcomeData } from "../message-parts/workflow-outcome";
|
|
26
27
|
import type { OrgSharedTransitionTrigger } from "../org/shared-transition";
|
|
28
|
+
import type { ExplainedWorkflowOutcome } from "../workflows/deferred-route";
|
|
27
29
|
|
|
28
30
|
// =============================================================================
|
|
29
31
|
// Per-template facts
|
|
@@ -189,6 +191,39 @@ export interface SourceSyncReviewReadyFacts {
|
|
|
189
191
|
readonly checkpoint: "candidates" | "synthesis";
|
|
190
192
|
}
|
|
191
193
|
|
|
194
|
+
// =============================================================================
|
|
195
|
+
// Workflow settlement facts (control ADR-CTRL-540 phase 2)
|
|
196
|
+
// =============================================================================
|
|
197
|
+
|
|
198
|
+
/**
|
|
199
|
+
* The settlement data part a pushed chat may carry: an explained, fact-free
|
|
200
|
+
* outcome.
|
|
201
|
+
*/
|
|
202
|
+
export type ExplainedWorkflowOutcomeData = Omit<
|
|
203
|
+
WorkflowOutcomeData,
|
|
204
|
+
"outcome"
|
|
205
|
+
> & {
|
|
206
|
+
readonly outcome: ExplainedWorkflowOutcome;
|
|
207
|
+
};
|
|
208
|
+
|
|
209
|
+
/**
|
|
210
|
+
* A workflow ended in a failure a person can act on, and nobody was watching.
|
|
211
|
+
*
|
|
212
|
+
* FACT-FREE (INV-PROACTIVE-CONTENT). `narration` is the BACKEND's deterministic
|
|
213
|
+
* settlement copy (src/chat/workflow-outcome-copy.ts), rendered from the
|
|
214
|
+
* fact-stripped outcome (`facts: null`) — the same sentence the launching chat
|
|
215
|
+
* would have received, so one module owns settlement wording and it already
|
|
216
|
+
* carries the call-to-action ban. `outcome` is the settlement data part with
|
|
217
|
+
* the frozen outcome's facts stripped: reason and affordance ids only, each
|
|
218
|
+
* affordance re-gated per viewer at render and executed by the server.
|
|
219
|
+
*/
|
|
220
|
+
export interface WorkflowNeedsAttentionFacts {
|
|
221
|
+
/** The backend settlement narration for this outcome, told with `facts: null`. */
|
|
222
|
+
readonly narration: string;
|
|
223
|
+
/** `{ workflowId, outcome }` — explained route only, `facts: null`. */
|
|
224
|
+
readonly outcome: ExplainedWorkflowOutcomeData;
|
|
225
|
+
}
|
|
226
|
+
|
|
192
227
|
// =============================================================================
|
|
193
228
|
// ProactiveChatFacts
|
|
194
229
|
// =============================================================================
|
|
@@ -207,4 +242,5 @@ export interface ProactiveChatFacts {
|
|
|
207
242
|
readonly billingSuspended: BillingSuspendedFacts;
|
|
208
243
|
readonly billingSubscriptionCanceled: BillingSubscriptionCanceledFacts;
|
|
209
244
|
readonly sourceSyncReviewReady: SourceSyncReviewReadyFacts;
|
|
245
|
+
readonly workflowNeedsAttention: WorkflowNeedsAttentionFacts;
|
|
210
246
|
}
|
package/src/proactive/index.ts
CHANGED
|
@@ -51,6 +51,8 @@ export type {
|
|
|
51
51
|
BillingSuspendedFacts,
|
|
52
52
|
BillingSubscriptionCanceledFacts,
|
|
53
53
|
SourceSyncReviewReadyFacts,
|
|
54
|
+
ExplainedWorkflowOutcomeData,
|
|
55
|
+
WorkflowNeedsAttentionFacts,
|
|
54
56
|
ProactiveChatFacts,
|
|
55
57
|
} from "./facts";
|
|
56
58
|
export { PROACTIVE_CHAT_MAX_REPLIES } from "./composer";
|
package/src/proactive/kinds.ts
CHANGED
|
@@ -302,6 +302,35 @@ export const PROACTIVE_EVENT_KIND_IDS = [
|
|
|
302
302
|
* times its job is redelivered.
|
|
303
303
|
*/
|
|
304
304
|
"orgStructure.review_ready",
|
|
305
|
+
|
|
306
|
+
// ===========================================================================
|
|
307
|
+
// Workflow settlements (control ADR-CTRL-540 phase 2; control ADR
|
|
308
|
+
// affordance-system)
|
|
309
|
+
// ===========================================================================
|
|
310
|
+
//
|
|
311
|
+
// A workflow that settles with no launching conversation (a scheduled
|
|
312
|
+
// rediscovery, an upkeep re-synthesis, a launch nobody docked into a chat)
|
|
313
|
+
// is ROUTED here by the backend, per reason, through the frozen
|
|
314
|
+
// `*_DEFERRED_ROUTE_OF_REASON` tables in ../workflows/deferred-route.
|
|
315
|
+
//
|
|
316
|
+
// TRIGGER: the committed `deferred → routed` transition of the settlement
|
|
317
|
+
// row (INV-PROACTIVE-TRIGGER). OCCURRENCE KEY:
|
|
318
|
+
// `workflow-settled:{workflowId}:{executionEpoch}` — one settlement announces
|
|
319
|
+
// once, however often the sweep runs.
|
|
320
|
+
/**
|
|
321
|
+
* A workflow ended in a failure a person can act on. `explained` — a pushed
|
|
322
|
+
* chat that says what happened, FACT-FREE (reason only), carrying the frozen
|
|
323
|
+
* outcome so its grounded affordances render as controls; plus a born-read
|
|
324
|
+
* inbox row. No banner: one background run is not the workspace broken.
|
|
325
|
+
*/
|
|
326
|
+
"workflow.needs_attention",
|
|
327
|
+
/**
|
|
328
|
+
* A workflow finished (or failed with nothing to offer) while nobody was
|
|
329
|
+
* watching. A `notice` — an unread inbox row and nothing else: telling it
|
|
330
|
+
* needs counts, which are authority-sensitive and must re-render under the
|
|
331
|
+
* reader's current authority, never freeze into a chat.
|
|
332
|
+
*/
|
|
333
|
+
"workflow.finished",
|
|
305
334
|
] as const;
|
|
306
335
|
export type ProactiveEventKind = (typeof PROACTIVE_EVENT_KIND_IDS)[number];
|
|
307
336
|
|
|
@@ -262,4 +262,26 @@ export const PROACTIVE_EVENT_KINDS = {
|
|
|
262
262
|
class: "notice",
|
|
263
263
|
inboxKind: "proactive.org_structure_review_ready",
|
|
264
264
|
},
|
|
265
|
+
|
|
266
|
+
// ===========================================================================
|
|
267
|
+
// Workflow settlements (control ADR-CTRL-540 phase 2)
|
|
268
|
+
// ===========================================================================
|
|
269
|
+
//
|
|
270
|
+
// Routed per reason by the backend through ../workflows/deferred-route. NO
|
|
271
|
+
// PLAN SETS `emailKind` or `actionItemKind`: a background run's ending is
|
|
272
|
+
// announced, not standing work.
|
|
273
|
+
/**
|
|
274
|
+
* A failure a person can act on. EXPLAINED: the fact-free chat carries the
|
|
275
|
+
* badge and the frozen outcome; the inbox row is born read.
|
|
276
|
+
*/
|
|
277
|
+
"workflow.needs_attention": {
|
|
278
|
+
class: "explained",
|
|
279
|
+
chatTemplate: "workflowNeedsAttention",
|
|
280
|
+
inboxKind: "proactive.workflow_needs_attention",
|
|
281
|
+
},
|
|
282
|
+
/** A finished run nobody watched. A NOTICE: the inbox row is the attention. */
|
|
283
|
+
"workflow.finished": {
|
|
284
|
+
class: "notice",
|
|
285
|
+
inboxKind: "proactive.workflow_finished",
|
|
286
|
+
},
|
|
265
287
|
} as const satisfies Record<ProactiveEventKind, ProactiveEventDefinition>;
|
|
@@ -91,5 +91,7 @@ export const PROACTIVE_CHAT_TEMPLATES = [
|
|
|
91
91
|
"billingSubscriptionCanceled",
|
|
92
92
|
/** "A source review is ready — here is what it is and what happens if you leave it." */
|
|
93
93
|
"sourceSyncReviewReady",
|
|
94
|
+
/** "A background run did not finish — here is what happened." Fact-free. */
|
|
95
|
+
"workflowNeedsAttention",
|
|
94
96
|
] as const;
|
|
95
97
|
export type ProactiveChatTemplate = (typeof PROACTIVE_CHAT_TEMPLATES)[number];
|
|
@@ -66,6 +66,13 @@ chat. That is the precedent notify/'s README states for outbound notifications
|
|
|
66
66
|
nothing. It names no count, no document and no source: all three are
|
|
67
67
|
scope-gated, and a frozen sentence cannot be taken back.
|
|
68
68
|
`../__tests__/vocabulary.test.ts` enforces "no digit" for both checkpoints.
|
|
69
|
+
- **`workflow-needs-attention.ts` FRAMES; IT DOES NOT NARRATE.** Its `text` is
|
|
70
|
+
the backend's settlement narration verbatim (one owner of settlement wording),
|
|
71
|
+
its title is per workflow type with no digit and no next step, it has no
|
|
72
|
+
chips, and it carries the fact-free outcome as `outcome` so the outcome's
|
|
73
|
+
grounded affordances render as real controls (control ADR
|
|
74
|
+
`affordance-system`). `../__tests__/workflow-attention.test.ts` pins all of
|
|
75
|
+
it.
|
|
69
76
|
|
|
70
77
|
## Public API
|
|
71
78
|
|
|
@@ -80,10 +87,11 @@ chat. That is the precedent notify/'s README states for outbound notifications
|
|
|
80
87
|
| `billingSuspendedComposer` | The grace period elapsed; leads with "nothing was deleted" |
|
|
81
88
|
| `billingSubscriptionCanceledComposer` | Seats ended; AI funds are not forfeit |
|
|
82
89
|
| `sourceSyncReviewReadyComposer` | A source review waits; nothing has happened yet; no count |
|
|
90
|
+
| `workflowNeedsAttentionComposer` | A background run failed; frames the backend narration, no chips |
|
|
83
91
|
|
|
84
92
|
## Dependencies
|
|
85
93
|
|
|
86
94
|
Type-only imports of `ProactiveChatComposer` (`../composer`),
|
|
87
95
|
`ProactiveChatTemplate` (`../surfaces`) and the per-template facts
|
|
88
|
-
(`../facts`). No `zod`, no runtime imports beyond the sibling template
|
|
96
|
+
(`../facts`), and of `WorkflowType` (`../../workflows`). No `zod`, no runtime imports beyond the sibling template
|
|
89
97
|
modules.
|
|
@@ -31,6 +31,7 @@ import { billingSuspendedComposer } from "./billing-suspended";
|
|
|
31
31
|
import { morningBriefComposer } from "./morning-brief";
|
|
32
32
|
import { orgBecameSharedComposer } from "./org-became-shared";
|
|
33
33
|
import { sourceSyncReviewReadyComposer } from "./source-sync-review-ready";
|
|
34
|
+
import { workflowNeedsAttentionComposer } from "./workflow-needs-attention";
|
|
34
35
|
|
|
35
36
|
export const PROACTIVE_CHAT_COMPOSERS = {
|
|
36
37
|
orgBecameShared: orgBecameSharedComposer,
|
|
@@ -41,4 +42,5 @@ export const PROACTIVE_CHAT_COMPOSERS = {
|
|
|
41
42
|
billingSuspended: billingSuspendedComposer,
|
|
42
43
|
billingSubscriptionCanceled: billingSubscriptionCanceledComposer,
|
|
43
44
|
sourceSyncReviewReady: sourceSyncReviewReadyComposer,
|
|
45
|
+
workflowNeedsAttention: workflowNeedsAttentionComposer,
|
|
44
46
|
} as const satisfies Record<ProactiveChatTemplate, ProactiveChatComposer>;
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The workflow-needs-attention pushed chat — a background run ended in a
|
|
3
|
+
* failure a person can act on, while nobody was watching.
|
|
4
|
+
*
|
|
5
|
+
* THIS COMPOSER FRAMES; IT DOES NOT NARRATE. The sentence is the backend's
|
|
6
|
+
* deterministic settlement narration (one owner of settlement wording, already
|
|
7
|
+
* under the call-to-action ban), passed in as `narration`. The composer adds a
|
|
8
|
+
* title per workflow type and carries the fact-free outcome so its grounded
|
|
9
|
+
* affordances render as real controls (control ADR affordance-system). It
|
|
10
|
+
* offers no next step in words and no chips: a chip only sends a chat prompt,
|
|
11
|
+
* which is not grounded in a control.
|
|
12
|
+
*
|
|
13
|
+
* NO COUNTS (INV-PROACTIVE-CONTENT): the outcome's facts are stripped and the
|
|
14
|
+
* narration is told from the stripped outcome.
|
|
15
|
+
*/
|
|
16
|
+
import type { WorkflowType } from "../../workflows/types";
|
|
17
|
+
import type { ProactiveChatComposer } from "../composer";
|
|
18
|
+
|
|
19
|
+
const TITLE_OF_WORKFLOW_TYPE = {
|
|
20
|
+
"source-sync/discover": "The drive search did not finish",
|
|
21
|
+
"source-sync/synthesize": "The document update did not finish",
|
|
22
|
+
"org-structure/hris-connect": "The org structure analysis did not finish",
|
|
23
|
+
"org-structure/reanalyze": "The org structure analysis did not finish",
|
|
24
|
+
} as const satisfies Record<WorkflowType, string>;
|
|
25
|
+
|
|
26
|
+
export const workflowNeedsAttentionComposer: ProactiveChatComposer<"workflowNeedsAttention"> =
|
|
27
|
+
{
|
|
28
|
+
template: "workflowNeedsAttention",
|
|
29
|
+
compose: (facts) => ({
|
|
30
|
+
title: TITLE_OF_WORKFLOW_TYPE[facts.outcome.outcome.workflowType],
|
|
31
|
+
text: facts.narration,
|
|
32
|
+
replies: [],
|
|
33
|
+
outcome: facts.outcome,
|
|
34
|
+
}),
|
|
35
|
+
};
|
|
@@ -201,6 +201,8 @@ export type {
|
|
|
201
201
|
ProactiveEventDefinition,
|
|
202
202
|
OrgBecameSharedFacts,
|
|
203
203
|
MorningBriefFacts,
|
|
204
|
+
ExplainedWorkflowOutcomeData,
|
|
205
|
+
WorkflowNeedsAttentionFacts,
|
|
204
206
|
ProactiveChatFacts,
|
|
205
207
|
ProactiveChatComposer,
|
|
206
208
|
ProactiveChatMessage,
|
|
@@ -219,31 +221,60 @@ export type { WorkflowType } from "../workflows/index";
|
|
|
219
221
|
|
|
220
222
|
// The typed outcome of a workflow's terminal transition (control ADR-CTRL-540):
|
|
221
223
|
// per-type closed reasons, typed facts and an explicit disposition, classified
|
|
222
|
-
// once by the backend and frozen.
|
|
224
|
+
// once by the backend and frozen. Each ending also freezes the next steps its
|
|
225
|
+
// reason offers (control ADR affordance-system); `affordancesOfOutcome` is the
|
|
226
|
+
// one projection consumers render from. An ending with no launching
|
|
227
|
+
// conversation takes its reason's deferred route (explained | notice | covered
|
|
228
|
+
// | silent). See src/workflows/outcome.ts and src/workflows/deferred-route.ts.
|
|
223
229
|
export {
|
|
230
|
+
DEFERRED_ROUTES,
|
|
231
|
+
DISCOVER_AFFORDANCES_OF_REASON,
|
|
232
|
+
DISCOVER_DEFERRED_ROUTE_OF_REASON,
|
|
224
233
|
DISCOVER_OUTCOME_KIND_OF_REASON,
|
|
234
|
+
DeferredRouteSchema,
|
|
225
235
|
DiscoverOutcomeFactsSchema,
|
|
226
236
|
DiscoverWorkflowOutcomeSchema,
|
|
227
237
|
HrisConnectWorkflowOutcomeSchema,
|
|
238
|
+
ORG_STRUCTURE_AFFORDANCES_OF_REASON,
|
|
239
|
+
ORG_STRUCTURE_DEFERRED_ROUTE_OF_REASON,
|
|
228
240
|
ORG_STRUCTURE_OUTCOME_KIND_OF_REASON,
|
|
229
241
|
OrgStructureOutcomeFactsSchema,
|
|
230
242
|
ReanalyzeWorkflowOutcomeSchema,
|
|
243
|
+
SETTLEMENT_AUDIENCE_POLICIES,
|
|
244
|
+
SETTLEMENT_COVERING_KINDS,
|
|
245
|
+
SETTLEMENT_SILENT_REASONS,
|
|
246
|
+
SYNTHESIZE_AFFORDANCES_OF_REASON,
|
|
247
|
+
SYNTHESIZE_DEFERRED_ROUTE_OF_REASON,
|
|
231
248
|
SYNTHESIZE_OUTCOME_KIND_OF_REASON,
|
|
232
249
|
SynthesizeOutcomeFactsSchema,
|
|
233
250
|
SynthesizeWorkflowOutcomeSchema,
|
|
251
|
+
WORKFLOW_AFFORDANCES_OF_REASON,
|
|
252
|
+
WORKFLOW_DEFERRED_ROUTE_OF_REASON,
|
|
234
253
|
WORKFLOW_OUTCOME_KINDS,
|
|
235
254
|
WorkflowOutcomeDispositionSchema,
|
|
236
255
|
WorkflowOutcomeSchema,
|
|
256
|
+
affordancesOfOutcome,
|
|
257
|
+
affordancesOfReason,
|
|
258
|
+
deferredRouteOf,
|
|
259
|
+
toExplainedOutcome,
|
|
237
260
|
} from "../workflows/index";
|
|
238
261
|
|
|
239
262
|
export type {
|
|
263
|
+
DeferredRoute,
|
|
264
|
+
DeferredRouteKind,
|
|
240
265
|
DiscoverOutcomeFacts,
|
|
241
266
|
DiscoverOutcomeReason,
|
|
242
267
|
DiscoverWorkflowOutcome,
|
|
268
|
+
ExplainedDiscoverReason,
|
|
269
|
+
ExplainedOrgStructureReason,
|
|
270
|
+
ExplainedSynthesizeReason,
|
|
271
|
+
ExplainedWorkflowOutcome,
|
|
243
272
|
HrisConnectWorkflowOutcome,
|
|
244
273
|
OrgStructureOutcomeFacts,
|
|
245
274
|
OrgStructureOutcomeReason,
|
|
246
275
|
ReanalyzeWorkflowOutcome,
|
|
276
|
+
SettlementAudiencePolicy,
|
|
277
|
+
SettlementCoveringKind,
|
|
247
278
|
SynthesizeOutcomeFacts,
|
|
248
279
|
SynthesizeOutcomeReason,
|
|
249
280
|
SynthesizeWorkflowOutcome,
|
|
@@ -252,3 +283,40 @@ export type {
|
|
|
252
283
|
WorkflowOutcomeKind,
|
|
253
284
|
WorkflowOutcomeOf,
|
|
254
285
|
} from "../workflows/index";
|
|
286
|
+
|
|
287
|
+
// Affordances (control ADR affordance-system): the closed vocabulary of next
|
|
288
|
+
// steps the product may offer, as INSTANCES ({ id, args? }), with each one's
|
|
289
|
+
// binding kind and live availability condition. An affordance is offered only
|
|
290
|
+
// by a domain-owned deterministic rule; existing here never offers it. See
|
|
291
|
+
// src/affordances/README.md.
|
|
292
|
+
export {
|
|
293
|
+
AFFORDANCE_AVAILABILITY_IDS,
|
|
294
|
+
AFFORDANCE_AVAILABILITY_OF,
|
|
295
|
+
AFFORDANCE_ARGS,
|
|
296
|
+
AFFORDANCE_BINDING_KINDS,
|
|
297
|
+
AFFORDANCE_BINDING_KIND_OF,
|
|
298
|
+
AFFORDANCE_IDS,
|
|
299
|
+
AFFORDANCE_UNAVAILABLE_REASONS,
|
|
300
|
+
AffordanceAvailabilityIdSchema,
|
|
301
|
+
AffordanceAvailabilitySchema,
|
|
302
|
+
AffordanceIdSchema,
|
|
303
|
+
AffordanceSchema,
|
|
304
|
+
MAX_OFFERED_AFFORDANCES,
|
|
305
|
+
NO_ARG_AFFORDANCE_IDS,
|
|
306
|
+
OfferedAffordanceSchema,
|
|
307
|
+
PROJECTED_AFFORDANCE_IDS,
|
|
308
|
+
ReviewOpenArgsSchema,
|
|
309
|
+
sameAffordances,
|
|
310
|
+
} from "../affordances/index";
|
|
311
|
+
|
|
312
|
+
export type {
|
|
313
|
+
Affordance,
|
|
314
|
+
AffordanceArgs,
|
|
315
|
+
AffordanceAvailability,
|
|
316
|
+
AffordanceAvailabilityId,
|
|
317
|
+
AffordanceBindingKind,
|
|
318
|
+
AffordanceId,
|
|
319
|
+
AffordanceUnavailableReason,
|
|
320
|
+
OfferedAffordance,
|
|
321
|
+
ProjectedAffordanceId,
|
|
322
|
+
} from "../affordances/index";
|
|
@@ -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 `./
|
|
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
|
+
});
|
|
@@ -20,6 +20,9 @@
|
|
|
20
20
|
* the same binding.
|
|
21
21
|
* - `DOCUMENT_TYPES` is reachable from the root as the same binding, and its
|
|
22
22
|
* two types are named by the barrel.
|
|
23
|
+
* - Every index-first discovery name (operating-knowledge families, the
|
|
24
|
+
* enumeration vocabulary, attention evidence) is reachable from the root as
|
|
25
|
+
* the same binding, and its types are named by the barrel.
|
|
23
26
|
* - `ACTION_ITEM_KINDS` at the root carries `sourceSync.approval_pending`.
|
|
24
27
|
* - The root re-exports the domain barrel explicitly.
|
|
25
28
|
*
|
|
@@ -69,7 +72,9 @@ import type {
|
|
|
69
72
|
SourceSyncStandingOrder,
|
|
70
73
|
SourceSyncStandingOrdersResponse,
|
|
71
74
|
} from "../../index";
|
|
75
|
+
import * as attentionEvidence from "../attention-evidence";
|
|
72
76
|
import * as candidateItem from "../candidate-item";
|
|
77
|
+
import * as enumeration from "../enumeration";
|
|
73
78
|
import * as reach from "../reach";
|
|
74
79
|
import * as relevance from "../relevance";
|
|
75
80
|
import * as rules from "../rules";
|
|
@@ -260,6 +265,40 @@ describe("source-sync Release C package surface", () => {
|
|
|
260
265
|
expect([...barrelTypeExports]).toContain("KnownDocumentType");
|
|
261
266
|
});
|
|
262
267
|
|
|
268
|
+
it("every index-first discovery name is reachable from the root as the same binding", () => {
|
|
269
|
+
const root = rootBarrel as Record<string, unknown>;
|
|
270
|
+
for (const [name, module] of [
|
|
271
|
+
["OPERATING_KNOWLEDGE_FAMILIES", vocabulary],
|
|
272
|
+
["ENUMERATION_COMPLETENESS", enumeration],
|
|
273
|
+
["ENUMERATION_ROOT_KINDS", enumeration],
|
|
274
|
+
["combineEnumerationCompleteness", enumeration],
|
|
275
|
+
["EnumerationSkippedRangeSchema", enumeration],
|
|
276
|
+
["RootCoverageSchema", enumeration],
|
|
277
|
+
["DiscoveryStageCountersSchema", enumeration],
|
|
278
|
+
["ATTENTION_SIGNAL_KINDS", attentionEvidence],
|
|
279
|
+
["AttentionSignalSourceSchema", attentionEvidence],
|
|
280
|
+
["AttentionSignalSchema", attentionEvidence],
|
|
281
|
+
["AttentionEvidenceSchema", attentionEvidence],
|
|
282
|
+
] as const) {
|
|
283
|
+
const declared = (module as Record<string, unknown>)[name];
|
|
284
|
+
expect(declared, `${name} is declared`).toBeDefined();
|
|
285
|
+
expect(root[name], `${name} at the root`).toBe(declared);
|
|
286
|
+
}
|
|
287
|
+
for (const type of [
|
|
288
|
+
"OperatingKnowledgeFamily",
|
|
289
|
+
"EnumerationCompleteness",
|
|
290
|
+
"EnumerationRootKind",
|
|
291
|
+
"RootCoverage",
|
|
292
|
+
"DiscoveryStageCounters",
|
|
293
|
+
"AttentionSignalKind",
|
|
294
|
+
"AttentionSignalSource",
|
|
295
|
+
"AttentionSignal",
|
|
296
|
+
"AttentionEvidence",
|
|
297
|
+
]) {
|
|
298
|
+
expect([...barrelTypeExports]).toContain(type);
|
|
299
|
+
}
|
|
300
|
+
});
|
|
301
|
+
|
|
263
302
|
it("exposes sourceSync.approval_pending in ACTION_ITEM_KINDS at the root", () => {
|
|
264
303
|
expect(rootBarrel.ACTION_ITEM_KINDS).toContain(
|
|
265
304
|
"sourceSync.approval_pending",
|