You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
emrg: an approval can be spent twice if the gate dies between the send and the ledger write #2449
scripts/feishu-gate.py marks an approval spent only after the CLI has accepted the
message: cmd_send calls send_message(...), and only then sets row["status"] = "sent"
and rewrites the ledger. If the process ends between those two points — a crash, a kill, a
host that sleeps through the CLI's 120 s timeout after the CLI already accepted — the row is
still pending.
The next send with the same token then passes every check the gate makes: the status is pending, the body digest still matches, the request is inside --max-age, and the host's
reply still appears after the request in the stream. So the message goes out a second time.
Why that matters
"One approval, one message, one recipient" is the property the gate exists for, and it is
the property the tests arm in both directions. This path breaks it without any of those
tests noticing, because every test drives the gate through a CLI that answers.
Expected
An approval is spendable at most once, including across a crash of the gate itself.
What the fix must satisfy
The row must already be unspendable before the CLI is invoked, so that a crash leaves
it in a state send refuses rather than one it accepts.
A genuine refusal from the CLI must not silently burn the approval. "Fail closed" and
"keep the approval usable" are different choices with different costs, and whichever is
taken must be stated in the script and pinned by a test — not decided by where a crash
happens to land.
A test that ends the gate between the send and the ledger write, then runs send again,
must observe a refusal and exactly one message in the stand-in CLI's stream. Testing
only the second half would pass on a gate that refuses everything.
The Windows leg cannot run these tests (a stand-in CLI cannot carry the multi-line
argument there), so the new test inherits the module's existing POSIX-only marker rather
than inventing a second skip rule.
Scope
The window is narrow; this is a gap in a stated invariant, not a claim that the gate is
unsafe to use. The redundancy noted alongside it — the status == "sent" branch and the status != "pending" branch below it both refuse, so removing either alone keeps the tests
green — is by design and needs no change.
Activity
argszero commentedon Oct 21, 2026
Handled by #2450