@zenwave360/workspace-mcp 1.0.0-next.0 → 1.0.0-next.18.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,208 +1,208 @@
1
- # ZenWave Manifest
2
-
3
- [![Maven Central](https://img.shields.io/maven-central/v/io.zenwave360.manifest/manifest-core.svg?label=Maven%20Central&logo=apachemaven)](https://search.maven.org/artifact/io.zenwave360.manifest/manifest-core)
4
- [![build](https://github.com/ZenWave360/zenwave-manifest/actions/workflows/publish-maven-snapshots.yml/badge.svg?branch=develop)](https://github.com/ZenWave360/zenwave-manifest/actions/workflows/publish-maven-snapshots.yml)
5
- [![line coverage](https://raw.githubusercontent.com/ZenWave360/zenwave-manifest/badges/coverage.svg)](https://github.com/ZenWave360/zenwave-manifest/actions/workflows/main.yml)
6
- [![branch coverage](https://raw.githubusercontent.com/ZenWave360/zenwave-manifest/badges/branches.svg)](https://github.com/ZenWave360/zenwave-manifest/actions/workflows/main.yml)
7
- [![License](https://img.shields.io/badge/license-MIT-green.svg)](https://github.com/ZenWave360/zenwave-manifest/blob/main/LICENSE)
8
-
9
- ZenWave Manifest is the architecture contract shared by ZenWave tools. A hand-authored `zenwave-architecture.yml` describes domains, services, documentation, and artifacts; the Kotlin Multiplatform library resolves that content deterministically from workspace, Git, Apicurio Registry, generic Artifactory, or Maven sources. Its source-aware editor can update existing scalar values without reserializing the surrounding YAML.
10
-
11
-
12
- - Versioned schema: `https://schemas.zenwave360.io/zenwave-architecture/1.0/schema.json`
13
- - Latest schema: `https://schemas.zenwave360.io/zenwave-architecture/latest/schema.json`
14
- - Detailed authoring guide: [docs/source-resolution-guide.md](docs/source-resolution-guide.md)
1
+ # ZenWave Manifest
2
+
3
+ [![Maven Central](https://img.shields.io/maven-central/v/io.zenwave360.manifest/manifest-core.svg?label=Maven%20Central&logo=apachemaven)](https://search.maven.org/artifact/io.zenwave360.manifest/manifest-core)
4
+ [![build](https://github.com/ZenWave360/zenwave-manifest/actions/workflows/publish-maven-snapshots.yml/badge.svg?branch=develop)](https://github.com/ZenWave360/zenwave-manifest/actions/workflows/publish-maven-snapshots.yml)
5
+ [![line coverage](https://raw.githubusercontent.com/ZenWave360/zenwave-manifest/badges/coverage.svg)](https://github.com/ZenWave360/zenwave-manifest/actions/workflows/main.yml)
6
+ [![branch coverage](https://raw.githubusercontent.com/ZenWave360/zenwave-manifest/badges/branches.svg)](https://github.com/ZenWave360/zenwave-manifest/actions/workflows/main.yml)
7
+ [![License](https://img.shields.io/badge/license-MIT-green.svg)](https://github.com/ZenWave360/zenwave-manifest/blob/main/LICENSE)
8
+
9
+ ZenWave Manifest is the architecture contract shared by ZenWave tools. A hand-authored `zenwave-architecture.yml` describes domains, services, documentation, and artifacts; the Kotlin Multiplatform library resolves that content deterministically from workspace, Git, Apicurio Registry, generic Artifactory, or Maven sources. Its source-aware editor can update existing scalar values without reserializing the surrounding YAML.
10
+
11
+
12
+ - Versioned schema: `https://schemas.zenwave360.io/zenwave-architecture/1.0/schema.json`
13
+ - Latest schema: `https://schemas.zenwave360.io/zenwave-architecture/latest/schema.json`
14
+ - Detailed authoring guide: [docs/source-resolution-guide.md](docs/source-resolution-guide.md)
15
15
  - Normative source-resolution specification: [docs/source-resolution-specification.md](docs/source-resolution-specification.md)
16
16
  - Workspace diagnostic codes: [docs/diagnostics.md](docs/diagnostics.md)
17
-
18
- ## Complete manifest
19
-
20
- ```yaml
21
- # schema-test: valid
22
- # yaml-language-server: $schema=https://schemas.zenwave360.io/zenwave-architecture/1.0/schema.json
23
-
24
- config:
25
- title: Arcadia architecture
26
- groupIdExpression: "io.arcadia.${service.id}"
27
-
28
- contentResolution:
29
- - workspace
30
- - git
31
- - apicurio
32
- - artifactory
33
- - maven
34
-
35
- sources:
36
- workspace:
37
- basePathExpression: "../../${domain.id}/${subdomain.id}/${service.id}"
38
-
39
- git:
40
- provider: gitlab
41
- server: https://gitlab.com
42
-
43
- apicurio:
44
- server: https://registry.example.com
45
-
46
- artifactory:
47
- server: https://artifacts.example.com
48
-
49
- maven:
50
- provider: artifactory
51
- server: https://artifacts.example.com/artifactory
52
- repository: maven-releases
53
-
54
- domains:
55
- commerce:
56
- version: 1.0.0
57
-
58
- services:
59
- orders:
60
- id: orders-api
61
- docs:
62
- summary: docs/SUMMARY.md
63
- catalog: docs/EVENT_CATALOG.md
64
- readme: docs/README.md
65
- artifacts:
66
- - type: openapi
67
- path: contracts/orders.openapi.yaml
68
- version: 1.1.0
69
-
70
- subdomains:
71
- fulfillment:
72
- version: 2.0.0
73
- services:
74
- shipping:
75
- artifacts:
76
- - type: asyncapi
77
- path: contracts/shipping.asyncapi.yaml
78
- version: 2.0.0
79
- ```
80
-
81
- Domains, subdomains, and services use a non-blank explicit `id` when present and otherwise use their YAML map key. A direct service has an empty `subdomain.id`. Every artifact declares its own required `version`; domain, subdomain, and service versions are optional and apply only to service documents. The primary example deliberately omits artifact `name` and `artifactId`: names are never derived from paths, while artifact IDs use their configured fallback.
82
-
83
- For a manifest stored at `/work/architecture/manifests/zenwave-architecture.yml`, the `../../` prefix reaches `/work`; the first artifact's workspace candidate is:
84
-
85
- ```text
86
- /work/commerce/orders-api/contracts/orders.openapi.yaml
87
- ```
88
-
89
- The same artifact has these remote candidates:
90
-
91
- ```text
92
- https://gitlab.com/commerce/orders-api/-/raw/v1.1.0/contracts/orders.openapi.yaml
93
- https://registry.example.com/apis/registry/v3/groups/orders-api/artifacts/contracts%2Forders.openapi.yaml/versions/1.1.0/content
94
- https://artifacts.example.com/artifactory/contracts/commerce/orders-api/1.1.0/contracts/orders.openapi.yaml
95
- https://artifacts.example.com/artifactory/maven-releases/io/arcadia/orders-api/contracts%2Forders.openapi/1.1.0/contracts%2Forders.openapi-1.1.0.jar!/contracts/orders.openapi.yaml
96
- ```
97
-
98
- The empty direct-service subdomain segment is removed without damaging `https://`.
99
- The Artifactory URL above comes from its preset `contentUrlExpression`; declare the field only when the repository key or layout differs from `artifactory/contracts/...`.
100
-
101
- ## Runtime values
102
-
103
- Static `config.properties` substitution runs first. It cannot override canonical values or source-local `${server}`. Runtime interpolation is literal: no functions, conditionals, nested expressions, or fallback syntax are supported.
104
-
105
- | Variable | Value |
106
- | --- | --- |
107
- | `${owner.id}` | Resolved ID of the service or domain that declares the artifact. |
108
- | `${owner.repository}` | Explicit repository of the service or domain artifact owner; unresolved when omitted. |
109
- | `${domain.id}` | Explicit domain ID, otherwise its map key. |
110
- | `${subdomain.id}` | Explicit subdomain ID, otherwise its map key; empty for direct services. |
111
- | `${service.id}` | Explicit service ID, otherwise its map key. |
112
- | `${service.repository}` | Explicit service repository name; unresolved when omitted. |
113
- | `${domain.version}` | Explicit domain version, when declared. |
114
- | `${subdomain.version}` | Explicit subdomain version, when declared. |
115
- | `${service.version}` | Explicit service version, when declared. |
116
- | `${artifact.version}` | Declared artifact version; artifact `version` is required. |
117
- | `${artifact.path}` | Complete declared artifact path. |
118
- | `${artifact.pathWithoutExtension}` | Complete artifact path with only its final extension removed. |
119
- | `${artifact.name}` | Explicit non-blank artifact name only; unresolved when omitted. |
120
- | `${artifact.fileName}` | Final artifact path segment, with every extension. |
121
- | `${artifact.fileNameWithoutExtension}` | Filename with only its final extension removed. |
122
- | `${content.path}` | Current artifact path or selected document path. |
123
- | `${service.docs[key]}` | Declared document path for the literal key. |
124
- | `${groupId}` | Common resolved group coordinate. |
125
- | `${artifactId}` | Common resolved artifact coordinate. |
126
- | `${version}` | Effective content version: `artifact.version` for an artifact, the inherited service, subdomain, or domain version for a service document. |
127
-
128
- Services and domains may own artifacts. Owner identity and source-control location are deliberately separate; repository identity is never inferred from `owner.id`:
129
-
130
- ```yaml
131
- config:
132
- sources:
133
- workspace:
134
- basePathExpression: "../${service.repository}"
135
- git:
136
- provider: github
137
- server: https://github.com
138
- contentUrlExpression: "${server}/arcadia-editions/${service.repository}/raw/main/${content.path}"
139
- maven:
140
- provider: github
141
- server: https://maven.pkg.github.com
142
- repository: "arcadia-editions/${service.repository}"
143
- domains:
144
- catalog:
145
- services:
146
- products:
147
- id: catalog.catalog-management.catalog-products
148
- repository: catalog-products-api
149
- ```
150
-
151
- `maven.repository` accepts the same runtime expressions and is resolved once per artifact, so services publishing to their own GitHub Packages repositories share a single Maven source declaration.
152
-
153
- If `repository` is omitted, `${service.repository}` stays unresolved and any selected expression that uses it fails with the standard unresolved-runtime-variable diagnostic.
154
-
155
- Filename behavior is exact:
156
-
157
- | Path | `artifact.pathWithoutExtension` | `artifact.fileName` | `artifact.fileNameWithoutExtension` |
158
- | --- | --- | --- | --- |
159
- | `contracts/orders.openapi.yaml` | `contracts/orders.openapi` | `orders.openapi.yaml` | `orders.openapi` |
160
- | `archive.tar.gz` | `archive.tar` | `archive.tar.gz` | `archive.tar` |
161
- | `domain-model.zdl` | `domain-model` | `domain-model.zdl` | `domain-model` |
162
- | `.gitignore` | `.gitignore` | `.gitignore` | `.gitignore` |
163
- | `README` | `README` | `README` | `README` |
164
-
165
- Documents are selected from the service map. Keys may contain letters, numbers, `.`, `_`, and `-`:
166
-
167
- ```text
168
- ${service.docs[summary]} = docs/SUMMARY.md
169
- ${service.docs[catalog]} = docs/EVENT_CATALOG.md
170
- ${service.docs[readme]} = docs/README.md
171
- ```
172
-
173
- `${service.docs}` alone, an invalid lookup, or a missing key fails before I/O. Loading all docs evaluates the source URL once per entry, so `${content.path}` changes from `docs/SUMMARY.md` to `docs/EVENT_CATALOG.md` and then `docs/README.md`.
174
-
175
- `${version}` resolves differently for the two kinds of content. For an artifact it is exactly `artifact.version`, which is a required field and never inherits from the service, subdomain, or domain. For a service document it inherits service, then subdomain, then domain. The qualified version expressions expose only their corresponding explicit declaration and do not inherit. Blank versions count as absent. YAML strings and numbers normalize to strings; `config.version` and artifact contents never supply a content version.
176
-
177
- ## Coordinates and explicit overrides
178
-
179
- Common coordinates resolve as:
180
-
181
- ```text
182
- groupId = owner.groupId, otherwise config.groupIdExpression
183
- artifactId = artifact.artifactId, otherwise config.artifactIdExpression
184
- ```
185
-
186
- Defaults are `${owner.id}` and `${artifact.pathWithoutExtension}`. Coordinate expressions cannot recursively reference `${groupId}` or `${artifactId}`. Overrides are valid:
187
-
188
- ```yaml
189
- domains:
190
- commerce:
191
- services:
192
- orders:
193
- groupId: io.arcadia.orders
194
- artifacts:
195
- - name: public-orders-api
196
- artifactId: orders-openapi
197
- type: openapi
198
- path: contracts/orders.openapi.yaml
199
- version: 1.1.0
200
- ```
201
-
202
- `${groupId}` and `${artifactId}` can be selected by any artifact provider expression, not only Maven.
203
-
17
+
18
+ ## Complete manifest
19
+
20
+ ```yaml
21
+ # schema-test: valid
22
+ # yaml-language-server: $schema=https://schemas.zenwave360.io/zenwave-architecture/1.0/schema.json
23
+
24
+ config:
25
+ title: Arcadia architecture
26
+ groupIdExpression: "io.arcadia.${service.id}"
27
+
28
+ contentResolution:
29
+ - workspace
30
+ - git
31
+ - apicurio
32
+ - artifactory
33
+ - maven
34
+
35
+ sources:
36
+ workspace:
37
+ basePathExpression: "../../${domain.id}/${subdomain.id}/${service.id}"
38
+
39
+ git:
40
+ provider: gitlab
41
+ server: https://gitlab.com
42
+
43
+ apicurio:
44
+ server: https://registry.example.com
45
+
46
+ artifactory:
47
+ server: https://artifacts.example.com
48
+
49
+ maven:
50
+ provider: artifactory
51
+ server: https://artifacts.example.com/artifactory
52
+ repository: maven-releases
53
+
54
+ domains:
55
+ commerce:
56
+ version: 1.0.0
57
+
58
+ services:
59
+ orders:
60
+ id: orders-api
61
+ docs:
62
+ summary: docs/SUMMARY.md
63
+ catalog: docs/EVENT_CATALOG.md
64
+ readme: docs/README.md
65
+ artifacts:
66
+ - type: openapi
67
+ path: contracts/orders.openapi.yaml
68
+ version: 1.1.0
69
+
70
+ subdomains:
71
+ fulfillment:
72
+ version: 2.0.0
73
+ services:
74
+ shipping:
75
+ artifacts:
76
+ - type: asyncapi
77
+ path: contracts/shipping.asyncapi.yaml
78
+ version: 2.0.0
79
+ ```
80
+
81
+ Domains, subdomains, and services use a non-blank explicit `id` when present and otherwise use their YAML map key. A direct service has an empty `subdomain.id`. Every artifact declares its own required `version`; domain, subdomain, and service versions are optional and apply only to service documents. The primary example deliberately omits artifact `name` and `artifactId`: names are never derived from paths, while artifact IDs use their configured fallback.
82
+
83
+ For a manifest stored at `/work/architecture/manifests/zenwave-architecture.yml`, the `../../` prefix reaches `/work`; the first artifact's workspace candidate is:
84
+
85
+ ```text
86
+ /work/commerce/orders-api/contracts/orders.openapi.yaml
87
+ ```
88
+
89
+ The same artifact has these remote candidates:
90
+
91
+ ```text
92
+ https://gitlab.com/commerce/orders-api/-/raw/v1.1.0/contracts/orders.openapi.yaml
93
+ https://registry.example.com/apis/registry/v3/groups/orders-api/artifacts/contracts%2Forders.openapi.yaml/versions/1.1.0/content
94
+ https://artifacts.example.com/artifactory/contracts/commerce/orders-api/1.1.0/contracts/orders.openapi.yaml
95
+ https://artifacts.example.com/artifactory/maven-releases/io/arcadia/orders-api/contracts%2Forders.openapi/1.1.0/contracts%2Forders.openapi-1.1.0.jar!/contracts/orders.openapi.yaml
96
+ ```
97
+
98
+ The empty direct-service subdomain segment is removed without damaging `https://`.
99
+ The Artifactory URL above comes from its preset `contentUrlExpression`; declare the field only when the repository key or layout differs from `artifactory/contracts/...`.
100
+
101
+ ## Runtime values
102
+
103
+ Static `config.properties` substitution runs first. It cannot override canonical values or source-local `${server}`. Runtime interpolation is literal: no functions, conditionals, nested expressions, or fallback syntax are supported.
104
+
105
+ | Variable | Value |
106
+ | --- | --- |
107
+ | `${owner.id}` | Resolved ID of the service or domain that declares the artifact. |
108
+ | `${owner.repository}` | Explicit repository of the service or domain artifact owner; unresolved when omitted. |
109
+ | `${domain.id}` | Explicit domain ID, otherwise its map key. |
110
+ | `${subdomain.id}` | Explicit subdomain ID, otherwise its map key; empty for direct services. |
111
+ | `${service.id}` | Explicit service ID, otherwise its map key. |
112
+ | `${service.repository}` | Explicit service repository name; unresolved when omitted. |
113
+ | `${domain.version}` | Explicit domain version, when declared. |
114
+ | `${subdomain.version}` | Explicit subdomain version, when declared. |
115
+ | `${service.version}` | Explicit service version, when declared. |
116
+ | `${artifact.version}` | Declared artifact version; artifact `version` is required. |
117
+ | `${artifact.path}` | Complete declared artifact path. |
118
+ | `${artifact.pathWithoutExtension}` | Complete artifact path with only its final extension removed. |
119
+ | `${artifact.name}` | Explicit non-blank artifact name only; unresolved when omitted. |
120
+ | `${artifact.fileName}` | Final artifact path segment, with every extension. |
121
+ | `${artifact.fileNameWithoutExtension}` | Filename with only its final extension removed. |
122
+ | `${content.path}` | Current artifact path or selected document path. |
123
+ | `${service.docs[key]}` | Declared document path for the literal key. |
124
+ | `${groupId}` | Common resolved group coordinate. |
125
+ | `${artifactId}` | Common resolved artifact coordinate. |
126
+ | `${version}` | Effective content version: `artifact.version` for an artifact, the inherited service, subdomain, or domain version for a service document. |
127
+
128
+ Services and domains may own artifacts. Owner identity and source-control location are deliberately separate; repository identity is never inferred from `owner.id`:
129
+
130
+ ```yaml
131
+ config:
132
+ sources:
133
+ workspace:
134
+ basePathExpression: "../${service.repository}"
135
+ git:
136
+ provider: github
137
+ server: https://github.com
138
+ contentUrlExpression: "${server}/arcadia-editions/${service.repository}/raw/main/${content.path}"
139
+ maven:
140
+ provider: github
141
+ server: https://maven.pkg.github.com
142
+ repository: "arcadia-editions/${service.repository}"
143
+ domains:
144
+ catalog:
145
+ services:
146
+ products:
147
+ id: catalog.catalog-management.catalog-products
148
+ repository: catalog-products-api
149
+ ```
150
+
151
+ `maven.repository` accepts the same runtime expressions and is resolved once per artifact, so services publishing to their own GitHub Packages repositories share a single Maven source declaration.
152
+
153
+ If `repository` is omitted, `${service.repository}` stays unresolved and any selected expression that uses it fails with the standard unresolved-runtime-variable diagnostic.
154
+
155
+ Filename behavior is exact:
156
+
157
+ | Path | `artifact.pathWithoutExtension` | `artifact.fileName` | `artifact.fileNameWithoutExtension` |
158
+ | --- | --- | --- | --- |
159
+ | `contracts/orders.openapi.yaml` | `contracts/orders.openapi` | `orders.openapi.yaml` | `orders.openapi` |
160
+ | `archive.tar.gz` | `archive.tar` | `archive.tar.gz` | `archive.tar` |
161
+ | `domain-model.zdl` | `domain-model` | `domain-model.zdl` | `domain-model` |
162
+ | `.gitignore` | `.gitignore` | `.gitignore` | `.gitignore` |
163
+ | `README` | `README` | `README` | `README` |
164
+
165
+ Documents are selected from the service map. Keys may contain letters, numbers, `.`, `_`, and `-`:
166
+
167
+ ```text
168
+ ${service.docs[summary]} = docs/SUMMARY.md
169
+ ${service.docs[catalog]} = docs/EVENT_CATALOG.md
170
+ ${service.docs[readme]} = docs/README.md
171
+ ```
172
+
173
+ `${service.docs}` alone, an invalid lookup, or a missing key fails before I/O. Loading all docs evaluates the source URL once per entry, so `${content.path}` changes from `docs/SUMMARY.md` to `docs/EVENT_CATALOG.md` and then `docs/README.md`.
174
+
175
+ `${version}` resolves differently for the two kinds of content. For an artifact it is exactly `artifact.version`, which is a required field and never inherits from the service, subdomain, or domain. For a service document it inherits service, then subdomain, then domain. The qualified version expressions expose only their corresponding explicit declaration and do not inherit. Blank versions count as absent. YAML strings and numbers normalize to strings; `config.version` and artifact contents never supply a content version.
176
+
177
+ ## Coordinates and explicit overrides
178
+
179
+ Common coordinates resolve as:
180
+
181
+ ```text
182
+ groupId = owner.groupId, otherwise config.groupIdExpression
183
+ artifactId = artifact.artifactId, otherwise config.artifactIdExpression
184
+ ```
185
+
186
+ Defaults are `${owner.id}` and `${artifact.pathWithoutExtension}`. Coordinate expressions cannot recursively reference `${groupId}` or `${artifactId}`. Overrides are valid:
187
+
188
+ ```yaml
189
+ domains:
190
+ commerce:
191
+ services:
192
+ orders:
193
+ groupId: io.arcadia.orders
194
+ artifacts:
195
+ - name: public-orders-api
196
+ artifactId: orders-openapi
197
+ type: openapi
198
+ path: contracts/orders.openapi.yaml
199
+ version: 1.1.0
200
+ ```
201
+
202
+ `${groupId}` and `${artifactId}` can be selected by any artifact provider expression, not only Maven.
203
+
204
204
  ## Artifact catalog and selections
205
-
205
+
206
206
  `ManifestArtifactCatalog` resolves every declared artifact together with its owner and effective
207
207
  coordinates. Declared artifact `type` values remain an open string namespace: `openapi`,
208
208
  `asyncapi`, `zdl`, `zfl`, `grpc`, and future types require no registration with the library.
@@ -285,26 +285,26 @@ one owner:
285
285
 
286
286
  ```kotlin
287
287
  val owner = ManifestOwnerSelector.Repository("orders-api")
288
- val openApis = catalog.resolveByType(owner, "openapi").artifacts
289
- val contract = catalog.resolveByArtifactId(owner, "contracts/orders").artifact
290
- ```
291
-
292
- Type resolution also supports library-defined virtual behavior. `asyncapi-all` selects every
293
- declared `asyncapi` and `asyncapi-client` artifact on exactly one service and returns the concrete
294
- members; it is never inserted into `ZenWaveManifest.artifacts`.
295
-
296
- ```kotlin
297
- val catalog = ManifestArtifactCatalog.resolve(manifest, loader)
298
- val selection = catalog.resolve(
299
- ManifestArtifactSelector.typeInRepository(
300
- "orders-api",
301
- ManifestArtifactSelector.ASYNCAPI_ALL,
302
- ),
303
- )
304
- val bundleMembers: List<ResolvedManifestArtifact> = selection.artifacts
305
- ```
306
-
307
- Repository selectors may generally span several owners. An artifact-ID selector must resolve one
288
+ val openApis = catalog.resolveByType(owner, "openapi").artifacts
289
+ val contract = catalog.resolveByArtifactId(owner, "contracts/orders").artifact
290
+ ```
291
+
292
+ Type resolution also supports library-defined virtual behavior. `asyncapi-all` selects every
293
+ declared `asyncapi` and `asyncapi-client` artifact on exactly one service and returns the concrete
294
+ members; it is never inserted into `ZenWaveManifest.artifacts`.
295
+
296
+ ```kotlin
297
+ val catalog = ManifestArtifactCatalog.resolve(manifest, loader)
298
+ val selection = catalog.resolve(
299
+ ManifestArtifactSelector.typeInRepository(
300
+ "orders-api",
301
+ ManifestArtifactSelector.ASYNCAPI_ALL,
302
+ ),
303
+ )
304
+ val bundleMembers: List<ResolvedManifestArtifact> = selection.artifacts
305
+ ```
306
+
307
+ Repository selectors may generally span several owners. An artifact-ID selector must resolve one
308
308
  artifact. A declared type must resolve artifacts on one owner, while `asyncapi-all` must resolve one
309
309
  matching service; missing and ambiguous selections fail with `ManifestArtifactSelectionException`.
310
310
 
@@ -346,104 +346,104 @@ workspace-cli/build/install/zenwave/bin/zenwave validate /workspace/zenwave-arch
346
346
  ```
347
347
 
348
348
  ## Source-aware editing
349
-
350
- `ZenWaveManifestEditor.updateArtifactVersions` accepts one or more artifact-ID or type selectors.
351
- It snapshots the root and referenced documents, resolves every selection, patches only the existing
352
- version scalar ranges, and validates the complete overlay before returning document changes.
353
-
354
- ```kotlin
355
- val result = editor.updateArtifactVersions(
356
- manifestUri,
357
- listOf(
358
- ManifestArtifactVersionUpdate(
359
- ManifestArtifactSelector.typeInRepository(
360
- "orders-api",
361
- ManifestArtifactSelector.ASYNCAPI_ALL,
362
- ),
363
- "1.4.0",
364
- ),
365
- ),
366
- )
367
- ```
368
-
369
- The returned `ManifestDocumentTextUpdate` values preserve unrelated formatting, comments, scalar
370
- quote style, and source ownership through external `$ref` documents. The editor does not persist
371
- them; callers can compare `originalText` before writing. JVM and Java callers can use
372
- `BlockingZenWaveManifestEditor` with the default file and HTTP(S) document reader.
373
-
374
- `updateScalars` is the lower-level API for updating another existing owner or artifact scalar.
375
- Structural edits such as adding or removing manifest nodes remain out of scope.
376
-
377
- ## Ordered reads
378
-
379
- `contentResolution` defaults to `[workspace]`. Only listed sources participate; configuring a source does not activate it. Transport and not-found failures advance to the next applicable source. Apicurio and Maven are artifact-only and are skipped for service documents. A preferred source may reorder or restrict only active sources.
380
-
381
- Supported absolute URIs bypass the list. Exhausted reads report redacted candidates in attempted order and the final error. Unknown sources, active unconfigured sources, invalid providers, missing required fields, and unresolved expressions fail deterministically before transport I/O where possible.
382
-
383
- | Source | Artifacts | Service docs |
384
- | --- | --- | --- |
385
- | `workspace` | Yes | Yes |
386
- | `git` | Yes | Yes |
387
- | `apicurio` | Yes | No |
388
- | `artifactory` | Yes | Yes |
389
- | `maven` | Yes | No |
390
-
391
- See [the detailed source guide](docs/source-resolution-guide.md) for provider defaults, custom Git recipes, encoding, workspace traversal, Artifactory layouts, and Maven Central JAR extraction.
392
-
393
- ## Loading API
394
-
395
- `ZenWaveManifestLoader` is the canonical multiplatform API. Its loading operations are
396
- `suspend` functions so JVM and Node.js/Kotlin callers can perform manifest, document, and
397
- artifact I/O without blocking their server event loop:
398
-
399
- ```kotlin
400
- val loader = ZenWaveManifestLoader()
401
- val manifest = loader.load("file:///workspace/zenwave-architecture.yml") // http also work
402
- val service = manifest.findService("commerce/orders")!!
403
- val openApis = service.findArtifacts("openapi")
404
- val text = loader.loadArtifactText(manifest, service, openApis.first())
405
- val schema = loader.resolveArtifactReference(
406
- manifest,
407
- service,
408
- openApis.first(),
409
- "schemas/order.yaml",
410
- )
411
- ```
412
-
413
- Java and synchronous JVM integrations can use `BlockingZenWaveManifestLoader`. It exposes the
414
- same I/O operations without coroutine continuations and accepts either `String` or `java.net.URI`
415
- for root resources:
416
-
417
- ```java
418
- var loader = new BlockingZenWaveManifestLoader();
419
- var manifest = loader.load(URI.create("file:///workspace/zenwave-architecture.yml"));
420
- var service = manifest.findService("commerce/orders");
421
- var artifact = service.findArtifact("openapi");
422
- var options = new ManifestLoadOptions()
423
- .withPreferredSource("workspace")
424
- .withFallback(true);
425
- var text = loader.loadArtifactText(manifest, service, artifact, options);
426
- ```
427
-
428
- For batch service documents, `loadServiceDocResults` preserves one result per configured
429
- document, including its resolved resource or error message. `loadAvailableServiceDocs` is the
430
- convenience variant for generators that only need successfully loaded content.
431
- `loadServiceDocs` remains the fail-fast variant.
432
-
433
- Candidate and reference construction remains non-blocking on the common loader. Use
434
- `ManifestResolvedResource.referenceUri()` when a consumer needs a single URI-like reference,
435
- including a resource stored inside an archive.
436
-
437
- ## Kotlin module and verification
438
-
439
- `manifest-core` contains common parsing, diagnostics, interpolation, and candidate construction for JVM and JavaScript. JVM also provides Maven Central JAR entry extraction; JavaScript hosts can supply an archive-entry loader.
440
-
441
- Published library coordinates are `io.zenwave360.manifest:manifest-core`, `manifest-core-jvm`, and `manifest-core-js`.
442
-
443
- Run:
444
-
445
- ```text
446
- ./gradlew :manifest-core:jvmTest
447
- ```
448
-
449
- The suite validates both schemas and every complete README YAML block marked `schema-test: valid`. Release workflow details are in [RELEASING.md](RELEASING.md); this repository is licensed under the [MIT License](LICENSE).
349
+
350
+ `ZenWaveManifestEditor.updateArtifactVersions` accepts one or more artifact-ID or type selectors.
351
+ It snapshots the root and referenced documents, resolves every selection, patches only the existing
352
+ version scalar ranges, and validates the complete overlay before returning document changes.
353
+
354
+ ```kotlin
355
+ val result = editor.updateArtifactVersions(
356
+ manifestUri,
357
+ listOf(
358
+ ManifestArtifactVersionUpdate(
359
+ ManifestArtifactSelector.typeInRepository(
360
+ "orders-api",
361
+ ManifestArtifactSelector.ASYNCAPI_ALL,
362
+ ),
363
+ "1.4.0",
364
+ ),
365
+ ),
366
+ )
367
+ ```
368
+
369
+ The returned `ManifestDocumentTextUpdate` values preserve unrelated formatting, comments, scalar
370
+ quote style, and source ownership through external `$ref` documents. The editor does not persist
371
+ them; callers can compare `originalText` before writing. JVM and Java callers can use
372
+ `BlockingZenWaveManifestEditor` with the default file and HTTP(S) document reader.
373
+
374
+ `updateScalars` is the lower-level API for updating another existing owner or artifact scalar.
375
+ Structural edits such as adding or removing manifest nodes remain out of scope.
376
+
377
+ ## Ordered reads
378
+
379
+ `contentResolution` defaults to `[workspace]`. Only listed sources participate; configuring a source does not activate it. Transport and not-found failures advance to the next applicable source. Apicurio and Maven are artifact-only and are skipped for service documents. A preferred source may reorder or restrict only active sources.
380
+
381
+ Supported absolute URIs bypass the list. Exhausted reads report redacted candidates in attempted order and the final error. Unknown sources, active unconfigured sources, invalid providers, missing required fields, and unresolved expressions fail deterministically before transport I/O where possible.
382
+
383
+ | Source | Artifacts | Service docs |
384
+ | --- | --- | --- |
385
+ | `workspace` | Yes | Yes |
386
+ | `git` | Yes | Yes |
387
+ | `apicurio` | Yes | No |
388
+ | `artifactory` | Yes | Yes |
389
+ | `maven` | Yes | No |
390
+
391
+ See [the detailed source guide](docs/source-resolution-guide.md) for provider defaults, custom Git recipes, encoding, workspace traversal, Artifactory layouts, and Maven Central JAR extraction.
392
+
393
+ ## Loading API
394
+
395
+ `ZenWaveManifestLoader` is the canonical multiplatform API. Its loading operations are
396
+ `suspend` functions so JVM and Node.js/Kotlin callers can perform manifest, document, and
397
+ artifact I/O without blocking their server event loop:
398
+
399
+ ```kotlin
400
+ val loader = ZenWaveManifestLoader()
401
+ val manifest = loader.load("file:///workspace/zenwave-architecture.yml") // http also work
402
+ val service = manifest.findService("commerce/orders")!!
403
+ val openApis = service.findArtifacts("openapi")
404
+ val text = loader.loadArtifactText(manifest, service, openApis.first())
405
+ val schema = loader.resolveArtifactReference(
406
+ manifest,
407
+ service,
408
+ openApis.first(),
409
+ "schemas/order.yaml",
410
+ )
411
+ ```
412
+
413
+ Java and synchronous JVM integrations can use `BlockingZenWaveManifestLoader`. It exposes the
414
+ same I/O operations without coroutine continuations and accepts either `String` or `java.net.URI`
415
+ for root resources:
416
+
417
+ ```java
418
+ var loader = new BlockingZenWaveManifestLoader();
419
+ var manifest = loader.load(URI.create("file:///workspace/zenwave-architecture.yml"));
420
+ var service = manifest.findService("commerce/orders");
421
+ var artifact = service.findArtifact("openapi");
422
+ var options = new ManifestLoadOptions()
423
+ .withPreferredSource("workspace")
424
+ .withFallback(true);
425
+ var text = loader.loadArtifactText(manifest, service, artifact, options);
426
+ ```
427
+
428
+ For batch service documents, `loadServiceDocResults` preserves one result per configured
429
+ document, including its resolved resource or error message. `loadAvailableServiceDocs` is the
430
+ convenience variant for generators that only need successfully loaded content.
431
+ `loadServiceDocs` remains the fail-fast variant.
432
+
433
+ Candidate and reference construction remains non-blocking on the common loader. Use
434
+ `ManifestResolvedResource.referenceUri()` when a consumer needs a single URI-like reference,
435
+ including a resource stored inside an archive.
436
+
437
+ ## Kotlin module and verification
438
+
439
+ `manifest-core` contains common parsing, diagnostics, interpolation, and candidate construction for JVM and JavaScript. JVM also provides Maven Central JAR entry extraction; JavaScript hosts can supply an archive-entry loader.
440
+
441
+ Published library coordinates are `io.zenwave360.manifest:manifest-core`, `manifest-core-jvm`, and `manifest-core-js`.
442
+
443
+ Run:
444
+
445
+ ```text
446
+ ./gradlew :manifest-core:jvmTest
447
+ ```
448
+
449
+ The suite validates both schemas and every complete README YAML block marked `schema-test: valid`. Release workflow details are in [RELEASING.md](RELEASING.md); this repository is licensed under the [MIT License](LICENSE).