Install
$ agentstack add skill-tlaplus-agentskills-tlaplus-split-action ✓ scanned · ✓ verified, works with Claude Code, Cursor, and more.
Security review
✓ PassedNo issues found. Passed automated security review. · v0.1.0 How review works →
- ✓ Prompt-injection patterns
- ✓ Secret / credential exfiltration
- ✓ Dangerous shell & filesystem operations
- ✓ Untrusted network calls
- ✓ Known-malicious package signatures
What it can access
- ✓ Network access No
- ✓ Filesystem access No
- ✓ Shell / process execution No
- ✓ Environment & secrets No
- ✓ Dynamic code execution No
From automated source analysis of v0.1.0. “Used” means the capability is present in the source — more access means more to trust, not that it’s unsafe.
Verified badge
Passed review? Show it. Paste this badge into your README, it links to the public security report.
Reliability & compatibility
Declared compatibility
Compatibility is declared by the source manifest. End-to-end runtime verification is coming, see below.
We're building live execution health for every listing: tool-call success rate, median latency, uptime, and last-checked timestamps, measured, not self-reported. It isn't live yet, so we don't show numbers we can't stand behind.
How agent discovery & health will work →About
Split TLA+ Action into Two Actions
Split an existing TLA+ action into two sequential actions by introducing a new intermediate pc state. This is useful for modeling finer-grained atomicity or adding intermediate steps to an action sequence.
Prerequisites
- The specification must have a
pcvariable (or equivalent likestate,phase) that tracks action sequencing. If not, create one. - The action to split must update
pcto transition between states
Overview
Given an action like:
ActionX_1(thread) ==
/\ pc[thread] = PC_ActionX_1
/\ pc' = [pc EXCEPT ![thread] = PC_Start]
/\ y' = y + 2
/\ UNCHANGED >
Splitting it creates:
ActionX_1(thread) ==
/\ pc[thread] = PC_ActionX_1
/\ pc' = [pc EXCEPT ![thread] = PC_ActionX_2] \* Leads to new intermediate state
/\ y' = y + 2 \* Original logic (or subset per user)
/\ UNCHANGED >
ActionX_2(thread) == \* New action
/\ pc[thread] = PC_ActionX_2
/\ pc' = [pc EXCEPT ![thread] = PC_Start] \* Original destination
/\ UNCHANGED > \* All vars unchanged (or subset per user)
Workflow
Step 0: Add pc variable if not present
If the specification lacks a pc variable, first use the "tlaplus-add-variable" skill to add pc variable. If the actions are thread- or process-specific, pc variable must be a function of the thread or process identifier to a string constant. For example,
PC_Begin == "PC_Begin"
PC_ActionX_1 == "PC_ActionX_1"
...
Init ==
/\ pc = [thread \in Threads |-> PC_Begin]
/\ ...
Step 1: Analyze the Target Action
Read the specification and identify:
- The action to split
- Its current
pcguard (entry state) - Its
pc'assignment (exit state) - All variable updates in the action
- The UNCHANGED statement
- The naming convention used (numbered suffix like
_0,_1, etc.)
Step 2: Determine the New PC State Name
Follow the specification's naming convention:
If actions are numbered (e.g., ActionX_0, ActionX_1):
- Insert the new PC state between the split action and its successor
- Renumber subsequent PC states and actions
Example: Splitting ActionX_1 which goes to PC_Start:
- New intermediate state:
PC_ActionX_2 - Original
ActionX_1now leads toPC_ActionX_2 - New action
ActionX_2leads toPC_Start
If actions have descriptive names (e.g., PC_Fetch, PC_Execute):
- Create a descriptive name for the intermediate state
- Ask the user if the name is unclear
Step 3: Add New PC State Constant
Add the new PC state to the PCStates set (or equivalent):
\* Before:
PCStates == {
PC_Ready,
PC_ActionX_1,
...
}
\* After:
PCStates == {
PC_Ready,
PC_ActionX_1, PC_ActionX_2, \* Added PC_ActionX_2
...
}
Step 4: Modify the Original Action
Update the original action to:
- Change
pc'to transition to the NEW intermediate state - Update UNCHANGED to include variables that will be modified in the second action
Without user instructions (empty intermediate action):
\* Before:
ActionX_1(thread) ==
/\ pc[thread] = PC_ActionX_1
/\ pc' = [pc EXCEPT ![thread] = PC_Start]
/\ y' = y + 2
/\ UNCHANGED >
\* After:
ActionX_1(thread) ==
/\ pc[thread] = PC_ActionX_1
/\ pc' = [pc EXCEPT ![thread] = PC_ActionX_2]
/\ y' = y + 2
/\ UNCHANGED >
ActionX_2(thread) == ... will be created in the next step ...
With user instructions (specific split):
If user specifies which variables to update in first vs second action, follow those instructions:
\* User says: "Update x in first action, y in second"
\* Before:
ActionX_1(thread) ==
/\ pc[thread] = PC_ActionX_1
/\ pc' = [pc EXCEPT ![thread] = PC_Start]
/\ x' = x + 1
/\ y' = y + 2
/\ UNCHANGED >
\* After:
ActionX_1(thread) ==
/\ pc[thread] = PC_ActionX_1
/\ pc' = [pc EXCEPT ![thread] = PC_ActionX_2]
/\ x' = x + 1 \* User-specified update in first action
/\ UNCHANGED >
ActionX_2(thread) == ... will be created in the next step ...
Step 5: Create the New Action:
Create the new action that:
- Guards on the new intermediate PC state
- Transitions to the ORIGINAL destination state
- Contains the logic from the original action (or as specified by user)
- Has appropriate UNCHANGED statement
ActionX_2(thread) == \* New action
/\ pc[thread] = PC_ActionX_2
/\ pc' = [pc EXCEPT ![thread] = PC_Start] \* Original destination
/\ y' = y + 2 \* Logic from original (or per user)
/\ UNCHANGED >
Step 6: Renumber Subsequent Actions (if needed)
If the naming convention uses numbers and there are subsequent actions that would conflict:
Before splitting:
ActionX_0(thread) == ... \* PC_ActionX_0 -> PC_ActionX_1
ActionX_1(thread) == ... \* PC_ActionX_1 -> PC_Start (TO BE SPLIT)
After splitting:
ActionX_0(thread) == ... \* PC_ActionX_0 -> PC_ActionX_1 (unchanged)
ActionX_1(thread) == ... \* PC_ActionX_1 -> PC_ActionX_2 (new destination)
ActionX_2(thread) == ... \* PC_ActionX_2 -> PC_Start (NEW ACTION)
If splitting ActionX_0 where ActionX_1 already exists, renumber:
Before:
ActionX_0(thread) == ... \* PC_ActionX_0 -> PC_ActionX_1 (TO BE SPLIT)
ActionX_1(thread) == ... \* PC_ActionX_1 -> PC_Start
After:
ActionX_0(thread) == ... \* PC_ActionX_0 -> PC_ActionX_1 (NEW intermediate)
ActionX_1(thread) == ... \* PC_ActionX_1 -> PC_ActionX_2 (renumbered from old ActionX_1)
ActionX_2(thread) == ... \* PC_ActionX_2 -> PC_Start (renumbered)
Step 7: Update TypeOk Predicate
If TypeOk contains PCStates enumeration, add the new PC state:
\* Before:
TypeOk ==
/\ pc \in [Threads -> PCStates]
...
\* If PCStates is defined separately, it was updated in Step 3.
\* If PCStates is inline, update it here.
Step 8: Update Next Predicate (if needed)
If the Next predicate explicitly lists actions (not common), add the new action:
Next == \E thread \in Threads :
\/ ActionX_0(thread)
\/ ActionX_1(thread)
\/ ActionX_2(thread) \* Add new action
...
If actions are grouped (e.g., ActionX(thread) with internal disjunctions), the new disjunct is already included.
Step 9: Update Fairness Constraints (if needed)
If fairness constraints reference specific PC states or actions, update them:
\* Before:
Fairness ==
/\ WF_vars(ActionX_1(thread))
\* After - add fairness for new action:
Fairness ==
/\ WF_vars(ActionX_1(thread))
/\ WF_vars(ActionX_2(thread))
UNCHANGED Statement Rules
When splitting an action, follow these rules for UNCHANGED:
- First action (intermediate step):
- If user specifies no variable updates: add ALL non-pc variables to UNCHANGED
- If user specifies some updates: add remaining variables to UNCHANGED
- Second action (new action):
- Contains variables that are NOT modified in the new action
- Typically mirrors the original UNCHANGED (unless user specifies otherwise)
- Never forget variables: Every variable must either be primed (updated) or in UNCHANGED
Verification Checklist
After splitting, verify:
- [ ] New PC state constant is defined
- [ ] New PC state is in PCStates set
- [ ] Original action transitions to new intermediate PC state
- [ ] New action guards on new PC state
- [ ] New action transitions to original destination
- [ ] All variables are either primed or in UNCHANGED (both actions)
- [ ] Naming follows spec convention (renumbered if needed)
- [ ] TypeOk updated if needed
- [ ] Next predicate includes new action (if explicitly listed)
- [ ] Fairness constraints updated if present
Example: Full Split
User request: "Split ActionX_1 into two actions"
Before:
PC_ActionX_1 == "PC_ActionX_1"
PCStates == { PC_Ready, PC_ActionX_1 }
ActionX_0(thread) ==
/\ pc[thread] = PC_Start
/\ pc' = [pc EXCEPT ![thread] = PC_ActionX_1]
/\ x' = x + 1
/\ UNCHANGED >
ActionX_1(thread) ==
/\ pc[thread] = PC_ActionX_1
/\ pc' = [pc EXCEPT ![thread] = PC_Start]
/\ y' = y + 2
/\ UNCHANGED >
After:
PC_ActionX_1 == "PC_ActionX_1"
PC_ActionX_2 == "PC_ActionX_2" \* NEW
PCStates == { PC_Ready, PC_ActionX_1, PC_ActionX_2 } \* Updated
ActionX_0(thread) ==
/\ pc[thread] = PC_Start
/\ pc' = [pc EXCEPT ![thread] = PC_ActionX_1]
/\ x' = x + 1
/\ UNCHANGED >
ActionX_1(thread) ==
/\ pc[thread] = PC_ActionX_1
/\ pc' = [pc EXCEPT ![thread] = PC_ActionX_2] \* Changed destination
/\ UNCHANGED > \* All vars unchanged
ActionX_2(thread) == \* NEW ACTION
/\ pc[thread] = PC_ActionX_2
/\ pc' = [pc EXCEPT ![thread] = PC_Start]
/\ y' = y + 2
/\ UNCHANGED >
Source & license
This open-source skill is cataloged on AgentStack and links to its original source — we do not rehost the code.
- Author: tlaplus
- Source: tlaplus/AgentSkills
- License: MIT
Install and usage instructions live in the source repository linked above.
Reviews
No reviews yet, be the first.
Write a review
Versions
- v0.1.0 Imported from the upstream source.