Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
54 changes: 40 additions & 14 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
@@ -1,36 +1,62 @@
name: Release Revision
name: Release Contract

on:
workflow_dispatch:
inputs:
version:
description: 'Version to release (e.g. v2.1)'
required: true
type: string

jobs:
bump-version:
release:
runs-on: ubuntu-latest
permissions:
contents: write
steps:
- name: Checkout repository
uses: actions/checkout@v4
with:
fetch-depth: 0

- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: "3.11"

- name: Read release version
id: read_version
run: |
VERSION=$(cat VERSION | tr -d '[:space:]')
echo "version=$VERSION" >> "$GITHUB_OUTPUT"
echo "tag=v$VERSION" >> "$GITHUB_OUTPUT"

- name: Validate SemVer tag does not already exist
run: |
TAG="${{ steps.read_version.outputs.tag }}"
if git rev-parse "$TAG" >/dev/null 2>&1; then
echo "::error::Tag $TAG already exists"
exit 1
fi

- name: Validate changelog and governance
run: |
LATEST_TAG=$(git tag -l "v*" --sort=-v:refname | head -n1)
python3 scripts/compatibility_guard.py --base-tag "$LATEST_TAG"

- name: Build release notes from changelog
run: |
python3 scripts/extract_release_notes.py --version "${{ steps.read_version.outputs.version }}" --output release-notes.md

- name: Set up Git
run: |
git config user.name "github-actions"
git config user.email "github-actions@github.com"

- name: Bump revision and tag
- name: Create and push tag
run: |
VERSION=${{ github.event.inputs.version }}
git tag $VERSION
git push origin $VERSION
TAG="${{ steps.read_version.outputs.tag }}"
git tag "$TAG"
git push origin "$TAG"

- name: Create GitHub Release
uses: softprops/action-gh-release@v2
with:
tag_name: ${{ github.event.inputs.version }}
name: Release ${{ github.event.inputs.version }}
generate_release_notes: true
tag_name: ${{ steps.read_version.outputs.tag }}
name: MedTech Telemetry Contract ${{ steps.read_version.outputs.tag }}
body_path: release-notes.md
54 changes: 39 additions & 15 deletions .github/workflows/validate.yml
Original file line number Diff line number Diff line change
Expand Up @@ -8,24 +8,16 @@ on:

jobs:
validate:
name: Validate ${{ matrix.example }} against ${{ matrix.schema }}
name: Validate canonical vitals schema
runs-on: ubuntu-latest
permissions:
contents: read

strategy:
matrix:
include:
# This block will be auto-generated by a script step below
# Placeholder for manual fallback
- schema: schemas/vitals/v2.0.json
example: examples/vitals/v2.0.example.json
- schema: schemas/vitals/v2.1.json
example: examples/vitals/v2.1.example.json

steps:
- name: Checkout repository
uses: actions/checkout@v4
with:
fetch-depth: 0

- name: Set up Node.js
uses: actions/setup-node@v4
Expand All @@ -35,10 +27,42 @@ jobs:
- name: Install AJV CLI
run: npm install --global ajv-cli@5

- name: Validate schema ${{ matrix.schema }}
- name: Validate canonical schema
run: |
ajv compile -s schemas/vitals/vitals.schema.json

- name: Validate canonical example against canonical schema
run: |
ajv validate --spec=draft7 -s schemas/vitals/vitals.schema.json -d examples/vitals/vitals.example.json

compatibility:
name: Compatibility guard
if: github.event_name == 'pull_request'
runs-on: ubuntu-latest
permissions:
contents: read
steps:
- name: Checkout repository
uses: actions/checkout@v4
with:
fetch-depth: 0

- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: "3.11"

- name: Find latest released SemVer tag
id: latest_tag
run: |
ajv compile -s ${{ matrix.schema }}
LATEST_TAG=$(git tag -l "v*" --sort=-v:refname | head -n1)
echo "latest_tag=$LATEST_TAG" >> "$GITHUB_OUTPUT"
if [ -n "$LATEST_TAG" ]; then
echo "Latest released tag: $LATEST_TAG"
else
echo "No existing SemVer tag found; governance checks will run without baseline diff."
fi

- name: Validate example ${{ matrix.example }} against ${{ matrix.schema }}
- name: Run compatibility and governance checks
run: |
ajv validate --spec=draft7 -s ${{ matrix.schema }} -d ${{ matrix.example }}
python3 scripts/compatibility_guard.py --base-tag "${{ steps.latest_tag.outputs.latest_tag }}"
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
__pycache__/
*.pyc
31 changes: 31 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
# Changelog

All notable changes to this contract repository are documented in this file.

The format is based on Keep a Changelog, and this project follows Semantic Versioning.

## [Unreleased]

### Compatibility
PATCH

### Changed
- Repository governance and automation updates in progress.

## [2.1.1] - 2026-05-10

### Compatibility
PATCH (documentation-only / repository-governance)

### Changed
- Adopted a canonical schema path at `schemas/vitals/vitals.schema.json`.
- Replaced version-per-file contract management with Git tags/releases as the immutable version record.
- Added CI compatibility classification checks to detect breaking changes, backward-compatible additions, and patch-only updates.
- Added release workflow validation for SemVer tags and changelog-backed release notes.

### Migration Notes
- No payload field migration is required for consumers already aligned to the latest schema shape.
- Consumers should update vendoring/pinning paths from `schemas/vitals/v2.x.json` to `schemas/vitals/vitals.schema.json` and pin by tag/commit.

[Unreleased]: https://github.com/chaithubk/medtech-telemetry-contract/compare/v2.1.1...HEAD
[2.1.1]: https://github.com/chaithubk/medtech-telemetry-contract/releases/tag/v2.1.1
143 changes: 66 additions & 77 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,131 +1,120 @@
# MedTech Telemetry Contract

This repository is the **single source of truth** for all telemetry payload
contracts used across the MedTech platform. Every service that publishes or
consumes telemetry data (vitals-publisher, edge-analytics, clinician-ui, …)
must pin to a versioned schema from this repo to avoid schema drift.

---
This repository is the authoritative schema registry for MedTech telemetry contracts.
Each contract domain has one canonical schema path that evolves over time. Immutable
contract versions are represented by Git tags and GitHub Releases.

## Repository Structure

```
medtech-telemetry-contract/
├── schemas/
│ └── vitals/
│ └── v2.0.json # JSON Schema — vitals payload v2.0
│ └── v2.1.json # JSON Schema — vitals payload v2.1 (if present)
│ └── vitals.schema.json
├── examples/
│ └── vitals/
│ └── v2.0.example.json # Canonical example payload for v2.0
│ └── v2.1.example.json # Canonical example payload for v2.1 (if present)
│ └── vitals.example.json
├── docs/
│ └── vitals-v2.0.md # Human-readable field docs, units, invariants
└── .github/
└── workflows/
└── validate.yml # CI: validates every example against its schema
└── release.yml # CI: workflow to tag and release new schema versions
│ ├── vitals.md
│ └── contract-lifecycle.md
├── scripts/
│ ├── compatibility_guard.py
│ └── extract_release_notes.py
├── CHANGELOG.md
├── VERSION
└── .github/workflows/
├── validate.yml
└── release.yml
```

Future contracts (e.g. `schemas/predictions/sepsis/v1.0.json`) follow the same
pattern: schema → example → doc.

---

## Contracts
## Canonical Contract Paths

| Contract | Schema | Documentation |
|-------------------|----------------------------------|----------------------------|
| Vitals v2.0 | `schemas/vitals/v2.0.json` | `docs/vitals-v2.0.md` |
| Contract | Canonical Schema | Canonical Example | Documentation |
|---|---|---|---|
| Vitals | `schemas/vitals/vitals.schema.json` | `examples/vitals/vitals.example.json` | `docs/vitals.md` |

---
## Versioning and Immutability

## Versioning Policy
- The canonical schema file is mutable on `main`.
- Releases are immutable and identified by SemVer tags: `vMAJOR.MINOR.PATCH`.
- `VERSION` contains the next release version.
- `CHANGELOG.md` records release notes and compatibility class for each release.

Compatibility classes:

- **Versioned filenames:** Each schema and example file is named with its version (e.g. `v2.0.json`, `v2.1.json`). When a new version is released, create new files with the updated version in the filename. Do not overwrite or mutate existing versioned files.
- **Immutable contracts:** Never change a published schema file. Breaking changes require a new versioned file (e.g. `v2.1.json`, `v3.0.json`).
- **Release workflow:** Use the `release.yml` GitHub Actions workflow to tag and release new schema versions. Tags should match the version (e.g. `vitals/v2.1`).
- **Pinning:** Consumers should pin to a specific commit SHA for immutable builds (e.g. Yocto), and may also track the corresponding tag for release notes.
- `BREAKING`: removed/renamed fields, enum narrowing, type narrowing/change, or newly required fields.
- `MINOR`: backward-compatible additions or relaxations.
- `PATCH`: documentation-only or non-contract changes.

---
## Consumer Guidance

## Consuming a Contract

### Option A — Pin by commit SHA (recommended for immutable environments like Yocto)
Consumers should pin to a release tag (or commit SHA) and vendor/reference the canonical schema path.

```bash
# Add as a git submodule pinned to an immutable commit SHA
git submodule add https://github.com/chaithubk/medtech-telemetry-contract contracts/telemetry
cd contracts/telemetry && git checkout <commit-sha>
cd contracts/telemetry && git checkout v2.1.0
```

For human-readable contract reporting, maintain a version marker (for example,
`v2.0.0`) alongside your pinned commit in downstream packaging metadata.

### Option B — Vendor (copy) the schema file
Then use:

1. Copy `schemas/vitals/v2.0.json` into your repository under `contracts/vitals/v2.0.json`.
2. Add a comment at the top of your validation code noting the source tag:
```
# Vendored from chaithubk/medtech-telemetry-contract @ vitals/v2.0
```
3. When the contract is updated, repeat the copy step and bump the tag reference.
- `schemas/vitals/vitals.schema.json`

### Python example
Python:

```python
import json, jsonschema
import json
import jsonschema

with open("contracts/telemetry/schemas/vitals/vitals.schema.json", "r", encoding="utf-8") as f:
schema = json.load(f)

with open("contracts/vitals/v2.0.json") as f:
VITALS_SCHEMA = json.load(f)

def validate_vitals(payload: dict) -> None:
jsonschema.validate(instance=payload, schema=VITALS_SCHEMA)
jsonschema.validate(instance=payload, schema=schema)
```

### JavaScript / TypeScript example
JavaScript / TypeScript:

```js
import Ajv from "ajv";
import schema from "./contracts/vitals/v2.0.json";
import schema from "./contracts/telemetry/schemas/vitals/vitals.schema.json";

const validate = new Ajv().compile(schema);

function validateVitals(payload) {
if (!validate(payload)) throw new Error(JSON.stringify(validate.errors));
export function validateVitals(payload) {
if (!validate(payload)) {
throw new Error(JSON.stringify(validate.errors));
}
}
```

---
## CI Compatibility Governance

## CI Validation
On pull requests, CI:

1. Validates the canonical schema and canonical example.
2. Compares the current schema against the latest released SemVer tag.
3. Detects removed/renamed fields, type changes, enum narrowing, and new required fields.
4. Classifies compatibility as `BREAKING`, `MINOR`, or `PATCH`.
5. Fails if `CHANGELOG.md` and `VERSION` do not match the detected compatibility impact.

### CI Validation
## Release Workflow

Every push and pull-request runs `.github/workflows/validate.yml`, which uses [AJV CLI](https://github.com/ajv-validator/ajv-cli) to validate every example payload in `examples/` against its corresponding schema in `schemas/`. The workflow automatically checks all versioned schemas and examples, ensuring that the example and the schema never diverge.
Release workflow steps:

### Release Workflow
1. Read version from `VERSION`.
2. Ensure matching changelog entry exists.
3. Create and push tag `v<version>`.
4. Generate release notes from the matching changelog section.
5. Publish GitHub Release.

To release a new schema version:
1. Add new versioned schema and example files (e.g. `v2.1.json`, `v2.1.example.json`).
2. Run the `release.yml` workflow from the GitHub Actions tab, specifying the new version (e.g. `v2.1`).
3. The workflow will tag the release and create a GitHub Release entry.
## Comparing Versions

---

## Contributing

1. Add a new schema under `schemas/<domain>/<vX.Y>.json`.
2. Add a canonical example under `examples/<domain>/<vX.Y>.example.json`.
3. Add human-readable docs under `docs/<domain>-<vX.Y>.md`.
4. The CI will automatically validate the example against the schema.
5. Open a PR; tag it `schema:<domain>` for easy discovery.
6. After merge, create a git tag: `git tag <domain>/vX.Y && git push --tags`.

---
```bash
git fetch --tags
git diff v2.0.0..v2.1.0 -- schemas/vitals/vitals.schema.json
```

## License

This project is licensed under **Apache-2.0**. See [`LICENSE`](./LICENSE).
This project is licensed under Apache-2.0. See [LICENSE](LICENSE).
Loading
Loading