Files
Julian Knutsen af45955aa2 fix(dolt): preserve transaction outcome boundaries (#5341)
* fix(dolt): preserve transaction outcome boundary (ga-f7v2ft.79.1)

Agent-Signature: codex-unknown-model-unknown-reasoning on behalf of CI Bot

* fixup! fix(dolt): preserve transaction outcome boundary (ga-f7v2ft.79.1)

Agent-Signature: codex-unknown-model-unknown-reasoning on behalf of CI Bot

* fixup! fix(dolt): preserve transaction outcome boundary (ga-f7v2ft.79.1)

Agent-Signature: codex-unknown-model-unknown-reasoning on behalf of CI Bot

* fixup! fix(dolt): preserve transaction outcome boundary (ga-f7v2ft.79.1)

Agent-Signature: codex-unknown-model-unknown-reasoning on behalf of CI Bot

* fixup! fix(dolt): preserve transaction outcome boundary (ga-f7v2ft.79.1)

Agent-Signature: codex-unknown-model-unknown-reasoning on behalf of CI Bot

* fixup! fix(dolt): preserve transaction outcome boundary (ga-f7v2ft.79.1)

Agent-Signature: codex-unknown-model-unknown-reasoning on behalf of CI Bot

* fixup! fix(dolt): preserve transaction outcome boundary (ga-f7v2ft.79.1)

Agent-Signature: codex-unknown-model-unknown-reasoning on behalf of CI Bot

* fixup! fix(dolt): preserve transaction outcome boundary (ga-f7v2ft.79.1)

Agent-Signature: codex-unknown-model-unknown-reasoning on behalf of CI Bot

* fix(dolt): retain indeterminate mixed claim updates (ga-f7v2ft.79.1)

Agent-Signature: codex-unknown-model-unknown-reasoning on behalf of CI Bot

* fix(dolt): retain indeterminate guarded update errors (ga-f7v2ft.79.1)

Agent-Signature: codex-unknown-model-unknown-reasoning on behalf of CI Bot

* fix(dolt): prevent lifecycle callback replay (ga-f7v2ft.79.1)

Agent-Signature: codex-unknown-model-unknown-reasoning on behalf of CI Bot

* fix(dolt): classify publication commit ambiguity (ga-f7v2ft.79.1)

Agent-Signature: codex-unknown-model-unknown-reasoning on behalf of CI Bot

* fix(embeddeddolt): mark commit response loss indeterminate (ga-f7v2ft.79.1)

Agent-Signature: codex-unknown-model-unknown-reasoning on behalf of CI Bot

* fix(dolt): retain indeterminate claim outcomes (ga-f7v2ft.79.1)

Agent-Signature: codex-unknown-model-unknown-reasoning on behalf of CI Bot

* fix(dolt): retry rollback-safe transaction setup (ga-f7v2ft.79.1)

Agent-Signature: codex-unknown-model-unknown-reasoning on behalf of CI Bot

* fix(dolt): surface working-set staging failures (ga-f7v2ft.79.1)

Agent-Signature: codex-unknown-model-unknown-reasoning on behalf of CI Bot

* fix(dolt): account direct commit publication failures (ga-f7v2ft.79.1)

Agent-Signature: codex-unknown-model-unknown-reasoning on behalf of CI Bot

* fix(embeddeddolt): mark commit call response loss indeterminate (ga-f7v2ft.79.1)

Agent-Signature: codex-unknown-model-unknown-reasoning on behalf of CI Bot

* fix(dolt): account indeterminate transaction commits (ga-f7v2ft.79.1)

Agent-Signature: codex-unknown-model-unknown-reasoning on behalf of CI Bot

* fix(dolt): restore transaction circuit accounting (ga-f7v2ft.79.1)

Agent-Signature: codex-unknown-model-unknown-reasoning on behalf of CI Bot

* fix(storage): preserve settlement commit boundaries (ga-f7v2ft.79.1)

Agent-Signature: codex-unknown-model-unknown-reasoning on behalf of CI Bot

* fix(dolt): guard direct write circuit boundaries (ga-f7v2ft.79.1)

Agent-Signature: codex-unknown-model-unknown-reasoning on behalf of CI Bot

* fix(dolt): close remaining circuit write gaps (ga-f7v2ft.79.1)

Agent-Signature: codex-unknown-model-unknown-reasoning on behalf of CI Bot

* fix(dolt): account wisp dependency commits (ga-f7v2ft.79.1)

Agent-Signature: codex-unknown-model-unknown-reasoning on behalf of CI Bot

* fix(dolt): record circuit success only after claim verify; cut retry/patch complexity

Maintainer review fixups for PR #5341 (three majors):

- Correctness: verify-gated claim paths (ClaimReadyIssue, UnclaimIssue,
  UnclaimIssueIfAssignee, readyClaimer.ClaimNext, issueClaimer.Claim) now wrap
  verify+write in withCircuitWrite, so the circuit breaker records success only
  at the boundary after post-write verification returns nil — never on the bare
  SQL commit ahead of verification. Adds a regression test proving a
  committed-but-unverified claim does not reset the breaker.

- Maintainability: replace hasNonCoordinationPatch's 25-branch || chain with a
  straight-line nonCoordinationPatchSignals []bool enumeration (cyclomatic
  25 -> 3). Adds reflective exhaustiveness and count-guard tests so a new
  IssuePatch/LabelPatch/MetadataPatch field fails until it is classified.

- Readability: split withRetryClassified (cognitive 33) into classifyManagedRetry
  and recordRetryFailure (cognitive 3/3), preserving retry, indeterminate-commit,
  and breaker-trip behavior for both callers.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* fix(dolt): record circuit success after facade update verify; skip ignored tables in commit

Maintainer review fixup for PR #5341 (attempt 2).

Major: IssueLifecycle.Update ran its write on an unmanaged context, so
withCircuitWrite recorded RecordSuccess when the SQL commit landed --
before verifiedClaimWrite's re-read could contradict it, laundering a
phantom claim into breaker optimism. Wrap the verify path in
withCircuitWrite and invoke write with the circuit-managed context so
terminal success is recorded once at the boundary, only after verify
returns nil (matching issueClaimer.Claim). The ordinary patch path keeps
its original context and eager accounting.

Minor: commitWorkingSet fed every dolt_status row into a fail-hard
DOLT_ADD loop, so an ordinary Commit's success depended on Dolt's
version-specific handling of DOLT_ADD on a dolt_ignore'd table. Filter
ignored tables with the same anti-join HasCommittablePending uses.

Nit: drop the dead nested-error branch in verifiedClaimWrite (err is
already nil there).

Regressions: facade claim and guarded-update verify failures must not
reset the circuit breaker; a dirty ignored wisp is non-committable and
Commit tolerates it.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* fix(dolt/test): match commitWorkingSet dolt_ignore anti-join in sqlmock expectations

commitWorkingSet selects dirty tables with the dolt_ignore anti-join
(`SELECT s.table_name FROM dolt_status s WHERE NOT EXISTS (...)`) so ignored
wisp/lease tables stay out of fail-hard staging, but the draincall and
settlement sqlmock regression tests still expected the pre-anti-join
`SELECT table_name FROM dolt_status`. Under QueryMatcherRegexp the status query
no longer matched, so the commit path returned "could not match actual sql"
before reaching the indeterminate-commit / staging-cause / circuit-trip
assertions, failing the macOS Test job.

Update the six status-query expectations to the anti-join form. Behavioral
assertions are unchanged; this only realigns the mock's expected SQL with the
production query the review validated.

---------

Co-authored-by: CI Bot <ci@beads.test>
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
2026-08-06 04:34:46 -07:00

49 lines
2.1 KiB
Go

package backend
import (
"github.com/steveyegge/beads/internal/storage"
"github.com/steveyegge/beads/internal/types"
)
// ErrUnsupported reports an operation unavailable on a storage backend. A
// backend that legitimately does not support a method returns a
// *ErrUnsupported naming that method; the conformance suite's
// RunUnsupportedContract asserts exactly that shape.
type ErrUnsupported = storage.ErrUnsupported
// Sentinel errors the storage contract requires from an implementation.
// Each is the SAME value as the internal sentinel (and, where noted in
// internal/storage, as the public issueops sentinel), so errors.Is matches
// across the module boundary. The conformance suite asserts these exact
// identities: a backend must return (or wrap) these values, not
// equivalently-worded errors of its own.
var (
ErrAlreadyClaimed = storage.ErrAlreadyClaimed
ErrAlreadyExists = storage.ErrAlreadyExists
ErrAssigneeMismatch = storage.ErrAssigneeMismatch
ErrCloseBlocked = storage.ErrCloseBlocked
ErrCloseOpenChildren = storage.ErrCloseOpenChildren
ErrCommitIndeterminate = storage.ErrCommitIndeterminate
ErrNotClaimable = storage.ErrNotClaimable
ErrNotFound = storage.ErrNotFound
ErrNotInitialized = storage.ErrNotInitialized
ErrNotOwner = storage.ErrNotOwner
ErrPrefixMismatch = storage.ErrPrefixMismatch
ErrStatusMismatch = storage.ErrStatusMismatch
ErrValidation = storage.ErrValidation
ErrVersionMismatch = storage.ErrVersionMismatch
// ErrFieldTooLong pairs with MaxFieldLen.
ErrFieldTooLong = types.ErrFieldTooLong
)
// ClaimedByFragment and NotClaimableStatusFragment are the exact message
// fragments the claim path appends after ErrAlreadyClaimed / ErrNotClaimable
// to carry the conflicting assignee or status. A backend that produces claim
// conflicts itself must use the same format so ParseClaimConflict-style
// consumers keep working; see the documentation in internal/storage.
const (
ClaimedByFragment = storage.ClaimedByFragment
NotClaimableStatusFragment = storage.NotClaimableStatusFragment
)