rpc-versioning
**Scope**: oslo.messaging RPC API version negotiation for rolling upgrades. Does not cover REST API microversioning. **Version range**: oslo.messaging 14.x+, OpenStack 2023.x+ **Generated**: 2026-04-09
$ npx -y skills add notque/vexjoy-agent --agent claude-codeHow it fires
How this agent gets triggered: by you, by Claude, or both.
- Fires itselfAuto-invocation. Claude auto-loads it when your prompt matches the work.Auto-invocation is when the right skill fires by itself at the right moment, driven by a FLOW.md router and a hook, instead of you invoking it by name. It is the difference between a skill being installed and a skill actually getting used.Read the full definition →
- You can call itInvoke it directly when you want it.
Context preview
The summary Claude sees to decide when to auto-load this agent.
**Scope**: oslo.messaging RPC API version negotiation for rolling upgrades. Does not cover REST API microversioning. **Version range**: oslo.messaging 14.x+, OpenStack 2023.x+ **Generated**: 2026-04-09
Agent definition
rpc-versioning.mdOpenStack RPC Versioning Reference
> **Scope**: oslo.messaging RPC API version negotiation for rolling upgrades. Does not cover REST API microversioning. > **Version range**: oslo.messaging 14.x+, OpenStack 2023.x+ > **Generated**: 2026-04-09
---
Version Negotiation Model
Client (new node) Transport Server (old node)
| | |
| prepare(version='1.5')| |
|--------------------> | |
| | version_cap=1.3 |
| RPCVersionCapError | |
| <------------------ | |- `RPC_API_VERSION` on manager = server's maximum
- `version_cap` on client = client's declared maximum
- `prepare(version=X)` = what this specific call requires
---
Pattern Table
| Scenario | Version Bump | Server Change | Client Change | |----------|-------------|---------------|---------------| | New optional argument | Minor (`1.2` -> `1.3`) | Accept new arg with default | `prepare(version='1.3')` | | New required method | Minor (`1.2` -> `1.3`) | Add method | `prepare(version='1.3')` | | Remove argument | Major (`1.x` -> `2.0`) | New major series | Both sides update | | Change argument type | Major (`1.x` -> `2.0`) | New major series | Incompatible change |
---
Correct Patterns
Server Side — Versioned RPC Manager
class MyServiceManager(manager.Manager):
RPC_API_VERSION = '1.4'
target = messaging.Target(version=RPC_API_VERSION)
def create_resource(self, context, name, properties=None):
"""Available since 1.0. `properties` added in 1.2."""
if properties is None:
properties = {}
return db.resource_create(context, name, properties)
def resize_resource(self, context, resource_id, new_size, preserve_data=False):
"""Added in 1.4."""
return db.resource_resize(context, resource_id, new_size, preserve_data)---
Client Side — Version-Pinned Calls
class MyServiceAPI:
RPC_API_VERSION = '1.4'
def __init__(self):
transport = messaging.get_rpc_transport(CONF)
target = messaging.Target(topic='myservice', version=self.RPC_API_VERSION)
self._client = messaging.get_rpc_client(transport, target)
def create_resource(self, context, name):
"""Works against servers >= 1.0."""
cctxt = self._client.prepare(version='1.0')
return cctxt.call(context, 'create_resource', name=name)
def create_resource_with_props(self, context, name, properties):
"""Requires server >= 1.2."""
cctxt = self._client.prepare(version='1.2')
return cctxt.call(context, 'create_resource', name=name, properties=properties)
def resize_resource(self, context, resource_id, new_size, preserve_data=False):
"""Requires server >= 1.4."""
cctxt = self._client.prepare(version='1.4')
return cctxt.call(context, 'resize_resource',
resource_id=resource_id, new_size=new_size, preserve_data=preserve_data)---
Version Cap During Upgrades
cfg.StrOpt(
'rpc_current_version',
default=None,
help='RPC API version cap. Set to old version during upgrade, unset when all nodes upgraded.',
),
def __init__(self):
transport = messaging.get_rpc_transport(CONF)
version_cap = CONF.myservice.rpc_current_version or self.RPC_API_VERSION
target = messaging.Target(
topic='myservice', version=self.RPC_API_VERSION, version_cap=version_cap,
)
self._client = messaging.get_rpc_client(transport, target)---
Pattern Catalog
Bump Version When Changing Method Signature
**Detection**:
git diff HEAD~1 HEAD -- '*.py' | grep -E '^\+.*def (create|update|delete|get|list)_.*context' | head -20
git diff HEAD~1 HEAD -- '*.py' | grep 'RPC_API_VERSION'
Old nodes calling without new args crash with `TypeError`. Bump version, make new args optional.
---
Pin Version Before Calling New Methods
**Detection**:
grep -rn 'cctxt\.call\|cctxt\.cast' --include="*.py" -B 2 | grep -v "prepare"
`_client.call()` without `.prepare(version=X)` sends no version requirement. Server on older version fails with `MethodNotFound`.
---
Keep RPC_API_VERSION Current
**Detection**:
grep -rn "RPC_API_VERSION = '1\.0'" --include="*.py"
Never-changed version with added methods means clients can't use `prepare(version=X)` for protection.
---
Error-Fix Mappings
| Error | Root Cause | Fix | |-------|------------|-----| | `MessagingTimeout` | Exceeds `rpc_response_timeout` (60s default) | Increase timeout or use cast | | `RPCVersionCapError` | Client version > server cap | Set `version_cap`, or upgrade server first | | `NoSuchMethod` | Method not on server | Check version pins; server may be running older code | | `TypeError: unexpected keyword argument` | New arg sent to old server | `prepare(version=X)` where X introduced the arg |
---
Detection Commands Reference
grep -rn 'cctxt\.call\|cctxt\.cast' --include="*.py" -B 3 | grep -v "prepare"
grep -rn "RPC_API_VERSION" --include="*.py"
git diff -- '*.py' | grep '^[+-].*def .*context'
grep -rn "rpc_current_version\|version_cap" --include="*.py" --include="*.cfg"
---
See Also
- `oslo-patterns.md` — oslo.messaging transport and client setup
- `hacking-rules.md` — Code style for RPC handler code
Read more
OpenStack RPC Versioning Reference
> **Scope**: oslo.messaging RPC API version negotiation for rolling upgrades. Does not cover REST API microversioning. > **Version range**: oslo.messaging 14.x+, OpenStack 2023.x+ > **Generated**: 2026-04-09
---
Version Negotiation Model
Client (new node) Transport Server (old node)
| | |
| prepare(version='1.5')| |
|--------------------> | |
| | version_cap=1.3 |
| RPCVersionCapError | |
| <------------------ | |- `RPC_API_VERSION` on manager = server's maximum
- `version_cap` on client = client's declared maximum
- `prepare(version=X)` = what this specific call requires
---
Pattern Table
| Scenario | Version Bump | Server Change | Client Change | |----------|-------------|---------------|---------------| | New optional argument | Minor (`1.2` -> `1.3`) | Accept new arg with default | `prepare(version='1.3')` | | New required method | Minor (`1.2` -> `1.3`) | Add method | `prepare(version='1.3')` | | Remove argument | Major (`1.x` -> `2.0`) | New major series | Both sides update | | Change argument type | Major (`1.x` -> `2.0`) | New major series | Incompatible change |
---
Correct Patterns
Server Side — Versioned RPC Manager
class MyServiceManager(manager.Manager):
RPC_API_VERSION = '1.4'
target = messaging.Target(version=RPC_API_VERSION)
def create_resource(self, context, name, properties=None):
"""Available since 1.0. `properties` added in 1.2."""
if properties is None:
properties = {}
return db.resource_create(context, name, properties)
def resize_resource(self, context, resource_id, new_size, preserve_data=False):
"""Added in 1.4."""
return db.resource_resize(context, resource_id, new_size, preserve_data)---
Client Side — Version-Pinned Calls
class MyServiceAPI:
RPC_API_VERSION = '1.4'
def __init__(self):
transport = messaging.get_rpc_transport(CONF)
target = messaging.Target(topic='myservice', version=self.RPC_API_VERSION)
self._client = messaging.get_rpc_client(transport, target)
def create_resource(self, context, name):
"""Works against servers >= 1.0."""
cctxt = self._client.prepare(version='1.0')
return cctxt.call(context, 'create_resource', name=name)
def create_resource_with_props(self, context, name, properties):
"""Requires server >= 1.2."""
cctxt = self._client.prepare(version='1.2')
return cctxt.call(context, 'create_resource', name=name, properties=properties)
def resize_resource(self, context, resource_id, new_size, preserve_data=False):
"""Requires server >= 1.4."""
cctxt = self._client.prepare(version='1.4')
return cctxt.call(context, 'resize_resource',
resource_id=resource_id, new_size=new_size, preserve_data=preserve_data)---
Version Cap During Upgrades
cfg.StrOpt(
'rpc_current_version',
default=None,
help='RPC API version cap. Set to old version during upgrade, unset when all nodes upgraded.',
),
def __init__(self):
transport = messaging.get_rpc_transport(CONF)
version_cap = CONF.myservice.rpc_current_version or self.RPC_API_VERSION
target = messaging.Target(
topic='myservice', version=self.RPC_API_VERSION, version_cap=version_cap,
)
self._client = messaging.get_rpc_client(transport, target)---
Pattern Catalog
Bump Version When Changing Method Signature
**Detection**:
git diff HEAD~1 HEAD -- '*.py' | grep -E '^\+.*def (create|update|delete|get|list)_.*context' | head -20 git diff HEAD~1 HEAD -- '*.py' | grep 'RPC_API_VERSION'
Old nodes calling without new args crash with `TypeError`. Bump version, make new args optional.
---
Pin Version Before Calling New Methods
**Detection**:
grep -rn 'cctxt\.call\|cctxt\.cast' --include="*.py" -B 2 | grep -v "prepare"
`_client.call()` without `.prepare(version=X)` sends no version requirement. Server on older version fails with `MethodNotFound`.
---
Keep RPC_API_VERSION Current
**Detection**:
grep -rn "RPC_API_VERSION = '1\.0'" --include="*.py"
Never-changed version with added methods means clients can't use `prepare(version=X)` for protection.
---
Error-Fix Mappings
| Error | Root Cause | Fix | |-------|------------|-----| | `MessagingTimeout` | Exceeds `rpc_response_timeout` (60s default) | Increase timeout or use cast | | `RPCVersionCapError` | Client version > server cap | Set `version_cap`, or upgrade server first | | `NoSuchMethod` | Method not on server | Check version pins; server may be running older code | | `TypeError: unexpected keyword argument` | New arg sent to old server | `prepare(version=X)` where X introduced the arg |
---
Detection Commands Reference
grep -rn 'cctxt\.call\|cctxt\.cast' --include="*.py" -B 3 | grep -v "prepare" grep -rn "RPC_API_VERSION" --include="*.py" git diff -- '*.py' | grep '^[+-].*def .*context' grep -rn "rpc_current_version\|version_cap" --include="*.py" --include="*.cfg"
---
See Also
- `oslo-patterns.md` — oslo.messaging transport and client setup
- `hacking-rules.md` — Code style for RPC handler code
Essays and writing behind this toolkit live at vexjoy.com. AI agents skip steps. "Looks correct" replaces running tests. "Trivial change" replaces verification.
Repo: notque/vexjoy-agent
Other agents on vexjoy-agent.
- ansible-automation-engineer
Ansible automation: playbooks, roles, collections, Molecule testing, Vault security.
Open agent - modules
**Scope**: Module selection patterns, builtin vs command/shell decisions, collection modules, and version-specific module changes **Version range**: ansible-core 2.14+ / Ansible Collections (community.general 7.0+) **Generated**: 2026-04-04 — verify against current Ansible
Open agent - testing
**Scope**: Molecule test scenarios, ansible-lint rules, idempotency validation, and check-mode patterns **Version range**: Molecule 6.0+ / ansible-lint 6.0+ / ansible-core 2.14+ **Generated**: 2026-04-04 — verify against current Molecule and ansible-lint documentation
Open agent - base-instructions
Universal operational rules injected by /do at agent dispatch. Domain-specific rules live in each agent's .md file.
Open agent - communication-patterns
**Scope**: Failure modes in agent output style — over-reporting, self-congratulation, verbose narration, and hedging. Covers what to detect and how to fix each. **Version range**: all versions **Generated**: 2026-05-11
Open agent - combat-effects-upgrade
Zero-dependency combat visual upgrades: CSS particle replacement, Framer Motion combat juice, CSS 3D card transforms.
Open agent

