Idea is that you go from a "vault" state to "unvault" state to "spend" state via the hot wallet keys, with a fixed delay between unvault/spend, and the ability for the cold wallet holder to verify that the spend tx is valid, and if it's not for the cold wallet holder to be able to recover the funds during the delay, before they're lost/stolen/etc.
Spend:
input:
U:0 - witness: ipk = ColdK, HColdKN, S, []
outputs:
<amt> <alice>
<amt> <bob>
<amt> <carol>
<amt> <change>
S = "<H> CTV <D> CSV" -- spending script, no witness data needed
H = Hash_CTV(Spend_outputs) -- CTV hash
D = 100 blocks
ColdKN = "<C1> CHECKSIG <C2> CHECKSIGADD <C3> CHECKSIGADD .. <Cn> CHECKSIGADD <K> EQUAL"
HColdKN = H_TapLeaf(ColdKN)
-- k of n cold wallet recovery
ColdK = musg(C1,..,Ck)
-- first k of n, cold wallet recovery via key path if keys 1..k are available
Unvault:
input:
V:0 - witness: ipk = ColdK, HColdKN, U, [H, S1, S2, .. SM]
outputs:
<bal> TapRoot(ColdK, [ColdKN, S])
U = SIZE 32 EQUALVERIFY
[0xc02520] SWAP CAT [CTV;PUSH<D>;CSV] CAT
"TapLeaf" SHA256 DUP CAT SWAP CAT SHA256
0 SWAP 2 TLUV
<H1> CHECKSIG <H2> CHECKSIGADD .. <Hm> CHECKSIGADD <L> EQUALVERIFY
IN_OUT_AMOUNT SUB <FEES> LESSTHANOREQUAL
-- check the CTV hash, H, is the rigth size,
convert H into the spend script S, ready to be hashed
convert the spend script S into it's merkle hash
verify the output sPK is the input sPK with U replaced by S
check there are L signatures from hot wallet keys H1..HM
check that no more than FEES sats have been removed from the vault
S1..SM - signatures of the tx via the hot wallet keys H1..Hm, exactly
L of which should be valid, the remainder empty.
Vault address = TapRoot(ColdK, [ColdKN, U])