@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
@@ -1,7 +1,36 @@
1
1
  import { CORE_APP_ROLES, CoreResourceOperation, CoreResourceType, IntrospectionTenancyStance, PersistenceAdapter, ResourceOperationRiskLevel, ResourceOperationVariantType, ResourceRelationshipAccessScopeStrategy, ResourceRelationshipCardinality, RelationshipNature, createResourceConfiguration_Initialization, createResourcesRelationship, } from '@wildo-ai/saas-models';
2
+ import { z } from 'zod';
2
3
  import { WorkbenchShared_ResourceFieldIdentifier, WorkbenchShared_ResourceType, } from './workbench-resources-types.custom.shared.schemas.js';
3
- import { Workbench_ApplicationModule_Schema, Workbench_ResourceRelationship_Schema, } from './workbench-resources.custom.shared.schemas.js';
4
- const WorkbenchApplicationModule_Operations = undefined;
4
+ import { buildWorkbenchAspectRelationships, workbenchAspectResources_ConfigurationsFactoryMap, } from './workbench-aspect-resources.custom.shared.schemas.js';
5
+ import { Workbench_DomainResource_Schema, Workbench_SpecificationArtifact_Schema, WorkbenchSpecificationArtifactConcern, WorkbenchSpecificationArtifactState, } from './workbench-resources.custom.shared.schemas.js';
6
+ import { workbenchCreationPlane_ResourcesConfigurationsFactoryMap, workbenchCreationPlane_ResourcesRelationships, } from './workbench-creation-plane.custom.shared.resources-config.schemas.js';
7
+ /**
8
+ * The page ceiling every workbench LIST declares — and it is a correctness fix, not a tuning knob.
9
+ *
10
+ * `IntrospectionResourceRepository.list()` answers `pagination.limit = rows.length` over the WHOLE
11
+ * derived set, deliberately: a behavior computes its entire answer in one subprocess run, there is no
12
+ * cursor to seek, and reporting a total the caller cannot page past would be a lie. The response DTO
13
+ * then bounds that `limit` by the operation's `maxPaginatedResultPerPageLimit`, whose default is
14
+ * **100**. So any introspected resource whose derivation exceeds 100 rows fails with `too_big` at
15
+ * `pagination.limit` — the request having been served correctly right up to the last hop.
16
+ *
17
+ * Measured 2026-08-31, and the measurement is why this ceiling is a CORRECTNESS FIX rather than
18
+ * headroom. The domain model projects every resource the observed application RUNS, which is its own
19
+ * plus the engine's whole merged vocabulary: `saas-models`' core map alone carries **82** resources
20
+ * and **105** relationships before `saas-backend-lib`'s map or the application's own eight are
21
+ * counted. So the domain-model LIST is already comfortably past the default 100 on the smallest
22
+ * application there is, and would have failed with `too_big` at `pagination.limit` — the request
23
+ * having been served correctly right up to the last hop, and the bound named in none of these
24
+ * declarations. The number also scales with the OBSERVED application, which is the one input this
25
+ * repository does not control.
26
+ *
27
+ * Declared on the shared variant so every workbench resource is bounded by the same number, rather
28
+ * than one being raised and its siblings left waiting to hit the same wall.
29
+ *
30
+ * Above the ceiling the request FAILS rather than truncating, which is the correct direction: a
31
+ * silently short domain model is indistinguishable from a complete one once it is drawn.
32
+ */
33
+ export const WORKBENCH_INTROSPECTION_MAX_ROWS_PER_PAGE = 1000;
5
34
  /**
6
35
  * Read-only over HTTP, and PUBLIC.
7
36
  *
@@ -31,62 +60,49 @@ const WorkbenchApplicationModule_Operations = undefined;
31
60
  * If an introspected resource is ever added that projects something OTHER than the application's own
32
61
  * structure, this role is wrong for it — the stance and the role must move together.
33
62
  */
34
- function createReadOnlyApiVariant() {
63
+ export function createReadOnlyApiVariant() {
35
64
  return {
36
65
  variantType: ResourceOperationVariantType.API_CALL,
37
66
  isDefault: true,
67
+ /*
68
+ * `APP_PUBLIC` ALONE, and that single entry is now load-bearing evidence rather than a shorthand.
69
+ *
70
+ * It briefly read `[APP_PUBLIC, APP_USER]`. That was a WORKAROUND for an engine defect (#153),
71
+ * not a statement about these rows: the authorizer admitted a public operation only when the
72
+ * caller was ALSO a public execution, so an AUTHENTICATED caller fell past that carve-out to the
73
+ * ordinary role gate — where `APP_PUBLIC` is in nobody's granted set — and was refused. Measured
74
+ * 2026-08-31 on the workbench's module projection (a resource retired the same day): with no
75
+ * credential the LIST answered 200 with the eight modules; with a valid administrator bearer it
76
+ * answered 403 `authorizations_access_denied`. The workbench's own sidebar could not read the
77
+ * rows it exists to show, because its operator had logged in.
78
+ *
79
+ * `requiredRolesDeclarePublicAccess` in `@wildo-ai/saas-models` is now the one named source for
80
+ * what `APP_PUBLIC` means, and every role gate in the engine consults it — so the sentinel
81
+ * genuinely admits every caller and the second role has nothing left to add. Declaring it anyway
82
+ * would say these rows need an authenticated reader, which is the opposite of what they are.
83
+ *
84
+ * Keeping the bare declaration is deliberate: it is the surface the fix is exercised on. If the
85
+ * defect ever returns, the workbench sidebar goes blank for a logged-in operator — a loud,
86
+ * immediate signal — instead of being silently masked by a role that was only ever there to hide
87
+ * it.
88
+ */
38
89
  roles: [CORE_APP_ROLES.APP_PUBLIC],
39
90
  riskLevel: ResourceOperationRiskLevel.LOW,
40
91
  };
41
92
  }
42
93
  /**
43
- * The application's shared modules and the resources each owns.
94
+ * The LIST variant — the read-only variant plus the page ceiling.
44
95
  *
45
- * Rows are DERIVED per read from the companion's `resource-specifications` behavior; nothing is
46
- * stored. `rowsPath` names where that behavior keeps them, because behaviors answer their own
47
- * natural shape rather than a uniform envelope.
96
+ * Separate from {@link createReadOnlyApiVariant} because `maxPaginatedResultPerPageLimit` is only
97
+ * read on a LIST-like operation (`isListLike` in the resource factory); putting it on READ too would
98
+ * declare a field that operation does not have, which reads as though it did something.
48
99
  */
49
- export const workbenchApplicationModules_ResourceConfiguration_InitializationFactory = (resourcesRelationships) => createResourceConfiguration_Initialization({
50
- mainSchema: Workbench_ApplicationModule_Schema,
51
- resourceIdentifier: WorkbenchShared_ResourceType.APPLICATION_MODULES,
52
- resourceFieldIdentifier: WorkbenchShared_ResourceFieldIdentifier[WorkbenchShared_ResourceType.APPLICATION_MODULES],
53
- resourceRelationships: resourcesRelationships,
54
- isSystemResource: false,
55
- persistenceAdapter: PersistenceAdapter.INTROSPECTION,
56
- introspectionBinding: {
57
- behavior: 'resource-specifications',
58
- rowsPath: 'moduleOwnership',
59
- /*
60
- * A single-field key, and it is NOT redundant.
61
- *
62
- * The behavior's rows carry `moduleId`, but every lookup addresses a row by the resource's
63
- * `resourceFieldIdentifier` — `workbenchApplicationModuleId` — so a row that never carries
64
- * that field can be listed and never found. Measured: `GET …/workbench-application-modules`
65
- * answered 200 with 8 rows while `…/engine` answered 404, because the filter looked for a
66
- * field the row did not have.
67
- *
68
- * Composing from `moduleId` copies the behavior's own key onto the field the framework
69
- * addresses by, which keeps ONE identity rather than two names for it.
70
- */
71
- identityFields: ['moduleId'],
72
- /*
73
- * A module id and the resource types it registers describe the APPLICATION'S OWN STRUCTURE.
74
- * There is no tenant dimension for a scope predicate to filter on — every caller who may read
75
- * this resource at all sees the same rows, because they are a projection of the application's
76
- * source rather than of anyone's data.
77
- *
78
- * Declared rather than left implicit: an unstated stance is indistinguishable from an
79
- * overlooked one, which is the whole reason the field is required.
80
- */
81
- tenancy: IntrospectionTenancyStance.APPLICATION_STRUCTURE,
82
- },
83
- coreOperations: [CoreResourceOperation.READ, CoreResourceOperation.LIST],
84
- customOperation: WorkbenchApplicationModule_Operations,
85
- operationsConfiguration: {
86
- [CoreResourceOperation.READ]: { variants: [createReadOnlyApiVariant()] },
87
- [CoreResourceOperation.LIST]: { variants: [createReadOnlyApiVariant()] },
88
- },
89
- });
100
+ export function createReadOnlyListApiVariant() {
101
+ return {
102
+ ...createReadOnlyApiVariant(),
103
+ maxPaginatedResultPerPageLimit: WORKBENCH_INTROSPECTION_MAX_ROWS_PER_PAGE,
104
+ };
105
+ }
90
106
  /**
91
107
  * APPLICATION-scoped, anchored by a STANDALONE relationship.
92
108
  *
@@ -104,74 +120,164 @@ export const workbenchApplicationModules_ResourceConfiguration_InitializationFac
104
120
  * credential's constraint. The tenant-scoping trade remains recorded in issue #118.
105
121
  */
106
122
  export const workbenchShared_ResourcesRelationships = [
107
- createResourcesRelationship(CoreResourceType.APPLICATION, WorkbenchShared_ResourceType.APPLICATION_MODULES, ResourceRelationshipCardinality.ONE, ResourceRelationshipCardinality.MANY, {
123
+ /*
124
+ * A STANDALONE anchor for the domain-model resource. Every INTROSPECTION resource needs one:
125
+ * `resourcePrimaryScope` is DERIVED from a relationship flagged `isPrimaryScope`, so a resource
126
+ * without one has no scope at all — and STANDALONE keeps it off the URL, which is what gives these
127
+ * resources a flat route instead of `/applications/:id/...`.
128
+ */
129
+ createResourcesRelationship(CoreResourceType.APPLICATION, WorkbenchShared_ResourceType.DOMAIN_RESOURCES, ResourceRelationshipCardinality.ONE, ResourceRelationshipCardinality.MANY, {
108
130
  nature: RelationshipNature.COMPOSITION,
109
131
  isPrimaryScope: true,
110
132
  accessScopeStrategy: ResourceRelationshipAccessScopeStrategy.STANDALONE,
111
133
  contextPolicy: {},
112
134
  }),
113
- /*
114
- * The same STANDALONE anchor for the relationships resource. Every INTROSPECTION resource needs
115
- * one: `resourcePrimaryScope` is DERIVED from a relationship flagged `isPrimaryScope`, so a
116
- * resource without one has no scope at all — and STANDALONE keeps it off the URL, which is what
117
- * gives both resources a flat route instead of `/applications/:id/...`.
118
- */
119
- createResourcesRelationship(CoreResourceType.APPLICATION, WorkbenchShared_ResourceType.RESOURCE_RELATIONSHIPS, ResourceRelationshipCardinality.ONE, ResourceRelationshipCardinality.MANY, {
135
+ /* And for the specification-artifacts resource. Same anchor, same reason. */
136
+ createResourcesRelationship(CoreResourceType.APPLICATION, WorkbenchShared_ResourceType.SPECIFICATION_ARTIFACTS, ResourceRelationshipCardinality.ONE, ResourceRelationshipCardinality.MANY, {
120
137
  nature: RelationshipNature.COMPOSITION,
121
138
  isPrimaryScope: true,
122
139
  accessScopeStrategy: ResourceRelationshipAccessScopeStrategy.STANDALONE,
123
140
  contextPolicy: {},
124
141
  }),
142
+ /*
143
+ * Every family-aspect resource's relationships — the same STANDALONE anchor per aspect, plus
144
+ * one REFERENCE per declared edge whose `foreignKeyField` is what makes reference navigation
145
+ * resolve. Generated from the descriptor table beside the aspect configs.
146
+ */
147
+ ...buildWorkbenchAspectRelationships(),
148
+ /*
149
+ * The creation plane's four STANDALONE anchors — same form, declared beside its configs. Merged
150
+ * here so the one array every registrar consumes stays the single relationship authority.
151
+ */
152
+ ...workbenchCreationPlane_ResourcesRelationships,
125
153
  ];
126
154
  /**
127
- * Every relationship edge the observed application declares.
155
+ * The observed application's domain model — one row per resource, and the source the workbench's
156
+ * domain-model graph is drawn from.
128
157
  *
129
- * ## Why this resource exists as the SECOND surface
158
+ * ## Why a THIRD introspected resource beside modules and relationships
130
159
  *
131
- * It projects a DIFFERENT behavior (`resources-registry`) from the modules beside it
132
- * (`resource-specifications`), with a different row shape and no natural primary key. That is the
133
- * point: it proves the introspection adapter generalizes across the companion's 23 behaviors rather
134
- * than fitting the one it was built against — and the missing key is what surfaced
135
- * `identityFields` as a real seam instead of a guess.
160
+ * Its two siblings project two DIFFERENT behaviors on two different targets, so a graph assembled
161
+ * from both is assembled from two subprocess runs against a working tree an operator is editing —
162
+ * nodes from one state of the application, edges from the next, with nothing to detect the mismatch.
163
+ * `application-domain-model` answers ownership, edges, field names and specification prose from a
164
+ * SINGLE import, which is the only shape in which the three are internally consistent. Speed is the
165
+ * smaller half of the reason; consistency is the whole of it.
136
166
  *
137
- * Same tenancy stance and same PUBLIC role as its sibling, for the same reason: an edge between two
138
- * resource types is a fact about the application's SOURCE, with no tenant dimension and no subject
139
- * whose data could leak.
167
+ * Same tenancy stance and same PUBLIC role as its siblings, for the same reason: a resource, the
168
+ * module that registers it and the edges it declares are facts about the application's SOURCE, with
169
+ * no tenant dimension and no subject whose data could leak.
140
170
  */
141
- const WorkbenchResourceRelationship_Operations = undefined;
142
- export const workbenchResourceRelationships_ResourceConfiguration_InitializationFactory = (resourcesRelationships) => createResourceConfiguration_Initialization({
143
- mainSchema: Workbench_ResourceRelationship_Schema,
144
- resourceIdentifier: WorkbenchShared_ResourceType.RESOURCE_RELATIONSHIPS,
145
- resourceFieldIdentifier: WorkbenchShared_ResourceFieldIdentifier[WorkbenchShared_ResourceType.RESOURCE_RELATIONSHIPS],
171
+ const WorkbenchDomainResource_Operations = undefined;
172
+ export const workbenchDomainResources_ResourceConfiguration_InitializationFactory = (resourcesRelationships) => createResourceConfiguration_Initialization({
173
+ mainSchema: Workbench_DomainResource_Schema,
174
+ resourceIdentifier: WorkbenchShared_ResourceType.DOMAIN_RESOURCES,
175
+ resourceFieldIdentifier: WorkbenchShared_ResourceFieldIdentifier[WorkbenchShared_ResourceType.DOMAIN_RESOURCES],
146
176
  resourceRelationships: resourcesRelationships,
147
177
  isSystemResource: false,
148
178
  persistenceAdapter: PersistenceAdapter.INTROSPECTION,
149
179
  introspectionBinding: {
150
- behavior: 'resources-registry',
151
- rowsPath: 'resourceRelationships',
180
+ behavior: 'application-domain-model',
181
+ rowsPath: 'domainResources',
152
182
  tenancy: IntrospectionTenancyStance.APPLICATION_STRUCTURE,
153
183
  /*
154
- * The two endpoints plus the joining field.
184
+ * A resource type is unique in a registry by construction — a registry is keyed by it — so one
185
+ * field is the whole key here, and no discriminator can be needed.
155
186
  *
156
- * The pair ALONE is unique across all 16 edges in the dogfood application — measured, not
157
- * assumed — and is still not safe: the framework permits several edges between the same two
158
- * resources (a junction carrying two roles, a self-relationship). `foreignKeyField` is what
159
- * distinguishes those, so it earns its place in the key rather than padding it.
187
+ * Composed onto `workbenchDomainResourceId` rather than addressing the row by `resourceType`
188
+ * directly: `resourceFieldIdentifier` values are published into the GLOBAL field-identifier map
189
+ * that resolves a foreign-key field NAME to the resource it points at, and `resourceType` is a
190
+ * name any application could collide with on a map whose whole job is that names are
191
+ * unambiguous.
192
+ */
193
+ identityFields: ['resourceType'],
194
+ },
195
+ coreOperations: [CoreResourceOperation.READ, CoreResourceOperation.LIST],
196
+ customOperation: WorkbenchDomainResource_Operations,
197
+ operationsConfiguration: {
198
+ [CoreResourceOperation.READ]: { variants: [createReadOnlyApiVariant()] },
199
+ [CoreResourceOperation.LIST]: { variants: [createReadOnlyListApiVariant()] },
200
+ },
201
+ });
202
+ /**
203
+ * Every application-specification family the journey produces, projected for a human reviewer.
204
+ *
205
+ * ## What this is for
206
+ *
207
+ * Its three siblings project the application's STRUCTURE. This one projects the PRODUCT and its
208
+ * obligations — vision, market, requirements, roadmap, brand, business model, the assurance
209
+ * programme and the governed-document families — which until now were reachable only by opening a
210
+ * TypeScript file in an editor (issue #162).
211
+ *
212
+ * ## Why the same behavior does not serve it
213
+ *
214
+ * `application-domain-model` answers questions about the resource registry. A family's content is
215
+ * not in the registry at all: it is on the specifications package's companion exports, validated by
216
+ * the `_core` validator the semantic authority names for it. Different target, different question,
217
+ * different behavior — the split the two original workbench resources already demonstrate.
218
+ *
219
+ * ## Same posture as its siblings, for the same reason
220
+ *
221
+ * `APPLICATION_STRUCTURE` tenancy and `APP_PUBLIC`: a vision brief, a roadmap and a compliance
222
+ * programme are facts about what the application's own source SAYS about itself. There is no tenant
223
+ * dimension and no subject whose data could leak, and the companion's loopback bind is what keeps
224
+ * the whole workbench local.
225
+ *
226
+ * READ and LIST only, and structurally so — `PersistenceAdapter.INTROSPECTION` refuses every write
227
+ * verb, rows are derived per read and nothing is stored. Acceptance of a specification is a human
228
+ * git commit (`.claude/rules/one-source-of-truth.md` rule 2), so there is deliberately no place here
229
+ * for an acceptance state to accumulate, not even by accident.
230
+ */
231
+ const WorkbenchSpecificationArtifact_Operations = undefined;
232
+ export const workbenchSpecificationArtifacts_ResourceConfiguration_InitializationFactory = (resourcesRelationships) => createResourceConfiguration_Initialization({
233
+ mainSchema: Workbench_SpecificationArtifact_Schema,
234
+ resourceIdentifier: WorkbenchShared_ResourceType.SPECIFICATION_ARTIFACTS,
235
+ resourceFieldIdentifier: WorkbenchShared_ResourceFieldIdentifier[WorkbenchShared_ResourceType.SPECIFICATION_ARTIFACTS],
236
+ resourceRelationships: resourcesRelationships,
237
+ isSystemResource: false,
238
+ persistenceAdapter: PersistenceAdapter.INTROSPECTION,
239
+ introspectionBinding: {
240
+ behavior: 'specification-artifacts',
241
+ rowsPath: 'artifacts',
242
+ tenancy: IntrospectionTenancyStance.APPLICATION_STRUCTURE,
243
+ /*
244
+ * NO `identityFields`, and the omission is the point.
160
245
  *
161
- * If these three ever collide the source REFUSES the whole derivation and names both rows,
162
- * because the alternative is a row that LIST shows and READ can never reach.
246
+ * Every row already carries `family`, and `family` IS this resource's
247
+ * `resourceFieldIdentifier` — so there is nothing to compose. This is the `moduleId` case, not
248
+ * the relationships one: declaring an identity here would stamp a composed key over a real
249
+ * field, replacing the source's own id with a copy of itself.
163
250
  */
164
- identityFields: ['resourceType1', 'resourceType2', 'foreignKeyField'],
165
251
  },
166
252
  coreOperations: [CoreResourceOperation.READ, CoreResourceOperation.LIST],
167
- customOperation: WorkbenchResourceRelationship_Operations,
253
+ customOperation: WorkbenchSpecificationArtifact_Operations,
168
254
  operationsConfiguration: {
169
255
  [CoreResourceOperation.READ]: { variants: [createReadOnlyApiVariant()] },
170
- [CoreResourceOperation.LIST]: { variants: [createReadOnlyApiVariant()] },
256
+ [CoreResourceOperation.LIST]: {
257
+ variants: [{
258
+ ...createReadOnlyListApiVariant(),
259
+ /*
260
+ * The two review axes a reader slices fifteen rows by. Declaring them is an OBLIGATION,
261
+ * not a hint: the introspection source honors exactly the scalar-equality/IN filters that
262
+ * reach it and REFUSES shapes it cannot apply, because a declared filter that is silently
263
+ * dropped returns the unfiltered set wearing a filtered label — the `--state ready` defect
264
+ * class. The frontend's concern quick-filter tabs require `concern` to be here, or the
265
+ * tab strip silently never renders.
266
+ */
267
+ filterFields: {
268
+ concern: z.enum(WorkbenchSpecificationArtifactConcern),
269
+ state: z.enum(WorkbenchSpecificationArtifactState),
270
+ },
271
+ }],
272
+ },
171
273
  },
172
274
  });
173
275
  export const workbenchShared_ResourcesConfigurationsFactoryMap = {
174
- [WorkbenchShared_ResourceType.APPLICATION_MODULES]: workbenchApplicationModules_ResourceConfiguration_InitializationFactory,
175
- [WorkbenchShared_ResourceType.RESOURCE_RELATIONSHIPS]: workbenchResourceRelationships_ResourceConfiguration_InitializationFactory,
276
+ [WorkbenchShared_ResourceType.DOMAIN_RESOURCES]: workbenchDomainResources_ResourceConfiguration_InitializationFactory,
277
+ [WorkbenchShared_ResourceType.SPECIFICATION_ARTIFACTS]: workbenchSpecificationArtifacts_ResourceConfiguration_InitializationFactory,
278
+ // The 40 family-aspect resources — generated from the descriptor table, one mold for all.
279
+ ...workbenchAspectResources_ConfigurationsFactoryMap,
280
+ // The creation plane — companion-RUNTIME projections (authenticated, APPLICATION_RUNTIME tenancy).
281
+ ...workbenchCreationPlane_ResourcesConfigurationsFactoryMap,
176
282
  };
177
283
  //# sourceMappingURL=workbench-resources.custom.shared.resources-config.schemas.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"workbench-resources.custom.shared.resources-config.schemas.js","sourceRoot":"","sources":["../../../../src/workbench/workbench-resources.custom.shared.resources-config.schemas.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n CORE_APP_ROLES,\n CoreResourceOperation,\n CoreResourceType,\n IntrospectionTenancyStance,\n PersistenceAdapter,\n ResourceOperationRiskLevel,\n ResourceOperationVariantType,\n ResourceRelationship,\n ResourceRelationshipAccessScopeStrategy,\n ResourceRelationshipCardinality,\n RelationshipNature,\n createResourceConfiguration_Initialization,\n createResourcesRelationship,\n} from '@wildo-ai/saas-models';\n\nimport {\n WorkbenchShared_ResourceFieldIdentifier,\n WorkbenchShared_ResourceType,\n} from './workbench-resources-types.custom.shared.schemas';\nimport {\n Workbench_ApplicationModule_Schema,\n Workbench_ResourceRelationship_Schema,\n} from './workbench-resources.custom.shared.schemas';\n\nconst WorkbenchApplicationModule_Operations = undefined;\n\ntype WorkbenchApplicationModule_CoreOperations =\n | CoreResourceOperation.READ\n | CoreResourceOperation.LIST;\n\n/**\n * Read-only over HTTP, and PUBLIC.\n *\n * `APP_PUBLIC` means what its enum comment says — accessible to everyone, no credential — and as of\n * 2026-08-30 the runtime implements it: the authentication chain now admits an operation declaring\n * this role instead of demanding a credential it never required. Before that it was declared,\n * published as public by the OpenAPI generator, and refused at runtime.\n *\n * ## Why these rows are genuinely public\n *\n * They are a projection of the application's OWN SOURCE — module ids and the resource types each\n * registers — carrying no tenant dimension, which is exactly what the binding's\n * `APPLICATION_STRUCTURE` tenancy stance declares. There is no subject whose data could leak,\n * because no subject's data is here.\n *\n * This is the same category as a public price list, a frontend application configuration or an i18n\n * bundle: things every caller may read, which until now could only be served by escaping the\n * resource system into a manual controller.\n *\n * ## What still confines them\n *\n * The companion asserts a loopback bind at startup and fails closed otherwise, so this surface is\n * reachable only from the developer's own machine. `APP_PUBLIC` is the right declaration on its\n * merits; the loopback bind is what makes the whole workbench local, and both remain true\n * independently.\n *\n * If an introspected resource is ever added that projects something OTHER than the application's own\n * structure, this role is wrong for it — the stance and the role must move together.\n */\nfunction createReadOnlyApiVariant() {\n return {\n variantType: ResourceOperationVariantType.API_CALL as const,\n isDefault: true as const,\n roles: [CORE_APP_ROLES.APP_PUBLIC],\n riskLevel: ResourceOperationRiskLevel.LOW as const,\n };\n}\n\n/**\n * The application's shared modules and the resources each owns.\n *\n * Rows are DERIVED per read from the companion's `resource-specifications` behavior; nothing is\n * stored. `rowsPath` names where that behavior keeps them, because behaviors answer their own\n * natural shape rather than a uniform envelope.\n */\nexport const workbenchApplicationModules_ResourceConfiguration_InitializationFactory =\n (resourcesRelationships: ResourceRelationship[]) => createResourceConfiguration_Initialization<\n typeof WorkbenchApplicationModule_Operations,\n WorkbenchApplicationModule_CoreOperations,\n typeof Workbench_ApplicationModule_Schema\n >({\n mainSchema: Workbench_ApplicationModule_Schema,\n resourceIdentifier: WorkbenchShared_ResourceType.APPLICATION_MODULES,\n resourceFieldIdentifier:\n WorkbenchShared_ResourceFieldIdentifier[WorkbenchShared_ResourceType.APPLICATION_MODULES],\n resourceRelationships: resourcesRelationships,\n isSystemResource: false,\n persistenceAdapter: PersistenceAdapter.INTROSPECTION,\n introspectionBinding: {\n behavior: 'resource-specifications',\n rowsPath: 'moduleOwnership',\n /*\n * A single-field key, and it is NOT redundant.\n *\n * The behavior's rows carry `moduleId`, but every lookup addresses a row by the resource's\n * `resourceFieldIdentifier` — `workbenchApplicationModuleId` — so a row that never carries\n * that field can be listed and never found. Measured: `GET …/workbench-application-modules`\n * answered 200 with 8 rows while `…/engine` answered 404, because the filter looked for a\n * field the row did not have.\n *\n * Composing from `moduleId` copies the behavior's own key onto the field the framework\n * addresses by, which keeps ONE identity rather than two names for it.\n */\n identityFields: ['moduleId'],\n /*\n * A module id and the resource types it registers describe the APPLICATION'S OWN STRUCTURE.\n * There is no tenant dimension for a scope predicate to filter on — every caller who may read\n * this resource at all sees the same rows, because they are a projection of the application's\n * source rather than of anyone's data.\n *\n * Declared rather than left implicit: an unstated stance is indistinguishable from an\n * overlooked one, which is the whole reason the field is required.\n */\n tenancy: IntrospectionTenancyStance.APPLICATION_STRUCTURE,\n },\n coreOperations: [CoreResourceOperation.READ, CoreResourceOperation.LIST],\n customOperation: WorkbenchApplicationModule_Operations,\n operationsConfiguration: {\n [CoreResourceOperation.READ]: { variants: [createReadOnlyApiVariant()] },\n [CoreResourceOperation.LIST]: { variants: [createReadOnlyApiVariant()] },\n },\n });\n\n/**\n * APPLICATION-scoped, anchored by a STANDALONE relationship.\n *\n * The form the resource factory's own migration note prescribes: STANDALONE \"keeps the rel inert for\n * URL routing while declaring the structural ownership anchor\", so the resource is reachable at a\n * flat path rather than nested under `/applications/:id/`.\n *\n * APPLICATION rather than ORGANIZATIONS on purpose: these rows describe the application's own\n * structure, so the application IS their scope. There is no tenant dimension to anchor on — the\n * binding's `APPLICATION_STRUCTURE` stance above says the same thing from the data's side.\n *\n * This originally read as a consequence of API-key scoping, because the workbench browser was\n * expected to authenticate with an application-scope key. `APP_PUBLIC` removed that requirement\n * entirely (see the role note above), so the scope now rests on its own merits rather than on a\n * credential's constraint. The tenant-scoping trade remains recorded in issue #118.\n */\nexport const workbenchShared_ResourcesRelationships: ResourceRelationship[] = [\n createResourcesRelationship(\n CoreResourceType.APPLICATION,\n WorkbenchShared_ResourceType.APPLICATION_MODULES,\n ResourceRelationshipCardinality.ONE,\n ResourceRelationshipCardinality.MANY,\n {\n nature: RelationshipNature.COMPOSITION,\n isPrimaryScope: true,\n accessScopeStrategy: ResourceRelationshipAccessScopeStrategy.STANDALONE,\n contextPolicy: {},\n },\n ),\n /*\n * The same STANDALONE anchor for the relationships resource. Every INTROSPECTION resource needs\n * one: `resourcePrimaryScope` is DERIVED from a relationship flagged `isPrimaryScope`, so a\n * resource without one has no scope at all — and STANDALONE keeps it off the URL, which is what\n * gives both resources a flat route instead of `/applications/:id/...`.\n */\n createResourcesRelationship(\n CoreResourceType.APPLICATION,\n WorkbenchShared_ResourceType.RESOURCE_RELATIONSHIPS,\n ResourceRelationshipCardinality.ONE,\n ResourceRelationshipCardinality.MANY,\n {\n nature: RelationshipNature.COMPOSITION,\n isPrimaryScope: true,\n accessScopeStrategy: ResourceRelationshipAccessScopeStrategy.STANDALONE,\n contextPolicy: {},\n },\n ),\n];\n\n/**\n * Every relationship edge the observed application declares.\n *\n * ## Why this resource exists as the SECOND surface\n *\n * It projects a DIFFERENT behavior (`resources-registry`) from the modules beside it\n * (`resource-specifications`), with a different row shape and no natural primary key. That is the\n * point: it proves the introspection adapter generalizes across the companion's 23 behaviors rather\n * than fitting the one it was built against — and the missing key is what surfaced\n * `identityFields` as a real seam instead of a guess.\n *\n * Same tenancy stance and same PUBLIC role as its sibling, for the same reason: an edge between two\n * resource types is a fact about the application's SOURCE, with no tenant dimension and no subject\n * whose data could leak.\n */\nconst WorkbenchResourceRelationship_Operations = undefined;\n\ntype WorkbenchResourceRelationship_CoreOperations =\n | CoreResourceOperation.READ\n | CoreResourceOperation.LIST;\n\nexport const workbenchResourceRelationships_ResourceConfiguration_InitializationFactory =\n (resourcesRelationships: ResourceRelationship[]) => createResourceConfiguration_Initialization<\n typeof WorkbenchResourceRelationship_Operations,\n WorkbenchResourceRelationship_CoreOperations,\n typeof Workbench_ResourceRelationship_Schema\n >({\n mainSchema: Workbench_ResourceRelationship_Schema,\n resourceIdentifier: WorkbenchShared_ResourceType.RESOURCE_RELATIONSHIPS,\n resourceFieldIdentifier:\n WorkbenchShared_ResourceFieldIdentifier[WorkbenchShared_ResourceType.RESOURCE_RELATIONSHIPS],\n resourceRelationships: resourcesRelationships,\n isSystemResource: false,\n persistenceAdapter: PersistenceAdapter.INTROSPECTION,\n introspectionBinding: {\n behavior: 'resources-registry',\n rowsPath: 'resourceRelationships',\n tenancy: IntrospectionTenancyStance.APPLICATION_STRUCTURE,\n /*\n * The two endpoints plus the joining field.\n *\n * The pair ALONE is unique across all 16 edges in the dogfood application — measured, not\n * assumed — and is still not safe: the framework permits several edges between the same two\n * resources (a junction carrying two roles, a self-relationship). `foreignKeyField` is what\n * distinguishes those, so it earns its place in the key rather than padding it.\n *\n * If these three ever collide the source REFUSES the whole derivation and names both rows,\n * because the alternative is a row that LIST shows and READ can never reach.\n */\n identityFields: ['resourceType1', 'resourceType2', 'foreignKeyField'],\n },\n coreOperations: [CoreResourceOperation.READ, CoreResourceOperation.LIST],\n customOperation: WorkbenchResourceRelationship_Operations,\n operationsConfiguration: {\n [CoreResourceOperation.READ]: { variants: [createReadOnlyApiVariant()] },\n [CoreResourceOperation.LIST]: { variants: [createReadOnlyApiVariant()] },\n },\n });\n\nexport const workbenchShared_ResourcesConfigurationsFactoryMap = {\n [WorkbenchShared_ResourceType.APPLICATION_MODULES]:\n workbenchApplicationModules_ResourceConfiguration_InitializationFactory,\n [WorkbenchShared_ResourceType.RESOURCE_RELATIONSHIPS]:\n workbenchResourceRelationships_ResourceConfiguration_InitializationFactory,\n};\n"]}
1
+ {"version":3,"file":"workbench-resources.custom.shared.resources-config.schemas.js","sourceRoot":"","sources":["../../../../src/workbench/workbench-resources.custom.shared.resources-config.schemas.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n CORE_APP_ROLES,\n CoreResourceOperation,\n CoreResourceType,\n IntrospectionTenancyStance,\n PersistenceAdapter,\n ResourceOperationRiskLevel,\n ResourceOperationVariantType,\n ResourceRelationship,\n ResourceRelationshipAccessScopeStrategy,\n ResourceRelationshipCardinality,\n RelationshipNature,\n createResourceConfiguration_Initialization,\n createResourcesRelationship,\n} from '@wildo-ai/saas-models';\n\nimport { z } from 'zod';\n\nimport {\n WorkbenchShared_ResourceFieldIdentifier,\n WorkbenchShared_ResourceType,\n} from './workbench-resources-types.custom.shared.schemas';\nimport {\n buildWorkbenchAspectRelationships,\n workbenchAspectResources_ConfigurationsFactoryMap,\n} from './workbench-aspect-resources.custom.shared.schemas';\nimport {\n Workbench_DomainResource_Schema,\n Workbench_SpecificationArtifact_Schema,\n WorkbenchSpecificationArtifactConcern,\n WorkbenchSpecificationArtifactState,\n} from './workbench-resources.custom.shared.schemas';\nimport {\n workbenchCreationPlane_ResourcesConfigurationsFactoryMap,\n workbenchCreationPlane_ResourcesRelationships,\n} from './workbench-creation-plane.custom.shared.resources-config.schemas';\n\n/**\n * The page ceiling every workbench LIST declares — and it is a correctness fix, not a tuning knob.\n *\n * `IntrospectionResourceRepository.list()` answers `pagination.limit = rows.length` over the WHOLE\n * derived set, deliberately: a behavior computes its entire answer in one subprocess run, there is no\n * cursor to seek, and reporting a total the caller cannot page past would be a lie. The response DTO\n * then bounds that `limit` by the operation's `maxPaginatedResultPerPageLimit`, whose default is\n * **100**. So any introspected resource whose derivation exceeds 100 rows fails with `too_big` at\n * `pagination.limit` — the request having been served correctly right up to the last hop.\n *\n * Measured 2026-08-31, and the measurement is why this ceiling is a CORRECTNESS FIX rather than\n * headroom. The domain model projects every resource the observed application RUNS, which is its own\n * plus the engine's whole merged vocabulary: `saas-models`' core map alone carries **82** resources\n * and **105** relationships before `saas-backend-lib`'s map or the application's own eight are\n * counted. So the domain-model LIST is already comfortably past the default 100 on the smallest\n * application there is, and would have failed with `too_big` at `pagination.limit` — the request\n * having been served correctly right up to the last hop, and the bound named in none of these\n * declarations. The number also scales with the OBSERVED application, which is the one input this\n * repository does not control.\n *\n * Declared on the shared variant so every workbench resource is bounded by the same number, rather\n * than one being raised and its siblings left waiting to hit the same wall.\n *\n * Above the ceiling the request FAILS rather than truncating, which is the correct direction: a\n * silently short domain model is indistinguishable from a complete one once it is drawn.\n */\nexport const WORKBENCH_INTROSPECTION_MAX_ROWS_PER_PAGE = 1000;\n\n/**\n * Read-only over HTTP, and PUBLIC.\n *\n * `APP_PUBLIC` means what its enum comment says — accessible to everyone, no credential — and as of\n * 2026-08-30 the runtime implements it: the authentication chain now admits an operation declaring\n * this role instead of demanding a credential it never required. Before that it was declared,\n * published as public by the OpenAPI generator, and refused at runtime.\n *\n * ## Why these rows are genuinely public\n *\n * They are a projection of the application's OWN SOURCE — module ids and the resource types each\n * registers — carrying no tenant dimension, which is exactly what the binding's\n * `APPLICATION_STRUCTURE` tenancy stance declares. There is no subject whose data could leak,\n * because no subject's data is here.\n *\n * This is the same category as a public price list, a frontend application configuration or an i18n\n * bundle: things every caller may read, which until now could only be served by escaping the\n * resource system into a manual controller.\n *\n * ## What still confines them\n *\n * The companion asserts a loopback bind at startup and fails closed otherwise, so this surface is\n * reachable only from the developer's own machine. `APP_PUBLIC` is the right declaration on its\n * merits; the loopback bind is what makes the whole workbench local, and both remain true\n * independently.\n *\n * If an introspected resource is ever added that projects something OTHER than the application's own\n * structure, this role is wrong for it — the stance and the role must move together.\n */\nexport function createReadOnlyApiVariant() {\n return {\n variantType: ResourceOperationVariantType.API_CALL as const,\n isDefault: true as const,\n /*\n * `APP_PUBLIC` ALONE, and that single entry is now load-bearing evidence rather than a shorthand.\n *\n * It briefly read `[APP_PUBLIC, APP_USER]`. That was a WORKAROUND for an engine defect (#153),\n * not a statement about these rows: the authorizer admitted a public operation only when the\n * caller was ALSO a public execution, so an AUTHENTICATED caller fell past that carve-out to the\n * ordinary role gate — where `APP_PUBLIC` is in nobody's granted set — and was refused. Measured\n * 2026-08-31 on the workbench's module projection (a resource retired the same day): with no\n * credential the LIST answered 200 with the eight modules; with a valid administrator bearer it\n * answered 403 `authorizations_access_denied`. The workbench's own sidebar could not read the\n * rows it exists to show, because its operator had logged in.\n *\n * `requiredRolesDeclarePublicAccess` in `@wildo-ai/saas-models` is now the one named source for\n * what `APP_PUBLIC` means, and every role gate in the engine consults it — so the sentinel\n * genuinely admits every caller and the second role has nothing left to add. Declaring it anyway\n * would say these rows need an authenticated reader, which is the opposite of what they are.\n *\n * Keeping the bare declaration is deliberate: it is the surface the fix is exercised on. If the\n * defect ever returns, the workbench sidebar goes blank for a logged-in operator — a loud,\n * immediate signal — instead of being silently masked by a role that was only ever there to hide\n * it.\n */\n roles: [CORE_APP_ROLES.APP_PUBLIC],\n riskLevel: ResourceOperationRiskLevel.LOW as const,\n };\n}\n\n/**\n * The LIST variant — the read-only variant plus the page ceiling.\n *\n * Separate from {@link createReadOnlyApiVariant} because `maxPaginatedResultPerPageLimit` is only\n * read on a LIST-like operation (`isListLike` in the resource factory); putting it on READ too would\n * declare a field that operation does not have, which reads as though it did something.\n */\nexport function createReadOnlyListApiVariant() {\n return {\n ...createReadOnlyApiVariant(),\n maxPaginatedResultPerPageLimit: WORKBENCH_INTROSPECTION_MAX_ROWS_PER_PAGE,\n };\n}\n\n\n/**\n * APPLICATION-scoped, anchored by a STANDALONE relationship.\n *\n * The form the resource factory's own migration note prescribes: STANDALONE \"keeps the rel inert for\n * URL routing while declaring the structural ownership anchor\", so the resource is reachable at a\n * flat path rather than nested under `/applications/:id/`.\n *\n * APPLICATION rather than ORGANIZATIONS on purpose: these rows describe the application's own\n * structure, so the application IS their scope. There is no tenant dimension to anchor on — the\n * binding's `APPLICATION_STRUCTURE` stance above says the same thing from the data's side.\n *\n * This originally read as a consequence of API-key scoping, because the workbench browser was\n * expected to authenticate with an application-scope key. `APP_PUBLIC` removed that requirement\n * entirely (see the role note above), so the scope now rests on its own merits rather than on a\n * credential's constraint. The tenant-scoping trade remains recorded in issue #118.\n */\nexport const workbenchShared_ResourcesRelationships: ResourceRelationship[] = [\n /*\n * A STANDALONE anchor for the domain-model resource. Every INTROSPECTION resource needs one:\n * `resourcePrimaryScope` is DERIVED from a relationship flagged `isPrimaryScope`, so a resource\n * without one has no scope at all — and STANDALONE keeps it off the URL, which is what gives these\n * resources a flat route instead of `/applications/:id/...`.\n */\n createResourcesRelationship(\n CoreResourceType.APPLICATION,\n WorkbenchShared_ResourceType.DOMAIN_RESOURCES,\n ResourceRelationshipCardinality.ONE,\n ResourceRelationshipCardinality.MANY,\n {\n nature: RelationshipNature.COMPOSITION,\n isPrimaryScope: true,\n accessScopeStrategy: ResourceRelationshipAccessScopeStrategy.STANDALONE,\n contextPolicy: {},\n },\n ),\n /* And for the specification-artifacts resource. Same anchor, same reason. */\n createResourcesRelationship(\n CoreResourceType.APPLICATION,\n WorkbenchShared_ResourceType.SPECIFICATION_ARTIFACTS,\n ResourceRelationshipCardinality.ONE,\n ResourceRelationshipCardinality.MANY,\n {\n nature: RelationshipNature.COMPOSITION,\n isPrimaryScope: true,\n accessScopeStrategy: ResourceRelationshipAccessScopeStrategy.STANDALONE,\n contextPolicy: {},\n },\n ),\n /*\n * Every family-aspect resource's relationships — the same STANDALONE anchor per aspect, plus\n * one REFERENCE per declared edge whose `foreignKeyField` is what makes reference navigation\n * resolve. Generated from the descriptor table beside the aspect configs.\n */\n ...buildWorkbenchAspectRelationships(),\n /*\n * The creation plane's four STANDALONE anchors — same form, declared beside its configs. Merged\n * here so the one array every registrar consumes stays the single relationship authority.\n */\n ...workbenchCreationPlane_ResourcesRelationships,\n];\n\n\n/**\n * The observed application's domain model — one row per resource, and the source the workbench's\n * domain-model graph is drawn from.\n *\n * ## Why a THIRD introspected resource beside modules and relationships\n *\n * Its two siblings project two DIFFERENT behaviors on two different targets, so a graph assembled\n * from both is assembled from two subprocess runs against a working tree an operator is editing —\n * nodes from one state of the application, edges from the next, with nothing to detect the mismatch.\n * `application-domain-model` answers ownership, edges, field names and specification prose from a\n * SINGLE import, which is the only shape in which the three are internally consistent. Speed is the\n * smaller half of the reason; consistency is the whole of it.\n *\n * Same tenancy stance and same PUBLIC role as its siblings, for the same reason: a resource, the\n * module that registers it and the edges it declares are facts about the application's SOURCE, with\n * no tenant dimension and no subject whose data could leak.\n */\nconst WorkbenchDomainResource_Operations = undefined;\n\ntype WorkbenchDomainResource_CoreOperations =\n | CoreResourceOperation.READ\n | CoreResourceOperation.LIST;\n\nexport const workbenchDomainResources_ResourceConfiguration_InitializationFactory =\n (resourcesRelationships: ResourceRelationship[]) => createResourceConfiguration_Initialization<\n typeof WorkbenchDomainResource_Operations,\n WorkbenchDomainResource_CoreOperations,\n typeof Workbench_DomainResource_Schema\n >({\n mainSchema: Workbench_DomainResource_Schema,\n resourceIdentifier: WorkbenchShared_ResourceType.DOMAIN_RESOURCES,\n resourceFieldIdentifier:\n WorkbenchShared_ResourceFieldIdentifier[WorkbenchShared_ResourceType.DOMAIN_RESOURCES],\n resourceRelationships: resourcesRelationships,\n isSystemResource: false,\n persistenceAdapter: PersistenceAdapter.INTROSPECTION,\n introspectionBinding: {\n behavior: 'application-domain-model',\n rowsPath: 'domainResources',\n tenancy: IntrospectionTenancyStance.APPLICATION_STRUCTURE,\n /*\n * A resource type is unique in a registry by construction — a registry is keyed by it — so one\n * field is the whole key here, and no discriminator can be needed.\n *\n * Composed onto `workbenchDomainResourceId` rather than addressing the row by `resourceType`\n * directly: `resourceFieldIdentifier` values are published into the GLOBAL field-identifier map\n * that resolves a foreign-key field NAME to the resource it points at, and `resourceType` is a\n * name any application could collide with on a map whose whole job is that names are\n * unambiguous.\n */\n identityFields: ['resourceType'],\n },\n coreOperations: [CoreResourceOperation.READ, CoreResourceOperation.LIST],\n customOperation: WorkbenchDomainResource_Operations,\n operationsConfiguration: {\n [CoreResourceOperation.READ]: { variants: [createReadOnlyApiVariant()] },\n [CoreResourceOperation.LIST]: { variants: [createReadOnlyListApiVariant()] },\n },\n });\n\n/**\n * Every application-specification family the journey produces, projected for a human reviewer.\n *\n * ## What this is for\n *\n * Its three siblings project the application's STRUCTURE. This one projects the PRODUCT and its\n * obligations — vision, market, requirements, roadmap, brand, business model, the assurance\n * programme and the governed-document families — which until now were reachable only by opening a\n * TypeScript file in an editor (issue #162).\n *\n * ## Why the same behavior does not serve it\n *\n * `application-domain-model` answers questions about the resource registry. A family's content is\n * not in the registry at all: it is on the specifications package's companion exports, validated by\n * the `_core` validator the semantic authority names for it. Different target, different question,\n * different behavior — the split the two original workbench resources already demonstrate.\n *\n * ## Same posture as its siblings, for the same reason\n *\n * `APPLICATION_STRUCTURE` tenancy and `APP_PUBLIC`: a vision brief, a roadmap and a compliance\n * programme are facts about what the application's own source SAYS about itself. There is no tenant\n * dimension and no subject whose data could leak, and the companion's loopback bind is what keeps\n * the whole workbench local.\n *\n * READ and LIST only, and structurally so — `PersistenceAdapter.INTROSPECTION` refuses every write\n * verb, rows are derived per read and nothing is stored. Acceptance of a specification is a human\n * git commit (`.claude/rules/one-source-of-truth.md` rule 2), so there is deliberately no place here\n * for an acceptance state to accumulate, not even by accident.\n */\nconst WorkbenchSpecificationArtifact_Operations = undefined;\n\ntype WorkbenchSpecificationArtifact_CoreOperations =\n | CoreResourceOperation.READ\n | CoreResourceOperation.LIST;\n\nexport const workbenchSpecificationArtifacts_ResourceConfiguration_InitializationFactory =\n (resourcesRelationships: ResourceRelationship[]) => createResourceConfiguration_Initialization<\n typeof WorkbenchSpecificationArtifact_Operations,\n WorkbenchSpecificationArtifact_CoreOperations,\n typeof Workbench_SpecificationArtifact_Schema\n >({\n mainSchema: Workbench_SpecificationArtifact_Schema,\n resourceIdentifier: WorkbenchShared_ResourceType.SPECIFICATION_ARTIFACTS,\n resourceFieldIdentifier:\n WorkbenchShared_ResourceFieldIdentifier[WorkbenchShared_ResourceType.SPECIFICATION_ARTIFACTS],\n resourceRelationships: resourcesRelationships,\n isSystemResource: false,\n persistenceAdapter: PersistenceAdapter.INTROSPECTION,\n introspectionBinding: {\n behavior: 'specification-artifacts',\n rowsPath: 'artifacts',\n tenancy: IntrospectionTenancyStance.APPLICATION_STRUCTURE,\n /*\n * NO `identityFields`, and the omission is the point.\n *\n * Every row already carries `family`, and `family` IS this resource's\n * `resourceFieldIdentifier` — so there is nothing to compose. This is the `moduleId` case, not\n * the relationships one: declaring an identity here would stamp a composed key over a real\n * field, replacing the source's own id with a copy of itself.\n */\n },\n coreOperations: [CoreResourceOperation.READ, CoreResourceOperation.LIST],\n customOperation: WorkbenchSpecificationArtifact_Operations,\n operationsConfiguration: {\n [CoreResourceOperation.READ]: { variants: [createReadOnlyApiVariant()] },\n [CoreResourceOperation.LIST]: {\n variants: [{\n ...createReadOnlyListApiVariant(),\n /*\n * The two review axes a reader slices fifteen rows by. Declaring them is an OBLIGATION,\n * not a hint: the introspection source honors exactly the scalar-equality/IN filters that\n * reach it and REFUSES shapes it cannot apply, because a declared filter that is silently\n * dropped returns the unfiltered set wearing a filtered label — the `--state ready` defect\n * class. The frontend's concern quick-filter tabs require `concern` to be here, or the\n * tab strip silently never renders.\n */\n filterFields: {\n concern: z.enum(WorkbenchSpecificationArtifactConcern),\n state: z.enum(WorkbenchSpecificationArtifactState),\n },\n }],\n },\n },\n });\n\nexport const workbenchShared_ResourcesConfigurationsFactoryMap = {\n [WorkbenchShared_ResourceType.DOMAIN_RESOURCES]:\n workbenchDomainResources_ResourceConfiguration_InitializationFactory,\n [WorkbenchShared_ResourceType.SPECIFICATION_ARTIFACTS]:\n workbenchSpecificationArtifacts_ResourceConfiguration_InitializationFactory,\n // The 40 family-aspect resources — generated from the descriptor table, one mold for all.\n ...workbenchAspectResources_ConfigurationsFactoryMap,\n // The creation plane — companion-RUNTIME projections (authenticated, APPLICATION_RUNTIME tenancy).\n ...workbenchCreationPlane_ResourcesConfigurationsFactoryMap,\n};\n"]}