@ever-works/contracts 1.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,316 @@
1
+ /**
2
+ * Domain type classification for work content
3
+ */
4
+ declare enum DomainType {
5
+ SOFTWARE = "software",
6
+ ECOMMERCE = "ecommerce",
7
+ SERVICES = "services",
8
+ GENERAL = "general"
9
+ }
10
+ /**
11
+ * Result of domain analysis for a work
12
+ */
13
+ interface DomainAnalysis {
14
+ /** The detected domain type */
15
+ readonly domain_type: DomainType;
16
+ /** Confidence score from 0.0 to 1.0 */
17
+ readonly confidence: number;
18
+ /** The noun used to describe items in this domain (e.g., "tool", "product") */
19
+ readonly item_noun?: string;
20
+ /** Expected attributes for items in this domain */
21
+ readonly expected_attributes?: readonly string[];
22
+ /** URL patterns that indicate official sources */
23
+ readonly official_source_patterns?: readonly string[];
24
+ /** Domains known to be aggregators rather than official sources */
25
+ readonly aggregator_domains?: readonly string[];
26
+ }
27
+ /**
28
+ * Web page data retrieved during content extraction
29
+ */
30
+ interface WebPageData {
31
+ /** The source URL of the page */
32
+ readonly source_url: string;
33
+ /** ISO date string when the content was retrieved */
34
+ readonly retrieved_at: string;
35
+ /** Raw content extracted from the page */
36
+ readonly raw_content: string;
37
+ }
38
+ /**
39
+ * Relevance assessment for a piece of content
40
+ */
41
+ interface RelevanceAssessment {
42
+ /** Whether the content is relevant */
43
+ readonly relevant: boolean;
44
+ /** Relevance score from 0.0 to 1.0 */
45
+ readonly relevance_score: number;
46
+ /** Human-readable reason for the assessment */
47
+ readonly reason: string;
48
+ }
49
+
50
+ /**
51
+ * Work "kind" vocabulary — the single source of truth shared by the API,
52
+ * the agent package and the web app.
53
+ *
54
+ * This lives in `@ever-works/contracts` rather than in the TypeORM entity
55
+ * because `apps/web` deliberately does not depend on `@ever-works/agent`;
56
+ * contracts is the only package both sides import at runtime. The entity
57
+ * (`packages/agent/src/entities/work.entity.ts`) re-exports from here so
58
+ * there is exactly one list to extend when a new kind ships.
59
+ */
60
+ /**
61
+ * Kinds a user can pick from the chip catalog at creation time.
62
+ *
63
+ * `company` is deliberately absent: Company Works are minted only through
64
+ * the dedicated Register-Company flow (`WorkLifecycleService.createCompanyWork`),
65
+ * never through the general create path.
66
+ *
67
+ * `repo` (chip label "Repository") is the one selectable kind that is NOT a
68
+ * generated site: it registers an EXISTING code repository — the platform
69
+ * monorepo, a template repo, a website repo — as a first-class Work so
70
+ * Tasks, Goals and fleet runs can attach to it (self-build slice D,
71
+ * EW-766). Its data repository IS the code repository; see
72
+ * `WORK_KIND_CAPABILITIES.repo`.
73
+ */
74
+ declare const USER_SELECTABLE_WORK_KINDS: readonly ["website", "landing-page", "blog", "directory", "awesome-repo", "repo"];
75
+ type UserSelectableWorkKind = (typeof USER_SELECTABLE_WORK_KINDS)[number];
76
+ /**
77
+ * Every kind a persisted `work.kind` may hold.
78
+ *
79
+ * - `default` — the column default. Carried by every Work created before
80
+ * the kind-aware create path shipped, which is the overwhelming majority
81
+ * of existing rows. It must behave identically to `directory`; see
82
+ * `WORK_KIND_CAPABILITIES` for why that invariant is load-bearing.
83
+ * - `company` — see above.
84
+ * - `campaign` — a go-to-market campaign Work: the artifact home for the
85
+ * lead lists, drafts, and reports a go-to-market pipeline produces. Like
86
+ * `company` it is minted by a dedicated flow (template activation), not
87
+ * by the general create path, so it stays out of the chip catalog.
88
+ */
89
+ declare const WORK_KINDS: readonly ["website", "landing-page", "blog", "directory", "awesome-repo", "repo", "company", "campaign", "default"];
90
+ type WorkKind = (typeof WORK_KINDS)[number];
91
+ /**
92
+ * Coerce an arbitrary value into a known `WorkKind`.
93
+ *
94
+ * `work.kind` is persisted as `varchar(32)` and modelled as an open string
95
+ * union so the server can ship a new kind without a coordinated web deploy.
96
+ * Every consumer must therefore route unknown values through here and get
97
+ * the safe generic `default` behaviour rather than crashing or silently
98
+ * mis-rendering.
99
+ *
100
+ * `landing` is accepted as an alias for `landing-page`.
101
+ *
102
+ * NEVER throws.
103
+ */
104
+ declare function normalizeWorkKind(value?: string | null): WorkKind;
105
+ /** True when `value` is a kind the create-path chip catalog offers. */
106
+ declare function isUserSelectableWorkKind(value?: string | null): value is UserSelectableWorkKind;
107
+ /**
108
+ * True when `value` names the Repository kind (`repo`) — the one kind whose
109
+ * data repository is a code repository the user already owns rather than
110
+ * something the platform generated (self-build slice D, EW-766).
111
+ *
112
+ * This is deliberately a KIND test rather than a capability test. The
113
+ * content pipelines (item generation, README regeneration, deployment) are
114
+ * not capability-gated for any kind today; what is special about a
115
+ * Repository Work is not "it has no items" — a Company Work has none either
116
+ * — but that running the directory pipeline against its data repository
117
+ * would write generated scaffolding into a human's source tree. Callers
118
+ * that must never touch that repository check this predicate. Accepts the
119
+ * same loose input as `normalizeWorkKind` and never throws.
120
+ */
121
+ declare function isRepositoryWorkKind(value?: string | null): boolean;
122
+
123
+ /**
124
+ * Goal "kind" vocabulary — the single source of truth shared by the API,
125
+ * the agent package, the MCP server and the web app.
126
+ *
127
+ * Lives in `@ever-works/contracts` for the same reason `work-kind.ts` does:
128
+ * `apps/web` deliberately does not depend on `@ever-works/agent`, and
129
+ * contracts is the only package every side imports. The Goal entity
130
+ * (`packages/agent/src/entities/goal.entity.ts`) re-exports from here so
131
+ * there is exactly one list to extend when a new kind ships.
132
+ *
133
+ * - `metric` — the original Goal: reads a number from a metrics-provider
134
+ * plugin on a schedule and completes when the comparator is
135
+ * satisfied against `targetValue`. Every row that predates
136
+ * the `goalKind` column is a metric Goal.
137
+ * - `delivery` — "ship feature X across three repos": there is NO metric,
138
+ * no comparator and no target value. The Goal completes on
139
+ * its approved Definition of Done alone — every approved
140
+ * criterion done or waived — and is otherwise driven by the
141
+ * same iteration loop and budget rules as a metric Goal.
142
+ */
143
+ declare const GOAL_KINDS: readonly ["metric", "delivery"];
144
+ type GoalKind = (typeof GOAL_KINDS)[number];
145
+ /** The column default, and what every pre-existing Goal row reads as. */
146
+ declare const DEFAULT_GOAL_KIND: GoalKind;
147
+ /**
148
+ * Type guard for WRITE paths. Fail closed: an unknown kind is rejected by
149
+ * the caller, never coerced — a Goal that is neither metric nor delivery
150
+ * has no completion rule and must not be persisted.
151
+ */
152
+ declare function isGoalKind(value: unknown): value is GoalKind;
153
+ /**
154
+ * Coerce an arbitrary value into a known `GoalKind` for READ paths only.
155
+ *
156
+ * `goals.goalKind` is `varchar(16)`, so a row written by a newer server (or
157
+ * by hand) may carry a value this build does not know. Rendering it as the
158
+ * generic `metric` behaviour is the safe default for a display surface; a
159
+ * write path must use {@link isGoalKind} instead.
160
+ *
161
+ * NEVER throws.
162
+ */
163
+ declare function normalizeGoalKind(value?: string | null): GoalKind;
164
+ /** True when `value` names the Delivery kind. Accepts loose input; never throws. */
165
+ declare function isDeliveryGoalKind(value?: string | null): boolean;
166
+
167
+ /**
168
+ * Per-Work-kind metric vocabulary.
169
+ *
170
+ * A Work's headline statistics depend on what the Work *is*: counting
171
+ * "Total Items" on a landing page is noise, and counting "Comparisons" on a
172
+ * blog is meaningless. The kind → metric mapping lives in
173
+ * `work-capabilities.ts`; this module defines the metrics themselves.
174
+ *
175
+ * Definitions are data, not components — `@ever-works/contracts` is
176
+ * framework-free and must stay importable from the API, the agent package
177
+ * and the web app alike. The web app maps `WorkMetricDefinition.id` to a
178
+ * Lucide icon in its own presentation layer.
179
+ */
180
+ /** Every metric the platform can render on a Work. */
181
+ declare const WORK_METRIC_IDS: readonly ["total-items", "posts", "categories", "tags", "comparisons", "generation-status", "deploy-status", "days-active", "registered-users", "team-members", "agents", "open-tasks", "works-owned", "page-views", "sessions", "conversions"];
182
+ type WorkMetricId = (typeof WORK_METRIC_IDS)[number];
183
+ /**
184
+ * Where a metric's value comes from. Drives which resolver runs and, when
185
+ * the value is unavailable, which remediation hint the UI offers.
186
+ */
187
+ type WorkMetricSource =
188
+ /** Read straight off the Work row — always available. */
189
+ 'work-column'
190
+ /** Computed from Work columns (e.g. days since creation). */
191
+ | 'derived'
192
+ /** A cheap `COUNT(*)` against a table the platform owns. */
193
+ | 'db-count'
194
+ /** Supplied by a connected metrics-provider plugin. */
195
+ | 'provider';
196
+ /** How the raw number should be presented. */
197
+ type WorkMetricFormat = 'integer' | 'percent' | 'duration-days' | 'status';
198
+ /**
199
+ * Message keys under `dashboard.workDetail.stats`.
200
+ *
201
+ * A literal union rather than `string`: the web app looks these up through
202
+ * next-intl, whose `useTranslations` type-checks keys against the message
203
+ * catalogue at compile time. Widening this to `string` would silently opt
204
+ * every tile label out of that check.
205
+ */
206
+ type WorkMetricLabelKey = 'totalItems' | 'posts' | 'categories' | 'tags' | 'comparisons' | 'generationStatus' | 'deployStatus' | 'daysActive' | 'registeredUsers' | 'teamMembers' | 'agents' | 'openTasks' | 'worksOwned' | 'pageViews' | 'sessions' | 'conversions';
207
+ interface WorkMetricDefinition {
208
+ readonly id: WorkMetricId;
209
+ /**
210
+ * Key under `dashboard.workDetail.stats` in the web app's messages.
211
+ * Kept as a key rather than a label so contracts stays locale-free.
212
+ */
213
+ readonly labelKey: WorkMetricLabelKey;
214
+ readonly source: WorkMetricSource;
215
+ readonly format: WorkMetricFormat;
216
+ }
217
+ /**
218
+ * The resolution state of a single metric.
219
+ *
220
+ * Distinguishing these from a plain `0` is the whole point: a Website Work
221
+ * that has never had analytics connected has *unknown* page views, not zero
222
+ * page views, and showing "0" reads as a broken product.
223
+ */
224
+ type WorkMetricState = 'ok'
225
+ /** No analytics/metrics provider is connected to this Work yet. */
226
+ | 'not_configured'
227
+ /** Metric needs a deployment that does not exist yet. */
228
+ | 'not_deployed'
229
+ /** Metric needs content generation that has not run yet. */
230
+ | 'not_generated'
231
+ /** Resolution failed. Never fatal — the tile degrades, the page renders. */
232
+ | 'error';
233
+ interface WorkMetricValue {
234
+ readonly id: WorkMetricId;
235
+ readonly state: WorkMetricState;
236
+ /** Present only when `state === 'ok'`. */
237
+ readonly value?: number | string;
238
+ /**
239
+ * Change versus the previous comparable period, as a signed ratio
240
+ * (`0.12` = +12%). Omitted when a comparison is unavailable or would be
241
+ * expensive.
242
+ */
243
+ readonly delta?: number;
244
+ }
245
+ declare const WORK_METRIC_DEFINITIONS: Record<WorkMetricId, WorkMetricDefinition>;
246
+ /** Definition lookup that tolerates an unknown id from a newer server. */
247
+ declare function getWorkMetricDefinition(id: string): WorkMetricDefinition | undefined;
248
+
249
+ /**
250
+ * What a Work of a given kind actually has.
251
+ *
252
+ * Before this registry, "given `work.kind`, what does this Work support?"
253
+ * was answered independently in half a dozen places — the create-page chip
254
+ * catalogs, the website-template resolver, and (mostly) nowhere at all,
255
+ * because every surface simply assumed a directory. That is why a Landing
256
+ * Page Work shows an Items tab and a "Total Items: 0" tile.
257
+ *
258
+ * Every kind-conditional decision in the platform should route through
259
+ * `getWorkCapabilities()` rather than testing `kind === 'directory'` inline.
260
+ */
261
+ interface WorkCapabilities {
262
+ /**
263
+ * The Items surface: the Items tab, the `/items` routes, and the
264
+ * submission / import / export endpoints that operate on them.
265
+ * `labelKey` lets a kind keep the machinery but rename it — a blog's
266
+ * items are "posts", a website's are "pages".
267
+ */
268
+ readonly items: {
269
+ readonly enabled: boolean;
270
+ readonly labelKey: 'items' | 'posts' | 'pages';
271
+ };
272
+ /** Categories / tags / collections CRUD and their tiles. */
273
+ readonly taxonomy: boolean;
274
+ /** The comparison generator sub-tab and its endpoints. */
275
+ readonly comparisons: boolean;
276
+ /** Community pull-request intake settings and processing. */
277
+ readonly communityPr: boolean;
278
+ /** Bulk item import / export. */
279
+ readonly itemImportExport: boolean;
280
+ /** Source-URL validation for items. */
281
+ readonly sourceValidation: boolean;
282
+ /** The Deploy tab and deployment endpoints. */
283
+ readonly deploy: boolean;
284
+ /** The knowledge-base workbench. */
285
+ readonly kb: boolean;
286
+ /** Ordered metric tiles rendered on the Overview tab. */
287
+ readonly metrics: readonly WorkMetricId[];
288
+ /**
289
+ * Which repositories this kind provisions.
290
+ *
291
+ * ⚠️ These keys are the PERSISTED `RepositoryRole` values and must not
292
+ * be renamed to match the newer UI labels. Mapping, for the avoidable
293
+ * confusion it saves: `data` → "Data Repository", `work` → the
294
+ * "{provider} Repository" shown to users, `website` → the "Work
295
+ * Repository" (the template output, which is not always a website).
296
+ */
297
+ readonly repos: {
298
+ readonly data: boolean;
299
+ readonly work: boolean;
300
+ readonly website: boolean;
301
+ };
302
+ }
303
+ declare const WORK_KIND_CAPABILITIES: Record<WorkKind, WorkCapabilities>;
304
+ /**
305
+ * Capabilities for a possibly-unknown kind.
306
+ *
307
+ * ALWAYS use this instead of indexing `WORK_KIND_CAPABILITIES` directly.
308
+ * `work.kind` is an open string union — a server can ship a kind this build
309
+ * has never heard of, and indexing would hand back `undefined` and crash
310
+ * whatever destructured it.
311
+ */
312
+ declare function getWorkCapabilities(kind?: string | null): WorkCapabilities;
313
+ /** Convenience predicate — the Items tab / routes are meaningful here. */
314
+ declare function workKindHasItems(kind?: string | null): boolean;
315
+
316
+ export { DEFAULT_GOAL_KIND, type DomainAnalysis, DomainType, GOAL_KINDS, type GoalKind, type RelevanceAssessment, USER_SELECTABLE_WORK_KINDS, type UserSelectableWorkKind, WORK_KINDS, WORK_KIND_CAPABILITIES, WORK_METRIC_DEFINITIONS, WORK_METRIC_IDS, type WebPageData, type WorkCapabilities, type WorkKind, type WorkMetricDefinition, type WorkMetricFormat, type WorkMetricId, type WorkMetricLabelKey, type WorkMetricSource, type WorkMetricState, type WorkMetricValue, getWorkCapabilities, getWorkMetricDefinition, isDeliveryGoalKind, isGoalKind, isRepositoryWorkKind, isUserSelectableWorkKind, normalizeGoalKind, normalizeWorkKind, workKindHasItems };
@@ -0,0 +1,316 @@
1
+ /**
2
+ * Domain type classification for work content
3
+ */
4
+ declare enum DomainType {
5
+ SOFTWARE = "software",
6
+ ECOMMERCE = "ecommerce",
7
+ SERVICES = "services",
8
+ GENERAL = "general"
9
+ }
10
+ /**
11
+ * Result of domain analysis for a work
12
+ */
13
+ interface DomainAnalysis {
14
+ /** The detected domain type */
15
+ readonly domain_type: DomainType;
16
+ /** Confidence score from 0.0 to 1.0 */
17
+ readonly confidence: number;
18
+ /** The noun used to describe items in this domain (e.g., "tool", "product") */
19
+ readonly item_noun?: string;
20
+ /** Expected attributes for items in this domain */
21
+ readonly expected_attributes?: readonly string[];
22
+ /** URL patterns that indicate official sources */
23
+ readonly official_source_patterns?: readonly string[];
24
+ /** Domains known to be aggregators rather than official sources */
25
+ readonly aggregator_domains?: readonly string[];
26
+ }
27
+ /**
28
+ * Web page data retrieved during content extraction
29
+ */
30
+ interface WebPageData {
31
+ /** The source URL of the page */
32
+ readonly source_url: string;
33
+ /** ISO date string when the content was retrieved */
34
+ readonly retrieved_at: string;
35
+ /** Raw content extracted from the page */
36
+ readonly raw_content: string;
37
+ }
38
+ /**
39
+ * Relevance assessment for a piece of content
40
+ */
41
+ interface RelevanceAssessment {
42
+ /** Whether the content is relevant */
43
+ readonly relevant: boolean;
44
+ /** Relevance score from 0.0 to 1.0 */
45
+ readonly relevance_score: number;
46
+ /** Human-readable reason for the assessment */
47
+ readonly reason: string;
48
+ }
49
+
50
+ /**
51
+ * Work "kind" vocabulary — the single source of truth shared by the API,
52
+ * the agent package and the web app.
53
+ *
54
+ * This lives in `@ever-works/contracts` rather than in the TypeORM entity
55
+ * because `apps/web` deliberately does not depend on `@ever-works/agent`;
56
+ * contracts is the only package both sides import at runtime. The entity
57
+ * (`packages/agent/src/entities/work.entity.ts`) re-exports from here so
58
+ * there is exactly one list to extend when a new kind ships.
59
+ */
60
+ /**
61
+ * Kinds a user can pick from the chip catalog at creation time.
62
+ *
63
+ * `company` is deliberately absent: Company Works are minted only through
64
+ * the dedicated Register-Company flow (`WorkLifecycleService.createCompanyWork`),
65
+ * never through the general create path.
66
+ *
67
+ * `repo` (chip label "Repository") is the one selectable kind that is NOT a
68
+ * generated site: it registers an EXISTING code repository — the platform
69
+ * monorepo, a template repo, a website repo — as a first-class Work so
70
+ * Tasks, Goals and fleet runs can attach to it (self-build slice D,
71
+ * EW-766). Its data repository IS the code repository; see
72
+ * `WORK_KIND_CAPABILITIES.repo`.
73
+ */
74
+ declare const USER_SELECTABLE_WORK_KINDS: readonly ["website", "landing-page", "blog", "directory", "awesome-repo", "repo"];
75
+ type UserSelectableWorkKind = (typeof USER_SELECTABLE_WORK_KINDS)[number];
76
+ /**
77
+ * Every kind a persisted `work.kind` may hold.
78
+ *
79
+ * - `default` — the column default. Carried by every Work created before
80
+ * the kind-aware create path shipped, which is the overwhelming majority
81
+ * of existing rows. It must behave identically to `directory`; see
82
+ * `WORK_KIND_CAPABILITIES` for why that invariant is load-bearing.
83
+ * - `company` — see above.
84
+ * - `campaign` — a go-to-market campaign Work: the artifact home for the
85
+ * lead lists, drafts, and reports a go-to-market pipeline produces. Like
86
+ * `company` it is minted by a dedicated flow (template activation), not
87
+ * by the general create path, so it stays out of the chip catalog.
88
+ */
89
+ declare const WORK_KINDS: readonly ["website", "landing-page", "blog", "directory", "awesome-repo", "repo", "company", "campaign", "default"];
90
+ type WorkKind = (typeof WORK_KINDS)[number];
91
+ /**
92
+ * Coerce an arbitrary value into a known `WorkKind`.
93
+ *
94
+ * `work.kind` is persisted as `varchar(32)` and modelled as an open string
95
+ * union so the server can ship a new kind without a coordinated web deploy.
96
+ * Every consumer must therefore route unknown values through here and get
97
+ * the safe generic `default` behaviour rather than crashing or silently
98
+ * mis-rendering.
99
+ *
100
+ * `landing` is accepted as an alias for `landing-page`.
101
+ *
102
+ * NEVER throws.
103
+ */
104
+ declare function normalizeWorkKind(value?: string | null): WorkKind;
105
+ /** True when `value` is a kind the create-path chip catalog offers. */
106
+ declare function isUserSelectableWorkKind(value?: string | null): value is UserSelectableWorkKind;
107
+ /**
108
+ * True when `value` names the Repository kind (`repo`) — the one kind whose
109
+ * data repository is a code repository the user already owns rather than
110
+ * something the platform generated (self-build slice D, EW-766).
111
+ *
112
+ * This is deliberately a KIND test rather than a capability test. The
113
+ * content pipelines (item generation, README regeneration, deployment) are
114
+ * not capability-gated for any kind today; what is special about a
115
+ * Repository Work is not "it has no items" — a Company Work has none either
116
+ * — but that running the directory pipeline against its data repository
117
+ * would write generated scaffolding into a human's source tree. Callers
118
+ * that must never touch that repository check this predicate. Accepts the
119
+ * same loose input as `normalizeWorkKind` and never throws.
120
+ */
121
+ declare function isRepositoryWorkKind(value?: string | null): boolean;
122
+
123
+ /**
124
+ * Goal "kind" vocabulary — the single source of truth shared by the API,
125
+ * the agent package, the MCP server and the web app.
126
+ *
127
+ * Lives in `@ever-works/contracts` for the same reason `work-kind.ts` does:
128
+ * `apps/web` deliberately does not depend on `@ever-works/agent`, and
129
+ * contracts is the only package every side imports. The Goal entity
130
+ * (`packages/agent/src/entities/goal.entity.ts`) re-exports from here so
131
+ * there is exactly one list to extend when a new kind ships.
132
+ *
133
+ * - `metric` — the original Goal: reads a number from a metrics-provider
134
+ * plugin on a schedule and completes when the comparator is
135
+ * satisfied against `targetValue`. Every row that predates
136
+ * the `goalKind` column is a metric Goal.
137
+ * - `delivery` — "ship feature X across three repos": there is NO metric,
138
+ * no comparator and no target value. The Goal completes on
139
+ * its approved Definition of Done alone — every approved
140
+ * criterion done or waived — and is otherwise driven by the
141
+ * same iteration loop and budget rules as a metric Goal.
142
+ */
143
+ declare const GOAL_KINDS: readonly ["metric", "delivery"];
144
+ type GoalKind = (typeof GOAL_KINDS)[number];
145
+ /** The column default, and what every pre-existing Goal row reads as. */
146
+ declare const DEFAULT_GOAL_KIND: GoalKind;
147
+ /**
148
+ * Type guard for WRITE paths. Fail closed: an unknown kind is rejected by
149
+ * the caller, never coerced — a Goal that is neither metric nor delivery
150
+ * has no completion rule and must not be persisted.
151
+ */
152
+ declare function isGoalKind(value: unknown): value is GoalKind;
153
+ /**
154
+ * Coerce an arbitrary value into a known `GoalKind` for READ paths only.
155
+ *
156
+ * `goals.goalKind` is `varchar(16)`, so a row written by a newer server (or
157
+ * by hand) may carry a value this build does not know. Rendering it as the
158
+ * generic `metric` behaviour is the safe default for a display surface; a
159
+ * write path must use {@link isGoalKind} instead.
160
+ *
161
+ * NEVER throws.
162
+ */
163
+ declare function normalizeGoalKind(value?: string | null): GoalKind;
164
+ /** True when `value` names the Delivery kind. Accepts loose input; never throws. */
165
+ declare function isDeliveryGoalKind(value?: string | null): boolean;
166
+
167
+ /**
168
+ * Per-Work-kind metric vocabulary.
169
+ *
170
+ * A Work's headline statistics depend on what the Work *is*: counting
171
+ * "Total Items" on a landing page is noise, and counting "Comparisons" on a
172
+ * blog is meaningless. The kind → metric mapping lives in
173
+ * `work-capabilities.ts`; this module defines the metrics themselves.
174
+ *
175
+ * Definitions are data, not components — `@ever-works/contracts` is
176
+ * framework-free and must stay importable from the API, the agent package
177
+ * and the web app alike. The web app maps `WorkMetricDefinition.id` to a
178
+ * Lucide icon in its own presentation layer.
179
+ */
180
+ /** Every metric the platform can render on a Work. */
181
+ declare const WORK_METRIC_IDS: readonly ["total-items", "posts", "categories", "tags", "comparisons", "generation-status", "deploy-status", "days-active", "registered-users", "team-members", "agents", "open-tasks", "works-owned", "page-views", "sessions", "conversions"];
182
+ type WorkMetricId = (typeof WORK_METRIC_IDS)[number];
183
+ /**
184
+ * Where a metric's value comes from. Drives which resolver runs and, when
185
+ * the value is unavailable, which remediation hint the UI offers.
186
+ */
187
+ type WorkMetricSource =
188
+ /** Read straight off the Work row — always available. */
189
+ 'work-column'
190
+ /** Computed from Work columns (e.g. days since creation). */
191
+ | 'derived'
192
+ /** A cheap `COUNT(*)` against a table the platform owns. */
193
+ | 'db-count'
194
+ /** Supplied by a connected metrics-provider plugin. */
195
+ | 'provider';
196
+ /** How the raw number should be presented. */
197
+ type WorkMetricFormat = 'integer' | 'percent' | 'duration-days' | 'status';
198
+ /**
199
+ * Message keys under `dashboard.workDetail.stats`.
200
+ *
201
+ * A literal union rather than `string`: the web app looks these up through
202
+ * next-intl, whose `useTranslations` type-checks keys against the message
203
+ * catalogue at compile time. Widening this to `string` would silently opt
204
+ * every tile label out of that check.
205
+ */
206
+ type WorkMetricLabelKey = 'totalItems' | 'posts' | 'categories' | 'tags' | 'comparisons' | 'generationStatus' | 'deployStatus' | 'daysActive' | 'registeredUsers' | 'teamMembers' | 'agents' | 'openTasks' | 'worksOwned' | 'pageViews' | 'sessions' | 'conversions';
207
+ interface WorkMetricDefinition {
208
+ readonly id: WorkMetricId;
209
+ /**
210
+ * Key under `dashboard.workDetail.stats` in the web app's messages.
211
+ * Kept as a key rather than a label so contracts stays locale-free.
212
+ */
213
+ readonly labelKey: WorkMetricLabelKey;
214
+ readonly source: WorkMetricSource;
215
+ readonly format: WorkMetricFormat;
216
+ }
217
+ /**
218
+ * The resolution state of a single metric.
219
+ *
220
+ * Distinguishing these from a plain `0` is the whole point: a Website Work
221
+ * that has never had analytics connected has *unknown* page views, not zero
222
+ * page views, and showing "0" reads as a broken product.
223
+ */
224
+ type WorkMetricState = 'ok'
225
+ /** No analytics/metrics provider is connected to this Work yet. */
226
+ | 'not_configured'
227
+ /** Metric needs a deployment that does not exist yet. */
228
+ | 'not_deployed'
229
+ /** Metric needs content generation that has not run yet. */
230
+ | 'not_generated'
231
+ /** Resolution failed. Never fatal — the tile degrades, the page renders. */
232
+ | 'error';
233
+ interface WorkMetricValue {
234
+ readonly id: WorkMetricId;
235
+ readonly state: WorkMetricState;
236
+ /** Present only when `state === 'ok'`. */
237
+ readonly value?: number | string;
238
+ /**
239
+ * Change versus the previous comparable period, as a signed ratio
240
+ * (`0.12` = +12%). Omitted when a comparison is unavailable or would be
241
+ * expensive.
242
+ */
243
+ readonly delta?: number;
244
+ }
245
+ declare const WORK_METRIC_DEFINITIONS: Record<WorkMetricId, WorkMetricDefinition>;
246
+ /** Definition lookup that tolerates an unknown id from a newer server. */
247
+ declare function getWorkMetricDefinition(id: string): WorkMetricDefinition | undefined;
248
+
249
+ /**
250
+ * What a Work of a given kind actually has.
251
+ *
252
+ * Before this registry, "given `work.kind`, what does this Work support?"
253
+ * was answered independently in half a dozen places — the create-page chip
254
+ * catalogs, the website-template resolver, and (mostly) nowhere at all,
255
+ * because every surface simply assumed a directory. That is why a Landing
256
+ * Page Work shows an Items tab and a "Total Items: 0" tile.
257
+ *
258
+ * Every kind-conditional decision in the platform should route through
259
+ * `getWorkCapabilities()` rather than testing `kind === 'directory'` inline.
260
+ */
261
+ interface WorkCapabilities {
262
+ /**
263
+ * The Items surface: the Items tab, the `/items` routes, and the
264
+ * submission / import / export endpoints that operate on them.
265
+ * `labelKey` lets a kind keep the machinery but rename it — a blog's
266
+ * items are "posts", a website's are "pages".
267
+ */
268
+ readonly items: {
269
+ readonly enabled: boolean;
270
+ readonly labelKey: 'items' | 'posts' | 'pages';
271
+ };
272
+ /** Categories / tags / collections CRUD and their tiles. */
273
+ readonly taxonomy: boolean;
274
+ /** The comparison generator sub-tab and its endpoints. */
275
+ readonly comparisons: boolean;
276
+ /** Community pull-request intake settings and processing. */
277
+ readonly communityPr: boolean;
278
+ /** Bulk item import / export. */
279
+ readonly itemImportExport: boolean;
280
+ /** Source-URL validation for items. */
281
+ readonly sourceValidation: boolean;
282
+ /** The Deploy tab and deployment endpoints. */
283
+ readonly deploy: boolean;
284
+ /** The knowledge-base workbench. */
285
+ readonly kb: boolean;
286
+ /** Ordered metric tiles rendered on the Overview tab. */
287
+ readonly metrics: readonly WorkMetricId[];
288
+ /**
289
+ * Which repositories this kind provisions.
290
+ *
291
+ * ⚠️ These keys are the PERSISTED `RepositoryRole` values and must not
292
+ * be renamed to match the newer UI labels. Mapping, for the avoidable
293
+ * confusion it saves: `data` → "Data Repository", `work` → the
294
+ * "{provider} Repository" shown to users, `website` → the "Work
295
+ * Repository" (the template output, which is not always a website).
296
+ */
297
+ readonly repos: {
298
+ readonly data: boolean;
299
+ readonly work: boolean;
300
+ readonly website: boolean;
301
+ };
302
+ }
303
+ declare const WORK_KIND_CAPABILITIES: Record<WorkKind, WorkCapabilities>;
304
+ /**
305
+ * Capabilities for a possibly-unknown kind.
306
+ *
307
+ * ALWAYS use this instead of indexing `WORK_KIND_CAPABILITIES` directly.
308
+ * `work.kind` is an open string union — a server can ship a kind this build
309
+ * has never heard of, and indexing would hand back `undefined` and crash
310
+ * whatever destructured it.
311
+ */
312
+ declare function getWorkCapabilities(kind?: string | null): WorkCapabilities;
313
+ /** Convenience predicate — the Items tab / routes are meaningful here. */
314
+ declare function workKindHasItems(kind?: string | null): boolean;
315
+
316
+ export { DEFAULT_GOAL_KIND, type DomainAnalysis, DomainType, GOAL_KINDS, type GoalKind, type RelevanceAssessment, USER_SELECTABLE_WORK_KINDS, type UserSelectableWorkKind, WORK_KINDS, WORK_KIND_CAPABILITIES, WORK_METRIC_DEFINITIONS, WORK_METRIC_IDS, type WebPageData, type WorkCapabilities, type WorkKind, type WorkMetricDefinition, type WorkMetricFormat, type WorkMetricId, type WorkMetricLabelKey, type WorkMetricSource, type WorkMetricState, type WorkMetricValue, getWorkCapabilities, getWorkMetricDefinition, isDeliveryGoalKind, isGoalKind, isRepositoryWorkKind, isUserSelectableWorkKind, normalizeGoalKind, normalizeWorkKind, workKindHasItems };