From 2263e0c7aef74446a7ca6e8b2c5c8418e37c48ee Mon Sep 17 00:00:00 2001 From: Matthew Bain <66839492+rocketstack-matt@users.noreply.github.com> Date: Tue, 27 Oct 2026 10:52:39 +0000 Subject: [PATCH 1/3] ci: keep the drafts on calm.finos.org This reverts commit 9fb16cc96dfd04f7d42f4c4ec0a20dcc6f411ce7 (#3511). --- .github/workflows/s3-docs-sync.yml | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/.github/workflows/s3-docs-sync.yml b/.github/workflows/s3-docs-sync.yml index e7806bf91b..53060ced47 100644 --- a/.github/workflows/s3-docs-sync.yml +++ b/.github/workflows/s3-docs-sync.yml @@ -7,7 +7,7 @@ on: workflow_dispatch: {} push: branches: ['main'] - paths: ['docs/**', '.github/workflows/s3-docs-sync.yml'] + paths: ['docs/**'] jobs: sync-to-s3: @@ -43,10 +43,9 @@ jobs: aws-secret-access-key: ${{ secrets.AWS_S3_SECRET_ACCESS_KEY }} aws-region: us-east-1 - # draft/2025-03 stays because OpenRewrite's rewrite-prethink declares it: #3507. - name: Sync docs to S3 run: | - aws s3 sync build s3://calm.finos.org/ --delete --include "*" --exclude "advent/*" --exclude "draft/2025-03/*" --exclude "interfaces/*" --exclude "release/*" --exclude "samples/*" --exclude "standards/*" --exclude "video/*" --exclude "workshop/*" + aws s3 sync build s3://calm.finos.org/ --delete --include "*" --exclude "advent/*" --exclude "draft/*" --exclude "interfaces/*" --exclude "release/*" --exclude "samples/*" --exclude "standards/*" --exclude "video/*" --exclude "workshop/*" working-directory: "docs" - name: Invalidate CloudFront cache From 493082006f7dd7fb720072539423f65ad21ac03e Mon Sep 17 00:00:00 2001 From: Matthew Bain <66839492+rocketstack-matt@users.noreply.github.com> Date: Tue, 27 Oct 2026 11:13:36 +0000 Subject: [PATCH 2/3] fix(cli): bundle the 2026-03 draft again --- AGENTS.md | 4 +- calm/draft/2026-03/README.md | 21 ++ calm/draft/2026-03/meta/calm-timeline.json | 23 ++ calm/draft/2026-03/meta/calm.json | 23 ++ .../2026-03/meta/control-requirement.json | 33 ++ calm/draft/2026-03/meta/control.json | 64 ++++ calm/draft/2026-03/meta/core.json | 328 ++++++++++++++++++ calm/draft/2026-03/meta/decorators.json | 65 ++++ calm/draft/2026-03/meta/evidence.json | 37 ++ calm/draft/2026-03/meta/flow.json | 78 +++++ calm/draft/2026-03/meta/interface.json | 60 ++++ calm/draft/2026-03/meta/timeline.json | 57 +++ calm/draft/2026-03/meta/units.json | 115 ++++++ calm/draft/2026-03/standards/README.md | 82 +++++ .../2026-03/standards/deployment/README.md | 92 +++++ .../deployment.decorator.standard.json | 75 ++++ .../standards/deployment/examples/README.md | 104 ++++++ .../examples/failed-deployment.decorator.json | 21 ++ .../kubernetes-deployment.decorator.json | 27 ++ .../multi-target-deployment.decorator.json | 28 ++ .../examples/simple-deployment.decorator.json | 21 ++ .../kubernetes.decorator.standard.json | 49 +++ cli/AGENTS.md | 3 +- cli/package.json | 2 +- cli/src/cli.e2e.spec.ts | 16 + 25 files changed, 1424 insertions(+), 4 deletions(-) create mode 100644 calm/draft/2026-03/README.md create mode 100644 calm/draft/2026-03/meta/calm-timeline.json create mode 100644 calm/draft/2026-03/meta/calm.json create mode 100644 calm/draft/2026-03/meta/control-requirement.json create mode 100644 calm/draft/2026-03/meta/control.json create mode 100644 calm/draft/2026-03/meta/core.json create mode 100644 calm/draft/2026-03/meta/decorators.json create mode 100644 calm/draft/2026-03/meta/evidence.json create mode 100644 calm/draft/2026-03/meta/flow.json create mode 100644 calm/draft/2026-03/meta/interface.json create mode 100644 calm/draft/2026-03/meta/timeline.json create mode 100644 calm/draft/2026-03/meta/units.json create mode 100644 calm/draft/2026-03/standards/README.md create mode 100644 calm/draft/2026-03/standards/deployment/README.md create mode 100644 calm/draft/2026-03/standards/deployment/deployment.decorator.standard.json create mode 100644 calm/draft/2026-03/standards/deployment/examples/README.md create mode 100644 calm/draft/2026-03/standards/deployment/examples/failed-deployment.decorator.json create mode 100644 calm/draft/2026-03/standards/deployment/examples/kubernetes-deployment.decorator.json create mode 100644 calm/draft/2026-03/standards/deployment/examples/multi-target-deployment.decorator.json create mode 100644 calm/draft/2026-03/standards/deployment/examples/simple-deployment.decorator.json create mode 100644 calm/draft/2026-03/standards/deployment/kubernetes.decorator.standard.json diff --git a/AGENTS.md b/AGENTS.md index 93fe8047d0..669714c956 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -32,7 +32,7 @@ This is the **FINOS Architecture as Code** monorepo. It holds the tools for the | Repository | Owns | Send here | |---|---|---| | [architecture-as-code](https://github.com/finos/architecture-as-code) | This repository: the tools, the docs site and CALM Lab. The tools get the schema from the `@finos/calm-schema` npm package (see [CALM Schema Updates](#calm-schema-updates)) | Tool changes, the documentation site, examples that the tools use | -| [calm-schema](https://github.com/finos/calm-schema) | The CALM Meta Schema, its tests and `CHANGELOG.md`, the interface definitions. Published to npm and to `calm.finos.org` | Schema changes, schema releases, interface definitions. Do not change a schema here: `calm/` holds only CALM's own architecture | +| [calm-schema](https://github.com/finos/calm-schema) | The CALM Meta Schema, its tests and `CHANGELOG.md`, the interface definitions. Published to npm and to `calm.finos.org` | Schema changes, schema releases, interface definitions. Do not change a schema here: `calm/` holds only CALM's own architecture and the 2026-03 draft, which [#3533](https://github.com/finos/architecture-as-code/issues/3533) deletes | | [calm-governance](https://github.com/finos/calm-governance) | Project-wide governance, the Maintainer roster, contribution guidelines, the Code of Conduct. No code | Governance and roster changes | Clone the three repositories side by side, with the same parent directory. Start the agent here and @@ -46,7 +46,7 @@ against `finos/`. ``` architecture-as-code/ -├── calm/ # CALM's own architecture +├── calm/ # CALM's own architecture and the 2026-03 schema draft ├── cli/ # TypeScript CLI (@finos/calm-cli) ├── calm-hub/ # Java/Quarkus REST API backend ├── calm-hub-ui/ # React frontend for CALM Hub diff --git a/calm/draft/2026-03/README.md b/calm/draft/2026-03/README.md new file mode 100644 index 0000000000..b92a80c846 --- /dev/null +++ b/calm/draft/2026-03/README.md @@ -0,0 +1,21 @@ +# CALM Draft 2026-03 + +This directory contains draft schemas and specifications for the CALM (Common Architecture Language Model) 1.3 release. + +## Deployment Decorator Schema + +This draft includes the deployment decorator schema, which provides a standardized way to annotate CALM architectures with deployment-related metadata. + +### Related Issues + +- **[#1908](https://github.com/finos/architecture-as-code/issues/1908)** - Deployment decorator schema +- **[#2183](https://github.com/finos/architecture-as-code/issues/2183)** - Update the deployment decorator to include target-type + +### Contents + +- `meta/` - Core CALM meta-schemas +- `standards/deployment/` - Deployment decorator standard and examples + +## Status + +**Draft** - This specification is under active development and subject to change. diff --git a/calm/draft/2026-03/meta/calm-timeline.json b/calm/draft/2026-03/meta/calm-timeline.json new file mode 100644 index 0000000000..f9eea1276a --- /dev/null +++ b/calm/draft/2026-03/meta/calm-timeline.json @@ -0,0 +1,23 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "https://calm.finos.org/draft/2026-03/meta/calm-timeline.json", + "$vocabulary": { + "https://json-schema.org/draft/2020-12/vocab/core": true, + "https://json-schema.org/draft/2020-12/vocab/applicator": true, + "https://json-schema.org/draft/2020-12/vocab/validation": true, + "https://json-schema.org/draft/2020-12/vocab/meta-data": true, + "https://json-schema.org/draft/2020-12/vocab/format-annotation": true, + "https://json-schema.org/draft/2020-12/vocab/content": true, + "https://calm.finos.org/draft/2026-03/meta/core.json": true + }, + "$dynamicAnchor": "meta", + "title": "Common Architecture Language Model (CALM) Timelines Schema", + "allOf": [ + { + "$ref": "https://json-schema.org/draft/2020-12/schema" + }, + { + "$ref": "https://calm.finos.org/draft/2026-03/meta/timeline.json" + } + ] +} \ No newline at end of file diff --git a/calm/draft/2026-03/meta/calm.json b/calm/draft/2026-03/meta/calm.json new file mode 100644 index 0000000000..ed089b2adc --- /dev/null +++ b/calm/draft/2026-03/meta/calm.json @@ -0,0 +1,23 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "https://calm.finos.org/draft/2026-03/meta/calm.json", + "$vocabulary": { + "https://json-schema.org/draft/2020-12/vocab/core": true, + "https://json-schema.org/draft/2020-12/vocab/applicator": true, + "https://json-schema.org/draft/2020-12/vocab/validation": true, + "https://json-schema.org/draft/2020-12/vocab/meta-data": true, + "https://json-schema.org/draft/2020-12/vocab/format-annotation": true, + "https://json-schema.org/draft/2020-12/vocab/content": true, + "https://calm.finos.org/draft/2026-03/meta/core.json": true + }, + "$dynamicAnchor": "meta", + "title": "Common Architecture Language Model (CALM) Schema", + "allOf": [ + { + "$ref": "https://json-schema.org/draft/2020-12/schema" + }, + { + "$ref": "https://calm.finos.org/draft/2026-03/meta/core.json" + } + ] +} \ No newline at end of file diff --git a/calm/draft/2026-03/meta/control-requirement.json b/calm/draft/2026-03/meta/control-requirement.json new file mode 100644 index 0000000000..b61c7161da --- /dev/null +++ b/calm/draft/2026-03/meta/control-requirement.json @@ -0,0 +1,33 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "https://calm.finos.org/draft/2026-03/meta/control-requirement.json", + "title": "Common Architecture Language Model Control Requirement", + "description": "Schema for defining control requirements within the Common Architecture Language Model.", + "type": "object", + "properties": { + "control-id": { + "type": "string", + "description": "The unique identifier of this control, which has the potential to be used for linking evidence" + }, + "name": { + "type": "string", + "description": "The name of the control requirement that provides contextual meaning within a given domain" + }, + "description": { + "type": "string", + "description": "A more detailed description of the control and information on what a developer needs to consider" + } + }, + "required": [ + "control-id", + "name", + "description" + ], + "examples": [ + { + "control-id": "CR-001", + "name": "Access Control", + "description": "Ensure that access to sensitive information is restricted." + } + ] +} \ No newline at end of file diff --git a/calm/draft/2026-03/meta/control.json b/calm/draft/2026-03/meta/control.json new file mode 100644 index 0000000000..9802149f95 --- /dev/null +++ b/calm/draft/2026-03/meta/control.json @@ -0,0 +1,64 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "https://calm.finos.org/draft/2026-03/meta/control.json", + "title": "Common Architecture Language Model Controls", + "description": "Controls model requirements for domains. For example, a security domain contains a series of control requirements", + "defs": { + "control-detail": { + "type": "object", + "properties": { + "requirement-url": { + "type": "string", + "description": "The requirement schema that specifies how a control should be defined" + }, + "config-url": { + "type": "string", + "description": "The configuration of how the control requirement schema is met" + }, + "config": { + "type": "object", + "description": "Inline configuration of how the control requirement schema is met" + } + }, + "required": [ + "requirement-url" + ], + "oneOf": [ + { + "required": [ + "config-url" + ] + }, + { + "required": [ + "config" + ] + } + ] + }, + "controls": { + "type": "object", + "patternProperties": { + "^[a-zA-Z0-9-]+$": { + "type": "object", + "properties": { + "description": { + "type": "string", + "description": "A description of a control and how it applies to a given architecture" + }, + "requirements": { + "type": "array", + "items": { + "$ref": "#/defs/control-detail" + } + } + }, + "required": [ + "description", + "requirements" + ] + } + } + } + } +} \ No newline at end of file diff --git a/calm/draft/2026-03/meta/core.json b/calm/draft/2026-03/meta/core.json new file mode 100644 index 0000000000..34a38510ab --- /dev/null +++ b/calm/draft/2026-03/meta/core.json @@ -0,0 +1,328 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "https://calm.finos.org/draft/2026-03/meta/core.json", + "title": "Common Architecture Language Model (CALM) Vocab", + "type": "object", + "properties": { + "nodes": { + "type": "array", + "items": { + "$ref": "#/defs/node" + } + }, + "relationships": { + "type": "array", + "items": { + "$ref": "#/defs/relationship" + } + }, + "metadata": { + "$ref": "#/defs/metadata" + }, + "controls": { + "$ref": "control.json#/defs/controls" + }, + "flows": { + "type": "array", + "items": { + "$ref": "flow.json#/defs/flow" + } + }, + "adrs": { + "type": "array", + "items": { + "type": "string" + }, + "description": "External links to ADRs (Architecture Decision Records) or similar documents that provide context or decisions related to the architecture. These can be URLs or references to internal documentation." + }, + "additionalProperties": false + }, + "defs": { + "node": { + "type": "object", + "properties": { + "unique-id": { + "type": "string" + }, + "node-type": { + "$ref": "#/defs/node-type-definition" + }, + "name": { + "type": "string" + }, + "description": { + "type": "string" + }, + "details": { + "type": "object", + "properties": { + "detailed-architecture": { + "type": "string" + }, + "required-pattern": { + "type": "string" + } + }, + "additionalProperties": false + }, + "interfaces": { + "type": "array", + "items": { + "anyOf": [ + { + "$ref": "interface.json#/defs/interface-definition" + }, + { + "$ref": "interface.json#/defs/interface-type" + } + ] + } + }, + "controls": { + "$ref": "control.json#/defs/controls" + }, + "metadata": { + "$ref": "#/defs/metadata" + } + }, + "required": [ + "unique-id", + "node-type", + "name", + "description" + ], + "additionalProperties": true + }, + "relationship": { + "type": "object", + "properties": { + "unique-id": { + "type": "string" + }, + "description": { + "type": "string" + }, + "relationship-type": { + "type": "object", + "properties": { + "interacts": { + "$ref": "#/defs/interacts-type" + }, + "connects": { + "$ref": "#/defs/connects-type" + }, + "deployed-in": { + "$ref": "#/defs/deployed-in-type" + }, + "composed-of": { + "$ref": "#/defs/composed-of-type" + }, + "options": { + "$ref": "#/defs/option-type" + } + }, + "oneOf": [ + { + "required": [ + "deployed-in" + ] + }, + { + "required": [ + "composed-of" + ] + }, + { + "required": [ + "interacts" + ] + }, + { + "required": [ + "connects" + ] + }, + { + "required": [ + "options" + ] + } + ] + }, + "protocol": { + "$ref": "#/defs/protocol" + }, + "metadata": { + "$ref": "#/defs/metadata" + }, + "controls": { + "$ref": "control.json#/defs/controls" + } + }, + "required": [ + "unique-id", + "relationship-type" + ], + "additionalProperties": true + }, + "protocol": { + "enum": [ + "HTTP", + "HTTPS", + "FTP", + "SFTP", + "JDBC", + "WebSocket", + "SocketIO", + "LDAP", + "AMQP", + "TLS", + "mTLS", + "TCP" + ] + }, + "node-type-definition": { + "anyOf": [ + { + "enum": [ + "actor", + "ecosystem", + "system", + "service", + "database", + "network", + "ldap", + "webclient", + "data-asset" + ] + }, + { + "type": "string" + } + ] + }, + "interacts-type": { + "type": "object", + "properties": { + "actor": { + "type": "string" + }, + "nodes": { + "type": "array", + "minItems": 1, + "items": { + "type": "string" + } + } + }, + "required": [ + "actor", + "nodes" + ] + }, + "connects-type": { + "type": "object", + "properties": { + "source": { + "$ref": "interface.json#/defs/node-interface" + }, + "destination": { + "$ref": "interface.json#/defs/node-interface" + } + }, + "required": [ + "source", + "destination" + ] + }, + "deployed-in-type": { + "type": "object", + "properties": { + "container": { + "type": "string" + }, + "nodes": { + "type": "array", + "minItems": 1, + "items": { + "type": "string" + } + } + }, + "required": [ + "container", + "nodes" + ] + }, + "composed-of-type": { + "type": "object", + "properties": { + "container": { + "type": "string" + }, + "nodes": { + "type": "array", + "minItems": 1, + "items": { + "type": "string" + } + } + }, + "required": [ + "container", + "nodes" + ] + }, + "option-type": { + "type": "array", + "items": { + "$ref": "#/defs/decision" + } + }, + "decision": { + "type": "object", + "properties": { + "description": { + "type": "string" + }, + "nodes": { + "type": "array", + "items": { + "type": "string" + } + }, + "relationships": { + "type": "array", + "items": { + "type": "string" + } + }, + "controls": { + "type": "array", + "items": { + "type": "string" + } + } + }, + "required": [ + "description", + "nodes", + "relationships" + ] + }, + "metadata": { + "oneOf": [ + { + "type": "array", + "items": { + "type": "object" + } + }, + { + "type": "object", + "additionalProperties": true + } + ] + } + } +} \ No newline at end of file diff --git a/calm/draft/2026-03/meta/decorators.json b/calm/draft/2026-03/meta/decorators.json new file mode 100644 index 0000000000..792b91db6e --- /dev/null +++ b/calm/draft/2026-03/meta/decorators.json @@ -0,0 +1,65 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "https://calm.finos.org/draft/2026-03/meta/decorators.json", + "title": "Common Architecture Language Model Decorators", + "description": "Decorators attach supplementary information to multiple nodes and relationships simultaneously, enabling cross-cutting concerns like guides, threat models, business context, and deployment information", + "defs": { + "decorator": { + "type": "object", + "properties": { + "unique-id": { + "type": "string", + "description": "Unique identifier for this decorator" + }, + "type": { + "type": "string", + "description": "Type of decorator - a free-form string identifying the decorator category" + }, + "target-type": { + "type": "array", + "items": { + "type": "string", + "enum": [ + "architecture", + "pattern", + "standard", + "control" + ] + }, + "minItems": 1, + "uniqueItems": true, + "description": "Optional array restricting which types of CALM documents this decorator can target. If omitted, the decorator can target any document type." + }, + "target": { + "type": "array", + "items": { + "type": "string" + }, + "minItems": 1, + "description": "Array of file paths or URLs referencing the CALM documents (patterns, architectures, or controls) this decorator targets" + }, + "applies-to": { + "type": "array", + "items": { + "type": "string" + }, + "minItems": 1, + "description": "Array of unique-ids referencing nodes, relationships, flows, or other architecture elements" + }, + "data": { + "type": "object", + "minProperties": 1, + "description": "Free-form JSON object containing the decorator's data" + } + }, + "required": [ + "unique-id", + "type", + "target", + "applies-to", + "data" + ], + "additionalProperties": false + } + } +} \ No newline at end of file diff --git a/calm/draft/2026-03/meta/evidence.json b/calm/draft/2026-03/meta/evidence.json new file mode 100644 index 0000000000..604e0bf2ee --- /dev/null +++ b/calm/draft/2026-03/meta/evidence.json @@ -0,0 +1,37 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "https://calm.finos.org/draft/2026-03/meta/evidence.json", + "title": "Common Architecture Language Model Evidence", + "description": "Schema for defining evidence for control requirements within the Common Architecture Language Model.", + "type": "object", + "properties": { + "evidence": { + "type": "object", + "properties": { + "unique-id": { + "type": "string", + "description": "CALM unique-id for future linking and uniquely defining this evidence" + }, + "evidence-paths": { + "type": "array", + "description": "Paths to the evidence relating to a specific control", + "items": { + "type": "string" + } + }, + "control-config-url": { + "type": "string", + "description": "URI for the control configuration this evidence relates to" + } + }, + "required": [ + "unique-id", + "evidence-paths", + "control-config-url" + ] + } + }, + "required": [ + "evidence" + ] +} \ No newline at end of file diff --git a/calm/draft/2026-03/meta/flow.json b/calm/draft/2026-03/meta/flow.json new file mode 100644 index 0000000000..5368eded41 --- /dev/null +++ b/calm/draft/2026-03/meta/flow.json @@ -0,0 +1,78 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "https://calm.finos.org/draft/2026-03/meta/flow.json", + "title": "Business Flow Model", + "description": "Defines business flows that relate to technical architectures, allowing mapping of flows to technical components and attaching control requirements.", + "defs": { + "transition": { + "type": "object", + "properties": { + "relationship-unique-id": { + "type": "string", + "description": "Unique identifier for the relationship in the architecture" + }, + "sequence-number": { + "type": "integer", + "description": "Indicates the sequence of the relationship in the flow" + }, + "description": { + "type": "string", + "description": "Functional summary of what is happening in the transition" + }, + "direction": { + "enum": [ + "source-to-destination", + "destination-to-source" + ], + "default": "source-to-destination" + } + }, + "required": [ + "relationship-unique-id", + "sequence-number", + "description" + ] + }, + "flow": { + "type": "object", + "properties": { + "unique-id": { + "type": "string", + "description": "Unique identifier for the flow" + }, + "name": { + "type": "string", + "description": "Descriptive name for the business flow" + }, + "description": { + "type": "string", + "description": "Detailed description of the flow's purpose" + }, + "requirement-url": { + "type": "string", + "description": "Link to a detailed requirement document" + }, + "transitions": { + "type": "array", + "items": { + "$ref": "#/defs/transition" + }, + "minItems": 1 + }, + "controls": { + "$ref": "control.json#/defs/controls" + }, + "metadata": { + "$ref": "core.json#/defs/metadata" + } + }, + "required": [ + "unique-id", + "name", + "description", + "transitions" + ], + "additionalProperties": false + } + } +} \ No newline at end of file diff --git a/calm/draft/2026-03/meta/interface.json b/calm/draft/2026-03/meta/interface.json new file mode 100644 index 0000000000..e76b7beeda --- /dev/null +++ b/calm/draft/2026-03/meta/interface.json @@ -0,0 +1,60 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "https://calm.finos.org/draft/2026-03/meta/interface.json", + "title": "Common Architecture Language Model Interfaces", + "defs": { + "interface-definition": { + "type": "object", + "description": "A modular interface definition referencing an external schema", + "properties": { + "unique-id": { + "type": "string", + "description": "Unique identifier for this interface instance" + }, + "definition-url": { + "type": "string", + "description": "URI of the external schema this interface configuration conforms to" + }, + "config": { + "type": "object", + "description": "Inline configuration conforming to the external interface schema" + } + }, + "required": [ + "unique-id", + "definition-url", + "config" + ], + "additionalProperties": false + }, + "interface-type": { + "type": "object", + "properties": { + "unique-id": { + "type": "string" + } + }, + "required": [ + "unique-id" + ], + "additionalProperties": true + }, + "node-interface": { + "type": "object", + "properties": { + "node": { + "type": "string" + }, + "interfaces": { + "type": "array", + "items": { + "type": "string" + } + } + }, + "required": [ + "node" + ] + } + } +} \ No newline at end of file diff --git a/calm/draft/2026-03/meta/timeline.json b/calm/draft/2026-03/meta/timeline.json new file mode 100644 index 0000000000..d41f8ba7e7 --- /dev/null +++ b/calm/draft/2026-03/meta/timeline.json @@ -0,0 +1,57 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "https://calm.finos.org/draft/2026-03/meta/timeline.json", + "title": "Common Architecture Language Model (CALM) Timelines Vocab", + "type": "object", + "required": [ + "moments" + ], + "properties": { + "current-moment": { + "type": "string", + "description": "The unique-id of the current architecture moment within the timeline." + }, + "moments": { + "type": "array", + "items": { + "allOf": [ + { + "$ref": "core.json#/defs/node" + }, + { + "$ref": "#/defs/node-moment" + } + ] + }, + "description": "A list of significant architecture states or points in time, each represented as a 'moment'." + }, + "metadata": { + "$ref": "core.json#/defs/metadata" + } + }, + "defs": { + "node-moment": { + "required": [ + "details" + ], + "properties": { + "node-type": { + "const": "moment" + }, + "valid-from": { + "type": "string", + "format": "date", + "description": "The date when this architecture moment came into effect." + }, + "adrs": { + "type": "array", + "items": { + "type": "string" + }, + "description": "External links to ADRs (Architecture Decision Records) or similar documents that provide context or decisions related to why the architecture changed." + }, + "interfaces": false + } + } + } +} \ No newline at end of file diff --git a/calm/draft/2026-03/meta/units.json b/calm/draft/2026-03/meta/units.json new file mode 100644 index 0000000000..1e652b38f6 --- /dev/null +++ b/calm/draft/2026-03/meta/units.json @@ -0,0 +1,115 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "https://calm.finos.org/draft/2026-03/meta/units.json", + "title": "Common Architecture Language Model Units", + "defs": { + "time-unit": { + "type": "object", + "description": "A unit of time with a value and a unit type.", + "properties": { + "unit": { + "enum": [ + "nanoseconds", + "microseconds", + "milliseconds", + "seconds", + "minutes", + "hours", + "days", + "weeks", + "months", + "quarters", + "years" + ], + "description": "The unit of time (e.g., seconds, minutes, hours)." + }, + "value": { + "type": "number", + "minimum": 0, + "description": "The numeric value representing the amount of time." + } + }, + "required": [ + "unit", + "value" + ], + "additionalProperties": false, + "examples": [ + { + "unit": "seconds", + "value": 30 + }, + { + "unit": "minutes", + "value": 15 + }, + { + "unit": "hours", + "value": 1 + }, + { + "unit": "days", + "value": 7 + } + ] + }, + "rate-unit": { + "type": "object", + "description": "A unit representing a rate (e.g., operations per second).", + "properties": { + "rate": { + "type": "number", + "minimum": 0, + "description": "The numeric value representing the rate." + }, + "per": { + "enum": [ + "nanosecond", + "microsecond", + "millisecond", + "second", + "minute", + "hour", + "day", + "week", + "month", + "quarter", + "year" + ], + "description": "The time unit defining the rate interval." + } + }, + "required": [ + "rate", + "per" + ], + "additionalProperties": false, + "examples": [ + { + "rate": 30, + "per": "second" + }, + { + "rate": 1000, + "per": "minute" + }, + { + "rate": 5, + "per": "millisecond" + } + ] + }, + "cron-expression": { + "type": "string", + "title": "Cron Expression", + "description": "A valid Unix-style cron expression.", + "pattern": "^([0-5]?\\d)\\s([01]?\\d|2[0-3])\\s(3[01]|[12]\\d|0?[1-9])\\s(1[0-2]|0?[1-9])\\s([0-6])$", + "examples": [ + "0 0 * * 0", + "30 14 1 * 5", + "15 10 * * *" + ] + } + }, + "additionalProperties": false +} \ No newline at end of file diff --git a/calm/draft/2026-03/standards/README.md b/calm/draft/2026-03/standards/README.md new file mode 100644 index 0000000000..bdfd17af3b --- /dev/null +++ b/calm/draft/2026-03/standards/README.md @@ -0,0 +1,82 @@ +# FINOS Official CALM Standards + +This directory contains the official repository of standardized CALM decorator schemas and other reusable standards published and maintained by FINOS. + +## Purpose + +Standards in this directory represent validated, approved decorator schemas and cross-cutting architectural standards that organizations can use directly or extend for their specific needs. These standards enable consistent application of supplementary information across CALM architectures without modifying the core architecture definitions. + +## Organization + +Standards are organized by category to facilitate discovery and reuse: + +- **Deployment**: Deployment tracking decorators for various platforms (Kubernetes, AWS, Azure, etc.) +- **Security**: Security context decorators including threat models, compliance frameworks, and security controls +- **Business**: Business context decorators such as cost allocation, ownership, and organizational metadata +- **Operational**: Operational decorators including runbooks, SLAs, incident contacts, and support information +- **Observability**: Monitoring and observability decorators with dashboard links, alerting rules, and health checks +- **Guide**: Architectural decision records (ADRs) and design guidance decorators + +## Standard Types + +Each standard definition includes: + +- **Schema definition**: JSON Schema extending the base decorator schema +- **Required properties**: Mandatory fields for the decorator type +- **Optional properties**: Additional fields for enhanced context +- **Validation rules**: Constraints and patterns for data integrity +- **Documentation**: Usage examples and implementation guidelines + +## Decorator Standards + +Decorator standards define typed schemas that extend the base decorator schema (`decorators.json#/defs/decorator`). Each decorator standard: + +1. Constrains the `type` field to a specific value +2. Defines the structure of the `data` field +3. Can be further extended by more specific schemas +4. Maintains backward compatibility through versioning + +## Versioning + +All standards in this directory follow semantic versioning to ensure backward compatibility and clear evolution paths. + +## Publishing + +Standards in this directory are automatically published to the hosted CALM Hub when they are added, modified, or removed. This ensures that the latest approved standards are always available to the community. + +## Contributing + +To contribute new standards or modify existing ones: + +1. Follow the CALM decorator schema specification +2. Ensure your standard extends the base decorator schema using `allOf` +3. Provide clear documentation with comprehensive examples +4. Include test cases demonstrating valid and invalid instances +5. Submit a pull request with a detailed description of the standard's purpose and use cases + +## Usage + +Organizations can reference these standards directly in their decorator documents by using the appropriate `$schema` reference. + +```json +{ + "$schema": "https://calm.finos.org/standards/deployment/deployment.decorator.standard.json", + "unique-id": "my-deployment-001", + "type": "deployment", + "target": ["my-architecture.json"], + "applies-to": ["my-service"], + "data": { + "start-time": "2026-02-23T10:00:00Z", + "status": "completed" + } +} +``` + +## Standard vs Other CALM Concepts + +| Concept | Purpose | Location | +|---------|---------|----------| +| **Standard** | Reusable decorator schemas and cross-cutting patterns | `calm/standards/` | +| **Interface** | Communication protocol definitions | `calm/interfaces/` | +| **Control** | Compliance and governance requirements | Defined by each organisation | +| **Schema** | Core CALM meta-schemas | `calm/release/X.Y/meta/` | diff --git a/calm/draft/2026-03/standards/deployment/README.md b/calm/draft/2026-03/standards/deployment/README.md new file mode 100644 index 0000000000..1a916861b5 --- /dev/null +++ b/calm/draft/2026-03/standards/deployment/README.md @@ -0,0 +1,92 @@ +# Deployment Decorator Standards + +This directory contains standardized deployment decorator schemas for tracking deployment information across various platforms and environments. + +## Overview + +Deployment decorators attach deployment tracking information to architecture elements without modifying the core architecture definition. They enable tracking of deployment status, timing, deployment details links, and platform-specific details across the architecture lifecycle. + +## Base Deployment Decorator + +The base deployment decorator standard (`deployment.decorator.standard.json`) defines common deployment attributes applicable across all platforms: + +- **start-time**: ISO 8601 timestamp of when the deployment started (required) +- **status**: Current deployment status (pending, in-progress, completed, failed, rolled-back) (required) +- **target-type**: Type of resources this decorator targets - must be ["architecture"] (required) +- **end-time**: ISO 8601 timestamp of when the deployment completed or failed +- **deployment-details**: URI linking to logs, metrics, or observability dashboards +- **notes**: Free-form notes or comments about the deployment + +## Platform-Specific Extensions + +Platform-specific deployment decorators extend the base deployment standard and add platform-specific attributes: + +- **Kubernetes**: Helm charts, clusters, namespaces (example provided) +- **AWS**: ECS services, CloudFormation stacks, regions +- **Azure**: Resource groups, subscriptions, deployment slots +- **Google Cloud**: Projects, regions, deployment managers + +## Usage + +### Basic Deployment Decorator + +```json +{ + "$schema": "https://calm.finos.org/draft/2026-03/standards/deployment/deployment.decorator.standard.json", + "unique-id": "api-service-deployment-001", + "type": "deployment", + "target": ["my-architecture.json"], + "target-type": ["architecture"], + "applies-to": ["api-service"], + "data": { + "start-time": "2026-02-23T10:00:00Z", + "end-time": "2026-02-23T10:05:00Z", + "status": "completed", + "deployment-details": "https://grafana.example.com/d/api-service", + "notes": "Successful deployment with zero downtime" + } +} +``` + +### Kubernetes Deployment Decorator (Example) + +```json +{ + "$schema": "https://calm.finos.org/draft/2026-03/standards/deployment/kubernetes.decorator.standard.json", + "unique-id": "k8s-deployment-001", + "type": "deployment", + "target": ["my-architecture.json"], + "target-type": ["architecture"], + "applies-to": ["api-service"], + "data": { + "start-time": "2026-02-23T10:00:00Z", + "end-time": "2026-02-23T10:08:00Z", + "status": "completed", + "deployment-details": "https://grafana.example.com/d/k8s-prod", + "notes": "Rolling update with new features", + "kubernetes": { + "helm-chart-version": "1.2.3", + "cluster": "prod-us-west-01", + "namespace": "production" + } + } +} +``` + +## Standard Inheritance + +``` +decorators.json (base decorator) + └── deployment.decorator.standard.json (deployment standard) + └── Platform-specific extensions (add platform sub-objects in data) +``` + +## Contributing + +To contribute new platform-specific deployment decorators: + +1. Use the base `deployment.decorator.standard.json` standard +2. Add platform-specific properties in a namespaced sub-object within `data` +3. Provide comprehensive examples demonstrating the platform-specific fields +4. Document all required and optional fields +5. Submit examples to the `examples/` directory diff --git a/calm/draft/2026-03/standards/deployment/deployment.decorator.standard.json b/calm/draft/2026-03/standards/deployment/deployment.decorator.standard.json new file mode 100644 index 0000000000..81b2470fc9 --- /dev/null +++ b/calm/draft/2026-03/standards/deployment/deployment.decorator.standard.json @@ -0,0 +1,75 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "https://calm.finos.org/draft/2026-03/standards/deployment/deployment.decorator.standard.json", + "title": "CALM Deployment Decorator Standard", + "description": "Deployment decorator standard for tracking deployment information across various platforms and environments. Extends the base CALM decorator standard to add deployment-specific attributes.", + "allOf": [ + { + "$ref": "https://calm.finos.org/draft/2026-03/meta/decorators.json#/defs/decorator" + }, + { + "type": "object", + "properties": { + "type": { + "const": "deployment", + "description": "Must be 'deployment' for deployment decorators" + }, + "target-type": { + "type": "array", + "items": { + "type": "string", + "enum": [ + "architecture" + ] + }, + "description": "Type of resources this decorator can target. Deployment decorators can only target architecture files." + }, + "data": { + "type": "object", + "properties": { + "start-time": { + "type": "string", + "format": "date-time", + "description": "ISO 8601 timestamp indicating when the deployment started" + }, + "end-time": { + "type": "string", + "format": "date-time", + "description": "ISO 8601 timestamp indicating when the deployment completed or failed" + }, + "status": { + "type": "string", + "enum": [ + "pending", + "in-progress", + "completed", + "failed", + "rolled-back" + ], + "description": "Current status of the deployment" + }, + "deployment-details": { + "type": "string", + "format": "uri", + "description": "URI linking to deployment logs, metrics, dashboards, or observability platform" + }, + "notes": { + "type": "string", + "description": "Free-form notes or comments about the deployment" + } + }, + "required": [ + "start-time", + "status" + ], + "additionalProperties": true + } + }, + "required": [ + "type", + "target-type", + "data" + ] + } + ] +} \ No newline at end of file diff --git a/calm/draft/2026-03/standards/deployment/examples/README.md b/calm/draft/2026-03/standards/deployment/examples/README.md new file mode 100644 index 0000000000..f118141b30 --- /dev/null +++ b/calm/draft/2026-03/standards/deployment/examples/README.md @@ -0,0 +1,104 @@ +# Deployment Decorator Examples + +This directory contains example deployment decorator instances demonstrating various use cases and patterns. + +## Examples Overview + +| Example | Standard | Demonstrates | +|---------|--------|-------------| +| `simple-deployment.decorator.json` | Base deployment | Successful deployment with all optional fields | +| `kubernetes-deployment.decorator.json` | Kubernetes-specific | Multi-service deployment with Kubernetes details | +| `failed-deployment.decorator.json` | Base deployment | Failed deployment with rollback notes | +| `multi-target-deployment.decorator.json` | Kubernetes-specific | Coordinated deployment across multiple architectures | + +## Usage Patterns + +### Single Service Deployment + +The simplest case - tracking a deployment for one service in one architecture: + +```json +{ + "target": ["my-architecture.json"], + "applies-to": ["my-service"] +} +``` + +See: `simple-deployment.decorator.json` + +### Multi-Service Deployment + +Track a single deployment event that affects multiple services (e.g., coordinated release): + +```json +{ + "target": ["my-architecture.json"], + "applies-to": ["service-a", "service-b", "service-c"] +} +``` + +See: `kubernetes-deployment.decorator.json` + +### Cross-Architecture Deployment + +Track a deployment that spans multiple architecture documents: + +```json +{ + "target": [ + "frontend-architecture.json", + "backend-architecture.json" + ], + "applies-to": ["react-app", "api-gateway", "bff"] +} +``` + +See: `multi-target-deployment.decorator.json` + +### Failed Deployment + +Document deployment failures with context for troubleshooting: + +```json +{ + "data": { + "status": "failed", + "notes": "Deployment failed due to..." + } +} +``` + +See: `failed-deployment.decorator.json` + +## Platform-Specific Examples + +### Kubernetes + +When deploying to Kubernetes clusters, add Kubernetes-specific information as a sub-object: + +- Include Helm chart version +- Specify cluster and namespace + +See: `kubernetes-deployment.decorator.json` + +## Best Practices + +1. **Unique IDs**: Use descriptive unique-id values that include the service name and deployment identifier +2. **Target Type**: Always include `"target-type": ["architecture"]` as deployment decorators only target architecture files +3. **Timestamps**: Always use ISO 8601 format with timezone (preferably UTC) +4. **Deployment Details Links**: Link to deployment-specific dashboards or filtered log views +5. **Notes**: Include context about what changed, why, and any issues encountered + +## Validation + +Validate decorator instances using the CALM CLI: + +```bash +# Validate a deployment decorator +calm validate --schema https://calm.finos.org/draft/2026-03/standards/deployment/deployment.decorator.standard.json \ + simple-deployment.decorator.json + +# Validate a Kubernetes-specific deployment decorator +calm validate --schema https://calm.finos.org/draft/2026-03/standards/deployment/kubernetes.decorator.standard.json \ + kubernetes-deployment.decorator.json +``` diff --git a/calm/draft/2026-03/standards/deployment/examples/failed-deployment.decorator.json b/calm/draft/2026-03/standards/deployment/examples/failed-deployment.decorator.json new file mode 100644 index 0000000000..990deda94f --- /dev/null +++ b/calm/draft/2026-03/standards/deployment/examples/failed-deployment.decorator.json @@ -0,0 +1,21 @@ +{ + "$schema": "https://calm.finos.org/draft/2026-03/standards/deployment/deployment.decorator.standard.json", + "unique-id": "failed-deployment-example", + "type": "deployment", + "target": [ + "example-architecture.json" + ], + "target-type": [ + "architecture" + ], + "applies-to": [ + "payment-processor" + ], + "data": { + "start-time": "2026-02-23T08:15:00Z", + "end-time": "2026-02-23T08:23:45Z", + "status": "failed", + "deployment-details": "https://grafana.example.com/d/payment-processor-prod", + "notes": "Deployment failed due to database migration timeout. Rolling back to 2.9.1." + } +} \ No newline at end of file diff --git a/calm/draft/2026-03/standards/deployment/examples/kubernetes-deployment.decorator.json b/calm/draft/2026-03/standards/deployment/examples/kubernetes-deployment.decorator.json new file mode 100644 index 0000000000..ef495c70d5 --- /dev/null +++ b/calm/draft/2026-03/standards/deployment/examples/kubernetes-deployment.decorator.json @@ -0,0 +1,27 @@ +{ + "$schema": "https://calm.finos.org/draft/2026-03/standards/deployment/kubernetes.decorator.standard.json", + "unique-id": "k8s-deployment-example", + "type": "deployment", + "target": [ + "example-architecture.json" + ], + "target-type": [ + "architecture" + ], + "applies-to": [ + "api-gateway", + "auth-service" + ], + "data": { + "start-time": "2026-02-23T14:30:00Z", + "end-time": "2026-02-23T14:42:15Z", + "status": "completed", + "deployment-details": "https://grafana.example.com/d/platform-services", + "notes": "Rolling update with new authentication features", + "kubernetes": { + "helm-chart-version": "2.1.0", + "cluster": "prod-eu-west-01", + "namespace": "platform" + } + } +} \ No newline at end of file diff --git a/calm/draft/2026-03/standards/deployment/examples/multi-target-deployment.decorator.json b/calm/draft/2026-03/standards/deployment/examples/multi-target-deployment.decorator.json new file mode 100644 index 0000000000..d770f3a40a --- /dev/null +++ b/calm/draft/2026-03/standards/deployment/examples/multi-target-deployment.decorator.json @@ -0,0 +1,28 @@ +{ + "$schema": "https://calm.finos.org/draft/2026-03/standards/deployment/kubernetes.decorator.standard.json", + "unique-id": "multi-target-deployment-example", + "type": "deployment", + "target": [ + "frontend-architecture.json", + "backend-architecture.json" + ], + "target-type": [ + "architecture" + ], + "applies-to": [ + "react-web-app", + "mobile-bff", + "graphql-gateway" + ], + "data": { + "start-time": "2026-02-23T12:00:00Z", + "status": "in-progress", + "deployment-details": "https://datadog.example.com/dashboard/release-2024-02-23", + "notes": "Coordinated release across frontend and backend services for new feature rollout", + "kubernetes": { + "helm-chart-version": "3.0.0", + "cluster": "staging-us-central-01", + "namespace": "apps" + } + } +} \ No newline at end of file diff --git a/calm/draft/2026-03/standards/deployment/examples/simple-deployment.decorator.json b/calm/draft/2026-03/standards/deployment/examples/simple-deployment.decorator.json new file mode 100644 index 0000000000..a95affd402 --- /dev/null +++ b/calm/draft/2026-03/standards/deployment/examples/simple-deployment.decorator.json @@ -0,0 +1,21 @@ +{ + "$schema": "https://calm.finos.org/draft/2026-03/standards/deployment/deployment.decorator.standard.json", + "unique-id": "simple-deployment-example", + "type": "deployment", + "target": [ + "example-architecture.json" + ], + "target-type": [ + "architecture" + ], + "applies-to": [ + "web-service" + ], + "data": { + "start-time": "2026-02-23T10:00:00Z", + "end-time": "2026-02-23T10:05:30Z", + "status": "completed", + "deployment-details": "https://grafana.example.com/d/web-service-prod", + "notes": "Initial production deployment with baseline configuration" + } +} \ No newline at end of file diff --git a/calm/draft/2026-03/standards/deployment/kubernetes.decorator.standard.json b/calm/draft/2026-03/standards/deployment/kubernetes.decorator.standard.json new file mode 100644 index 0000000000..1f089fe85a --- /dev/null +++ b/calm/draft/2026-03/standards/deployment/kubernetes.decorator.standard.json @@ -0,0 +1,49 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "https://calm.finos.org/draft/2026-03/standards/deployment/kubernetes.decorator.standard.json", + "title": "CALM Kubernetes Deployment Decorator Standard", + "description": "Kubernetes-specific deployment decorator standard. Extends the base deployment decorator with Kubernetes cluster, namespace, and Helm chart information.", + "allOf": [ + { + "$ref": "https://calm.finos.org/draft/2026-03/standards/deployment/deployment.decorator.standard.json" + }, + { + "type": "object", + "properties": { + "data": { + "type": "object", + "properties": { + "kubernetes": { + "type": "object", + "description": "Kubernetes-specific deployment information", + "properties": { + "helm-chart-version": { + "type": "string", + "pattern": "^[0-9]+\\.[0-9]+\\.[0-9]+$", + "description": "Helm chart semantic version in format 'major.minor.patch'" + }, + "cluster": { + "type": "string", + "description": "Kubernetes cluster identifier or name" + }, + "namespace": { + "type": "string", + "description": "Kubernetes namespace where the resource is deployed", + "pattern": "^[a-z0-9]([-a-z0-9]*[a-z0-9])?$" + } + }, + "required": [ + "cluster", + "namespace" + ], + "additionalProperties": false + } + }, + "required": [ + "kubernetes" + ] + } + } + } + ] +} \ No newline at end of file diff --git a/cli/AGENTS.md b/cli/AGENTS.md index 5910007458..e5c0a0dbe7 100644 --- a/cli/AGENTS.md +++ b/cli/AGENTS.md @@ -33,7 +33,7 @@ npm run link:cli # Link CLI globally for testing calm --help # Test globally linked CLI # Build steps (executed by npm run build) -npm run copy-calm-schema # Copy CALM meta schemas from npm +npm run copy-calm-schema # Copy CALM meta schemas from npm and ../calm/ npm run copy-docify-templates # Copy docify templates from ../shared/ npm run copy-widgets # Copy widget files from ../calm-widgets/ npm run copy-ai-tools # Copy AI agent files from ../calm-ai/ @@ -107,6 +107,7 @@ Note: `copy-calm-schema` only bundles meta schemas into `dist/calm/` (see Schema ### Schema Handling - Released CALM schemas come from the `@finos/calm-schema` npm package. The root `package.json` pins each released version by alias (`calm-schema-1.0`, `calm-schema-1.1`, ...) and the latest version as `@finos/calm-schema` +- Drafts live in `../calm/draft/` - Default schema directory: the bundled `dist/calm` (`CALM_META_SCHEMA_DIRECTORY` in `shared`) - CLI accepts `-s, --schema-directory` to override - Schemas are copied during build, not at runtime diff --git a/cli/package.json b/cli/package.json index 27b6573832..9c4a190554 100644 --- a/cli/package.json +++ b/cli/package.json @@ -20,7 +20,7 @@ "scripts": { "build": "tsup && npm run copy-calm-schema && npm run copy-docify-templates && npm run copy-widgets && npm run copy-ai-tools && npm run copy-workspace-templates", "watch": "node watch.mjs", - "copy-calm-schema": "node ../scripts/copy-calm-schemas.mjs dist/calm", + "copy-calm-schema": "copyfiles \"../calm/draft/**/meta/*\" dist/calm/ && node ../scripts/copy-calm-schemas.mjs dist/calm", "copy-docify-templates": "copyfiles \"../shared/dist/template-bundles/**/*\" dist --up 3", "copy-widgets": "copyfiles \"../calm-widgets/dist/cli/widgets/**/*\" dist --up 4", "copy-ai-tools": "copyfiles \"../calm-ai/tools/**/*\" \"../calm-ai/ai-assistants/**/*\" \"../calm-ai/templates/**/*\" dist/calm-ai/ --up 2", diff --git a/cli/src/cli.e2e.spec.ts b/cli/src/cli.e2e.spec.ts index 717b57f4c4..e9fba9de09 100644 --- a/cli/src/cli.e2e.spec.ts +++ b/cli/src/cli.e2e.spec.ts @@ -575,6 +575,22 @@ describe('CLI Integration Tests', () => { expect(parsedOutput).toEqual(expectedOutput); }); + test('validate command loads the 2026-03 draft from the bundled schemas', async () => { + const architecture = path.join(tempDir, 'draft-2026-03.arch.json'); + fs.writeFileSync(architecture, JSON.stringify({ + $schema: 'https://calm.finos.org/draft/2026-03/meta/calm.json', + nodes: [], + relationships: [], + })); + + // calm.finos.org is not an allowed host, so the CLI cannot fetch the draft. + const { stdout } = await cli.run(['validate', '-a', architecture], { + env: { CALM_ALLOWED_REMOTE_HOSTS: 'remote-loading-disabled.invalid' }, + }); + + expect(JSON.parse(stdout)).toHaveProperty('hasErrors', false); + }); + test('validate command validates a timeline only', async () => { const apiGatewayTimelinePath = path.join(__dirname, '../test_fixtures/api-gateway/api-gateway-timeline.json'); const targetOutputFile = path.join(tempDir, 'validate-timeline-output2.json'); From 53f636d5f4e2b1cac9e9427c39768ec315272327 Mon Sep 17 00:00:00 2001 From: Matthew Bain <66839492+rocketstack-matt@users.noreply.github.com> Date: Tue, 27 Oct 2026 11:13:37 +0000 Subject: [PATCH 3/3] ci: publish the drafts to calm.finos.org again --- .github/workflows/s3-draft-sync.yml | 34 +++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) create mode 100644 .github/workflows/s3-draft-sync.yml diff --git a/.github/workflows/s3-draft-sync.yml b/.github/workflows/s3-draft-sync.yml new file mode 100644 index 0000000000..6c6c1cfd39 --- /dev/null +++ b/.github/workflows/s3-draft-sync.yml @@ -0,0 +1,34 @@ +name: Sync Drafts to S3 + +# No --delete: draft/2025-03 is on calm.finos.org but not in calm/draft (#3507). +# s3-docs-sync.yml excludes draft/*, so its --delete does not remove the drafts either. + +permissions: + contents: read + +on: + workflow_dispatch: {} + push: + branches: ['main'] + paths: ['calm/draft/**', '.github/workflows/s3-draft-sync.yml'] + +jobs: + sync-to-s3: + runs-on: ubuntu-latest + + steps: + - name: Checkout repository + uses: actions/checkout@d23441a48e516b6c34aea4fa41551a30e30af803 # v6 + - name: Configure AWS credentials + uses: aws-actions/configure-aws-credentials@e1253824e5c10ff9df46874f81ed3ec929e19cfd # v6 + with: + aws-access-key-id: ${{ secrets.AWS_S3_ACCESS_KEY }} + aws-secret-access-key: ${{ secrets.AWS_S3_SECRET_ACCESS_KEY }} + aws-region: us-east-1 + - name: Sync calm/draft folder to S3 + run: | + aws s3 sync calm/draft s3://${{ vars.AWS_S3_BUCKET }}/draft + + - name: Invalidate CloudFront cache + run: | + aws cloudfront create-invalidation --distribution-id ${{ secrets.AWS_CLOUDFRONT_DISTRIBUTION_ID }} --paths "/draft/*"