refactor(webhook): split apply command into error-returning core - #805
Conversation
First prerequisite for durable issue_comment/apply (WH-9b): applyCommandCore classifies each outcome as retryable-transient or terminal so a later durable driver can decide whether to re-drive. The goSafe wrapper discards the disposition, so current behavior is unchanged.
There was a problem hiding this comment.
Pull request overview
Refactors the webhook apply command handler by extracting an error-returning core (applyCommandCore) that reports a durability disposition (retry, err) intended for a future durable issue_comment driver, while keeping the current webhook behavior unchanged by discarding that disposition in handleApplyCommand.
Changes:
- Split
handleApplyCommandinto a thin wrapper that callsapplyCommandCore. - Introduced
(retry bool, err error)returns fromapplyCommandCoreand updated early returns throughout to map outcomes to retryable-vs-terminal dispositions. - Added contract tests covering key retryable and terminal disposition cases.
Reviewed changes
Copilot reviewed 2 out of 2 changed files in this pull request and generated 1 comment.
| File | Description |
|---|---|
| pkg/webhook/apply_handlers.go | Extracts applyCommandCore and adds retry/terminal disposition returns across apply flow. |
| pkg/webhook/apply_error_contract_test.go | Adds tests to pin the disposition contract for selected core outcomes. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
…ntract Review findings on the error-contract core: transient GitHub reads during schema discovery landed terminal (a durable driver would drop the apply), while deterministic plan rejections and lock contention landed retryable. handleSchemaRequestError now reports recognized-vs-unexpected, plan failures classify via isTransientRemotePlanError, ErrLockHeld at acquire is terminal, and the contract doc no longer implies posted-comment => terminal.
|
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 7f8dd93. Verdict: behavior-preserving and safe to land. Every old Findings1. Gate-internal operational failures are terminal, but the posted comment says "retry". Every gate mapped to 2. The plan-failure split treats transient target-DB failures as deterministic. Action items
Verified (tried to break, couldn't)Behavior preservation at every exit (comments/logs/acks/lock-release identical; This review was generated by Claude Code (claude-fable-5). |
…ailures Gate-internal operational failures are deliberately terminal (gates fail closed and post their own retry guidance), and the plan-failure branch treats everything except remote-deployment unavailability as deterministic even though planning runs against a live target database.
|
🤖 Thanks for the review - here is the response summary
|
Summary
Splits
handleApplyCommandinto a thingoSafewrapper over a new error-returning core,applyCommandCore, that classifies every outcome as retryable-transient or terminal. This is the first prerequisite for durableissue_comment/apply: a later durable driver needs a retry-vs-terminal signal to decide whether to re-drive a delivery. Behavior is unchanged in this PR — the wrapper discards the disposition.What
handleApplyCommandbecomes the synchronous entry point; it callsapplyCommandCoreand discards the returned(retry, err).applyCommandCorereturns(retry bool, err error):retry=true, err≠nil— transient infrastructure failure (command bootstrap, a GitHub read, a storage op, plan execution) the same delivery could clear on a later attempt.retry=false, err=nil— a terminal outcome that is the command's answer (silent skips, gate blocks, config/stale rejections, no-op plans, or a dispatched apply).apply-confirmis intentionally left for a follow-up PR to keep this change small.Why
Durable command dispatch must distinguish "re-drive this" from "this is the final answer." Extracting the classification now, without wiring a consumer, keeps the behavior-preserving refactor isolated from the durable-routing change that follows — smaller blast radius, reviewable independently.
Note: some retryable paths already post an operator-facing error comment before returning. A future durable retry would re-post those; that duplication is a known concern for the durable-driver slice, not introduced here.
Observability note: the two new typed schema-request errors shift the schema-request failure metric reason from
unexpectedtodatabase_not_configured/environment_not_configuredand the corresponding log level from Error to Warn — for every command routed throughhandleSchemaRequestError, not just apply.Before / after