feat(webhook): dispatch unlock commands through the durable inbox - #945
feat(webhook): dispatch unlock commands through the durable inbox#945Kiran01bm wants to merge 4 commits into
Conversation
WH-9b-iii: unlock now rides the durable issue_comment path added in #922 — the request path keeps its synchronous routing/usage gates and ack reaction, then enqueues instead of launching a goroutine. The leased driver re-parses the stored comment and routes it onto unlockCommandCore (#923), whose (retry, err) disposition drives lease completion, retry, or terminal failure. The core now takes a parent context so lease loss or shutdown cancels in-flight work.
There was a problem hiding this comment.
Pull request overview
Routes schemabot unlock through the same durable issue_comment inbox/driver pipeline as apply and apply-confirm, ensuring acknowledged unlock requests survive process restarts and transient failures and are re-driven according to the core’s (retry, err) disposition.
Changes:
- Added durable-dispatch enqueue path for
unlockin theissue_commentwebhook handler and enabled durable-driver routing tounlockCommandCore. - Updated
unlockCommandCoreto accept a parent context so durable-driver cancellation (lease loss/shutdown) can cancel in-flight unlock work while preserving request-path behavior. - Extended the durable-driver and disposition contract tests to cover queued unlock, terminal no-locks behavior, and retryable lock-lookup failures.
Reviewed changes
Copilot reviewed 4 out of 4 changed files in this pull request and generated no comments.
| File | Description |
|---|---|
| pkg/webhook/issue_comment.go | Enqueues unlock into the durable inbox when enabled; driver re-parses and routes unlock to unlockCommandCore; expands “durably dispatched command” eligibility. |
| pkg/webhook/apply_handlers.go | Threads a parent context into unlockCommandCore and preserves synchronous wrapper behavior by passing context.Background(). |
| pkg/webhook/durable_issue_comment_test.go | Adds durable queue/ack coverage for unlock and new driver-through-core unlock tests for terminal and retryable outcomes. |
| pkg/webhook/unlock_error_contract_test.go | Updates unlock core contract tests for the new context-bearing unlockCommandCore signature. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
…mmand A re-driven or redelivered unlock could release locks acquired after the command was issued — including another session's CLI lock on --force. The core now takes the delivery's received-at as a freshness cutoff, treats lost release races (lock already gone) as the command's answer, and the driver-context cancellation of in-flight unlock releases is pinned by a test. Unrouted durable commands are now Warn + metric instead of a silent Info swallow.
|
You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard. |
|
🤖 Adversarial correctness review, requested by Armand and performed by his agent. Reviewed at head Verdict: ship-ready once two small findings are addressed — a silent-answer gap on a concurrency edge, and a documentation correction on what the freshness bound actually guarantees under redelivery. The wiring itself is correct and consistent with the apply/apply-confirm slices, and I verified the failure mode that would have been catastrophic here (a zero-time freshness cutoff) cannot occur. Finding 1: when every matched lock vanishes concurrently, the acked command ends with no answerThe new Finding 2 (docs): redelivery refreshes the cutoff — the body and godoc overstate the invariantThe PR body says "a re-driven or redelivered unlock can no longer release a lock acquired after the command was issued", and the godoc says the bound holds "arbitrarily later via redelivery". Neither is what the code does: GitHub Redeliver reuses the delivery GUID, and Observation (non-blocking): the cutoff compares two clocks
Verified solid: the cutoff can never be the zero time — This review was generated by Claude Code (claude-fable-5). |
aparajon
left a comment
There was a problem hiding this comment.
🤖 Approving per the adversarial review above — findings are small and non-blocking (an answer-gap on a narrow concurrency edge, and a doc correction on the redelivery cutoff). Stamped by Armand's agent (claude-fable-5).
Summary
WH-9b-iii of the durable-webhook workstream:
schemabot unlockis the thirdissue_commentcommand routed through the durable inbox, joiningapplyandapply-confirm(#922). An acknowledged unlock now survives process restarts and transient infrastructure failures instead of dying with an in-process goroutine.What
goSafegoroutine (durableIssueCommentCommandReadynow acceptsaction.Unlock).unlockCommandCore(refactor(webhook): expose retry disposition for unlock commands #923); its(retry, err)disposition drives lease completion, scheduled retry, or terminal failure — identical to the apply/apply-confirm flow.unlockCommandCoregains a parent-context parameter: the synchronous wrapper passescontext.Background()(unchanged behavior), while the driver passes its run context so lease loss or shutdown cancels in-flight work.ErrLockNotFound/ErrLockNotOwned) are terminal-idempotent instead of retried, and an admitted-but-unrouted durable command is now Warn + metric instead of a silent Info swallow.Why
Without durable dispatch, a deploy or crash after the 👀 ack silently drops the unlock — the user saw the acknowledgment but the locks stay held, and GitHub won't redeliver an acked webhook. With #922's driver and #923's disposition core both on
main, this slice is pure wiring plus the freshness bound that durability makes necessary for a lock-mutating command.Remaining WH-9b slices:
rollback/rollback-confirm/ controls, and the #806 lock-path disposition contract tests.Before / after
Deferred review follow-ups
References