skills/verification/verified-pseudocode-extractor/SKILL.md
Extracts human-readable pseudocode from a verified formal artifact (Dafny, Lean, TLA+) while preserving the verified properties as annotations, so the proof-carrying logic can be reimplemented in a production language. Use when porting verified code to an unverified target, when documenting what a formal spec actually does, or when handing a verified algorithm to an implementer.
npx skillsauth add santosomar/general-secure-coding-agent-skills verified-pseudocode-extractorInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
The reverse of → python-to-dafny-translator. You have verified code; you want to carry the verification obligations into a reimplementation without carrying the verification language.
The output is pseudocode annotated with preconditions, postconditions, and invariants — so whoever implements it knows exactly what the verifier was relying on.
| Dafny/Lean/TLA+ construct | In pseudocode |
| ------------------------------- | ------------------------------------------------------ |
| requires P | KEEP — // PRE: P |
| ensures Q | KEEP — // POST: Q |
| invariant I (loop) | KEEP — // INV: I at loop head |
| decreases e | KEEP — // TERMINATES BY: e decreases each iteration |
| ghost var | DROP from code, KEEP as comment explaining what it tracks |
| assert P (proof hint) | Usually DROP — it's a verifier hint, not a runtime check |
| lemma calls | DROP — they have no runtime effect |
| modifies clauses | KEEP as comment — tells implementer what's mutated |
Every dropped verification construct becomes a comment at the point where the verifier used it. Don't aggregate them into a docblock at the top — put the invariant comment at the loop head, where it's checked.
Dafny (verified):
method BinarySearch(a: array<int>, key: int) returns (idx: int)
requires forall i, j :: 0 <= i < j < a.Length ==> a[i] <= a[j] // sorted
ensures 0 <= idx ==> idx < a.Length && a[idx] == key
ensures idx < 0 ==> forall i :: 0 <= i < a.Length ==> a[i] != key
{
var lo, hi := 0, a.Length;
while lo < hi
invariant 0 <= lo <= hi <= a.Length
invariant forall i :: 0 <= i < lo ==> a[i] < key
invariant forall i :: hi <= i < a.Length ==> a[i] > key
decreases hi - lo
{
var mid := lo + (hi - lo) / 2;
if a[mid] < key { lo := mid + 1; }
else if a[mid] > key { hi := mid; }
else { return mid; }
}
return -1;
}
Extracted pseudocode:
function binary_search(a: sorted array of int, key: int) -> int
// PRE: a is sorted ascending (a[i] <= a[j] for i < j)
// POST: if result >= 0 then a[result] == key
// POST: if result < 0 then key is not in a
lo ← 0
hi ← length(a)
while lo < hi:
// INV: 0 <= lo <= hi <= length(a)
// INV: everything left of lo is < key (a[0..lo) all < key)
// INV: everything at/right of hi is > key (a[hi..) all > key)
// TERMINATES BY: (hi - lo) strictly decreases each iteration
mid ← lo + (hi - lo) / 2 // no overflow — NOT (lo+hi)/2
if a[mid] < key:
lo ← mid + 1
else if a[mid] > key:
hi ← mid
else:
return mid
return -1
What got preserved: The three loop invariants are the essence of why binary search works. An implementer who maintains them can't get the algorithm wrong. The overflow note on mid — that's from the verified lo + (hi - lo) / 2, not the naive (lo + hi) / 2.
What got dropped: Nothing here — this Dafny had no ghost state or lemmas. If it had ghost var numCompares, that'd become a comment: // (verifier tracked comparison count for complexity proof — not needed at runtime).
TLA+ → pseudocode is different: TLA+ actions become pseudocode steps, and the invariant becomes a global assertion.
// GLOBAL INVARIANT (must hold after every step):
// at most one process in critical section
step AcquireLock(p):
// ENABLED WHEN: lock is free
wait until lock == FREE
lock ← p
step ReleaseLock(p):
// ENABLED WHEN: p holds the lock
assert lock == p
lock ← FREE
The ENABLED WHEN annotations are the TLA+ guards. They tell the implementer: this operation blocks (or fails) if the condition isn't met.
(lo + hi) / 2 is simpler than lo + (hi - lo) / 2 and also wrong at scale. Keep what the verifier verified.decreases clause. Termination is a correctness property. "This loop terminates because X decreases" belongs in the output.## Source
<Dafny | Lean | TLA+> — <file/module>
## Verified properties
PRE: <list>
POST: <list>
INV: <per-loop list>
TERM: <decreases measures>
## Pseudocode
<annotated pseudocode — annotations inline at point-of-use>
## Dropped constructs
<ghost vars, lemmas, proof hints — what they were for>
## Implementation warnings
<anything the verifier relied on that a naive implementer might get wrong — overflow, aliasing, atomicity>
development
Translates natural-language or pseudocode descriptions of concurrent and distributed systems into TLA+ specifications ready for the TLC model checker. Identifies state variables, actions, type invariants, safety properties, and liveness properties from the description. Use when formalizing a protocol, when the user describes a distributed algorithm to verify, when designing a consensus or locking scheme, or when starting formal verification of a concurrent system.
testing
Reduces a TLA+ model so TLC can actually check it — shrinks constants, adds state constraints, abstracts data, or applies symmetry — when the state space is too large to enumerate. Use when TLC runs out of memory, when checking takes hours, or when a spec works at N=2 and you need confidence at larger scale.
development
TLA+-specific instance of model-guided repair — reads a TLC error trace, identifies the enabling condition that should have been false, strengthens the corresponding action, and maps the fix to source code. Use when TLC reports an invariant violation or deadlock and you have the code-to-TLA+ mapping from extraction.
testing
Translates specifications into temporal logic formulas (LTL, CTL, or TLA) by matching the specification's shape to the right logic and operators. Use when formalizing requirements for any model checker, when choosing between LTL and CTL for a property, or when the user has a temporal claim and doesn't know which operators express it.