JMA
Version 2.0.0
API version: 10.0.0
Releases as 2.0.0 (MAJOR). Two of the three MAJOR criteria in README “Choosing the version” apply:
the REST contract breaks (the Admin/RestoreJobReadModels endpoint is removed and the input
SetFolderSource action takes FolderUrl instead of a Folder path — hence the ApiVersion major
step to 10.0.0), and the database change needs manual intervention when upgrading (the event-store
cutover is irreversible, drops columns the previous release still reads, installs a legacy-write fence,
and requires a maintenance window). RabbitMQ message contracts are unchanged.
Added
- Action templates can source a collection input data slot from a folder, the same way jobs can (
SetFolderSourceaction on a template input data slot, recursive by default; mutually exclusive with item-by-item selection). The folder is stored as a snapshot path; jobs created from the template — including those started by a trigger — resolve and materialize the folder’s current contents at their own execution time, so a triggered template becomes an “inbox” that processes whatever is in the folder on each run. CreateJobaccepts an optionalFolderto create the job directly inside a folder (omit for the root). The target folder must exist for the owner, mirroring theMoveToFolderaction.- Output data slots can be directed into a folder (
SetOutputFolderaction on a job’s output data slot). The slot stores the folder as a snapshot path; the WorkData produced for that slot is filed into the folder (created if missing) when the output placeholders are built at execution time. An empty or root path clears the target, writing the output to the owner’s root (the default, unchanged behaviour).
Changed
- Job and template input
SetFolderSourceactions acceptFolderUrlinstead of aFolderpath. Send an existing folder’s resource URL, or explicitnullfor the owner’s root. The selected folder must belong to the job/template owner; its current path remains the stored snapshot. Output folder destinations still accept paths and can be created at execution time. - Jobs are no longer event-sourced. The event-sourced
JobAggregateand its read-model projection are replaced by a single plainJobdomain entity persisted through EF Core viaJobDto/IJobRepository. Writes now happen in a single transaction guarded by anEntityVersionoptimistic-concurrency token (no Marten event store, no dual write, no read-model repair jobs). Job REST payloads and action availability are unchanged (availability is computed live from the domainJobinstead of persisted*OpAvailablecolumns).
Removed
- The transitive Marten event-store dependency, through Foundation 3. Legacy migration tests use captured PostgreSQL schema and event fixtures, so the test projects do not require Marten either.
- The
Admin/RestoreJobReadModelsaction and endpoint (routeAdmin_RestoreJobReadModels). Job read models are no longer a separate projection that can lag, so there is nothing to rebuild. - Operator-facing: the second Postgres connection pool
PostgresEventStoreDband theJMA_EventStorehealth check (the event store shared the main database and is gone). - The
CopyToOrgandCopyToUseractions on a ProcessingStep, with their endpoints (routesProcessingStep_CopyPsFromUserToOrgandProcessingStep_CopyPsFromOrgToUser). Copying a ProcessingStep between a user and an organization never produced a usable step: the copy is given the destination owner, but job offers are routed on the owner (Job.<OwnerId>.<FunctionName>.<Version>.JobOffer) and the copy is created undeployed, so no worker was ever bound to the copy’s routing key and jobs selecting it were offered to a queue nobody consumed. Register the step under the destination owner instead (upload it with that owner’s API key), which binds a worker to the correct key. Copying WorkData between a user and an organization is unaffected and remains available.
Fixed
- Invalid folder paths retain their detailed validation messages instead of returning only “Validation of command parameters failed.”
- Upgrading from event-sourced jobs now validates and backfills legacy folder-input snapshots within the original cutover migration before destructive schema changes, preserving unresolved, resolved-empty and ordered populated inputs. Inconsistent history aborts the cutover transaction, and the existing startup migration check prevents serving with pending migrations. A database guard rejects legacy job-event appends during pod replacement.
- Disallowing output data deletion now notifies job subscribers, so the Allow/Disallow actions stop showing a stale state until the next unrelated job change.
- Resolving a folder-sourced input while a job is being scheduled no longer lets it start on a set that scheduling never prepared. Scheduling now aborts if a slot’s execution snapshot or folder source changed after preparation, leaving the job ready to be scheduled again.
- Scheduling a job on behalf of another owner no longer fails when one of its output slots targets a folder. The output placeholder is now created for the job’s owner, matching the folder it is filed into; the scheduling user is still recorded as its creator.
- Scheduling access denials and infrastructure failures no longer change jobs to Error. Folder-input validation failures recheck access, readiness, and the current inputs before failing a job.
- Scheduling holds the owner’s folder-tree lock while resolving output destinations and committing folders, placeholders, and job references together, preserving the hierarchy during concurrent tree changes.
- Retrying scheduling after an output-folder edit moves existing placeholders to the current destination without replacing their files or tags. A target change between preparation and scheduling aborts dispatch until the job is prepared again.
- Reading jobs with large input/output collections no longer multiplies those collections into a single SQL result. Job reads and folder pages load their graphs in a consistent database snapshot, including across transient read retries.
- Clearing or refreshing a template’s pinned folder inputs removes obsolete usage rows so released files can be deleted. Unchanged selections retain their usage rows, including in other template input slots.
- Setting a template input’s folder source with an invalid or missing slot index returns HTTP 400 with validation details and preserves the template’s existing configuration and input references.
- Job input
Resolve/ClearResolvedand outputSetOutputFolderactions are only advertised when the job is editable and the caller has execution permission. TemplateSetFolderSource/Resolve/ClearResolvedactions also check their respective command permissions, following the existing owner/admin rules. - A worker status, result, error or output-metadata message that arrives after its job was deleted is acknowledged as a late delivery instead of being dead-lettered into
Job.All.Updates.Rejected. Deleting a job removes its row, so such a message can never be applied and the rejected queue only accumulated messages nothing could act on; the skip is logged with the job id, and the worker’s access token is revoked as for any other settled job. Operators should expect fewer messages inJob.All.Updates.Rejected— the remaining ones are genuine failures.