Release Architecture
The public browser surfaces are @tellegen/engine, @tellegen/svelte, and
@tellegen/webmcp.
Stable release surfaces:
crates/tellegenRust API layer, including the serde request and response shapes insrc/api.rs;crates/tellegen-wasmwasm adapter;packages/engineTypeScript package, generated TypeScript types, and browser wasm entry points;packages/svelteSvelte component package;packages/webmcpWebMCP tool definitions and runtime validation without a UI framework dependency;- examples under
examples/.
The hosted demo under apps/web is one consumer of the packages. It keeps
routes, SEO, credits, privacy, and deployment specific behavior.
JavaScript Workspace
The repository uses npm workspaces with one root package-lock.json for:
packages/engine;packages/webmcp;packages/svelte;apps/web;examples/browser-minimal; andexamples/svelte-minimal.
Install JavaScript dependencies from the repository root:
npm ci
Root scripts define the package order:
npm run wasmbuilds both wasm packages intopackages/engine;npm run build:enginebuilds@tellegen/engine;npm run build:webmcpbuilds@tellegen/webmcp;npm run build:sveltebuilds@tellegen/svelte;npm run build:examplebuilds both downstream examples;npm run check:web,npm run build:web, andnpm run smoke:webgate the hosted demo;npm run pack:enginepreviews the engine npm package contents;npm run pack:webmcppreviews the WebMCP npm package contents;npm run pack:sveltepreviews the Svelte npm package contents.
Versioning
@tellegen/engine, @tellegen/svelte, and @tellegen/webmcp use independent
package versions.
Before 1.0, releases can refine public APIs while preserving the examples and
hosted demo behavior. After 1.0, breaking public TypeScript, Svelte prop, or
Rust API changes require a semver major version.
Examples of breaking changes after 1.0:
- removing or renaming public exports;
- removing or renaming request or response fields;
- changing enum tags, formulation ids, solve status tags, operand tags, or parameter tags;
- changing field units or meanings;
- tightening optional fields to required fields; and
- changing serialized request or response shapes.
Nonbreaking changes can ship in a minor version:
- adding optional fields;
- adding formulations, operands, parameters, statuses, or helper exports while preserving existing meanings; and
- adding component props with defaults.
Patch versions are for bug fixes and docs that do not change public APIs.
Saved cases
Tellegen saves the materialized network as a stored PowerIO module. The module
keeps applicable records, severs source linkage invalidated by edits, and adds
descriptive edit history. Loading it starts a fresh runtime Study at the
saved point. Its history records transformations applied to the stored value.
Exact OPF results are stored as PowerIO solution modules containing the instance that was solved.
Releasing
Package versions and the crate version move independently, so two release bots drive the pipeline. A release that touches one surface publishes that surface only.
Nobody cuts a tag by hand. This repository stores no registry token. Both
registries authenticate with OIDC, and each exchange runs in a deployment
environment (crates-io, npm). Reviewing and merging the generated version
pull request is the final human publication gate. Publication after that merge
is unattended.
Each environment allows deployments from main only and has no required
reviewer. Set the same environment on the registry’s trusted publisher, along
with the repository and workflow filename. The workflow also rejects non-main
refs; the publisher binding makes the registry enforce that identity.
Enabling the pipeline
The release workflows do nothing until the TELLEGEN_RELEASE_ENABLED repository
variable is true. What they need lives outside the repository. Set the
variable after you make all of these:
- a
crates-ioenvironment and annpmenvironment, each with a deployment branch rule limited tomainand no required reviewers; - a crates.io trusted publisher for
tellegen, set to this repository,release-crate.yml, and thecrates-ioenvironment; - an npm trusted publisher for
@tellegen/engine,@tellegen/svelte, and@tellegen/webmcp, each set to this repository,release-npm.yml, and thenpmenvironment. The publisher binds to the workflow filename. If you rename the workflow, publishing stops until you change the publisher; - a GitHub App on this repository with
contents: writeandpull-requests: write. Put its id in theRELEASE_PLZ_APP_IDvariable and its private key in theRELEASE_PLZ_APP_PRIVATE_KEYsecret. Both release bots use it so their version pull requests start CI. Pull requests opened byGITHUB_TOKENdo not start workflows.
Protect main with a repository ruleset that requires pull requests, at least
one approval, approval of the most recent reviewable push (or dismissal of
stale approvals), an up-to-date branch, and the CI checks before merge. Block
force pushes and deletion. Do not give the Release App a ruleset bypass. Those
protections ensure an approval cannot survive a bot refresh with different
release contents and make the version pull-request merge the publication gate.
If a package or crate does not yet exist at its registry, publish its first version manually before configuring the trusted publisher. Later releases use this pipeline end to end.
Delete any NPM_TOKEN or CARGO_REGISTRY_TOKEN repository secrets. The
workflows do not read long-lived registry credentials.
Packages
@tellegen/engine, @tellegen/svelte, and @tellegen/webmcp release through
changesets.
A change to any of these packages needs a changeset. Run npm run changeset
and commit the file it writes. See .changeset/README.md.
On a push to main, .github/workflows/release-npm.yml keeps a “Release
packages” pull request open. That pull request applies every pending changeset:
version bumps, changelog entries, the engine CONTRACT_VERSION, and the
lockfile. Merge it to run the gates and publish. Tags take the form
@tellegen/<name>@X.Y.Z.
The workflow selects version or publish mode before it requests privileged
permissions. Versioning runs without the GitHub App, validates an exact output
allowlist, and passes a SHA-256-verified patch to a fresh runner. That runner
revalidates and commits the patch before minting the App token; only git and
gh run afterward. Publishing builds immutable tarballs in an unprivileged
job, then gives only the final npm job the npm environment and OIDC
permission. Merging the version pull request is therefore the only manual
release action.
@tellegen/svelte resolves @tellegen/engine from the registry, so a release
that moves both publishes the engine first.
Crate
tellegen is the only crate that publishes to crates.io. tellegen-wasm,
tellegen-server, tellegen-cli, and benchmarks carry publish = false, and
the release-plz workspace defaults to release = false. A new crate needs an
explicit package opt-in.
On a push to main, .github/workflows/release-crate.yml keeps a pull request
open that bumps the version. Merge it to run the gates, continue the existing
vX.Y.Z tag series, make the GitHub release, and publish. The configuration
sets release_always = false, limiting publication to a merged release-plz pull
request. The unprivileged gate runs cargo package --locked; the OIDC-enabled
release uses the ordinary verified Cargo publish path against that exact
commit. release-plz obtains its short-lived crates.io credential directly from
OIDC.
The crate update uses the same sealed-patch privilege boundary as package
versioning. Its fixed release-plz-main branch is intentional: with
release_always = false, release-plz recognizes a release commit by the
release-plz- head prefix. Merge this generated pull request with a normal
merge commit, not squash, so its release commit remains unambiguous if another
change reaches main nearby.
An emergency repair to a generated crate release must retain its
release-plz-* branch name; that is how release_always = false recognizes the
merged release commit.
Inspecting an artifact
.github/workflows/package-inspect.yml is a manual run. It builds the artifacts
a release would upload, cargo package and all three npm pack tarballs, and
attaches them to the run. It publishes nothing.
Changelogs
Each surface keeps its own generated changelog:
crates/tellegen/CHANGELOG.md, packages/engine/CHANGELOG.md, and
packages/svelte/CHANGELOG.md, and packages/webmcp/CHANGELOG.md. The root
CHANGELOG.md is the curated view across all four and the only record for
releases before the split.
CI Gates
.github/workflows/gates-rust.yml and .github/workflows/gates-js.yml define
what green means. CI calls both on every pull request. The crate release calls
the Rust gates, and the npm release calls both. Add a gate to those files, not
to a caller.
The Rust gates are cargo fmt --check, clippy with warnings denied on the
shipping crates, cargo-deny, the EPL guard, the workspace tests, the engine’s
conic path, tellegen-wasm built with conic, and cargo package --locked
for the published crate. The wasm test matters
because tellegen-wasm declares default = [], so the workspace run skips the
tests that assert an untrusted package or case rejects rather than panicking.
The JavaScript gates install once from the root lockfile, check and pack
packages/webmcp, build and check packages/engine before packages/svelte,
run the engine import smoke test, check the Svelte package through a packed
temporary consumer, build the hosted demo, and run its browser tests. just ci
runs everything locally.