@wildo-ai/platform-core-shared 1.1.0 → 1.1.2

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.
Files changed (51) hide show
  1. package/dist/esm/index.d.ts +7 -0
  2. package/dist/esm/index.d.ts.map +1 -1
  3. package/dist/esm/index.js +7 -0
  4. package/dist/esm/index.js.map +1 -1
  5. package/dist/esm/wildo-application-executions/wildo-application-executions.custom.shared.resources-config.schemas.d.ts +3 -3
  6. package/dist/esm/wildo-application-executions/wildo-application-executions.custom.shared.resources-config.schemas.d.ts.map +1 -1
  7. package/dist/esm/wildo-application-executions/wildo-application-executions.custom.shared.resources-config.schemas.js +39 -8
  8. package/dist/esm/wildo-application-executions/wildo-application-executions.custom.shared.resources-config.schemas.js.map +1 -1
  9. package/dist/esm/wildo-application-tasks/wildo-application-tasks.custom.shared.resources-config.schemas.d.ts +4 -3
  10. package/dist/esm/wildo-application-tasks/wildo-application-tasks.custom.shared.resources-config.schemas.d.ts.map +1 -1
  11. package/dist/esm/wildo-application-tasks/wildo-application-tasks.custom.shared.resources-config.schemas.js +37 -6
  12. package/dist/esm/wildo-application-tasks/wildo-application-tasks.custom.shared.resources-config.schemas.js.map +1 -1
  13. package/dist/esm/wildo-application-tasks/wildo-application-tasks.custom.shared.schemas.d.ts +1 -0
  14. package/dist/esm/wildo-application-tasks/wildo-application-tasks.custom.shared.schemas.d.ts.map +1 -1
  15. package/dist/esm/wildo-application-tasks/wildo-application-tasks.custom.shared.schemas.js +13 -0
  16. package/dist/esm/wildo-application-tasks/wildo-application-tasks.custom.shared.schemas.js.map +1 -1
  17. package/dist/esm/workbench/workbench-aspect-resources.custom.shared.schemas.d.ts +100 -0
  18. package/dist/esm/workbench/workbench-aspect-resources.custom.shared.schemas.d.ts.map +1 -0
  19. package/dist/esm/workbench/workbench-aspect-resources.custom.shared.schemas.js +578 -0
  20. package/dist/esm/workbench/workbench-aspect-resources.custom.shared.schemas.js.map +1 -0
  21. package/dist/esm/workbench/workbench-creation-board.custom.shared.schemas.d.ts +98 -0
  22. package/dist/esm/workbench/workbench-creation-board.custom.shared.schemas.d.ts.map +1 -0
  23. package/dist/esm/workbench/workbench-creation-board.custom.shared.schemas.js +86 -0
  24. package/dist/esm/workbench/workbench-creation-board.custom.shared.schemas.js.map +1 -0
  25. package/dist/esm/workbench/workbench-creation-plane.custom.shared.resources-config.schemas.d.ts +157 -0
  26. package/dist/esm/workbench/workbench-creation-plane.custom.shared.resources-config.schemas.d.ts.map +1 -0
  27. package/dist/esm/workbench/workbench-creation-plane.custom.shared.resources-config.schemas.js +258 -0
  28. package/dist/esm/workbench/workbench-creation-plane.custom.shared.resources-config.schemas.js.map +1 -0
  29. package/dist/esm/workbench/workbench-creation-plane.custom.shared.schemas.d.ts +221 -0
  30. package/dist/esm/workbench/workbench-creation-plane.custom.shared.schemas.d.ts.map +1 -0
  31. package/dist/esm/workbench/workbench-creation-plane.custom.shared.schemas.js +292 -0
  32. package/dist/esm/workbench/workbench-creation-plane.custom.shared.schemas.js.map +1 -0
  33. package/dist/esm/workbench/workbench-domain-model.custom.shared.schemas.d.ts +48 -0
  34. package/dist/esm/workbench/workbench-domain-model.custom.shared.schemas.d.ts.map +1 -0
  35. package/dist/esm/workbench/workbench-domain-model.custom.shared.schemas.js +48 -0
  36. package/dist/esm/workbench/workbench-domain-model.custom.shared.schemas.js.map +1 -0
  37. package/dist/esm/workbench/workbench-resources-types.custom.shared.schemas.d.ts +146 -6
  38. package/dist/esm/workbench/workbench-resources-types.custom.shared.schemas.d.ts.map +1 -1
  39. package/dist/esm/workbench/workbench-resources-types.custom.shared.schemas.js +256 -8
  40. package/dist/esm/workbench/workbench-resources-types.custom.shared.schemas.js.map +1 -1
  41. package/dist/esm/workbench/workbench-resources.custom.shared.resources-config.schemas.d.ts +280 -55
  42. package/dist/esm/workbench/workbench-resources.custom.shared.resources-config.schemas.d.ts.map +1 -1
  43. package/dist/esm/workbench/workbench-resources.custom.shared.resources-config.schemas.js +191 -85
  44. package/dist/esm/workbench/workbench-resources.custom.shared.resources-config.schemas.js.map +1 -1
  45. package/dist/esm/workbench/workbench-resources.custom.shared.schemas.d.ts +227 -49
  46. package/dist/esm/workbench/workbench-resources.custom.shared.schemas.d.ts.map +1 -1
  47. package/dist/esm/workbench/workbench-resources.custom.shared.schemas.js +466 -78
  48. package/dist/esm/workbench/workbench-resources.custom.shared.schemas.js.map +1 -1
  49. package/dist/tsconfig.build.tsbuildinfo +1 -1
  50. package/package.json +4 -5
  51. package/dist/esm/.builder.pid +0 -9
@@ -2,110 +2,498 @@ import { z } from 'zod';
2
2
  import { initZodDecorators } from '@wildo-ai/zod-decorators';
3
3
  initZodDecorators(z);
4
4
  /**
5
- * An application's shared MODULE and the resources it owns — derived, never stored.
5
+ * One relationship, as it rides on BOTH resources it joins.
6
6
  *
7
- * The first resource backed by `PersistenceAdapter.INTROSPECTION`. Rows come from the companion's
8
- * `resource-specifications` behavior, whose `moduleOwnership` is exactly this shape
9
- * (`{ moduleId, resourceTypes }`), so nothing is reshaped on the way through: the binding names the
10
- * behavior and the path, and the schema describes what that behavior already answers.
7
+ * ## Why an edge appears on two rows rather than on one
11
8
  *
12
- * ## Why `moduleId` is the primary key
9
+ * A relationship genuinely IS a property of both resources — "what is `deals` related to?" is a
10
+ * question about `deals` and about everything `deals` touches — so carrying it on both is a
11
+ * statement of what it means, not an encoding chosen to fit a row shape. Hanging every edge off one
12
+ * arbitrarily-chosen endpoint would be the encoding, and the choice would have to be re-derived by
13
+ * every reader.
13
14
  *
14
- * There is no store to mint a surrogate in. A derived row must be identified by something the SOURCE
15
- * already guarantees unique, and a module id is that by construction — two modules with one id would
16
- * be a defect in the application, not in this projection.
15
+ * The consumer draws each edge once, keyed by {@link relationshipId}. The duplication costs one extra
16
+ * copy of a small object per edge and buys a row that is complete on its own.
17
+ *
18
+ * ## Why the vocabularies are plain strings
19
+ *
20
+ * These values cross a subprocess boundary from the OBSERVED application's registry, which may be
21
+ * built against a different engine revision than the workbench. Binding `nature` or the cardinalities to the engine enums would make a
22
+ * row the observed application can legitimately produce unparseable here — the workbench refusing to
23
+ * show exactly the drift it exists to reveal.
24
+ */
25
+ export const Workbench_DomainResourceRelationship_Schema = z.object({
26
+ /**
27
+ * Stable identity for ONE edge within this projection, composed from every field carried about it.
28
+ *
29
+ * NOT the `resourceType1__resourceType2__foreignKeyField` triple the retired
30
+ * `workbenchResourceRelationships` resource addressed a row by: that triple is not total here,
31
+ * because a MANY-to-MANY edge carries
32
+ * `jointResourceType` and no `foreignKeyField`. Two edges agreeing on every field below are
33
+ * indistinguishable to a graph, so merging them loses nothing a reader could have seen.
34
+ */
35
+ relationshipId: z.string().min(1),
36
+ resourceType1: z.string().min(1),
37
+ resourceType2: z.string().min(1),
38
+ cardinality1: z.string().min(1),
39
+ cardinality2: z.string().min(1),
40
+ /**
41
+ * `composition`, `reference` or `association` — every member `RelationshipNature` declares — kept as
42
+ * a plain string for the drift-tolerance reason above, not because a fourth one exists today.
43
+ */
44
+ nature: z.string().min(1),
45
+ /** The field carrying the join. Absent on a junction-backed MANY-to-MANY, which has no FK column. */
46
+ foreignKeyField: z.string().min(1).nullable(),
47
+ /**
48
+ * Which side's rows CARRY the foreign key — the direction an arrow must point.
49
+ *
50
+ * Resolved by the framework's own `resolveForeignKeyHolderResourceType`, from CARDINALITY, never
51
+ * from declaration order: `(TODOS, USERS, MANY, ONE, { foreignKeyField })` is a valid child-first
52
+ * declaration that puts the referenced resource in `resourceType2`, so a consumer reading position
53
+ * draws half its arrows backwards.
54
+ */
55
+ foreignKeyHolderResourceType: z.string().min(1),
56
+ /** The junction resource of a MANY-to-MANY, which the framework requires such an edge to name. */
57
+ jointResourceType: z.string().min(1).nullable(),
58
+ /** Whether this edge is the structural ownership anchor giving `resourceType2` its scope. */
59
+ isPrimaryScope: z.boolean(),
60
+ /**
61
+ * Whether the edge is DECLARED BUT INACTIVE.
62
+ *
63
+ * The one field here whose absence made the graph WRONG rather than thinner: a disabled edge
64
+ * constrains nothing at runtime, so drawing it like a live one tells a reader the application has
65
+ * a relationship it does not have. Carried even though the dogfood application disables none
66
+ * (measured 2026-08-31: 0 of 16) — a field that is uniformly `false` today is exactly the one that
67
+ * silently misleads the first time it is not.
68
+ */
69
+ isDisabled: z.boolean(),
70
+ /**
71
+ * How a caller reaches rows on the far side — `requires_context`, `standalone`, and whatever else
72
+ * the observed application's engine revision declares.
73
+ *
74
+ * A plain string for the same drift-tolerance reason `nature` is one.
75
+ */
76
+ accessScopeStrategy: z.string().min(1).nullable(),
77
+ /** Whether the parent endpoint is `mandatory` or `optional` for the child's addressing. */
78
+ parentResourceRequirement: z.string().min(1).nullable(),
79
+ /**
80
+ * WHICH resource this edge anchors scope to, when it anchors one.
81
+ *
82
+ * Not derivable from {@link isPrimaryScope}, which says only THAT it anchors. On an edge whose
83
+ * endpoints are both scoped, the two answer different questions.
84
+ */
85
+ primaryScope: z.string().min(1).nullable(),
86
+ /** Whether a cycle through this edge is permitted — a graph-shape fact a graph should be able to show. */
87
+ allowCycle: z.boolean(),
88
+ });
89
+ /**
90
+ * ONE resource of the observed application's domain model — node, module membership, specification
91
+ * and edges in a single row.
92
+ *
93
+ * ## Why every field is a summary field
94
+ *
95
+ * A LIST renders the SUMMARY projection, and this resource's list IS its whole purpose: the graph
96
+ * consumes the collection. A field left out of the summary is a field the graph cannot draw, and the
97
+ * omission is silent — the row arrives, the field is simply absent. So the summary here is the full
98
+ * row by design rather than by neglect.
99
+ *
100
+ * ## Why this is one row per RESOURCE rather than one row per module or one row for the graph
101
+ *
102
+ * Edges are resource-to-resource. A module-per-node projection would have to AGGREGATE them, which
103
+ * destroys precisely the meanings the framework refuses to merge — nature, cardinality, foreign-key
104
+ * direction — and a graph that draws every edge as one grey line has lost information the flat table
105
+ * at least preserved.
17
106
  */
18
- export const Workbench_ApplicationModule_Schema = z.object({
19
- moduleId: z.string().min(1).isPrimaryKey().isSummaryField(),
107
+ export const Workbench_DomainResource_Schema = z.object({
108
+ /**
109
+ * Composed by the introspection source from the binding's `identityFields` (`['resourceType']`).
110
+ *
111
+ * `.isSummaryField()` on the IDENTITY field is load-bearing rather than cosmetic: the projection a
112
+ * list renders is the summary, and a row with no id is a row nothing can address — the failure that
113
+ * made every relationship row unclickable before its id was marked.
114
+ */
115
+ workbenchDomainResourceId: z.string().min(1).isPrimaryKey().isSummaryField(),
116
+ resourceType: z.string().min(1).isSummaryField(),
117
+ /**
118
+ * The module that registers this resource, or `null`.
119
+ *
120
+ * `null` is a FACT, not a gap: no module of the observed application owns this resource, which is
121
+ * true of every ENGINE resource — and an application merges all of them, since `initializeResources`
122
+ * applies the engine's core factory map unconditionally and both registries merge it. They are
123
+ * carried rather than dropped because they are part of the running application, and because an edge
124
+ * to the resource that gives a row its tenant scope is the most load-bearing edge in the graph.
125
+ *
126
+ * No `origin` field restates this. The absence of a module IS the distinction, and a second field
127
+ * saying the same thing is a second thing that can disagree with the first.
128
+ */
129
+ moduleId: z.string().min(1).isSummaryField().nullable(),
130
+ /** `domain` or `engine` — the kind the owning module declares, when there is one. */
131
+ moduleKind: z.string().min(1).isSummaryField().nullable(),
132
+ /**
133
+ * Which part of its owner this resource belongs to — a SECOND grouping axis, under the module.
134
+ *
135
+ * Sourced from `ResourceSpecificationConfig.subDomain`. For engine resources that is the closed
136
+ * `EngineResourceSubDomain` vocabulary (`auth`, `billing`, `users`, …), which exists because the
137
+ * engine's 85 resources otherwise arrive as one flat set and every surface that has to present them
138
+ * invents a grouping of its own.
139
+ *
140
+ * A plain `string` here rather than that enum, and deliberately: this schema is shared by a
141
+ * workbench that observes an application whose own sub-domains it cannot enumerate. Pinning the
142
+ * engine's vocabulary here would make an application's value invalid at the wire.
143
+ *
144
+ * `null` means the resource specification declares none — an application resource today, or an engine
145
+ * resource with no spec at all. A consumer grouping by this must carry an "unclassified" bucket and
146
+ * say so, rather than folding those rows into a real group.
147
+ */
148
+ subDomain: z.string().min(1).isSummaryField().nullable(),
20
149
  /**
21
- * The resource types this module registers.
150
+ * Every field of the materialized `mainSchema`, with everything the framework knows about it.
22
151
  *
23
- * A plain string array rather than foreign keys: these name resources in the OBSERVED
24
- * application's registry, not rows in any store this workbench can reach. Modelling them as
25
- * relationships would promise a join that cannot exist.
152
+ * This replaced a bare `fieldNames: string[]`, whose JSDoc argued that "a domain-model panel
153
+ * answers what a resource HOLDS" and that per-field extraction "belongs to `resource-specifications`".
154
+ * The premise was right and the conclusion aged badly: a NAME answers "does this field exist",
155
+ * which the node already implies by counting them, and a reader who opens a resource wants what
156
+ * the field IS. The cost was then MEASURED rather than assumed — 715 fields across the 82 engine
157
+ * resources extract to 264 KB, a fraction of what the relationship edges already cost — so the
158
+ * reason to withhold it was gone.
159
+ *
160
+ * `params`, `derived` and `origin` are OPAQUE records on purpose. They are the extractor's own
161
+ * output — authored spec prose, framework-derived structure, base-vs-variant provenance — and each
162
+ * is an open shape that grows with the decorators. Restating them field by field here would make
163
+ * this schema a second, always-lagging copy of a contract `@wildo-ai/saas-specifications` owns;
164
+ * a consumer that needs one reads it through that package's type.
26
165
  */
27
- resourceTypes: z.array(z.string()).isSummaryField(),
166
+ fields: z.array(z.object({
167
+ fieldName: z.string().min(1),
168
+ /** Which `{primitive}Spec()` decorator carried the authored prose, when one did. */
169
+ specDecorator: z.string().min(1).nullable(),
170
+ params: z.record(z.string(), z.unknown()).nullable(),
171
+ derived: z.record(z.string(), z.unknown()).nullable(),
172
+ origin: z.record(z.string(), z.unknown()).nullable(),
173
+ /**
174
+ * The field's DECORATOR record, as the schema declares it — beside `derived`, which is the
175
+ * extractor's interpretation of it.
176
+ *
177
+ * It carries what the interpretation drops: `isSummaryField`, `isHidden`, `excludeFromCreate` /
178
+ * `excludeFromUpdate`, `dataCategories`, and the index / unique / primary-key trio.
179
+ *
180
+ * It does NOT carry `*.ui-behavior.tsx` configuration, and that is a reachability limit rather
181
+ * than an omission: those decorators are applied by a FRONTEND module the companion never loads,
182
+ * because the subprocess resolves `library`, `specifications` and `service` targets and there is
183
+ * no frontend one. Measured on the engine's resources — zero UI keys present.
184
+ */
185
+ metadata: z.record(z.string(), z.unknown()).nullable(),
186
+ })).isSummaryField(),
28
187
  /**
29
- * `domain` or `engine` — or absent, which an application is free to leave unset.
188
+ * Every operation the resource declares, projected to what a READER needs.
30
189
  *
31
- * Carried because it is the ONE field that distinguishes a business-domain module from an engine
32
- * contribution, and the projection used to discard it. Without it a module carrying roles,
33
- * features, milestones and a product catalog is indistinguishable from a broken empty one.
190
+ * Never the DTOs. A materialized operation carries six Zod schemas — request, response, summary,
191
+ * context, internal, repository — each serializing to tens of kilobytes, so carrying the operation
192
+ * objects would put megabytes on the wire to say which verbs exist. What is here is the identity,
193
+ * the variant, the authorization and the flags that change what the operation DOES.
34
194
  *
35
- * A plain nullable string rather than the `ApplicationModuleKind` union: this is a projection of
36
- * the OBSERVED application's declaration, which may be built against a different engine revision
37
- * than the workbench. Binding the union here would make the workbench refuse to display a kind the
38
- * observed app can legitimately declare — refusing to show exactly the drift it exists to reveal.
195
+ * An EMPTY `roles` array is a real answer — no role gate on that variant — and not a gap.
39
196
  */
40
- kind: z.string().min(1).nullable(),
197
+ operations: z.array(z.object({
198
+ operationIdentifier: z.string().min(1),
199
+ /** The TRANSPORT kind: `api_call`, `internal_call`, `repository_only`, … */
200
+ variantType: z.string().min(1).nullable(),
201
+ /**
202
+ * WHICH variant of that transport — `admin`, `summary`, `context` — or `null` for the default.
203
+ *
204
+ * The missing half of an operation's IDENTITY, and its absence was a defect rather than missing
205
+ * detail: `organizations` declares four operations that are all `read` × `api_call`, separated
206
+ * only by this key, and across the engine's core map 133 of 794 operations collided on the pair
207
+ * without it.
208
+ */
209
+ variantKey: z.string().min(1).nullable(),
210
+ /** For a CUSTOM operation, the core verb it behaves like. `null` for a core operation. */
211
+ resourceOperationLike: z.string().min(1).nullable(),
212
+ isDefault: z.boolean(),
213
+ roles: z.array(z.string()),
214
+ riskLevel: z.string().min(1).nullable(),
215
+ isUnimplemented: z.boolean(),
216
+ isDisabled: z.boolean(),
217
+ requiresStepUpAuthentication: z.boolean(),
218
+ admitsCrossTenantPlatformAdministration: z.boolean(),
219
+ auditableEventType: z.string().min(1).nullable(),
220
+ isMcpExposed: z.boolean(),
221
+ requiredFeatures: z.array(z.string()),
222
+ /**
223
+ * Everything else the operation declares — HTTP verb, runtime mode, bulk and API-key posture,
224
+ * notification fan-out, custom-implementation modes, authoritative fields.
225
+ *
226
+ * A DENY LIST over the operation's own keys on the producing side, for the same reason `advanced`
227
+ * is, and this is the one that proved the argument: a hand-picked set of twelve fields shipped
228
+ * without `variantKey`, `httpVerb`, `isBulkOperation` or `serviceRuntimeMode`. The six DTO
229
+ * schemas are excluded by name — each serializes to tens of kilobytes.
230
+ */
231
+ attributes: z.record(z.string(), z.unknown()),
232
+ /**
233
+ * The authored specification for this operation, keyed by `operationIdentifier` — so every
234
+ * VARIANT of one verb carries the same entry, which is what the specification describes.
235
+ *
236
+ * `null` means the specification does not mention the operation: a gap a reader can act on.
237
+ */
238
+ specification: z.record(z.string(), z.unknown()).nullable(),
239
+ /** The authored `frontend.views` overlay for this operation, when there is one. */
240
+ view: z.record(z.string(), z.unknown()).nullable(),
241
+ })).isSummaryField(),
41
242
  /**
42
- * What the module contributes, and how much of each.
243
+ * The operations for which the resource specification declares a FRONTEND view overlay (`frontend.views`).
43
244
  *
44
- * DERIVED from the module's own declared keys rather than from a fixed list, so a contribution
45
- * kind added to `SharedSaaSModule` appears here without a schema or projection change. Counts
46
- * rather than content: an inventory answers whether and how much, and the content belongs to
47
- * whichever behavior projects that contribution specifically.
245
+ * Refs only. The entries are authoring prose about how an operation surfaces, which belongs to a
246
+ * specification reader; what a domain-model panel can act on is which operations have an authored
247
+ * view and — by difference with `operations` — which do not.
48
248
  */
49
- contributions: z.array(z.object({
50
- contribution: z.string().min(1),
51
- count: z.number().int().nonnegative(),
52
- })),
249
+ viewRefs: z.array(z.string()).isSummaryField(),
250
+ /**
251
+ * The configuration facts that are neither prose nor structure — persistence, policy, bindings.
252
+ *
253
+ * Everything on the resource's `ResourceConfiguration` that decides how it BEHAVES rather than what
254
+ * it holds: which database it lives in, whether it is a system resource, its retention,
255
+ * transposition, portability and data-subject policies, its HTTP and introspection bindings.
256
+ *
257
+ * An OPAQUE record, and deliberately so. The producer builds it by DENY list over the
258
+ * configuration's own keys rather than by enumerating the ones that exist today, because
259
+ * `ResourceConfiguration` has grown several of these fields in as many months and an enumeration
260
+ * reports the subset its author knew about. Typing it here would put that enumeration back.
261
+ */
262
+ advanced: z.record(z.string(), z.unknown()).isSummaryField(),
263
+ /**
264
+ * The authored specification's PROSE, or `null` when this resource has none.
265
+ *
266
+ * It rides in the row rather than being fetched on selection because a per-selection fetch is not
267
+ * cheaper: `deriveRows` has no per-row path, so an addressed READ derives the WHOLE set and filters
268
+ * it afterwards. "Fetch the specification when a node is clicked" would therefore be a second full
269
+ * derivation of the entire application.
270
+ */
271
+ specification: z.object({
272
+ /** Why this resource exists in the domain. */
273
+ purpose: z.string(),
274
+ /** The functional role it plays inside the business architecture. */
275
+ businessRole: z.string(),
276
+ /** How it evolves over time. Optional in the specification contract, so nullable here. */
277
+ lifecycleRole: z.string().nullable(),
278
+ /** Domain caveats not encoded structurally elsewhere. */
279
+ businessConstraints: z.array(z.string()),
280
+ }).isSummaryField().nullable(),
281
+ /** Every relationship this resource participates in, on either side. */
282
+ relationships: z.array(Workbench_DomainResourceRelationship_Schema).isSummaryField(),
53
283
  });
54
284
  /**
55
- * One relationship edge between two of the observed application's resources.
285
+ * The QUESTION a specification family answers — the axis the workbench groups them by.
56
286
  *
57
- * ## Where the shape comes from
287
+ * ## Why the families are grouped at all
58
288
  *
59
- * MEASURED against a live `resources-registry` derivation rather than transcribed from the engine's
60
- * `ResourceRelationship` type: 16 edges, 12 keys present on all of them and `primaryScope` on 8. The
61
- * engine type carries more than the behavior emits, and a schema written from the type would refuse
62
- * every row for fields the projection never sends.
289
+ * Fifteen rows in one undifferentiated list is a directory, not a review surface. A creator opening
290
+ * the workbench is not looking for "the `document-release-policy` family"; they are looking for what
291
+ * was decided about who this product is for, or about what it must promise. Grouping by the question
292
+ * answered is what makes the list navigable without inventing twelve destinations.
63
293
  *
64
- * ## Why the vocabularies are plain strings here
294
+ * ## Why this is a display vocabulary and not a property of the family
65
295
  *
66
- * `nature`, `cardinality*` and `accessScopeStrategy` ARE closed engine enums, and this is one of the
67
- * documented exceptions rather than an oversight: these rows cross a subprocess boundary as JSON
68
- * from the OBSERVED application's registry, which may be built against a different engine revision
69
- * than the workbench. Binding to the enum would make a row the observed app can legitimately produce
70
- * unparseable here — the workbench would refuse to show exactly the drift it exists to reveal. This
71
- * is a projection of someone else's runtime, not an authored value the framework controls.
296
+ * `ApplicationSpecificationFamilySemanticSchemaRef` names what a family MEANS and what validates it.
297
+ * Nothing in the runtime branches on the concern below — it exists so a reader can find a row. Adding
298
+ * it to the semantic authority would put a workbench presentation choice inside the contract the
299
+ * journey lands artifacts against.
72
300
  *
73
- * `resolvedSemantics` is deliberately ABSENT. It is a deep nested object (handling modes, foreign-key
74
- * persistence) that no current view reads, and copying it into the schema would freeze a shape whose
75
- * consumer does not exist yet.
301
+ * The family → concern mapping deliberately lives with the projection that derives it (the
302
+ * companion's `specification-artifacts` behavior), beside the semantic authority it must stay
303
+ * exhaustive over, rather than here where the authority is not reachable.
76
304
  */
77
- export const Workbench_ResourceRelationship_Schema = z.object({
305
+ export var WorkbenchSpecificationArtifactConcern;
306
+ (function (WorkbenchSpecificationArtifactConcern) {
307
+ /** Why the product exists and who it is for — `vision-brief`, `market-snapshot`. */
308
+ WorkbenchSpecificationArtifactConcern["INTENT"] = "intent";
309
+ /** What it must do and what it costs — `requirements`, `feature-drafts`, `business-model`. */
310
+ WorkbenchSpecificationArtifactConcern["SCOPE"] = "scope";
311
+ /** How and when it gets built — `domain-plan`, `build-plan`, `roadmap`, `placement-plan`. */
312
+ WorkbenchSpecificationArtifactConcern["DELIVERY"] = "delivery";
313
+ /** How it presents itself — `brand-brief`. */
314
+ WorkbenchSpecificationArtifactConcern["IDENTITY"] = "identity";
315
+ /** What it must promise and publish — the assurance and governed-document families. */
316
+ WorkbenchSpecificationArtifactConcern["OBLIGATIONS"] = "obligations";
78
317
  /**
79
- * Composed by the introspection source from the binding's `identityFields`, because a relationship
80
- * row carries no identifier of its own — see `IntrospectionResourceBinding.identityFields`.
318
+ * A family the workbench has not been taught to group.
81
319
  *
82
- * ## Why this is named for the resource rather than `relationshipId`
320
+ * A named member rather than a throw, and rather than silence. The population is derived from the
321
+ * semantic authority, so a family added there arrives here with no classification — and the two
322
+ * alternatives are both worse than a visible bucket: refusing the derivation would blank the whole
323
+ * page over one unmapped row, and defaulting into a real concern would file the new family
324
+ * somewhere plausible and wrong, which nobody would notice.
83
325
  *
84
- * A COMPOSED identity is written to the resource's own `resourceFieldIdentifier`. That is the one
85
- * name the source can resolve from the registry without introspecting decorators — the schema's
86
- * `.isPrimaryKey()` marker is not readable as a field NAME from the built configuration, and
87
- * duplicating the name onto the binding would create a second declaration that can disagree.
326
+ * A companion test asserts this member is currently UNUSED, so it stays a signal rather than
327
+ * becoming a habit.
328
+ */
329
+ WorkbenchSpecificationArtifactConcern["UNCLASSIFIED"] = "unclassified";
330
+ })(WorkbenchSpecificationArtifactConcern || (WorkbenchSpecificationArtifactConcern = {}));
331
+ /**
332
+ * Whether a family has landed, and whether what landed still validates.
333
+ *
334
+ * THREE states, not two, because "not landed" and "landed but invalid" are the opposite instructions
335
+ * to a reviewer: the first is work the journey has not done, the second is work it did that no longer
336
+ * holds. A boolean `isValid` would report both as `false`.
337
+ */
338
+ export var WorkbenchSpecificationArtifactState;
339
+ (function (WorkbenchSpecificationArtifactState) {
340
+ /** The family is exported by the application and its `_core` validator reports no error. */
341
+ WorkbenchSpecificationArtifactState["LANDED_VALID"] = "landed_valid";
342
+ /** The family is exported and its `_core` validator reports at least one ERROR-severity issue. */
343
+ WorkbenchSpecificationArtifactState["LANDED_INVALID"] = "landed_invalid";
344
+ /**
345
+ * The application exports nothing for this family.
88
346
  *
89
- * Its sibling `workbenchApplicationModules` names its key `moduleId` instead, and the difference
90
- * is real rather than inconsistent: that behavior's rows arrive carrying `moduleId`, so nothing is
91
- * composed and the schema is free to keep the source's own name.
347
+ * Reported as a ROW rather than omitted — the same call `definition-specifications.behavior.ts`
348
+ * makes for the same reason: *"an omitted row and a family that does not exist would look the same
349
+ * to the reader."* On a fresh application an empty list would say nothing; fifteen NOT_LANDED rows
350
+ * say what the journey has left to produce.
92
351
  */
93
- workbenchResourceRelationshipId: z.string().min(1).isPrimaryKey(),
94
- resourceType1: z.string().min(1).isSummaryField(),
95
- resourceType2: z.string().min(1).isSummaryField(),
96
- /** `one` / `many`, as the observed application's registry reports them. */
97
- cardinality1: z.string().min(1),
98
- cardinality2: z.string().min(1),
99
- /** `composition`, `aggregation`, `reference`, … — the observed app's vocabulary, not ours. */
100
- nature: z.string().min(1).isSummaryField(),
101
- /** The field carrying the join. Present on every measured edge. */
102
- foreignKeyField: z.string().min(1),
103
- accessScopeStrategy: z.string().min(1),
104
- parentResourceRequirement: z.string().min(1),
105
- isPrimaryScope: z.boolean(),
106
- allowCycle: z.boolean(),
107
- isDisabled: z.boolean(),
108
- /** Present on 8 of 16 measured edges — optional because the projection genuinely omits it. */
109
- primaryScope: z.string().min(1).optional(),
352
+ WorkbenchSpecificationArtifactState["NOT_LANDED"] = "not_landed";
353
+ })(WorkbenchSpecificationArtifactState || (WorkbenchSpecificationArtifactState = {}));
354
+ /**
355
+ * ONE application-specification family, projected for a human reviewer.
356
+ *
357
+ * ## Why the prose fields are markdown strings
358
+ *
359
+ * `StringDisplayMode.MARKDOWN` renders GFM through the framework's own display chrome, which is what
360
+ * lets headings, lists and paragraphs survive into the page. The alternative — modelling each
361
+ * family's shape as nested schema fields — would have to be done fifteen times, would freeze twelve
362
+ * `_core` contracts into this projection, and would still render as a form rather than as a document.
363
+ *
364
+ * The rendering is an OUTLINE, never a table. That is the acceptance criterion this resource exists
365
+ * to satisfy: these artifacts are prose-heavy and their value is in the rationale, so a projection
366
+ * that put them in columns would destroy exactly what makes them worth reviewing.
367
+ *
368
+ * ## Why the heavy fields are NOT summary fields
369
+ *
370
+ * `document`, `issues`, `decisionRecord` and `diagram` are excluded from the summary by omission, so
371
+ * a list carries the INDEX while a detail page carries the documents. One derivation still computes
372
+ * all of them either way — this bounds the RESPONSE, not the work.
373
+ *
374
+ * Which variant a caller gets matters, and is easy to state wrongly: the LIST operation's DEFAULT
375
+ * variant answers the full row, and it is the operation's `summary` variant that applies this
376
+ * projection. Measured against the live wonder-crm companion, 2026-08-31:
377
+ * `GET /api/v1/workbench-specification-artifacts` → **149,089 B**, and
378
+ * `…/workbench-specification-artifacts/summary` → **3,654 B** carrying the eight fields marked
379
+ * below. A list layout renders the summary variant — which is also why the addressing field must be
380
+ * marked, or every row in that list is unclickable.
381
+ */
382
+ export const Workbench_SpecificationArtifact_Schema = z.object({
383
+ /**
384
+ * The family id — `vision-brief`, `market-snapshot`, `compliance-program`.
385
+ *
386
+ * The source's own key, marked `.isSummaryField()` because a list row with no id is a row nothing
387
+ * can address: the summary projection is what a click reads the target from.
388
+ */
389
+ family: z.string().min(1).isPrimaryKey().isSummaryField(),
390
+ /** The family's name in a creator's words — `Vision`, `Market`, `Assurance programme`. */
391
+ title: z.string().min(1).isSummaryField(),
392
+ /**
393
+ * What this family IS, in one sentence a creator recognizes — static per family, present whether
394
+ * or not anything landed. This is the summary card's subtitle: a card reading only
395
+ * "Governed-document drafts · Not landed" tells a reader nothing they can act on; "The drafts of
396
+ * the public documents this product must publish" does.
397
+ */
398
+ description: z.string().min(1).isSummaryField(),
399
+ /**
400
+ * A one-line PULSE of the landed content — `"3 ICPs · 5 personas · 2 positionings"`, the vision's
401
+ * own one-liner — or `null` when the family has not landed.
402
+ *
403
+ * Derived server-side from the RAW value so the list answers "how much is in there" without any
404
+ * client downloading fifteen full documents. It is a COUNT-and-headline summary, never a
405
+ * truncation of prose: a clipped sentence reads as an accident, a count reads as an answer.
406
+ */
407
+ contentSummary: z.string().nullable().isSummaryField(),
408
+ /** The question this family answers, which is the axis the list groups by. */
409
+ concern: z.enum(WorkbenchSpecificationArtifactConcern).isSummaryField(),
410
+ /** Landed and valid, landed and invalid, or not landed at all. */
411
+ state: z.enum(WorkbenchSpecificationArtifactState).isSummaryField(),
412
+ /** The `_core` contract the family's content must satisfy, verbatim from the semantic authority. */
413
+ schemaRef: z.string().min(1),
414
+ /**
415
+ * Where the family's file lives, relative to the application root — the file a reader opens when
416
+ * the rendering is not enough.
417
+ *
418
+ * `null` when this runtime places no file for the family. A placement and a RENDER PROFILE are
419
+ * different things (`playbook-output-landing-target`), and only the placement is asked for here.
420
+ */
421
+ sourcePath: z.string().min(1).nullable().isSummaryField(),
422
+ /** ERROR-severity issues the family's own validator reports on the landed content, right now. */
423
+ errorCount: z.number().int().nonnegative().isSummaryField(),
424
+ /** WARN-severity issues — authoring drift that does not invalidate the artifact. */
425
+ warnCount: z.number().int().nonnegative().isSummaryField(),
426
+ /**
427
+ * Every playbook whose declared `outputs[].family` is this family.
428
+ *
429
+ * This is the durable half of "what produced it": a fact about the compiled catalog, in git, true
430
+ * whether or not any run's state survives. A link to the RUN that produced this particular value
431
+ * was deliberately rejected — runs, traces and the write register are a per-run cache under
432
+ * `one-source-of-truth.md` Rule 5, so a durable artifact pointing into deletable state is dangling
433
+ * by design. The link runs the other way: a run may name what it wrote.
434
+ *
435
+ * Empty for a family no playbook produces — which is a finding, not a gap.
436
+ */
437
+ producedByPlaybookRefs: z.array(z.string()).isSummaryField(),
438
+ /**
439
+ * The landed family value, RAW — exactly the object the specifications package exports.
440
+ *
441
+ * ## Why the raw value rides beside its rendering
442
+ *
443
+ * The per-family display templates (workbench `artifact-templates/`) render EDITORIALLY — an ICP
444
+ * card, a requirement with its acceptance criteria, a placement decision with its rationale — and
445
+ * an editorial template needs the VALUE, not a markdown projection of it. The markdown `document`
446
+ * below stays: it is the structural fallback every family keeps behind the template error
447
+ * boundary, and the whole rendering for a family no template covers yet.
448
+ *
449
+ * ## Untyped HERE, typed at the EDGE — a measured decision, not an omission
450
+ *
451
+ * The typed `_core` snapshot schemas live in `@wildo-ai/saas-specifications`, which depends on
452
+ * `saas-frontend-lib` AND `saas-backend-lib` (its package.json) — while this package is the
453
+ * platform's light contracts-only spine (`platform/CLAUDE.md`). Typing this field would drag the
454
+ * whole engine into every consumer of this package for a shape only two consumers read. The two
455
+ * readers type it themselves: the companion behavior validates through the semantic authority
456
+ * before emitting, and the workbench templates narrow via TYPE-ONLY imports (erased at compile,
457
+ * zero runtime dependency). Recorded with alternatives in
458
+ * `.claude/plans/workbench-product-artifacts-ux-analysis.md` (D1).
459
+ *
460
+ * Deliberately NOT a summary field: the LIST carries the index, and this is the heaviest thing a
461
+ * row owns. Emitted from the RAW landed value — never from a validator's parsed output, whose
462
+ * schema `.default()`s would inject content the author never wrote.
463
+ */
464
+ content: z.record(z.string(), z.unknown()).nullable(),
465
+ /**
466
+ * The artifact rendered as a document, or `null` when the family has not landed.
467
+ *
468
+ * Produced by `renderSpecificationFamilyReviewDocument` — an outline over the landed value, not a
469
+ * prompt block. The prompt renderers (`market-render-prompt`, `brand-render-prompt`) read the same
470
+ * content for a language model and must never be reused here.
471
+ */
472
+ document: z.string().nullable(),
473
+ /**
474
+ * The validator's own findings, rendered for a reader, or `null` when there are none.
475
+ *
476
+ * Carried as content rather than as counts alone because a reviewer deciding whether to accept an
477
+ * artifact needs to see WHICH rule failed and where, and the counts beside them in the list are
478
+ * only the signal to come and look.
479
+ */
480
+ issues: z.string().nullable(),
481
+ /**
482
+ * `DECISIONS.md` committed beside the family, verbatim, or `null` when there is none.
483
+ *
484
+ * The strongest provenance this surface has: it names the playbook that produced the artifact and,
485
+ * more importantly, records every question the creator was asked — including the ones they did NOT
486
+ * answer, where "the derivation chose for itself". A run that asked nothing writes no such file, so
487
+ * `null` here means "no clarification was recorded", never "no clarification happened".
488
+ */
489
+ decisionRecord: z.string().nullable(),
490
+ /**
491
+ * `DIAGRAM.md` committed beside the family, verbatim, or `null`.
492
+ *
493
+ * Three families have one today (`domain-plan`, `build-plan`, `roadmap`). It is surfaced rather
494
+ * than re-derived: the diagram is already a committed output of the family, regenerated on every
495
+ * landing, and rendering a second picture here would be a second thing that can disagree with it.
496
+ */
497
+ diagram: z.string().nullable(),
110
498
  });
111
499
  //# sourceMappingURL=workbench-resources.custom.shared.schemas.js.map