Install
openclaw skills install @loonghao/rez-package-orderersHow rez decides which version of a package to pick — the package_orderers setting, the five built-in orderers sorted / version_split / per_family / soft_timestamp / no_order, how orderers apply to variants as well as packages, writing and registering a custom orderer, and how to prove an orderer is or is not what changed your resolve. Use when rez picks a version you did not expect and the resolve is not failing, or when a studio needs python-2-style version pinning to survive an upgrade. Covers Rez 3.4.0.
openclaw skills install @loonghao/rez-package-orderersOne-sentence summary: an orderer reorders the version list rez sorts before it solves, so the "latest" version rez returns is the latest by the orderer's rule — and a configured orderer is the only reason a successful resolve picks an older version without any conflict to explain it.
Skill scope: the orderer mechanism itself — which orderers exist, how they are configured, and
how to tell whether one is in play. For how version tokens compare (1.0.0 vs 1.0.0-beta,
alphanumeric vs numeric segments) see rez-core-concepts; for a resolve that fails see
rez-resolve-troubleshooting, since orderers only matter when the resolve succeeds; for reading the
solver trace see rez-resolve.
The solver always asks the package repository for versions in a defined order and takes the first that fits. Orderers change that order. They do not forbid anything: a higher version is still reachable if you request it explicitly.
That is the diagnostic signature. If rez-env foo gives you 1.1.0 while rez-env foo-2 happily
gives you 2.0.0, and there is no conflict anywhere, an orderer is almost always why.
Orderers apply to variants as well as to package versions, so a variant list is reordered the same way.
Check before you theorise — the default is empty:
rez-config package_orderers
An empty list means no orderer is configured and the default (sorted, descending) applies. A
non-empty list is inherited through the normal config layering, so also confirm where it came from:
rez-config --source-list package_orderers
type | What it does |
|---|---|
sorted | the default — sort by version, descending |
version_split | versions less than or equal to first_version first, then the default order |
per_family | apply different orderers to different package families |
soft_timestamp | versions released before timestamp first, then the rest; rank relaxes it |
no_order | a no-op — explicitly leave a package's order alone |
The default. You only need to name it to change its direction, or to scope it:
package_orderers = [
{
"type": "sorted", # required
"descending": True, # required
"packages": ["python"], # optional; omit to apply to every package
}
]
descending: False inverts it, so the lowest version wins. Verified against a repository holding
foo-1.0.0, foo-1.1.0, foo-2.0.0:
| Orderer | rez-env foo resolves |
|---|---|
| none (default) | foo-2.0.0 |
sorted with descending: False | foo-1.0.0 |
Orders every version less than or equal to first_version first, then falls back to the default
order for the rest. Given [5, 4, 3, 2, 1], an orderer with first_version=3 yields
[3, 2, 1, 5, 4].
package_orderers = [
{
"type": "version_split",
"first_version": "1.1.0",
}
]
With the same three foo versions, first_version: "1.1.0" makes rez-env foo resolve
foo-1.1.0 instead of foo-2.0.0. Note the comparison is less than or equal to, so the named
version itself is included in the preferred group.
The canonical studio use is easing a python-2 to python-3 migration:
package_orderers = [
{
"type": "per_family",
"orderers": [
{
"packages": ["python"],
"type": "version_split",
"first_version": "2.7.16",
}
],
}
]
For the python family, versions at or below 2.7.16 win. An explicit rez-env python-3 still
gets 3.7.4.
Applies different orderers to different families. Any orderer above can be nested in it, together
with a packages list. A family not named by any nested orderer keeps the default order.
This is also how you scope no_order.
Prefers packages released before timestamp (a Unix epoch integer), in descending order, followed by
everything released after. rank relaxes the boundary: a non-zero rank lets version changes at that
rank and above win over the timestamp.
package_orderers = [
{
"type": "soft_timestamp",
"timestamp": 1568001600, # 2019-09-09
"rank": 3,
}
]
rank: 3 is the usual choice under semantic versioning — it allows a different patch number
through. Generate the timestamp rather than hand-computing it:
python -c "import datetime, time; print(int(time.mktime(datetime.date(2019, 9, 9).timetuple())))"
Which version wins depends on rank, and the effect is not monotonic — a rank that lets a later
patch through can land on a different version than a rank that lets a later minor through. Measure
it against your own repository rather than assuming, because the answer depends on when each version
was actually released.
A no-op. It exists so that inside a per_family block you can apply an orderer broadly but exempt
one package:
package_orderers = [
{
"type": "per_family",
"orderers": [
{"packages": ["foo"], "type": "no_order"},
],
}
]
Subclass PackageOrder, implement the mandatory methods, and register it with register_orderer.
Everything except sort_key_implementation is boilerplate you must supply for serialization:
# rezconfig.py
from rez.package_order import PackageOrder, register_orderer
class MyOrderer(PackageOrder):
name = "my_orderer"
def __init__(self, custom_arg, **kwargs):
super().__init__(**kwargs)
self.custom_arg = custom_arg
def sort_key_implementation(self, package_name, version):
...
def __str__(self):
...
def __eq__(self, other):
...
def to_pod(self, other):
...
@classmethod
def from_pod(cls, data):
...
register_orderer(MyOrderer)
package_orderers = [
{
"type": "my_orderer",
"custom_arg": "value here",
}
]
rezconfig.py is ordinary Python, so the class is defined and registered at config load time.
Do not pass
selftosuper().__init__()—super().__init__(self, **kwargs)raisesTypeError: 'MyOrderer' object is not iterable, becauseselflands in thepackagesargument that the base class sorts. The upstreampackage_orderers.rstat 3.4.0 shows the broken form in its custom-orderer example; the correct call issuper().__init__(**kwargs).
Upstream explicitly warns against this: a custom orderer makes environments behave in ways users do
not expect, and makes the affected packages harder to share. Prefer a built-in, and prefer
version_split over anything bespoke.
Never infer the effect from one command — compare the resolve against the version list.
rez-search foo # every version, as the repository holds them
rez-env foo -o context.rxt # what the solver actually picked
rez-context --so # the resolved version, with the request that produced it
If rez-context --so shows the version you did not expect, and rez-search shows a newer one
exists, the orderer is the explanation. Confirm by repeating the resolve with the setting emptied —
package_orderers is a normal config key, so a temporary override is enough to prove it.
| Symptom | Cause |
|---|---|
| An older version wins with no conflict | an orderer reordered the version list |
rez-env foo and rez-env foo-2 disagree | working as intended — orderers do not forbid explicit requests |
no_order seems to do nothing | that is what it does; it only matters inside per_family |
An unknown type is rejected at resolve time | only sorted, version_split, per_family, soft_timestamp, no_order and registered custom names are accepted |
| The orderer applies to packages I did not name | omit packages (outside per_family) and it applies to everything |
| A variant is chosen oddly | orderers apply to variants too, not just versions |
soft_timestamp picks a surprising version | rank decides how far past the timestamp a version may drift; check it |
rez-config package_orderers first — an empty list means orderers are not your problem.rez-config --source-list package_orderers.rez-search) from "the solve failed" —
the latter is rez-resolve-troubleshooting, not this skill.version_split and per_family over a custom orderer; scope with packages.rez-context --so, then repeat with the orderer removed
to prove it is the cause.