fix(artwork): treat agent not-found as breaker success

This commit is contained in:
Deluan 2026-07-22 19:58:57 -04:00
parent 190c291e61
commit 39e939686b
2 changed files with 22 additions and 2 deletions

View File

@ -267,8 +267,9 @@ func (b *breaker) allow() bool {
func (b *breaker) record(err error) {
b.mu.Lock()
defer b.mu.Unlock()
// A not-found is a definitive answer, not a fault; only real errors trip the breaker.
if err == nil || errors.Is(err, model.ErrNotFound) {
// A not-found (from either package) is a definitive answer, not a fault; only real
// errors trip the breaker. Must stay consistent with isTransientExternal.
if err == nil || errors.Is(err, model.ErrNotFound) || errors.Is(err, agents.ErrNotFound) {
b.failures = 0
return
}

View File

@ -283,6 +283,25 @@ var _ = Describe("Worker", func() {
Expect(calls).To(Equal(5), "the breaker should have re-closed after the success")
})
It("does not open the breaker on a run of agent not-found misses", func() {
// Regression: agents.ErrNotFound is a definitive miss, not a fault. A run of
// artless items must never trip the breaker, or they'd loop in retry instead of
// settling absent. Uses the real gate, not passthroughGate.
notFound := func() (io.ReadCloser, string, error) { return nil, "", agents.ErrNotFound }
for range breakerThreshold + 3 {
_, _, err := w.gate("A", notFound)
Expect(err).To(MatchError(agents.ErrNotFound), "a miss passes through, never errBreakerOpen")
}
var calls int
counting := func() (io.ReadCloser, string, error) {
calls++
return nil, "", errors.New("boom")
}
_, _, _ = w.gate("A", counting)
Expect(calls).To(Equal(1), "the breaker stayed closed, so the step still runs")
})
It("isolates each agent's breaker: one open gate does not block another", func() {
failing := func() (io.ReadCloser, string, error) { return nil, "", errors.New("boom") }
for range breakerThreshold {