@openship/protocol 0.1.0 → 0.1.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
@@ -15,3 +15,5 @@ if (imported.snapshot.kind === "systems") validateSystems(imported.snapshot.docu
15
15
  The package contains the exact canonical `skills/openship` schemas, examples, and references. See the repository root README for the generated-skill workflow.
16
16
 
17
17
  Version 0.1.0 replaces legacy Systems with `systemsVersion: "2.0"`: `system.layers`, `system.refinements`, and optional `system.instances`. Sources and Changes retain their 1.0 formats. Consumers must migrate; `validateSystems` explicitly rejects the legacy graph. Each layer is independently renderable, while node IDs and shared context span the entire system.
18
+
19
+ Version 0.1.1 adds optional `system.domains` entries (`id`, `name`, `nodeIds`, optional `description`). Membership may overlap and span layers. Documents without domains remain valid; `openship: "1.0"` and `systemsVersion: "2.0"` do not change.
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "openship": "1.0",
3
3
  "package": "@openship/protocol",
4
- "packageVersion": "0.1.0",
4
+ "packageVersion": "0.1.1",
5
5
  "source": "https://github.com/openshipdev/openship/tree/main/skills/openship",
6
- "sourceCommit": "f1338ebcb6a49927383b5b7d55be70a3d966d8d2"
6
+ "sourceCommit": "fad5993fea4e1a21d95ec76d5ec89621364822b8"
7
7
  }
@@ -326,6 +326,27 @@
326
326
  }
327
327
  ]
328
328
  }
329
+ ],
330
+ "domains": [
331
+ {
332
+ "id": "web",
333
+ "name": "Web app",
334
+ "nodeIds": [
335
+ "logical.web",
336
+ "p.web",
337
+ "provider.web"
338
+ ]
339
+ },
340
+ {
341
+ "id": "state",
342
+ "name": "State",
343
+ "nodeIds": [
344
+ "logical.data",
345
+ "technical.data",
346
+ "provider.data",
347
+ "p.web"
348
+ ]
349
+ }
329
350
  ]
330
351
  },
331
352
  "systemsVersion": "2.0"
@@ -65,6 +65,23 @@ Layer-local edges have `id`, `type`, `fromNodeId`, `toNodeId`, and optional meta
65
65
 
66
66
  Refinements have `id`, `fromNodeId`, and `toNodeId`. IDs MUST be unique within `system.refinements`. The source MUST belong to a later layer than the target. Thus the concrete source implements the more abstract target. Many-to-many mappings, skipped layers, and root mappings are allowed. Refinements neither imply containment nor copy configuration, documents, or runtime edges.
67
67
 
68
+ ## Domains (optional)
69
+
70
+ `system.domains` MAY declare an ordered list of domains. A domain groups blocks independently of layers, containment, and refinement. Each requires a unique `id`, a nonempty `name`, and `nodeIds`; an optional nonempty `description` explains its scope. Domain IDs use the standard ID grammar. Member IDs MUST be unique within the domain and MUST reference existing nodes anywhere in the system.
71
+
72
+ ```json
73
+ "domains": [
74
+ { "id": "web", "name": "Web app", "nodeIds": ["logical.web", "technical.web"] },
75
+ { "id": "state", "name": "State", "nodeIds": ["technical.web", "technical.database"] }
76
+ ]
77
+ ```
78
+
79
+ A node MAY belong to zero, one, or multiple domains. Empty domains and an empty domain list are valid. Membership is explicit and is not inherited through parents or refinements. Omitting domains preserves the existing Systems behavior.
80
+
81
+ Viewers SHOULD select all domains initially. When filtering, a node matches if it has no domain or belongs to any selected domain. Keep the layer root and ancestors needed to render matching nodes as structural boundaries; this does not make other children visible. Hide connections whose endpoints do not match. Domain filters are presentation controls, not access controls. Keep domain selections across layer changes.
82
+
83
+ Domains are an additive capability in package 0.1.1. The envelope remains `openship: "1.0"` and `systemsVersion: "2.0"`.
84
+
68
85
  ## Configuration
69
86
 
70
87
  Nodes and instance bindings MAY contain a `configuration` array. Each entry requires a unique `name`, a nonempty `description`, and a boolean `required` flag. Optional `value` contains JSON data; its absence means unresolved, whereas an explicit null is a supplied value.
@@ -159,7 +176,7 @@ Artifact IDs are unique within the system.
159
176
  1. Envelope, Systems version, and embedded Sources integrity.
160
177
  2. Layer IDs, global node IDs, roots, containment, configuration and source selectors.
161
178
  3. Layer-local edges and cycle checks.
162
- 4. Refinement endpoints and order.
179
+ 4. Domain declarations and member references; refinement endpoints and order.
163
180
  5. Instance membership, bindings, configuration, state and secret scopes.
164
181
  6. Shared document hashes, concerns, matrix/artifact references, prompts and supersession chains.
165
182
 
@@ -77,7 +77,13 @@
77
77
  },
78
78
  "nodes": false,
79
79
  "edges": false,
80
- "rootNodeId": false
80
+ "rootNodeId": false,
81
+ "domains": {
82
+ "type": "array",
83
+ "items": {
84
+ "$ref": "#/$defs/domain"
85
+ }
86
+ }
81
87
  },
82
88
  "additionalProperties": true
83
89
  },
@@ -577,6 +583,35 @@
577
583
  }
578
584
  },
579
585
  "additionalProperties": true
586
+ },
587
+ "domain": {
588
+ "type": "object",
589
+ "required": [
590
+ "id",
591
+ "name",
592
+ "nodeIds"
593
+ ],
594
+ "properties": {
595
+ "id": {
596
+ "$ref": "#/$defs/id"
597
+ },
598
+ "name": {
599
+ "type": "string",
600
+ "minLength": 1
601
+ },
602
+ "description": {
603
+ "type": "string",
604
+ "minLength": 1
605
+ },
606
+ "nodeIds": {
607
+ "type": "array",
608
+ "items": {
609
+ "$ref": "#/$defs/id"
610
+ },
611
+ "uniqueItems": true
612
+ }
613
+ },
614
+ "additionalProperties": true
580
615
  }
581
616
  },
582
617
  "additionalProperties": true
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@openship/protocol",
3
- "version": "0.1.0",
3
+ "version": "0.1.1",
4
4
  "description": "Canonical OpenShip 1.0 types, validators, consumer helpers, schemas, and skill assets.",
5
5
  "type": "module",
6
6
  "license": "MIT",
package/src/index.d.ts CHANGED
@@ -21,7 +21,8 @@ export interface SystemsLayer { id: string; name: string; role: "logical" | "tec
21
21
  export interface SystemsRefinement { id: string; fromNodeId: string; toNodeId: string; [key: string]: unknown }
22
22
  export interface SystemsBinding { nodeId: string; resourceId?: string; configuration?: SystemsConfiguration[]; state?: { appliedMigration?: string; snapshot?: { ref: string; capturedAt: string; digest?: string } }; [key: string]: unknown }
23
23
  export interface SystemsInstance { id: string; name: string; environment: string; layerId: string; bindings: SystemsBinding[]; [key: string]: unknown }
24
- export interface SystemsGraph { id: string; name: string; layers: SystemsLayer[]; refinements: SystemsRefinement[]; instances?: SystemsInstance[]; metadata?: Record<string, unknown>; context?: Record<string, unknown>; [key: string]: unknown }
24
+ export interface SystemsDomain { id: string; name: string; description?: string; nodeIds: string[]; [key: string]: unknown }
25
+ export interface SystemsGraph { id: string; name: string; layers: SystemsLayer[]; refinements: SystemsRefinement[]; domains?: SystemsDomain[]; instances?: SystemsInstance[]; metadata?: Record<string, unknown>; context?: Record<string, unknown>; [key: string]: unknown }
25
26
  export interface SystemsDocument { openship: "1.0"; capability: "systems"; systemsVersion: "2.0"; source: { manifest: SourcesManifest; bundle: SourcesBundle; [key: string]: unknown }; system: SystemsGraph; [key: string]: unknown }
26
27
  export interface VerifiedSourceFile { metadata: SourceFileMetadata; bytes: Uint8Array }
27
28
  export interface VerifiedSources { manifest: SourcesManifest; bundle: SourcesBundle; files: VerifiedSourceFile[]; decodedBytes: number }
package/src/index.js CHANGED
@@ -326,6 +326,16 @@ export function validateSystems(value, options = {}) {
326
326
  }
327
327
  for (const type of ["Dataflow", "Dependency"]) assertAcyclic([...local.keys()], edges.filter((edge) => edge.type === type).map((edge) => [edge.fromNodeId, edge.toNodeId]), `${at}.edges[${type}]`);
328
328
  }
329
+ const domains = array(system.domains === undefined ? [] : system.domains, "$.system.domains");
330
+ unique(domains.map((domain) => identifier(object(domain, "$.system.domains").id, "$.system.domains.id")), "$.system.domains");
331
+ for (const domain of domains) {
332
+ const at = `$.system.domains.${domain.id}`;
333
+ string(domain.name, `${at}.name`);
334
+ if (domain.description !== undefined) string(domain.description, `${at}.description`);
335
+ const members = array(domain.nodeIds, `${at}.nodeIds`);
336
+ unique(members, `${at}.nodeIds`);
337
+ for (const nodeId of members) if (!nodeById.has(nodeId)) fail(`${at}.nodeIds`, "must reference existing system nodes");
338
+ }
329
339
  const refinements = array(system.refinements, "$.system.refinements");
330
340
  unique(refinements.map((ref) => identifier(object(ref, "$.system.refinements").id, "$.system.refinements.id")), "$.system.refinements");
331
341
  for (const ref of refinements) {