@vev-software/atlas-contracts 0.1.4 → 0.1.6
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 +6 -0
- package/dist/index.d.ts +155 -1
- package/dist/index.js +43 -0
- package/package.json +2 -1
- package/schemas/v1/common.schema.json +44 -1
- package/schemas/v1/landscape-digest.schema.json +105 -0
- package/schemas/v1/target-architecture.schema.json +321 -0
- package/schemas/v2/landscape-digest.schema.json +73 -0
package/README.md
CHANGED
|
@@ -23,6 +23,12 @@ import type { LandscapeDocument, Asset } from "@vev-software/atlas-contracts";
|
|
|
23
23
|
|
|
24
24
|
Ships type declarations (`dist/index.d.ts`) and ESM; no runtime dependencies.
|
|
25
25
|
|
|
26
|
+
Digest v1 remains unchanged. `LandscapeDigestV2` adds required per-digest refs,
|
|
27
|
+
optional layer and default-off closed relations. After validating raw JSON against
|
|
28
|
+
`schemas/v2/landscape-digest.schema.json`, call `validateLandscapeDigestV2` for
|
|
29
|
+
cross-item rules. Scope never authorizes sharing; `integrates` requires separate
|
|
30
|
+
stage 2 consent. Both schema versions and their relative dependencies are shipped.
|
|
31
|
+
|
|
26
32
|
## Versioning
|
|
27
33
|
|
|
28
34
|
[SemVer](https://semver.org), derived from the git tag (`vX.Y.Z`) at release. Pre-1.0, the v1
|
package/dist/index.d.ts
CHANGED
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
*/
|
|
6
6
|
/** The atlas-contracts schema major version. */
|
|
7
7
|
export declare const CONTRACT_VERSION: "1";
|
|
8
|
-
export type AssetKind = "system" | "application" | "server" | "infrastructure" | "data-area" | "dataset" | "column";
|
|
8
|
+
export type AssetKind = "system" | "application" | "server" | "infrastructure" | "data-area" | "dataset" | "column" | "integration" | "business-process";
|
|
9
9
|
export type Lifecycle = "draft" | "active" | "retired";
|
|
10
10
|
export type RelationshipType = "runs-on" | "hosts" | "connects-to" | "depends-on" | "part-of" | "joins-on";
|
|
11
11
|
/** A manual, lightweight classification: a key with an optional value. */
|
|
@@ -350,3 +350,157 @@ export interface AtlasBundle {
|
|
|
350
350
|
restore?: RestoreHints | null;
|
|
351
351
|
excluded?: ExcludedCategory[];
|
|
352
352
|
}
|
|
353
|
+
/** Digest classification of an item. Deliberately narrower than `AssetKind`. */
|
|
354
|
+
export type DigestKind = "system" | "application" | "platform" | "vendor";
|
|
355
|
+
/**
|
|
356
|
+
* The intended change for an item, taken from the Target Architecture contract's change intents
|
|
357
|
+
* and extended with `replace` for digest purposes.
|
|
358
|
+
*/
|
|
359
|
+
export type PlannedChange = "add" | "change" | "replace" | "retire";
|
|
360
|
+
/**
|
|
361
|
+
* Which asset kinds and tags were included in a digest. Both arrays are required and may be
|
|
362
|
+
* empty: an empty `kinds` means no kinds were included, and an empty `tags` means no tag filter
|
|
363
|
+
* was applied (tag matching is OR within the list, AND with the kind filter).
|
|
364
|
+
*/
|
|
365
|
+
export interface LandscapeDigestScope {
|
|
366
|
+
kinds: DigestKind[];
|
|
367
|
+
tags: Tag[];
|
|
368
|
+
}
|
|
369
|
+
/**
|
|
370
|
+
* One minimized landscape item. Carries only the fields a consented consumer needs; everything
|
|
371
|
+
* else is forbidden by the schema.
|
|
372
|
+
*/
|
|
373
|
+
export interface LandscapeDigestItem {
|
|
374
|
+
kind: DigestKind;
|
|
375
|
+
name: string;
|
|
376
|
+
lifecycle: Lifecycle;
|
|
377
|
+
vendor?: string | null;
|
|
378
|
+
plannedChange?: PlannedChange | null;
|
|
379
|
+
/** A count only — never the identities of the integrated parties. */
|
|
380
|
+
integrationCount?: number | null;
|
|
381
|
+
}
|
|
382
|
+
/**
|
|
383
|
+
* A minimized, versioned summary of an Atlas landscape for consented consumers. `sequence` is
|
|
384
|
+
* monotonic per `sourceInstanceId` (first is 1, strictly higher after, gaps allowed); consumers
|
|
385
|
+
* reject a sequence at or below the last accepted for the same source (replay protection).
|
|
386
|
+
*/
|
|
387
|
+
export interface LandscapeDigest {
|
|
388
|
+
contractVersion: typeof CONTRACT_VERSION;
|
|
389
|
+
digestId: string;
|
|
390
|
+
generatedAt: string;
|
|
391
|
+
sourceInstanceId: string;
|
|
392
|
+
sequence: number;
|
|
393
|
+
scope: LandscapeDigestScope;
|
|
394
|
+
items: LandscapeDigestItem[];
|
|
395
|
+
}
|
|
396
|
+
export declare const LANDSCAPE_DIGEST_V2_VERSION: "2";
|
|
397
|
+
export type DigestLayer = "business" | "application" | "data" | "infrastructure";
|
|
398
|
+
/** integrates is contract-only stage 2, requiring separate explicit consent. */
|
|
399
|
+
export type DigestRelationKind = "runsOn" | "suppliedBy" | "integrates";
|
|
400
|
+
/** Signed inclusion scope; omitted relations means false. Never authorization. */
|
|
401
|
+
export interface LandscapeDigestScopeV2 extends LandscapeDigestScope {
|
|
402
|
+
relations?: boolean;
|
|
403
|
+
}
|
|
404
|
+
export interface LandscapeDigestItemV2 extends Omit<LandscapeDigestItem, "plannedChange" | "integrationCount"> {
|
|
405
|
+
/** Required, opaque, 1–64 characters; minted per digest, never a stable asset id. */
|
|
406
|
+
ref: string;
|
|
407
|
+
layer?: DigestLayer;
|
|
408
|
+
plannedChange?: PlannedChange;
|
|
409
|
+
integrationCount?: number;
|
|
410
|
+
}
|
|
411
|
+
/** Closed edge shape: JSON Schema rejects every additional property. */
|
|
412
|
+
export interface LandscapeDigestRelation {
|
|
413
|
+
from: string;
|
|
414
|
+
to: string;
|
|
415
|
+
kind: DigestRelationKind;
|
|
416
|
+
}
|
|
417
|
+
export interface LandscapeDigestV2 extends Omit<LandscapeDigest, "contractVersion" | "scope" | "items"> {
|
|
418
|
+
contractVersion: typeof LANDSCAPE_DIGEST_V2_VERSION;
|
|
419
|
+
scope: LandscapeDigestScopeV2;
|
|
420
|
+
items: LandscapeDigestItemV2[];
|
|
421
|
+
/** Omit entirely unless scope.relations is true, including when empty. */
|
|
422
|
+
relations?: LandscapeDigestRelation[];
|
|
423
|
+
}
|
|
424
|
+
/** Run after JSON Schema validation. Does not grant sharing or consumption permission. */
|
|
425
|
+
export declare function validateLandscapeDigestV2(digest: LandscapeDigestV2): string[];
|
|
426
|
+
export type TargetScopeKind = "estate" | "domain" | "capability-area";
|
|
427
|
+
export type TargetVersionStatus = "draft" | "proposed" | "approved" | "active" | "superseded" | "rejected" | "archived";
|
|
428
|
+
export type ChangeIntent = "add" | "change" | "retire";
|
|
429
|
+
/** What part of the estate a Target Architecture governs. */
|
|
430
|
+
export interface TargetScope {
|
|
431
|
+
kind: TargetScopeKind;
|
|
432
|
+
ref?: string | null;
|
|
433
|
+
name?: string | null;
|
|
434
|
+
}
|
|
435
|
+
/** One asset-level intended change in a target version or gap. */
|
|
436
|
+
export interface TargetAssetMember {
|
|
437
|
+
intent: ChangeIntent;
|
|
438
|
+
assetRef?: string | null;
|
|
439
|
+
asset?: Asset | null;
|
|
440
|
+
rationale?: string | null;
|
|
441
|
+
}
|
|
442
|
+
/** One relationship-level intended change in a target version or gap. */
|
|
443
|
+
export interface TargetRelationshipMember {
|
|
444
|
+
intent: ChangeIntent;
|
|
445
|
+
relationshipRef?: string | null;
|
|
446
|
+
relationship?: Relationship | null;
|
|
447
|
+
rationale?: string | null;
|
|
448
|
+
}
|
|
449
|
+
/** One ordered To-Be version (Plateau analogue) in a Target Architecture. */
|
|
450
|
+
export interface TargetVersion {
|
|
451
|
+
id: string;
|
|
452
|
+
name: string;
|
|
453
|
+
sequence: number;
|
|
454
|
+
status: TargetVersionStatus;
|
|
455
|
+
rationale?: string | null;
|
|
456
|
+
intendedHorizon?: string | null;
|
|
457
|
+
effectiveDate?: string | null;
|
|
458
|
+
assetChanges?: TargetAssetMember[];
|
|
459
|
+
relationshipChanges?: TargetRelationshipMember[];
|
|
460
|
+
}
|
|
461
|
+
/** One optional intermediate gap slice inside a transition. */
|
|
462
|
+
export interface TargetGap {
|
|
463
|
+
id: string;
|
|
464
|
+
name: string;
|
|
465
|
+
description?: string | null;
|
|
466
|
+
assetChanges?: TargetAssetMember[];
|
|
467
|
+
relationshipChanges?: TargetRelationshipMember[];
|
|
468
|
+
}
|
|
469
|
+
/** One deliverable within a transition work package. */
|
|
470
|
+
export interface Deliverable {
|
|
471
|
+
id: string;
|
|
472
|
+
name: string;
|
|
473
|
+
description?: string | null;
|
|
474
|
+
}
|
|
475
|
+
/** A transition work package grouping one or more deliverables. */
|
|
476
|
+
export interface WorkPackage {
|
|
477
|
+
id: string;
|
|
478
|
+
name: string;
|
|
479
|
+
description?: string | null;
|
|
480
|
+
deliverables?: Deliverable[];
|
|
481
|
+
}
|
|
482
|
+
/** Optional intermediate transition architecture between two target versions. */
|
|
483
|
+
export interface TargetTransition {
|
|
484
|
+
id: string;
|
|
485
|
+
name: string;
|
|
486
|
+
fromVersionId: string;
|
|
487
|
+
toVersionId: string;
|
|
488
|
+
description?: string | null;
|
|
489
|
+
gaps?: TargetGap[];
|
|
490
|
+
workPackages?: WorkPackage[];
|
|
491
|
+
}
|
|
492
|
+
/**
|
|
493
|
+
* A versioned, landscape-level To-Be target: the named target, its ordered versions, the status
|
|
494
|
+
* vocabulary and optional transition/gap shapes.
|
|
495
|
+
*/
|
|
496
|
+
export interface TargetArchitectureDocument {
|
|
497
|
+
contractVersion: typeof CONTRACT_VERSION;
|
|
498
|
+
kind: "target-architecture";
|
|
499
|
+
id: string;
|
|
500
|
+
name: string;
|
|
501
|
+
description?: string | null;
|
|
502
|
+
scope: TargetScope;
|
|
503
|
+
generator?: Generator | null;
|
|
504
|
+
versions: TargetVersion[];
|
|
505
|
+
transitions?: TargetTransition[];
|
|
506
|
+
}
|
package/dist/index.js
CHANGED
|
@@ -5,3 +5,46 @@
|
|
|
5
5
|
*/
|
|
6
6
|
/** The atlas-contracts schema major version. */
|
|
7
7
|
export const CONTRACT_VERSION = "1";
|
|
8
|
+
// --- Landscape share digest v2 (v1 remains unchanged) ---
|
|
9
|
+
export const LANDSCAPE_DIGEST_V2_VERSION = "2";
|
|
10
|
+
/** Run after JSON Schema validation. Does not grant sharing or consumption permission. */
|
|
11
|
+
export function validateLandscapeDigestV2(digest) {
|
|
12
|
+
const errors = [];
|
|
13
|
+
const items = new Map();
|
|
14
|
+
for (const item of digest.items) {
|
|
15
|
+
if (items.has(item.ref))
|
|
16
|
+
errors.push("duplicate-ref");
|
|
17
|
+
items.set(item.ref, item);
|
|
18
|
+
if (!digest.scope.kinds.includes(item.kind))
|
|
19
|
+
errors.push("item-outside-scope");
|
|
20
|
+
}
|
|
21
|
+
if (digest.relations === undefined)
|
|
22
|
+
return errors;
|
|
23
|
+
if (digest.scope.relations !== true)
|
|
24
|
+
errors.push("relations-not-enabled");
|
|
25
|
+
if (digest.relations.length > 20000)
|
|
26
|
+
errors.push("too-many-relations");
|
|
27
|
+
const edges = new Set();
|
|
28
|
+
const isSystem = (kind) => kind === "system" || kind === "application";
|
|
29
|
+
for (const edge of digest.relations) {
|
|
30
|
+
if (edge.from === edge.to)
|
|
31
|
+
errors.push("self-edge");
|
|
32
|
+
const endpoints = edge.kind === "integrates" ? [edge.from, edge.to].sort() : [edge.from, edge.to];
|
|
33
|
+
const key = JSON.stringify([edge.kind, ...endpoints]);
|
|
34
|
+
if (edges.has(key))
|
|
35
|
+
errors.push("duplicate-edge");
|
|
36
|
+
edges.add(key);
|
|
37
|
+
const source = items.get(edge.from);
|
|
38
|
+
const target = items.get(edge.to);
|
|
39
|
+
if (!source || !target) {
|
|
40
|
+
errors.push("dangling-ref");
|
|
41
|
+
continue;
|
|
42
|
+
}
|
|
43
|
+
const valid = edge.kind === "runsOn" ? isSystem(source.kind) && target.kind === "platform"
|
|
44
|
+
: edge.kind === "suppliedBy" ? source.kind !== "vendor" && target.kind === "vendor"
|
|
45
|
+
: edge.kind === "integrates" && isSystem(source.kind) && isSystem(target.kind);
|
|
46
|
+
if (!valid)
|
|
47
|
+
errors.push("invalid-relation-endpoints");
|
|
48
|
+
}
|
|
49
|
+
return errors;
|
|
50
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@vev-software/atlas-contracts",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.6",
|
|
4
4
|
"description": "Public, versioned data model and interop contracts for VEV Atlas — the catalogue asset model plus manual relationships, tags and the landscape import/export document.",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"type": "module",
|
|
@@ -16,6 +16,7 @@
|
|
|
16
16
|
},
|
|
17
17
|
"scripts": {
|
|
18
18
|
"build": "tsc -p tsconfig.json",
|
|
19
|
+
"test": "npm run build && tsc --ignoreConfig --noEmit --strict --target ES2022 --module nodenext test/landscape-digest-v2.types.ts && node --test test/*.test.mjs",
|
|
19
20
|
"prepack": "node scripts/prepack.mjs"
|
|
20
21
|
},
|
|
21
22
|
"devDependencies": {
|
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
"AssetKind": {
|
|
8
8
|
"type": "string",
|
|
9
9
|
"description": "The kind of asset held in the catalogue. The data-layer kinds (data-area, dataset, column) model the data architecture beneath a system, down to the column. The AI kinds (ai-service, ai-model) catalogue AI platforms and models as first-class assets.",
|
|
10
|
-
"enum": ["system", "application", "server", "infrastructure", "data-area", "dataset", "column", "ai-service", "ai-model"]
|
|
10
|
+
"enum": ["system", "application", "server", "infrastructure", "data-area", "dataset", "column", "ai-service", "ai-model", "integration", "business-process"]
|
|
11
11
|
},
|
|
12
12
|
"Lifecycle": {
|
|
13
13
|
"type": "string",
|
|
@@ -21,6 +21,49 @@
|
|
|
21
21
|
"maxLength": 128,
|
|
22
22
|
"pattern": "^[A-Za-z0-9][A-Za-z0-9._:-]*$"
|
|
23
23
|
},
|
|
24
|
+
"RelationshipId": {
|
|
25
|
+
"type": "string",
|
|
26
|
+
"description": "Stable identifier for a relationship. Never reused or re-meaning'd once published.",
|
|
27
|
+
"minLength": 1,
|
|
28
|
+
"maxLength": 128,
|
|
29
|
+
"pattern": "^[A-Za-z0-9][A-Za-z0-9._:-]*$"
|
|
30
|
+
},
|
|
31
|
+
"TargetScopeKind": {
|
|
32
|
+
"type": "string",
|
|
33
|
+
"description": "The landscape scope a Target Architecture governs.",
|
|
34
|
+
"enum": ["estate", "domain", "capability-area"]
|
|
35
|
+
},
|
|
36
|
+
"TargetVersionStatus": {
|
|
37
|
+
"type": "string",
|
|
38
|
+
"description": "Lifecycle status of a TargetVersion. A TargetArchitecture must carry exactly one active version.",
|
|
39
|
+
"enum": ["draft", "proposed", "approved", "active", "superseded", "rejected", "archived"]
|
|
40
|
+
},
|
|
41
|
+
"ChangeIntent": {
|
|
42
|
+
"type": "string",
|
|
43
|
+
"description": "How a target member changes the live estate.",
|
|
44
|
+
"enum": ["add", "change", "retire"]
|
|
45
|
+
},
|
|
46
|
+
"TargetScope": {
|
|
47
|
+
"type": "object",
|
|
48
|
+
"description": "What part of the estate a Target Architecture governs: the whole estate, one domain, or one capability area.",
|
|
49
|
+
"additionalProperties": false,
|
|
50
|
+
"required": ["kind"],
|
|
51
|
+
"properties": {
|
|
52
|
+
"kind": { "$ref": "#/$defs/TargetScopeKind" },
|
|
53
|
+
"ref": {
|
|
54
|
+
"type": ["string", "null"],
|
|
55
|
+
"description": "Optional opaque identifier of the scoped thing when the target does not cover the whole estate.",
|
|
56
|
+
"minLength": 1,
|
|
57
|
+
"maxLength": 128
|
|
58
|
+
},
|
|
59
|
+
"name": {
|
|
60
|
+
"type": ["string", "null"],
|
|
61
|
+
"description": "Optional human-readable scope name.",
|
|
62
|
+
"minLength": 1,
|
|
63
|
+
"maxLength": 256
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
},
|
|
24
67
|
"Tag": {
|
|
25
68
|
"type": "object",
|
|
26
69
|
"description": "Manual, lightweight classification. A key with an optional value.",
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
3
|
+
"$id": "https://contracts.vev.software/atlas/v1/landscape-digest.schema.json",
|
|
4
|
+
"title": "Atlas landscape share digest (v1)",
|
|
5
|
+
"description": "A minimized, versioned summary of an Atlas landscape for consented consumers. Push-only sharing surface: Atlas produces the digest and delivers it as a file or over an outbound HTTPS call; a consumer never pulls from the customer's network. Minimization is enforced by the schema (additionalProperties: false): no hostnames, IP addresses, server or discovery details, descriptions, attachments, people, column-level data or credentials. The digest is a summary for sharing — not the customer-owned portability export (landscape.schema.json) and not an import document. See docs/landscape-digest.md.",
|
|
6
|
+
"type": "object",
|
|
7
|
+
"additionalProperties": false,
|
|
8
|
+
"required": ["contractVersion", "digestId", "generatedAt", "sourceInstanceId", "sequence", "scope", "items"],
|
|
9
|
+
"properties": {
|
|
10
|
+
"contractVersion": {
|
|
11
|
+
"type": "string",
|
|
12
|
+
"description": "The atlas-contracts schema major version this digest conforms to.",
|
|
13
|
+
"const": "1"
|
|
14
|
+
},
|
|
15
|
+
"digestId": {
|
|
16
|
+
"type": "string",
|
|
17
|
+
"description": "Stable identifier for this digest. Opaque to consumers; unique per produced digest (a UUIDv7 is recommended).",
|
|
18
|
+
"minLength": 1,
|
|
19
|
+
"maxLength": 128,
|
|
20
|
+
"pattern": "^[A-Za-z0-9][A-Za-z0-9._:-]*$"
|
|
21
|
+
},
|
|
22
|
+
"generatedAt": {
|
|
23
|
+
"type": "string",
|
|
24
|
+
"description": "When the digest was produced (RFC 3339).",
|
|
25
|
+
"format": "date-time"
|
|
26
|
+
},
|
|
27
|
+
"sourceInstanceId": {
|
|
28
|
+
"type": "string",
|
|
29
|
+
"description": "Opaque identifier of the producing Atlas instance. Consumers use it to scope sequence/replay state; it carries no tenant or customer semantics. A source that resets its sequence must mint a new sourceInstanceId.",
|
|
30
|
+
"minLength": 1,
|
|
31
|
+
"maxLength": 128
|
|
32
|
+
},
|
|
33
|
+
"sequence": {
|
|
34
|
+
"type": "integer",
|
|
35
|
+
"description": "Monotonic per sourceInstanceId: the first digest is sequence 1, each later digest is strictly higher (gaps allowed). Consumers must reject a sequence at or below the last one accepted for the same source (replay protection).",
|
|
36
|
+
"minimum": 1
|
|
37
|
+
},
|
|
38
|
+
"scope": {
|
|
39
|
+
"$ref": "#/$defs/DigestScope"
|
|
40
|
+
},
|
|
41
|
+
"items": {
|
|
42
|
+
"type": "array",
|
|
43
|
+
"description": "The minimized landscape items. Every item's kind must be a member of scope.kinds (checked by the conformance kit; JSON Schema cannot express it).",
|
|
44
|
+
"items": { "$ref": "#/$defs/DigestItem" }
|
|
45
|
+
}
|
|
46
|
+
},
|
|
47
|
+
"$defs": {
|
|
48
|
+
"DigestKind": {
|
|
49
|
+
"type": "string",
|
|
50
|
+
"description": "Digest classification of an item. Deliberately narrower than the catalogue AssetKind: platform folds in server/infrastructure/AI substrate, vendor is a producer-derived summary entry, and data-layer kinds (data-area, dataset, column) are excluded from digests.",
|
|
51
|
+
"enum": ["system", "application", "platform", "vendor"]
|
|
52
|
+
},
|
|
53
|
+
"PlannedChange": {
|
|
54
|
+
"type": "string",
|
|
55
|
+
"description": "The intended change for an item, taken from the Target Architecture contract's change intents (add/change/retire) and extended with replace for digest purposes. The shared ChangeIntent vocabulary is not modified.",
|
|
56
|
+
"enum": ["add", "change", "replace", "retire"]
|
|
57
|
+
},
|
|
58
|
+
"DigestScope": {
|
|
59
|
+
"type": "object",
|
|
60
|
+
"description": "Which asset kinds and tags were included in this digest. Both arrays are required and may be empty: an empty kinds array means no kinds were included, and an empty tags array means no tag filter was applied (tag matching is OR within the list, AND with the kind filter).",
|
|
61
|
+
"additionalProperties": false,
|
|
62
|
+
"required": ["kinds", "tags"],
|
|
63
|
+
"properties": {
|
|
64
|
+
"kinds": {
|
|
65
|
+
"type": "array",
|
|
66
|
+
"description": "The digest item kinds included.",
|
|
67
|
+
"items": { "$ref": "#/$defs/DigestKind" },
|
|
68
|
+
"uniqueItems": true
|
|
69
|
+
},
|
|
70
|
+
"tags": {
|
|
71
|
+
"type": "array",
|
|
72
|
+
"description": "The tag filters that qualified inclusion (key, and value when present).",
|
|
73
|
+
"items": { "$ref": "common.schema.json#/$defs/Tag" }
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
},
|
|
77
|
+
"DigestItem": {
|
|
78
|
+
"type": "object",
|
|
79
|
+
"description": "One minimized landscape item. Carries only the fields a consented consumer needs: classification, name, lifecycle, and optional vendor / planned change / integration count. Everything else (hostnames, IPs, server and discovery details, descriptions, attachments, people, column-level data, credentials) is forbidden by additionalProperties: false.",
|
|
80
|
+
"additionalProperties": false,
|
|
81
|
+
"required": ["kind", "name", "lifecycle"],
|
|
82
|
+
"properties": {
|
|
83
|
+
"kind": { "$ref": "#/$defs/DigestKind" },
|
|
84
|
+
"name": {
|
|
85
|
+
"type": "string",
|
|
86
|
+
"description": "The item's catalogue name.",
|
|
87
|
+
"minLength": 1,
|
|
88
|
+
"maxLength": 256
|
|
89
|
+
},
|
|
90
|
+
"lifecycle": { "$ref": "common.schema.json#/$defs/Lifecycle" },
|
|
91
|
+
"vendor": {
|
|
92
|
+
"type": ["string", "null"],
|
|
93
|
+
"description": "Optional vendor of the item, when known.",
|
|
94
|
+
"maxLength": 128
|
|
95
|
+
},
|
|
96
|
+
"plannedChange": { "$ref": "#/$defs/PlannedChange" },
|
|
97
|
+
"integrationCount": {
|
|
98
|
+
"type": "integer",
|
|
99
|
+
"description": "Optional count of integrations the item participates in. A count only — never the identities of the integrated parties.",
|
|
100
|
+
"minimum": 0
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
}
|
|
@@ -0,0 +1,321 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
3
|
+
"$id": "https://contracts.vev.software/atlas/v1/target-architecture.schema.json",
|
|
4
|
+
"title": "Atlas Target Architecture document (v1)",
|
|
5
|
+
"description": "A versioned, landscape-level To-Be target: the named target, its ordered versions, the status vocabulary and optional transition/gap shapes. Product-native truth only: ArchiMate remains a peripheral mapping. See atlas-contracts#35.",
|
|
6
|
+
"type": "object",
|
|
7
|
+
"additionalProperties": false,
|
|
8
|
+
"required": ["contractVersion", "kind", "id", "name", "scope", "versions"],
|
|
9
|
+
"properties": {
|
|
10
|
+
"contractVersion": {
|
|
11
|
+
"type": "string",
|
|
12
|
+
"description": "The atlas-contracts schema major version this document conforms to.",
|
|
13
|
+
"const": "1"
|
|
14
|
+
},
|
|
15
|
+
"kind": {
|
|
16
|
+
"type": "string",
|
|
17
|
+
"const": "target-architecture"
|
|
18
|
+
},
|
|
19
|
+
"id": {
|
|
20
|
+
"type": "string",
|
|
21
|
+
"description": "Stable identifier for the target architecture.",
|
|
22
|
+
"minLength": 1,
|
|
23
|
+
"maxLength": 128,
|
|
24
|
+
"pattern": "^[A-Za-z0-9][A-Za-z0-9._:-]*$"
|
|
25
|
+
},
|
|
26
|
+
"name": {
|
|
27
|
+
"type": "string",
|
|
28
|
+
"description": "Human-readable name of the target architecture.",
|
|
29
|
+
"minLength": 1,
|
|
30
|
+
"maxLength": 256
|
|
31
|
+
},
|
|
32
|
+
"description": {
|
|
33
|
+
"type": ["string", "null"],
|
|
34
|
+
"maxLength": 4096
|
|
35
|
+
},
|
|
36
|
+
"scope": {
|
|
37
|
+
"$ref": "common.schema.json#/$defs/TargetScope"
|
|
38
|
+
},
|
|
39
|
+
"generator": {
|
|
40
|
+
"description": "Optional provenance: what produced this target document.",
|
|
41
|
+
"oneOf": [
|
|
42
|
+
{ "type": "null" },
|
|
43
|
+
{ "$ref": "common.schema.json#/$defs/Generator" }
|
|
44
|
+
]
|
|
45
|
+
},
|
|
46
|
+
"versions": {
|
|
47
|
+
"type": "array",
|
|
48
|
+
"minItems": 1,
|
|
49
|
+
"items": { "$ref": "#/$defs/TargetVersion" },
|
|
50
|
+
"contains": {
|
|
51
|
+
"type": "object",
|
|
52
|
+
"required": ["status"],
|
|
53
|
+
"properties": {
|
|
54
|
+
"status": { "const": "active" }
|
|
55
|
+
}
|
|
56
|
+
},
|
|
57
|
+
"minContains": 1,
|
|
58
|
+
"maxContains": 1
|
|
59
|
+
},
|
|
60
|
+
"transitions": {
|
|
61
|
+
"type": "array",
|
|
62
|
+
"items": { "$ref": "#/$defs/TargetTransition" },
|
|
63
|
+
"default": []
|
|
64
|
+
}
|
|
65
|
+
},
|
|
66
|
+
"$defs": {
|
|
67
|
+
"TargetAssetMember": {
|
|
68
|
+
"type": "object",
|
|
69
|
+
"description": "One asset-level intended change in a TargetVersion or transition/gap step.",
|
|
70
|
+
"additionalProperties": false,
|
|
71
|
+
"required": ["intent"],
|
|
72
|
+
"properties": {
|
|
73
|
+
"intent": { "$ref": "common.schema.json#/$defs/ChangeIntent" },
|
|
74
|
+
"assetRef": {
|
|
75
|
+
"oneOf": [
|
|
76
|
+
{ "type": "null" },
|
|
77
|
+
{ "$ref": "common.schema.json#/$defs/AssetId" }
|
|
78
|
+
]
|
|
79
|
+
},
|
|
80
|
+
"asset": {
|
|
81
|
+
"oneOf": [
|
|
82
|
+
{ "type": "null" },
|
|
83
|
+
{ "$ref": "asset.schema.json" }
|
|
84
|
+
]
|
|
85
|
+
},
|
|
86
|
+
"rationale": {
|
|
87
|
+
"type": ["string", "null"],
|
|
88
|
+
"maxLength": 4096
|
|
89
|
+
}
|
|
90
|
+
},
|
|
91
|
+
"allOf": [
|
|
92
|
+
{
|
|
93
|
+
"if": { "properties": { "intent": { "const": "add" } }, "required": ["intent"] },
|
|
94
|
+
"then": { "required": ["asset"] }
|
|
95
|
+
},
|
|
96
|
+
{
|
|
97
|
+
"if": { "properties": { "intent": { "const": "change" } }, "required": ["intent"] },
|
|
98
|
+
"then": { "required": ["assetRef", "asset"] }
|
|
99
|
+
},
|
|
100
|
+
{
|
|
101
|
+
"if": { "properties": { "intent": { "const": "retire" } }, "required": ["intent"] },
|
|
102
|
+
"then": { "required": ["assetRef"] }
|
|
103
|
+
}
|
|
104
|
+
]
|
|
105
|
+
},
|
|
106
|
+
"TargetRelationshipMember": {
|
|
107
|
+
"type": "object",
|
|
108
|
+
"description": "One relationship-level intended change in a TargetVersion or transition/gap step.",
|
|
109
|
+
"additionalProperties": false,
|
|
110
|
+
"required": ["intent"],
|
|
111
|
+
"properties": {
|
|
112
|
+
"intent": { "$ref": "common.schema.json#/$defs/ChangeIntent" },
|
|
113
|
+
"relationshipRef": {
|
|
114
|
+
"oneOf": [
|
|
115
|
+
{ "type": "null" },
|
|
116
|
+
{ "$ref": "common.schema.json#/$defs/RelationshipId" }
|
|
117
|
+
]
|
|
118
|
+
},
|
|
119
|
+
"relationship": {
|
|
120
|
+
"oneOf": [
|
|
121
|
+
{ "type": "null" },
|
|
122
|
+
{ "$ref": "relationship.schema.json" }
|
|
123
|
+
]
|
|
124
|
+
},
|
|
125
|
+
"rationale": {
|
|
126
|
+
"type": ["string", "null"],
|
|
127
|
+
"maxLength": 4096
|
|
128
|
+
}
|
|
129
|
+
},
|
|
130
|
+
"allOf": [
|
|
131
|
+
{
|
|
132
|
+
"if": { "properties": { "intent": { "const": "add" } }, "required": ["intent"] },
|
|
133
|
+
"then": { "required": ["relationship"] }
|
|
134
|
+
},
|
|
135
|
+
{
|
|
136
|
+
"if": { "properties": { "intent": { "const": "change" } }, "required": ["intent"] },
|
|
137
|
+
"then": { "required": ["relationshipRef", "relationship"] }
|
|
138
|
+
},
|
|
139
|
+
{
|
|
140
|
+
"if": { "properties": { "intent": { "const": "retire" } }, "required": ["intent"] },
|
|
141
|
+
"then": { "required": ["relationshipRef"] }
|
|
142
|
+
}
|
|
143
|
+
]
|
|
144
|
+
},
|
|
145
|
+
"TargetVersion": {
|
|
146
|
+
"type": "object",
|
|
147
|
+
"description": "One ordered To-Be version (ArchiMate Plateau analogue) within a Target Architecture.",
|
|
148
|
+
"additionalProperties": false,
|
|
149
|
+
"required": ["id", "name", "sequence", "status"],
|
|
150
|
+
"properties": {
|
|
151
|
+
"id": {
|
|
152
|
+
"type": "string",
|
|
153
|
+
"minLength": 1,
|
|
154
|
+
"maxLength": 128,
|
|
155
|
+
"pattern": "^[A-Za-z0-9][A-Za-z0-9._:-]*$"
|
|
156
|
+
},
|
|
157
|
+
"name": {
|
|
158
|
+
"type": "string",
|
|
159
|
+
"minLength": 1,
|
|
160
|
+
"maxLength": 256
|
|
161
|
+
},
|
|
162
|
+
"sequence": {
|
|
163
|
+
"type": "integer",
|
|
164
|
+
"minimum": 1
|
|
165
|
+
},
|
|
166
|
+
"status": { "$ref": "common.schema.json#/$defs/TargetVersionStatus" },
|
|
167
|
+
"rationale": {
|
|
168
|
+
"type": ["string", "null"],
|
|
169
|
+
"maxLength": 4096
|
|
170
|
+
},
|
|
171
|
+
"intendedHorizon": {
|
|
172
|
+
"type": ["string", "null"],
|
|
173
|
+
"maxLength": 128
|
|
174
|
+
},
|
|
175
|
+
"effectiveDate": {
|
|
176
|
+
"type": ["string", "null"],
|
|
177
|
+
"format": "date"
|
|
178
|
+
},
|
|
179
|
+
"assetChanges": {
|
|
180
|
+
"type": "array",
|
|
181
|
+
"items": { "$ref": "#/$defs/TargetAssetMember" },
|
|
182
|
+
"default": []
|
|
183
|
+
},
|
|
184
|
+
"relationshipChanges": {
|
|
185
|
+
"type": "array",
|
|
186
|
+
"items": { "$ref": "#/$defs/TargetRelationshipMember" },
|
|
187
|
+
"default": []
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
},
|
|
191
|
+
"TargetGap": {
|
|
192
|
+
"type": "object",
|
|
193
|
+
"description": "An optional intermediate gap slice between two target versions.",
|
|
194
|
+
"additionalProperties": false,
|
|
195
|
+
"required": ["id", "name"],
|
|
196
|
+
"properties": {
|
|
197
|
+
"id": {
|
|
198
|
+
"type": "string",
|
|
199
|
+
"minLength": 1,
|
|
200
|
+
"maxLength": 128,
|
|
201
|
+
"pattern": "^[A-Za-z0-9][A-Za-z0-9._:-]*$"
|
|
202
|
+
},
|
|
203
|
+
"name": {
|
|
204
|
+
"type": "string",
|
|
205
|
+
"minLength": 1,
|
|
206
|
+
"maxLength": 256
|
|
207
|
+
},
|
|
208
|
+
"description": {
|
|
209
|
+
"type": ["string", "null"],
|
|
210
|
+
"maxLength": 4096
|
|
211
|
+
},
|
|
212
|
+
"assetChanges": {
|
|
213
|
+
"type": "array",
|
|
214
|
+
"items": { "$ref": "#/$defs/TargetAssetMember" },
|
|
215
|
+
"default": []
|
|
216
|
+
},
|
|
217
|
+
"relationshipChanges": {
|
|
218
|
+
"type": "array",
|
|
219
|
+
"items": { "$ref": "#/$defs/TargetRelationshipMember" },
|
|
220
|
+
"default": []
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
},
|
|
224
|
+
"Deliverable": {
|
|
225
|
+
"type": "object",
|
|
226
|
+
"description": "One deliverable within a transition work package.",
|
|
227
|
+
"additionalProperties": false,
|
|
228
|
+
"required": ["id", "name"],
|
|
229
|
+
"properties": {
|
|
230
|
+
"id": {
|
|
231
|
+
"type": "string",
|
|
232
|
+
"minLength": 1,
|
|
233
|
+
"maxLength": 128,
|
|
234
|
+
"pattern": "^[A-Za-z0-9][A-Za-z0-9._:-]*$"
|
|
235
|
+
},
|
|
236
|
+
"name": {
|
|
237
|
+
"type": "string",
|
|
238
|
+
"minLength": 1,
|
|
239
|
+
"maxLength": 256
|
|
240
|
+
},
|
|
241
|
+
"description": {
|
|
242
|
+
"type": ["string", "null"],
|
|
243
|
+
"maxLength": 4096
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
},
|
|
247
|
+
"WorkPackage": {
|
|
248
|
+
"type": "object",
|
|
249
|
+
"description": "A transition work package grouping one or more deliverables.",
|
|
250
|
+
"additionalProperties": false,
|
|
251
|
+
"required": ["id", "name"],
|
|
252
|
+
"properties": {
|
|
253
|
+
"id": {
|
|
254
|
+
"type": "string",
|
|
255
|
+
"minLength": 1,
|
|
256
|
+
"maxLength": 128,
|
|
257
|
+
"pattern": "^[A-Za-z0-9][A-Za-z0-9._:-]*$"
|
|
258
|
+
},
|
|
259
|
+
"name": {
|
|
260
|
+
"type": "string",
|
|
261
|
+
"minLength": 1,
|
|
262
|
+
"maxLength": 256
|
|
263
|
+
},
|
|
264
|
+
"description": {
|
|
265
|
+
"type": ["string", "null"],
|
|
266
|
+
"maxLength": 4096
|
|
267
|
+
},
|
|
268
|
+
"deliverables": {
|
|
269
|
+
"type": "array",
|
|
270
|
+
"items": { "$ref": "#/$defs/Deliverable" },
|
|
271
|
+
"default": []
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
},
|
|
275
|
+
"TargetTransition": {
|
|
276
|
+
"type": "object",
|
|
277
|
+
"description": "Optional intermediate transition architecture between two versions.",
|
|
278
|
+
"additionalProperties": false,
|
|
279
|
+
"required": ["id", "name", "fromVersionId", "toVersionId"],
|
|
280
|
+
"properties": {
|
|
281
|
+
"id": {
|
|
282
|
+
"type": "string",
|
|
283
|
+
"minLength": 1,
|
|
284
|
+
"maxLength": 128,
|
|
285
|
+
"pattern": "^[A-Za-z0-9][A-Za-z0-9._:-]*$"
|
|
286
|
+
},
|
|
287
|
+
"name": {
|
|
288
|
+
"type": "string",
|
|
289
|
+
"minLength": 1,
|
|
290
|
+
"maxLength": 256
|
|
291
|
+
},
|
|
292
|
+
"description": {
|
|
293
|
+
"type": ["string", "null"],
|
|
294
|
+
"maxLength": 4096
|
|
295
|
+
},
|
|
296
|
+
"fromVersionId": {
|
|
297
|
+
"type": "string",
|
|
298
|
+
"minLength": 1,
|
|
299
|
+
"maxLength": 128,
|
|
300
|
+
"pattern": "^[A-Za-z0-9][A-Za-z0-9._:-]*$"
|
|
301
|
+
},
|
|
302
|
+
"toVersionId": {
|
|
303
|
+
"type": "string",
|
|
304
|
+
"minLength": 1,
|
|
305
|
+
"maxLength": 128,
|
|
306
|
+
"pattern": "^[A-Za-z0-9][A-Za-z0-9._:-]*$"
|
|
307
|
+
},
|
|
308
|
+
"gaps": {
|
|
309
|
+
"type": "array",
|
|
310
|
+
"items": { "$ref": "#/$defs/TargetGap" },
|
|
311
|
+
"default": []
|
|
312
|
+
},
|
|
313
|
+
"workPackages": {
|
|
314
|
+
"type": "array",
|
|
315
|
+
"items": { "$ref": "#/$defs/WorkPackage" },
|
|
316
|
+
"default": []
|
|
317
|
+
}
|
|
318
|
+
}
|
|
319
|
+
}
|
|
320
|
+
}
|
|
321
|
+
}
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
3
|
+
"$id": "https://contracts.vev.software/atlas/v2/landscape-digest.schema.json",
|
|
4
|
+
"title": "Atlas landscape share digest (v2)",
|
|
5
|
+
"description": "Minimized push-only digest with per-digest refs, optional layer and opt-in relations. Payload scope records sender intent, never authorization. Cross-item invariants require semantic validation; see docs/landscape-digest.md.",
|
|
6
|
+
"type": "object",
|
|
7
|
+
"additionalProperties": false,
|
|
8
|
+
"required": ["contractVersion", "digestId", "generatedAt", "sourceInstanceId", "sequence", "scope", "items"],
|
|
9
|
+
"properties": {
|
|
10
|
+
"contractVersion": { "const": "2" },
|
|
11
|
+
"digestId": { "$ref": "../v1/landscape-digest.schema.json#/properties/digestId" },
|
|
12
|
+
"generatedAt": { "$ref": "../v1/landscape-digest.schema.json#/properties/generatedAt" },
|
|
13
|
+
"sourceInstanceId": { "$ref": "../v1/landscape-digest.schema.json#/properties/sourceInstanceId" },
|
|
14
|
+
"sequence": { "$ref": "../v1/landscape-digest.schema.json#/properties/sequence" },
|
|
15
|
+
"scope": { "$ref": "#/$defs/Scope" },
|
|
16
|
+
"items": { "type": "array", "items": { "$ref": "#/$defs/Item" } },
|
|
17
|
+
"relations": {
|
|
18
|
+
"type": "array",
|
|
19
|
+
"maxItems": 20000,
|
|
20
|
+
"uniqueItems": true,
|
|
21
|
+
"items": { "$ref": "#/$defs/Relation" }
|
|
22
|
+
}
|
|
23
|
+
},
|
|
24
|
+
"if": { "required": ["relations"] },
|
|
25
|
+
"then": {
|
|
26
|
+
"properties": {
|
|
27
|
+
"scope": { "required": ["relations"], "properties": { "relations": { "const": true } } }
|
|
28
|
+
}
|
|
29
|
+
},
|
|
30
|
+
"$defs": {
|
|
31
|
+
"Ref": {
|
|
32
|
+
"type": "string",
|
|
33
|
+
"minLength": 1,
|
|
34
|
+
"maxLength": 64,
|
|
35
|
+
"description": "Opaque, unique within this digest and meaningless outside it. Mint anew per digest; never an asset id or stable identifier."
|
|
36
|
+
},
|
|
37
|
+
"Scope": {
|
|
38
|
+
"type": "object",
|
|
39
|
+
"additionalProperties": false,
|
|
40
|
+
"required": ["kinds", "tags"],
|
|
41
|
+
"properties": {
|
|
42
|
+
"kinds": { "$ref": "../v1/landscape-digest.schema.json#/$defs/DigestScope/properties/kinds" },
|
|
43
|
+
"tags": { "$ref": "../v1/landscape-digest.schema.json#/$defs/DigestScope/properties/tags" },
|
|
44
|
+
"relations": { "type": "boolean", "default": false }
|
|
45
|
+
}
|
|
46
|
+
},
|
|
47
|
+
"Item": {
|
|
48
|
+
"type": "object",
|
|
49
|
+
"additionalProperties": false,
|
|
50
|
+
"required": ["ref", "kind", "name", "lifecycle"],
|
|
51
|
+
"properties": {
|
|
52
|
+
"ref": { "$ref": "#/$defs/Ref" },
|
|
53
|
+
"kind": { "$ref": "../v1/landscape-digest.schema.json#/$defs/DigestKind" },
|
|
54
|
+
"name": { "$ref": "../v1/landscape-digest.schema.json#/$defs/DigestItem/properties/name" },
|
|
55
|
+
"lifecycle": { "$ref": "../v1/common.schema.json#/$defs/Lifecycle" },
|
|
56
|
+
"vendor": { "$ref": "../v1/landscape-digest.schema.json#/$defs/DigestItem/properties/vendor" },
|
|
57
|
+
"plannedChange": { "$ref": "../v1/landscape-digest.schema.json#/$defs/PlannedChange" },
|
|
58
|
+
"integrationCount": { "$ref": "../v1/landscape-digest.schema.json#/$defs/DigestItem/properties/integrationCount" },
|
|
59
|
+
"layer": { "enum": ["business", "application", "data", "infrastructure"] }
|
|
60
|
+
}
|
|
61
|
+
},
|
|
62
|
+
"Relation": {
|
|
63
|
+
"type": "object",
|
|
64
|
+
"additionalProperties": false,
|
|
65
|
+
"required": ["from", "to", "kind"],
|
|
66
|
+
"properties": {
|
|
67
|
+
"from": { "$ref": "#/$defs/Ref" },
|
|
68
|
+
"to": { "$ref": "#/$defs/Ref" },
|
|
69
|
+
"kind": { "enum": ["runsOn", "suppliedBy", "integrates"] }
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
}
|