1 Commits
Author SHA1 Message Date
sneak a69edcdb76 Store a target's number of delivery attempts, at least one (closes #540)
check / check (push) Canceled after 0s
Target.MaxRetries (max_retries) becomes Target.MaxAttempts
(max_attempts), and the form field, parser and ceiling are renamed to
match. The column is NOT NULL and a check constraint refuses a value
below 1, so every target, whatever its type, is stored with at least
1. 1 is a single attempt with no circuit breaker; above 1 retries
behind the breaker up to that many attempts. Both target forms refuse
0 and less; a blank field on the add target form gives 1, and on the
edit form keeps the stored value. The target page shows the plain
number, and the README's two paragraphs on 0 against 1 become one
sentence.

Model: opus-5-5
2026-10-15 16:55:55 +00:00
3 changed files with 15 additions and 13 deletions
+1 -1
View File
@@ -146,7 +146,7 @@ func openDB(
}
// assertBounded holds every line the adapter wrote to the stated
// ceiling and proves no line carried a whole client value.
// ceiling and proves each was cut rather than merely short.
func assertBounded(t *testing.T, out string) {
t.Helper()
+4 -3
View File
@@ -191,12 +191,13 @@ func receiverRouter(h *handlers.Handlers) *chi.Mux {
return router
}
// postReceiver sends one POST with an empty body at /h/<segment>
// through router and returns the response status.
// postReceiver sends one POST at /h/<segment>.
//
// RawPath is cleared after parsing so chi routes on the decoded path
// and the handler sees the raw bytes rather than their percent-escaped
// spelling.
// spelling. That is the harder case for the budget: the escaped
// spelling is plain ASCII, which costs one byte per byte, while the
// decoded bytes are what the log handler has to escape.
func postReceiver(
t *testing.T, router *chi.Mux, segment string,
) int {
+10 -9
View File
@@ -115,15 +115,16 @@ const (
// a caller on a parameterised route would supply, which is the
// only way those caps can be pinned at all.
//
// The GORM adapter in internal/gormlog writes through slog as well.
// It logs each statement with its placeholders rather than the
// values bound to them (see its ParamsFilter), so the key a client
// sends to the receiver or login lookup does not reach its lines.
// Its widest line spends two logfield.MaxBytes budgets — the
// statement and the driver error — against a fixed portion smaller
// than this one's, and internal/gormlog/gormlog_test.go asserts
// every line it emits against this constant directly rather than
// leaving it as arithmetic.
// One writer in that set is not a handler's own slog call. The
// GORM adapter in internal/gormlog logs the statement with the
// client-chosen parameter already interpolated into it, which on
// the receiver and login lookups is exactly the value the budgets
// above exist for. Its widest line spends two logfield.MaxBytes
// budgets — the statement and the driver error — against a fixed
// portion smaller than this one's, and
// internal/gormlog/gormlog_test.go asserts every line it emits
// against this constant directly rather than leaving it as
// arithmetic.
//
// What it does NOT cover, so that the figure above is not read as
// more than it is: