Documentation
¶
Overview ¶
Package verdict is the engine's structured outcome contract: every migrate invocation ends in exactly one verdict — executed natively, refused with a typed reason and, where one exists, a safer native idiom, or failed during execution with the executor's stable outcome code and a disclosure of what committed before the failure. Refusals use a distinct exit code from operational errors. This type is the seam a future orchestrator adapter maps onto SchemaBot's ExecutionModeBlocked.
Index ¶
Constants ¶
const ExitCodeRefused = 2
ExitCodeRefused is the process exit code for a refusal verdict — distinct from 1, which means an operational error (could not connect, bad flag, SQL error). Automation branches on the difference.
Variables ¶
var ErrRefused = errors.New("refused")
ErrRefused is the sentinel the CLI returns after printing a refusal verdict, so the entry point can map it to ExitCodeRefused.
Functions ¶
This section is empty.
Types ¶
type Cause ¶
type Cause string
Cause narrows ReasonBudgetExceeded to the budget that was exceeded, so automation can branch on which limit fired without parsing prose.
const ( // CauseNone is the zero cause for verdicts that are not budget refusals. CauseNone Cause = "" // CauseLockBudget: the lock was not granted within lock_timeout; nothing // was executed. CauseLockBudget Cause = "lock-budget" // CauseStatementBudget: the statement ran past statement_timeout and was // cancelled; the change needs a rewrite. CauseStatementBudget Cause = "statement-budget" )
The budget causes a refusal can carry.
type Outcome ¶
type Outcome string
Outcome is what happened to the submitted change.
const ( // OutcomeExecuted means the change ran and committed natively within // its budgets. OutcomeExecuted Outcome = "executed-natively" // OutcomeRefused means the change was not executed; Reason says why. OutcomeRefused Outcome = "refused" // OutcomeFailed means execution was attempted and failed: an // operational error, not a refusal — the process still exits 1. Code // carries the executor's stable outcome code, and for a mid-sequence // failure FailedStep and ExecutedSQL disclose the failed step and the // committed prefix whose state remains, so automation can distinguish // "nothing happened" from "partial state left behind". OutcomeFailed Outcome = "failed" )
The outcomes a migrate run can end in.
type Reason ¶
type Reason string
Reason is the typed cause of a refusal. Reasons are flat kebab-case tokens — they are what automation switches on; prose belongs in Detail.
const ( // ReasonNone is the zero reason carried by an executed verdict. ReasonNone Reason = "" // ReasonUnsupportedStatement: only ALTER TABLE is supported. ReasonUnsupportedStatement Reason = "unsupported-statement" // ReasonIndexStatement: index maintenance has a native safe idiom // (CONCURRENTLY) and is never attempted here. ReasonIndexStatement Reason = "index-statement" // ReasonTableTooLarge: the size guard skipped the optimistic attempt. ReasonTableTooLarge Reason = "not-native-safe-table-too-large" // ReasonInsufficientPrivileges: the connected role lacks the access // the change needs; Detail names the exact missing GRANT (see // docs/engine-role.md). ReasonInsufficientPrivileges Reason = "insufficient-privileges" // ReasonUnsupportedPartitionedParent: the routed plan builds an index // on a partitioned parent, for which the required partition-aware // online sequence is not implemented. ReasonUnsupportedPartitionedParent Reason = "unsupported-partitioned-parent" // ReasonBudgetExceeded: the optimistic attempt exceeded its lock or // statement budget and was cancelled. ReasonBudgetExceeded Reason = "not-native-safe-budget-exceeded" // ReasonRewriteRequired: the submitted form blocks and must run as a // safer native sequence, but the planner could not construct one (a // multi-operation statement, or a pattern it cannot build). Running // the submitted form would falsify the plan's own reason, so the // engine refuses instead. ReasonRewriteRequired Reason = "not-native-safe-rewrite-required" // this build does not implement (copy-and-swap). ReasonBackendUnavailable Reason = "backend-unavailable" )
The refusal reasons Phase 1 can emit.
type Verdict ¶
type Verdict struct {
// Outcome is what happened.
Outcome Outcome `json:"outcome"`
// Reason is the typed refusal cause; empty when executed.
Reason Reason `json:"reason,omitempty"`
// Cause narrows a budget refusal to the budget that fired; empty
// otherwise.
Cause Cause `json:"cause,omitempty"`
// Code is the executor's stable outcome code (executor.OutcomeCode)
// carried by a failed verdict — flat kebab-case, part of the executor's
// report contract. It stays a plain string here so this contract
// package does not depend on the executor. Empty unless Outcome is
// OutcomeFailed.
Code string `json:"code,omitempty"`
// FailedStep is the 1-based position of the sequence step that failed,
// matching the numbering the planner's partial-failure contracts use;
// zero when the failure was not a mid-sequence one (a single-statement
// attempt rolls back and commits nothing).
FailedStep int `json:"failed_step,omitempty"`
// FailedStepSQL is the failed step's statement — the step the planner's
// partial-failure contract says a retry resumes from.
FailedStepSQL string `json:"failed_step_sql,omitempty"`
// Attempts is how many bounded attempts ran before a lock-budget
// refusal, so automation can tell an exhausted bounded retry from a
// single cancelled attempt; zero for every other verdict.
Attempts int `json:"attempts,omitempty"`
// Statement is the submitted SQL.
Statement string `json:"statement"`
// Table is the target table (schema-qualified when the statement was),
// when the statement has one.
Table string `json:"table,omitempty"`
// Detail is the human explanation: why refused, or what committed.
Detail string `json:"detail,omitempty"`
// SaferIdiom is a native alternative to the refused statement, when one
// exists (e.g. CREATE INDEX CONCURRENTLY, ADD CONSTRAINT ... NOT VALID).
SaferIdiom string `json:"safer_idiom,omitempty"`
// ExecutedSQL is the ordered SQL the engine actually ran and committed.
// On an executed verdict it is the substituted safer native sequence
// (empty when the submitted form ran as-is — a non-empty value is what
// tells automation a substitution happened). On a failed verdict it is
// the committed prefix that remains: empty means nothing committed.
ExecutedSQL []string `json:"executed_sql,omitempty"`
// Forced reports that --force overrode the engine's routing: the
// submitted form ran as-is instead of a safer substitution or a
// strategy refusal. It is the machine-readable audit record of the
// override.
Forced bool `json:"forced,omitempty"`
}
Verdict is the structured outcome of one migrate invocation.