Open-source design-controlled development infrastructure for medical device and SaMD teams — includes dhfkit
Project description
MedHarness
Design-controlled AI development for medical device software.
Building software for a medical device means every requirement, risk, architectural decision, and test has to be traced and documented in a Design History File (DHF) — before code ships, and in a form that holds up under FDA or notified body scrutiny.
MedHarness makes that work AI-assisted without making it ungoverned. It gives Claude a structured role in your DHF workflow — generating design items, implementing code, managing SOUP, building release records — while keeping you in the loop at every approval gate. The agent executes; you decide when to advance.
Install
pip install medharness[full]
[full] includes optional extras: ai (AI review) and docs (PDF export via WeasyPrint). Leave it off for a minimal install — dhfkit is always included either way.
From source:
git clone https://github.com/itercharles/MedHarness
cd MedHarness
pip install -e ".[dev]"
pytest dhfkit/tests/ tests/
Quick start
# 1. Scaffold a new DHF project
mkdir my-device && cd my-device
python -m venv .venv && source .venv/bin/activate
pip install medharness
medharness init
medharness init gives you a working DHF with sample requirements, risks, traceability config, document templates, and plans:
my-device/
├── DHF/
│ ├── config/ # project name, doc type schemas
│ ├── items/ # one YAML file per requirement, risk, CR, etc.
│ │ ├── 01_crs/ # Customer Requirements
│ │ ├── 02_sys/ # System Requirements
│ │ ├── 03_srs/ # Software Requirements
│ │ ├── 07_cr/ # Change Requests
│ │ └── ... # Use Cases, SOUP, Risk, RCM, Releases, Defects
│ └── documents/ # Jinja2 spec templates and plan documents
├── AI-harness/
│ └── context.md # Product context for AI agents
└── .github/
└── workflows/
└── dhf.yml # CI: validate on PR, evidence bundle on main
Replace the sample items with your own content, then commit:
# 2. Replace sample content and commit
git init && git add -A && git commit -m "feat: initialize DHF"
# 3. Edit DHF/items/07_cr/CR-001.yaml with your first change request, then run design phase
medharness --dhf DHF ci generate-dhf --cr CR-001
# → Claude triages the CR, generates DHF items, writes an implementation plan, opens a PR
# 4. After the design PR is reviewed and approved, run the implementation phase
medharness --dhf DHF ci develop-cr --cr CR-001
# → Claude implements code, annotates tests, verifies coverage, opens a code PR
The scaffolded project includes .github/workflows/dhf.yml — a GitHub Actions workflow that runs dhf-validate on every DHF PR and produces an evidence bundle on merge to main.
Coming from an existing system
If you have an existing DHF in Excel, Jira, Polarion, or a custom format, migration is writing YAML files — one per requirement, risk, or CR record. MedHarness items map directly to familiar artifact types: requirements spreadsheets become SRS/SYS items, risk registers become RISK + RCM items, SOUP lists become SOUP items.
See docs/adopting.md for the full mental model: starting fresh, migrating an existing DHF, using dhfkit standalone, and adopting incrementally.
How it works
Every non-trivial change flows through a Change Request (CR) in the DHF. MedHarness runs Claude in two phases, validates the output deterministically, and opens a PR for your review before anything moves forward.
Phase 1 — Design (generate-dhf)
Claude reads the CR, triages it (duplicate? out-of-scope? too large?), then reads the relevant source modules and reasons top-down through the V-model hierarchy:
CR → CRS → SYS → { SYSARCH, RISK, RCM } → SRS → SWDD
It writes DHF items via the CLI, validates schema and traceability inline, and finally produces a detailed implementation plan in implementation_notes on the CR item. A PR opens for your review — you see the design items and the implementation plan before any code is written.
Phase 2 — Implementation (develop-cr)
After the design PR is approved, Claude reads implementation_notes and the linked DHF items, implements the code, annotates tests with @links: requirement IDs, runs medharness ci test-coverage to verify every requirement has a passing test, and reconciles any deviations back onto implementation_notes and the SWDD items.
At each phase, MedHarness pre-computes DHF context — item lists, traceability graph, coverage gaps — and injects it into Claude's prompt so the agent reasons about your actual DHF rather than guessing. After Claude runs, a deterministic validator checks schema, traceability links, and test annotations, and self-corrects if it can.
Who it's for
- Medical device software teams working under IEC 62304, FDA 21 CFR 820.30, or MDR who want AI help that doesn't bypass the process
- Platform / DevOps engineers building regulated CI pipelines who need programmatic hooks into DHF validation and evidence generation
- Startups bootstrapping a DHF alongside their product without a dedicated RA team writing everything by hand
dhfkit — the DHF engine inside MedHarness — also works standalone if you only need YAML-based item storage, traceability graphs, and document generation without the full CR workflow.
CR workflow
# Phase 1 — Claude triages the CR, generates DHF items, and writes an
# implementation plan. Opens a PR for design review.
medharness --dhf DHF ci generate-dhf --cr CR-034
# Phase 2 — after the design PR is approved, Claude implements the code,
# verifies test coverage, and reconciles any deviations back onto the DHF.
medharness --dhf DHF ci develop-cr --cr CR-034
Got review comments on a design PR? Pass --pr N to revise based on the feedback:
medharness --dhf DHF ci generate-dhf --cr CR-034 --pr 42
medharness --dhf DHF ci develop-cr --cr CR-034 --pr 42
ANTHROPIC_MODEL selects the Claude model. GH_TOKEN is required when using --pr.
Each command outputs structured JSON with outcome, errors, timing, and artifact paths — so CI automation can act on results without parsing text.
CR lifecycle
A CR moves through these states as the workflow progresses:
| State | Meaning |
|---|---|
new |
CR created, awaiting design |
design |
generate-dhf has run; design PR open for review |
develop |
Design approved; develop-cr has run; code PR open |
completed |
Code PR merged |
cancelled |
PR closed without merging |
rejected |
Triage determined the CR is out-of-scope, duplicate, or too large |
States are recorded for traceability and audit. Only completed CRs may be included in a release baseline — cancelled and rejected CRs are not deliverables.
CI gates
DHF schema and traceability
medharness ci dhf-validate --dhf DHF
Validates item schemas, required fields, and traceability links across the entire DHF.
Requirement-to-test coverage
medharness ci test-coverage --dhf DHF --junit-dir test-results
Reads JUnit XML test results and checks that every verifiable requirement has at least one linked passing test. Tests link to DHF items via @links:<ITEM_ID> annotations in their source, surfaced through JUnit properties. Exits non-zero when gaps exist.
Branch and code validation
# Verify the CR's design items and traceability are complete before merging
medharness --dhf DHF ci validate-branch --cr CR-034
# Run deterministic implementation validation (schema + coverage) without Claude
medharness --dhf DHF ci validate-code --cr CR-034
Evidence bundle
medharness ci evidence bundle --dhf DHF --out-dir artifacts --junit-dir test-results
Produces a timestamped DHF snapshot and test evidence archive, typically run on merge to main.
Approval gating and stage management
# Guard against acting on the wrong event — check that a stage has an explicit approval
medharness ci approve-gate --cr CR-034 --stage design --pr 42
# Report machine-readable CR stage and label state (for routing in CI)
medharness --dhf DHF ci cr-status --cr CR-034 --pr 42
# Advance the stage label on a PR (removes old label, adds new one, idempotent)
medharness ci advance-stage --pr 42 --from-stage design --to-stage develop
# Parse a PR comment for /approve or /reject commands
medharness ci parse-approval --comment "$COMMENT_BODY"
SOUP management and release baseline
SOUP synchronisation (IEC 62304 §5.3.3)
soup-sync reads your package manifests and diffs them against SOUP items already in the DHF, reporting new dependencies, version drift, and orphaned records.
# Dry-run: show what would change
dhfkit --dhf DHF soup-sync \
--manifest requirements.txt \
--manifest apps/client/package.json
# Apply creates and updates
dhfkit --dhf DHF soup-sync \
--manifest requirements.txt \
--write --author "ci" --cr CR-034
Supports requirements.txt (pinned == entries only) and package.json (dependencies, devDependencies, peerDependencies). Fuzzy name matching handles hyphen/underscore/case variants; duplicate packages across manifests are deduplicated automatically.
Release baseline (IEC 62304 §9)
release-baseline verifies that all included CRs are completed, collects a software BOM from DHF SOUP items and package manifests, and writes release-baseline.json and software-bom.json to --out-dir.
# Dry-run: auto-collect completed unreleased CRs, write artifacts
dhfkit --dhf DHF release-baseline \
--version 1.0.0 \
--manifest requirements.txt \
--out-dir artifacts/release
# Explicitly specify CRs and create a REL item in the DHF
dhfkit --dhf DHF release-baseline \
--version 1.0.0 \
--cr CR-030 --cr CR-031 --cr CR-034 \
--manifest requirements.txt \
--out-dir artifacts/release \
--write --author "ci"
When --cr is omitted, all completed CRs not yet referenced in any existing REL item are collected automatically. The gate exits non-zero if any specified CR is not in completed state.
Automation model
MedHarness ships a minimal GitHub Actions workflow (scaffolded into new projects by medharness init). The stable interface is the CLI — wire it into whatever automation layer fits your team (GitHub Actions, GitLab CI, Jenkins, local scripts):
# ── DHF data operations (dhfkit) ────────────────────────────────────────────
dhfkit --dhf DHF item list --type SYS
dhfkit --dhf DHF item get CR-034
dhfkit --dhf DHF item transition CR-034 completed --by "alice"
dhfkit --dhf DHF validate schema
dhfkit --dhf DHF validate traceability
dhfkit --dhf DHF doc generate SYS
dhfkit --dhf DHF doc export SYS # PDF (requires [docs])
dhfkit --dhf DHF report # human-readable traceability coverage
# ── AI harness context (medharness) ─────────────────────────────────────────
medharness --dhf DHF dhf context implementation \ # AI/CI context bundle for a CR
--cr CR-034 --out-dir /tmp/ctx
# ── AI-assisted CR workflow ──────────────────────────────────────────────────
medharness --dhf DHF ci generate-dhf --cr CR-034 # design phase
medharness --dhf DHF ci develop-cr --cr CR-034 # implement phase
# ── SOUP and release ─────────────────────────────────────────────────────────
dhfkit --dhf DHF soup-sync --manifest requirements.txt
dhfkit --dhf DHF release-baseline --version 1.0.0 --out-dir artifacts/release
# ── CI gates ─────────────────────────────────────────────────────────────────
medharness ci dhf-validate --dhf DHF
medharness ci test-coverage --dhf DHF --junit-dir test-results
medharness ci evidence bundle --dhf DHF --out-dir artifacts
medharness --dhf DHF ci validate-branch --cr CR-034
medharness --dhf DHF ci validate-code --cr CR-034
# ── Observability and event routing ──────────────────────────────────────────
medharness --dhf DHF ci cr-status --cr CR-034 --pr 42
medharness ci approve-gate --cr CR-034 --stage design --pr 42
medharness ci advance-stage --pr 42 --from-stage design --to-stage develop
medharness ci parse-approval --comment "$COMMENT_BODY"
medharness ci github-event --event "$GITHUB_EVENT_PATH"
Python API
dhfkit — no dependency on medharness:
from dhfkit.local_adapter import LocalDHFAdapter
adapter = LocalDHFAdapter(Path("DHF"))
items = adapter.list_items("SRS")
Repository layout
| Directory | Purpose |
|---|---|
medharness/ |
CLI harness, CI gates, CR workflows, init scaffolding |
dhfkit/ |
DHF engine: items, lifecycle, traceability, document generation |
dhfkit/templates/ |
Starter DHF scaffold — config, specs, plans, sample items |
tests/ |
MedHarness and dhfkit test suites |
docs/ |
Architecture, ADRs, compatibility contracts |
dhfkit has no dependency on medharness and can be used on its own.
Docs
- docs/adopting.md — starting fresh, migrating an existing DHF, incremental adoption
- docs/architecture.md — packages, scaffold model, DHF lifecycle
- docs/adr/ — architecture decision records
- CHANGELOG.md — version history
License
MIT — see LICENSE.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file medharness-0.8.0.tar.gz.
File metadata
- Download URL: medharness-0.8.0.tar.gz
- Upload date:
- Size: 158.8 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
a7e8a08d8e7c342b9e4d7e737d9ec5a114906eea4b5eb2e156127813a30f04a4
|
|
| MD5 |
66213de1cfa2b2de2ad66e58e6e6010d
|
|
| BLAKE2b-256 |
98d0baf3609624ff2431fc9b97b90a92052f318c583a544b0ef3e6d8cb4ca225
|
Provenance
The following attestation bundles were made for medharness-0.8.0.tar.gz:
Publisher:
release.yml on itercharles/MedHarness
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
medharness-0.8.0.tar.gz -
Subject digest:
a7e8a08d8e7c342b9e4d7e737d9ec5a114906eea4b5eb2e156127813a30f04a4 - Sigstore transparency entry: 1572243485
- Sigstore integration time:
-
Permalink:
itercharles/MedHarness@b16ab4b5cdf7364aebdb8021129413bc9bbaa2cd -
Branch / Tag:
refs/tags/v0.8.0 - Owner: https://github.com/itercharles
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@b16ab4b5cdf7364aebdb8021129413bc9bbaa2cd -
Trigger Event:
push
-
Statement type:
File details
Details for the file medharness-0.8.0-py3-none-any.whl.
File metadata
- Download URL: medharness-0.8.0-py3-none-any.whl
- Upload date:
- Size: 200.4 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
ff437d3079b96ff13d9a60b00c268859ddf259217d30cc41921797ae88b430ef
|
|
| MD5 |
8f34147edfcc1c2f9243befe937626d1
|
|
| BLAKE2b-256 |
5a3d24f0e2623ad7729bfbdf4239fb88fed30c5ba322059002b9df955ca52e2a
|
Provenance
The following attestation bundles were made for medharness-0.8.0-py3-none-any.whl:
Publisher:
release.yml on itercharles/MedHarness
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
medharness-0.8.0-py3-none-any.whl -
Subject digest:
ff437d3079b96ff13d9a60b00c268859ddf259217d30cc41921797ae88b430ef - Sigstore transparency entry: 1572243552
- Sigstore integration time:
-
Permalink:
itercharles/MedHarness@b16ab4b5cdf7364aebdb8021129413bc9bbaa2cd -
Branch / Tag:
refs/tags/v0.8.0 - Owner: https://github.com/itercharles
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@b16ab4b5cdf7364aebdb8021129413bc9bbaa2cd -
Trigger Event:
push
-
Statement type: