Publication Pipeline
The publication pipeline converts an exact stored workflow revision into an immutable registry release. It never executes workflow content and never chooses participant work. Its responsibilities are deterministic compilation, manifest signing, release insertion, idempotent job handling, and optional channel activation.
This sequence is implemented by PublicationService.publish and compiler classes in src/rimthan_publication.
PublicationService
PublicationService is in src/rimthan_publication/service.py. It accepts a PostgresRegistryStore and a compiler. publish(...) first creates or reloads a publication job keyed by workflow, revision, release, channel, actor, and idempotency key.
Important behavior:
- If a job is already
published, the service returns the existing release and ensures requested channel activation is consistent or completed. - If a job is not
pendingorpublished, it raisesConcurrencyError. - It marks the job
compilingbefore loading the revision and compiling. - It verifies the compiled semantic source digest equals the stored revision digest.
- It inserts the compiled release, marks the job published, then activates a channel only after the immutable release exists.
- Any exception marks the job failed before re-raising.
tests/test_postgres_publication_e2e.py covers idempotency conflicts, activation generation conflicts, retry recovery, and rollback.
Legacy module-graph compiler
WorkflowDraftCompiler compiles legacy workflow-draft documents. It resolves exact module references and optional inline candidate modules from a source release, enforces one version per module ID, checks LF-terminated instructions, rejects mutation of an already published module version, resolves presentation bundles, validates graph reachability and dependency closure, emits module and presentation artifacts, and signs a manifest with schema version 1.0.0 or 1.1.0 depending on presentation entries.
This path is retained for legacy/operator compatibility and Studio migration. It is not the current participant workflow-engine protocol.
Workflow-engine aggregate compiler
WorkflowEngineDraftCompiler compiles current step-gate workflow drafts. It validates the full engine draft via validate_engine_draft, computes semantic_engine_draft_sha256, and produces manifest schema version 2.0.0.
Aggregate publication is deliberately cumulative. The compiler begins with all modules and presentation bundles from the source release, then merges workflow definitions, workflow steps, registry assets, and validator definitions from the source release with the draft's current entries. _merge_immutable_identity rejects a changed byte sequence for an existing identity and instructs the admin to choose a new version. When the compiled release is inserted, PostgresRegistryStore._insert_release also carries forward release_workflow_sources rows from the source registry release for every workflow except the one being published, preserving each retained workflow's source_kind, legacy route module fields, and step-gate workflow_version before inserting the new workflow source row.
The manifest includes:
modulesandpresentation_bundlesretained for legacy compatibility;workflow_definitionswith workflow ID, version, path, digest, size;workflow_stepswith workflow ID/version, step ID, path, digest, size;registry_assetswith kind, asset ID, version, path, digest, size;validator_definitionswith validator ID, version, path, digest, size;- publisher, source revision, release, release mode, and signatures.
After signing, _verify_compiled_release writes the manifest and artifacts into a temporary release directory and reloads it through ReleaseLoader with the target trust root and exact manifest digest. A release that cannot be loaded and trusted is never inserted.
Signing
Ed25519ManifestSigner signs manifest_signing_bytes(manifest with signatures = []), using the domain separator defined in rimthan_workflow_registry.trust. It returns a manifest with one signature envelope containing key ID, algorithm ed25519, and unpadded base64url signature. trust_root(...) exports a public-key trust root scoped to registry ID and publisher.
Signing is discussed further in Release Trust and Loader.
Channel activation and rollback
Publication can activate a release on a named channel by calling store.activate_release. Activation is generation-fenced with expected_channel_generation. Rollback is the same operation applied to an older immutable release ID; no release content changes.
Database-backed MCP reads the channel, loads and verifies the selected release, and refreshes its in-memory scope. tests/test_admin_api.py proves an MCP client observes release 0.3.1, then rollback to 0.3.0, then a workflow-engine aggregate publication with multiple workflow definitions.
Extension rules
When adding a new immutable artifact family:
- add a manifest section/schema and model entry;
- teach
ReleaseLoaderto load, validate, and identity-check it; - extend compiler output and
_compiled_manifest_diffif Studio must preview it; - update PostgreSQL release artifact kind checks and identity immutability triggers;
- add Admin API release detail if users need dependency visibility;
- add focused tests for immutable identity reuse and changed-byte rejection.
Focused validation
- Compiler and publication unit coverage:
uv run pytest tests/test_workflow_publication.py tests/test_workflow_engine.py -k compiles - PostgreSQL publication and rollback:
RIMTHAN_TEST_POSTGRES_DSN=... uv run pytest tests/test_postgres_publication_e2e.py - Admin API end-to-end:
RIMTHAN_TEST_ADMIN_POSTGRES_DSN=... uv run pytest tests/test_admin_api.py