Desktop-first Enterprise Capability Model studio with Git-managed JSONL storage.
Project description
ECM Studio
ECM Studio is a Windows-first desktop application for managing an Enterprise Capability Model as local, Git-managed JSONL files. It gives architects and capability owners a focused workspace for editing capability trees, reviewing change history, publishing model snapshots, and exporting portable artifacts without putting the authoritative model in a database.
Highlights
- Desktop UI built with React, Dockview, Fluent UI, and pywebview.
- Durable model storage in readable JSONL files under
ecm/. - Git-native workflow for checkpoints, scenarios, merges, restore, pull, and push.
- SQLite projection rebuilt locally for navigation and search speed.
- Capability map view with SVG and HTML export.
- Direct structural operations for retire, merge, and controlled Draft leaf delete.
- Model import/export for JSONL, CSV, and bundled JSON.
- Release workflow for tagged ECM model exports and GitHub release publication.
- Light/dark theme support with native Windows chrome integration.
Install
ECM Studio requires Python 3.13 or newer. Install it from PyPI:
pip install ecm-studio
On Windows, if pip points at a different Python installation, use the Python
launcher instead:
py -m pip install ecm-studio
With uv, install ECM Studio as a command-line tool:
uv tool install ecm-studio
Or install it into the current uv-managed environment:
uv pip install ecm-studio
Start the desktop app:
ecms
Open a workspace directly:
ecms C:\path\to\capability-model-repo
Check the installed package version:
ecms --version
Workspace Model
An ECM Studio workspace is a normal Git repository. The application stores the
authoritative model in ecm/*.jsonl files and keeps local runtime state in
.ecm-studio/, which should stay ignored by Git.
The SQLite database is only a local projection. It can be rebuilt from JSONL at any time and is not the source of truth.
Common Workflows
Open Or Create A Workspace
Use the workspace panel to open an existing Git-backed ECM repository or create a
new one. ECM Studio writes the model to ecm/*.jsonl, rebuilds the local SQLite
projection for search and navigation, and keeps runtime files under
.ecm-studio/.
Bootstrap An Existing Model
Many teams already have a capability model in a spreadsheet, architecture tool, or another repository. To bootstrap ECM Studio, first create or open the target workspace, then convert the existing model to CSV, JSONL, or bundled JSON and use the Import / Export panel.
Recommended first import flow:
- Prepare stable capability IDs. Use short durable IDs from the source system
when possible, because
parent_idandreplacement_capability_iddepend on those IDs. - Put top-level capabilities first when producing CSV or JSONL. The importer validates by ID rather than row order, but parent-first files are easier to inspect and review.
- Choose Validate only and preview the file. Fix duplicate IDs, duplicate names, missing parents, and hierarchy cycles before applying.
- Choose the apply mode. Use Replace current model for the first load of a new ECM Studio repo, Append new capabilities when adding new branches to an existing model, or Merge by ID when refreshing records from an external source while keeping the same stable IDs.
- Preview again in the selected apply mode, then apply the import. ECM Studio
writes
ecm/capabilities.jsonl, records a model import event, rebuilds the SQLite projection, and refreshes the workspace views.
If the workspace is already a Git repository, replace and merge-by-ID imports create a pre-import checkpoint before writing the imported model.
Bootstrap Schema
JSONL imports use one capability object per line. Bundled JSON imports use the same capability objects inside a wrapper:
{
"_t": "ecm_model_bundle",
"schema_version": "1.0",
"capabilities": []
}
CSV imports support the common bootstrap columns listed below. For JSONL and
bundled JSON, the same fields are accepted as JSON object properties, with
aliases, tags, and source_references represented as arrays. JSON imports
reject unknown fields so schema mistakes fail during preview.
| Field | Required | CSV | Notes |
|---|---|---|---|
_t |
No | Filled by importer | Use capability when present in JSONL or bundled JSON. |
schema_version |
No | Filled by importer | Use 1.0 when present in JSONL or bundled JSON. |
id |
No | Yes | Stable unique ID. Generated when omitted. |
name |
Yes | Yes | Capability names must be unique after trimming and case normalization. |
parent_id |
No | Yes | Parent capability ID. Blank means top-level capability. |
order |
No | Yes | Non-negative sibling order. Defaults to 0 when omitted. |
domain |
No | Yes | Free-text business or architecture domain. |
type |
No | Yes | leaf or abstract; imported value is recomputed from hierarchy. |
lifecycle_status |
No | Yes | Draft, Active, Deprecated, or Retired. Defaults to Draft. |
description |
No | Yes | Free-text description. |
aliases |
No | Yes | CSV accepts semicolon- or comma-separated aliases. JSON uses an array. |
tags |
No | Yes | CSV accepts semicolon- or comma-separated tags. JSON uses an array. |
steward_id |
No | Yes | Owner or steward identifier. |
steward_department |
No | Yes | Steward organization or department. |
replacement_capability_id |
No | Yes | Successor capability ID for retired or merged capabilities. |
effective_from |
No | JSON only | ISO-style date or timestamp for when the capability became effective. |
effective_to |
No | JSON only | ISO-style date or timestamp for retirement or end of validity. |
rationale |
No | JSON only | Reason for the current lifecycle or structural state. |
source_references |
No | JSON only | Array of source-system links, IDs, or references. |
created_at |
No | JSON only | Timestamp generated when omitted. |
updated_at |
No | JSON only | Timestamp generated when omitted. |
Minimal CSV example:
id,name,parent_id,order,domain,type,lifecycle_status,description,aliases,tags,steward_id,steward_department,replacement_capability_id
cap-payments,Payments,,0,Banking,abstract,Active,Payment capabilities,Payments Hub;Pay,finance,owner-123,Banking,
cap-domestic-payments,Domestic Payments,cap-payments,0,Banking,leaf,Active,Domestic clearing and settlement,Local transfers,finance,owner-123,Banking,
Minimal JSONL example:
{"_t":"capability","schema_version":"1.0","id":"cap-payments","name":"Payments","parent_id":null,"order":0,"domain":"Banking","lifecycle_status":"Active","description":"Payment capabilities","aliases":["Payments Hub","Pay"],"tags":["finance"],"steward_id":"owner-123","steward_department":"Banking"}
{"_t":"capability","schema_version":"1.0","id":"cap-domestic-payments","name":"Domestic Payments","parent_id":"cap-payments","order":0,"domain":"Banking","lifecycle_status":"Active","description":"Domestic clearing and settlement","aliases":["Local transfers"],"tags":["finance"],"steward_id":"owner-123","steward_department":"Banking"}
Add Or Edit Capabilities
Create capabilities from the tree, then use the inspector to edit descriptive
fields, lifecycle status, parent placement, aliases, and external references.
Capability type is derived from hierarchy: capabilities with children are
abstract, and capabilities without children are leaves. When edits change that
derived type, ECM Studio records promote or demote audit events.
Retire A Capability
Select a capability and choose Retire from the inspector's structural
actions. Provide a rationale, optionally choose a replacement capability, and
capture any downstream handling notes. Retiring sets the capability lifecycle to
Retired, sets an effective end date, stores the rationale in audit history,
and preserves replacement_capability_id when a replacement is selected.
Use an optional replacement when the capability should no longer be used but there is a clear successor that consumers should move to. The replacement link is useful because it gives architects, reviewers, search users, and downstream consumers an explicit redirect from the retired capability to the active one. That keeps historical references understandable, makes migration decisions auditable, and avoids leaving teams to infer the intended successor from names or notes. Leave it blank when the capability is ending without a direct successor.
Merge Duplicate Capabilities
Select the duplicate source capability and choose Merge. Pick the survivor, enter a rationale, and capture downstream handling notes. ECM Studio moves the source children under the survivor, folds the source name and aliases into the survivor aliases, and prevents merge targets that would create a hierarchy cycle.
Draft sources are removed after the merge. Non-Draft sources remain in the
model as Retired records with replacement_capability_id pointing at the
survivor, so historical references still have a visible redirect.
Delete An Erroneous Draft Leaf
Use Delete Draft only for capabilities that are both Draft and leaf nodes.
ECM Studio rejects delete attempts for non-Draft capabilities or capabilities
with descendants. Deleting requires a rationale, records audit evidence, removes
the capability from JSONL, rebuilds SQLite, and refreshes the UI.
Review, Checkpoint, And Publish
Review the audit panel after structural changes. It shows direct actions such as
retire, delete, and merge, plus derived promote and demote events when
hierarchy changes alter capability type. Run diagnostics, create a Git
checkpoint, and use the release workflow or JSONL, CSV, bundled JSON, SVG, and
HTML exports when the model is ready to share.
Development
Install Python and frontend dependencies:
py -m pip install -e .[dev]
npm install --prefix ui
Run the Vite dev server and launch the desktop shell against it:
npm run dev --prefix ui
py -m ecm_studio --dev-ui http://localhost:5173
Build the frontend assets:
npm run build --prefix ui
Run checks:
ruff check src tests scripts
pytest -q
npm run lint --prefix ui
npm test --prefix ui
npm run typecheck --prefix ui
Packaging And Releases
The Python package uses Hatchling and reads its version from
src/ecm_studio/__init__.py.
Cut a release from a clean working tree:
python scripts/release.py 0.1.1
git push origin master v0.1.1
The publish.yml workflow builds the React UI, stages it into the wheel, checks
the distribution with Twine, verifies packaged UI assets, and publishes tagged
v* releases to PyPI through trusted publishing.
Open the next development cycle after a release:
python scripts/release.py --post-release 0.1.2.dev0
License
ECM Studio is released under the MIT 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 ecm_studio-0.4.0.tar.gz.
File metadata
- Download URL: ecm_studio-0.4.0.tar.gz
- Upload date:
- Size: 1.2 MB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
94a6aaf89ad5adb214caa16c23211418293517c4cce6c6265e463d18d970240b
|
|
| MD5 |
80d4edd027d7210d35ca713d347d7288
|
|
| BLAKE2b-256 |
48fa2f0714abef1039dde66fd9f0c95ac8258f14583c71f7c074eee8d374567b
|
Provenance
The following attestation bundles were made for ecm_studio-0.4.0.tar.gz:
Publisher:
publish.yml on ThomasRohde/ecm-studio
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
ecm_studio-0.4.0.tar.gz -
Subject digest:
94a6aaf89ad5adb214caa16c23211418293517c4cce6c6265e463d18d970240b - Sigstore transparency entry: 1392583342
- Sigstore integration time:
-
Permalink:
ThomasRohde/ecm-studio@ceeeac1449a27c2aac8594b87da8c46d57ef89f1 -
Branch / Tag:
refs/tags/v0.4.0 - Owner: https://github.com/ThomasRohde
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@ceeeac1449a27c2aac8594b87da8c46d57ef89f1 -
Trigger Event:
push
-
Statement type:
File details
Details for the file ecm_studio-0.4.0-py3-none-any.whl.
File metadata
- Download URL: ecm_studio-0.4.0-py3-none-any.whl
- Upload date:
- Size: 843.8 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 |
43d06f7c1e181ae4daed01c0dfe02b7d58b07adc51e3ad0eafe778a0780142e2
|
|
| MD5 |
1a38e4706d76b8151b98287306347344
|
|
| BLAKE2b-256 |
86ad7d5a33773b52538be43009310c3ae51e2b3b47ea6fd28c4a86e886e1207b
|
Provenance
The following attestation bundles were made for ecm_studio-0.4.0-py3-none-any.whl:
Publisher:
publish.yml on ThomasRohde/ecm-studio
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
ecm_studio-0.4.0-py3-none-any.whl -
Subject digest:
43d06f7c1e181ae4daed01c0dfe02b7d58b07adc51e3ad0eafe778a0780142e2 - Sigstore transparency entry: 1392583349
- Sigstore integration time:
-
Permalink:
ThomasRohde/ecm-studio@ceeeac1449a27c2aac8594b87da8c46d57ef89f1 -
Branch / Tag:
refs/tags/v0.4.0 - Owner: https://github.com/ThomasRohde
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@ceeeac1449a27c2aac8594b87da8c46d57ef89f1 -
Trigger Event:
push
-
Statement type: