/package-version-safety
Trigger Pattern PACKAGE_UPGRADE flag (UpgradeCap detected, multiple package versions, upgrade policy references) - Inject Into Breadth agents, depth-external
$ npx -y skills add PlamenTSV/plamen --skill package-version-safety --agent claude-codeHow it fires
How this skill 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.
- Slash command
/package-version-safety
Context preview
The summary Claude sees to decide when to auto-load this skill.
Trigger Pattern PACKAGE_UPGRADE flag (UpgradeCap detected, multiple package versions, upgrade policy references) - Inject Into Breadth agents, depth-external
SKILL.md
package-version-safety.SKILL.mdname: "package-version-safety"
description: "Trigger Pattern PACKAGE_UPGRADE flag (UpgradeCap detected, multiple package versions, upgrade policy references) - Inject Into Breadth agents, depth-external"
Skill: PACKAGE_VERSION_SAFETY (Sui)
> **Trigger Pattern**: PACKAGE_UPGRADE flag (UpgradeCap detected, multiple package versions, upgrade policy references) > **Inject Into**: Breadth agents, depth-external > **Finding prefix**: `[PV-N]` > **Rules referenced**: R4, R8, R9, R10
Sui packages are immutable once published. "Upgrading" a package means publishing a NEW version at a NEW on-chain address, linked to the original via the UpgradeCap lineage. The old version's code remains callable forever. This creates a fundamentally different upgrade risk model compared to EVM proxies: instead of replacing logic in-place, Sui packages accumulate versions -- and shared objects may be accessible by ALL versions simultaneously.
---
Trigger Patterns
UpgradeCap|upgrade_policy|package::make_immutable|compatible|additive|dep_only|version|
migrate|old_version|new_version
---
Step 1: Upgrade Policy Inventory
For each package in scope:
| # | Package | UpgradeCap Location | UpgradeCap Holder | Has `store`? | Upgrade Policy | Destroyed? | |---|---------|--------------------|--------------------|-------------|---------------|-----------| | 1 | {pkg_name} | {init function:line} | {address / shared / wrapped in governance} | YES/NO | {compatible/additive/dep_only} | YES (immutable) / NO |
**Checks**:
- **Where is UpgradeCap stored?**
- Owned by deployer address: Single point of failure. Key loss -> permanent immutability. Key theft -> attacker can upgrade.
- Shared object: DANGEROUS -- anyone can pass it to upgrade functions.
- Wrapped in governance object: Good pattern -- upgrade requires governance approval.
- Destroyed via `make_immutable()`: Package is permanently immutable. No upgrade risk.
- Transferred to `@0x0` or burn address: Effectively immutable.
- **Can UpgradeCap be transferred?**
- UpgradeCap has `key + store` by default -> freely transferable via `public_transfer`.
- Is there a custom wrapper restricting transfer? (e.g., `GovernanceCap` wrapping `UpgradeCap`)
- If transferable and held by EOA -> attacker stealing key can transfer UpgradeCap.
- **Can UpgradeCap be destroyed?**
- `sui::package::make_immutable(cap)` consumes UpgradeCap -> permanent immutability.
- If UpgradeCap has `drop` via wrapper -> accidental destruction possible.
1b. UpgradeCap Governance Assessment
| Governance Model | Risk Level | Assessment | |-----------------|------------|------------| | Single EOA | CRITICAL | One key compromise replaces all package logic | | Multisig (2/3 or lower) | HIGH | Low collusion threshold | | Multisig (3/5+) | MEDIUM | Requires majority collusion | | Multisig + timelock | LOW | Users can exit before malicious upgrade takes effect | | DAO/governance contract | LOW | Distributed control, but check voter distribution | | Destroyed (immutable) | NONE | Cannot upgrade, but also cannot patch bugs |
---
Step 2: Version Consistency Check
For shared objects created by this package:
| Shared Object | Created By (Version) | Current Version Field? | V1 Functions Access? | V2 Functions Access? | Consistency Risk | |--------------|---------------------|----------------------|---------------------|---------------------|-----------------| | {obj_type} | V1 `init()` | YES: `version: u64` / NO | {list funcs} | {list funcs} | {describe} |
**What happens when package is upgraded?**
- Existing shared objects created by V1 remain at their original address
- V2 functions CAN access V1-created shared objects (types are preserved in compatible upgrades)
- V1 functions are STILL callable and CAN access the same shared objects
- This dual-access is the primary version safety concern
**Can old-version and new-version calls on same shared object create inconsistency?**
- V1 function writes field A based on formula F1
- V2 function writes field A based on formula F2
- User calls V1 then V2 in separate transactions -> field A has inconsistent state
- **Especially dangerous**: V2 adds a new check that V1 lacks. Attacker calls V1 to bypass V2's check.
---
Step 3: Dependency Version Pinning
For each dependency in `Move.toml`:
| Dependency | Source | Pinned To | Immutable? | Upgrade Risk | |-----------|--------|-----------|-----------|-------------| | Sui Framework | `sui = "..."` | {git rev or latest} | Upgraded by validators | Framework upgrade could change behavior | | MoveStdlib | `MoveStdlib = "..."` | {git rev} | Upgraded with framework | Same as above | | {third_party} | {git url or on-chain} | {specific rev / branch / on-chain version} | YES/NO | {describe} |
**Checks**:
- Are third-party dependencies pinned to specific git revisions? If pinned to `main` -> upstream changes included on recompile.
- For on-chain published dependencies: is the dependency package immutable? If it has active UpgradeCap -> behavior can change.
- Can a dependency upgrade break our package's invariants?
- Are there transitive dependencies with their own upgrade risks?
**Can dependency upgrade break our package?**
- Compatible dependency upgrade: function implementations can change but signatures preserved. Our calls still compile but behavior may differ.
- Additive dependency upgrade: only new functions/types added. Existing behavior frozen.
- Framework upgrades: `sui::*` packages upgraded by validators. Can change Move VM behavior, gas costs, object model rules.
---
Step 4: Type Compatibility Across Versions
When package V2 adds new types or fields:
| Type | V1 Definition | V2 Changes | Compatible Upgrade Rule | Migration Needed? | |------|-------------|-----------|------------------------|------------------| | {struct_name} | {fields} | {cannot change for compatible} | Struct layouts FROZEN | NO -- same layout | | {new_struct} | N/A | {new in V2} | New typ
Read more
name: "package-version-safety" description: "Trigger Pattern PACKAGE_UPGRADE flag (UpgradeCap detected, multiple package versions, upgrade policy references) - Inject Into Breadth agents, depth-external"
Skill: PACKAGE_VERSION_SAFETY (Sui)
> **Trigger Pattern**: PACKAGE_UPGRADE flag (UpgradeCap detected, multiple package versions, upgrade policy references) > **Inject Into**: Breadth agents, depth-external > **Finding prefix**: `[PV-N]` > **Rules referenced**: R4, R8, R9, R10
Sui packages are immutable once published. "Upgrading" a package means publishing a NEW version at a NEW on-chain address, linked to the original via the UpgradeCap lineage. The old version's code remains callable forever. This creates a fundamentally different upgrade risk model compared to EVM proxies: instead of replacing logic in-place, Sui packages accumulate versions -- and shared objects may be accessible by ALL versions simultaneously.
---
Trigger Patterns
UpgradeCap|upgrade_policy|package::make_immutable|compatible|additive|dep_only|version| migrate|old_version|new_version
---
Step 1: Upgrade Policy Inventory
For each package in scope:
| # | Package | UpgradeCap Location | UpgradeCap Holder | Has `store`? | Upgrade Policy | Destroyed? | |---|---------|--------------------|--------------------|-------------|---------------|-----------| | 1 | {pkg_name} | {init function:line} | {address / shared / wrapped in governance} | YES/NO | {compatible/additive/dep_only} | YES (immutable) / NO |
**Checks**:
- **Where is UpgradeCap stored?**
- Owned by deployer address: Single point of failure. Key loss -> permanent immutability. Key theft -> attacker can upgrade.
- Shared object: DANGEROUS -- anyone can pass it to upgrade functions.
- Wrapped in governance object: Good pattern -- upgrade requires governance approval.
- Destroyed via `make_immutable()`: Package is permanently immutable. No upgrade risk.
- Transferred to `@0x0` or burn address: Effectively immutable.
- **Can UpgradeCap be transferred?**
- UpgradeCap has `key + store` by default -> freely transferable via `public_transfer`.
- Is there a custom wrapper restricting transfer? (e.g., `GovernanceCap` wrapping `UpgradeCap`)
- If transferable and held by EOA -> attacker stealing key can transfer UpgradeCap.
- **Can UpgradeCap be destroyed?**
- `sui::package::make_immutable(cap)` consumes UpgradeCap -> permanent immutability.
- If UpgradeCap has `drop` via wrapper -> accidental destruction possible.
1b. UpgradeCap Governance Assessment
| Governance Model | Risk Level | Assessment | |-----------------|------------|------------| | Single EOA | CRITICAL | One key compromise replaces all package logic | | Multisig (2/3 or lower) | HIGH | Low collusion threshold | | Multisig (3/5+) | MEDIUM | Requires majority collusion | | Multisig + timelock | LOW | Users can exit before malicious upgrade takes effect | | DAO/governance contract | LOW | Distributed control, but check voter distribution | | Destroyed (immutable) | NONE | Cannot upgrade, but also cannot patch bugs |
---
Step 2: Version Consistency Check
For shared objects created by this package:
| Shared Object | Created By (Version) | Current Version Field? | V1 Functions Access? | V2 Functions Access? | Consistency Risk | |--------------|---------------------|----------------------|---------------------|---------------------|-----------------| | {obj_type} | V1 `init()` | YES: `version: u64` / NO | {list funcs} | {list funcs} | {describe} |
**What happens when package is upgraded?**
- Existing shared objects created by V1 remain at their original address
- V2 functions CAN access V1-created shared objects (types are preserved in compatible upgrades)
- V1 functions are STILL callable and CAN access the same shared objects
- This dual-access is the primary version safety concern
**Can old-version and new-version calls on same shared object create inconsistency?**
- V1 function writes field A based on formula F1
- V2 function writes field A based on formula F2
- User calls V1 then V2 in separate transactions -> field A has inconsistent state
- **Especially dangerous**: V2 adds a new check that V1 lacks. Attacker calls V1 to bypass V2's check.
---
Step 3: Dependency Version Pinning
For each dependency in `Move.toml`:
| Dependency | Source | Pinned To | Immutable? | Upgrade Risk | |-----------|--------|-----------|-----------|-------------| | Sui Framework | `sui = "..."` | {git rev or latest} | Upgraded by validators | Framework upgrade could change behavior | | MoveStdlib | `MoveStdlib = "..."` | {git rev} | Upgraded with framework | Same as above | | {third_party} | {git url or on-chain} | {specific rev / branch / on-chain version} | YES/NO | {describe} |
**Checks**:
- Are third-party dependencies pinned to specific git revisions? If pinned to `main` -> upstream changes included on recompile.
- For on-chain published dependencies: is the dependency package immutable? If it has active UpgradeCap -> behavior can change.
- Can a dependency upgrade break our package's invariants?
- Are there transitive dependencies with their own upgrade risks?
**Can dependency upgrade break our package?**
- Compatible dependency upgrade: function implementations can change but signatures preserved. Our calls still compile but behavior may differ.
- Additive dependency upgrade: only new functions/types added. Existing behavior frozen.
- Framework upgrades: `sui::*` packages upgraded by validators. Can change Move VM behavior, gas costs, object model rules.
---
Step 4: Type Compatibility Across Versions
When package V2 adds new types or fields:
| Type | V1 Definition | V2 Changes | Compatible Upgrade Rule | Migration Needed? | |------|-------------|-----------|------------------------|------------------| | {struct_name} | {fields} | {cannot change for compatible} | Struct layouts FROZEN | NO -- same layout | | {new_struct} | N/A | {new in V2} | New typ
Autonomous Web3 security auditor for Claude Code and OpenAI Codex CLI. Orchestrates 18-100 AI agents across 40+ phases to produce audit reports with verified PoC exploits — for smart contracts and L1 node-client infrastructure.
Repo: PlamenTSV/plamen
Other skills on plamen.
- /ability-analysis
Trigger Pattern Always (Aptos Move) - foundational security check - Inject Into Breadth agents, depth agents
Open skill - /bit-shift-safety
Trigger Pattern Always (Aptos Move) - Move VM aborts on shift = bit width - Inject Into Breadth agents, depth-edge-case
Open skill - /centralization-risk
Trigger Protocol has privileged roles (admin, operator, governance, resource account owner) - Covers Single points of failure, privilege escalation, external governance dependen...
Open skill - /cross-chain-timing
Trigger Pattern wormhole|layerzero|ccip|bridge|cross_chain|vaa|guardian|emitter|relay|remote_chain|payload|nonce.sequence - Inject Into Breadth agents, depth-external
Open skill - /dependency-audit
Trigger EXTERNAL_LIB flag detected (protocol uses third-party Move dependencies) - Used by Breadth agents, depth-external
Open skill - /economic-design-audit
Trigger Pattern MONETARY_PARAMETER flag (required) - Inject Into Breadth agents (merged via M4 hierarchy)
Open skill

