You could do something like this (psuedocode):
configuration <k> $PGM </k>
<state> WHATEVER_YOUR_STATE_IS </state>
syntax KItem ::= evaluateInContext ( Exp , StateCell )
syntax Exp ::= Exp "/" Exp | "HOLE1" | "HOLE2" | Value
syntax Value ::= Int
rule <k> A1 / A2 => evaluateInContext(A1, <state> STATE </state>) ~> evaluateInContext(A2, <state> STATE </state>) ~> HOLE1 / HOLE2 ... </k>
<state> STATE </state>
rule <k> evaluateInContext(A, <state> STATE </state>) => A ... </k>
<state> _ => STATE </state>
rule <k> V:Value ~> evaluateInContext(A, S) => evaluateInContext(A, S) ~> V ... </k>
rule <k> V:Value ~> V':Value ~> HOLE1 / HOLE2 => V' /Int V ... </k>
So you can always pass around configurations as first-class citizens.
An example of this is in KEVM, where we use this sort of mechanism to save/retrieve a list of callstacks.
Update to address comment.
If you want to check that a subterm makes a state transition, you can change it to something like this (psuedo-code again):
syntax KItem ::= "evaluated?" "(" Exp "," State ")"
rule <k> evaluateInContext(A, <state> STATE </state>) => A ~> evaluated?(A, <state> STATE </state> ... </k>
<state> _ => STATE </state>
rule <k> V:Value ~> evaluated?(A, <state> STATE </state>) => DO_SOMETHING_WHEN_EVALUATED ... </k>
rule <k> NOT_VALUE:Exp ~> evaluated?(A, <state> STATE </state>) => DO_SOMETHING_WHEN_NOT_EVALUATED ... </k>
Note that here I'm using "has reduced to sort Value
" to determine if it's "evaluated", but you can of course have any side-condition you like for determining that.