ChangeOrder
A ChangeOrder names one defined set of changes in a Space and follows it through promotion into the Units and Revisions of other Spaces. It answers "did this change reach that variant?", which no per-Unit revision number can: numbering is per Unit.
A ChangeSet names a change within one Space and locks the Units it is open on. A ChangeOrder names the change itself, and is the only identity a change keeps as it crosses a Space boundary. The two compose: a ChangeOrder can adopt a ChangeSet's end Tag as its boundary, and neither requires the other.
A ChangeOrder is not required, and it does not perform promotion. It supplies the identity, the revision range, and the target set; the existing upgrade and resolve paths do the work.
Where it lives, and what it fixes
A ChangeOrder resides in the Space holding the changes to promote — its base. That Space is what says which Units are in scope, which is why the start of the interval can be derived rather than described Unit by Unit.
It is created after the revisions it names, at the moment you decide to promote them. Creating one derives, per Unit of its Space:
- The Links of its
UpdateType(UpgradeUnit, the clone lineage, by default;MergeUnitsis the other accepted value) whose downstream Space is in scope. - The start — the revision each downstream has already taken, read from those Links' merge cursors. If they disagree, that is an error naming the Unit and the values, rather than a different range being silently promoted to each target.
- The end — the Unit's head revision, or the revision a supplied boundary Tag marks on it.
- Two Tags of its own, suffixed
-co-startand-co-end, and the ChangeOrder stamped onto the revisions in the interval.
InScopeSpaceIDs is where the change is headed: a literal list of Spaces supplied by the client, not a standing query. A query re-asked on every read would take in a Space that arrived weeks after the change had been released everywhere and put the ChangeOrder back to InProgress with nothing having changed. Whatever works out which Spaces a change should reach — a Filter, a where expression, the stages of a ChangeWorkflow — is resolved before the ChangeOrder is created, and what is recorded is the answer.
A Unit the change carries nothing for is marked, not dropped: both Tags land on the one revision every target is already at. SkippedUnits records the Units it carries no revisions of at all, and why of each.
Promoting and undoing
A promotion marks itself: the start Tag on the target's head before the merge, the end Tag on the revision the merge arrives at, and the ChangeOrder on every revision it creates. That makes each hop an inspectable fact and makes it undoable in one step however many revisions the range produced.
Promotion is idempotent. A Unit already carrying the end Tag has taken the change, whatever has happened to it since, and is passed over — which is what lets a promotion that landed partway be run again to finish it.
Setting AbortedReason aborts a ChangeOrder: the record that the change is not coming to the Spaces still waiting for it. An aborted ChangeOrder is refused by every promotion. Undoing one requires the abort, mints a third Tag (-co-restore), and cannot be taken back once anything has been restored — so a ChangeOrder travels in one direction, and a change to promote after an undoing is a new ChangeOrder.
Where it has got to
ResolvedSpaceIDs, ReleasedSpaceIDs, RestoredSpaceIDs, ReleasedRestoredSpaceIDs and State are derived when the ChangeOrder is read, not recorded as it travels, so they cannot go stale.
State reduces them to one word: New until a Space other than its own has taken it, InProgress while some have and some have not, Resolved once every Space in scope has, and Released once every Space in scope has released what it took. AbortedReason overrides all of it with Aborted, and the undoing overrides that in turn with Restored and RestoreReleased.
All of them are queryable: ResolvedSpaceIDs ? '<space-id>' asks whether one Space has it, LEN(ResolvedSpaceIDs) how many do.
Deleting a ChangeOrder takes its marks back — it is removed from every revision carrying it, and its own Tags are removed and deleted. A boundary Tag it only adopted is never touched.
See tracking and promoting changes for the workflow, and ChangeWorkflow for the definition that gives a ChangeOrder its stages.