Correct release-blocking documentation inaccuracies (closes #141) #144

Merged
clawbot merged 1 commits from issue-141-release-readiness into next 2026-08-12 13:15:06 +02:00
Collaborator

Closes #141.

What changed

  1. README.md env table gains RETENTION_SWEEP_INTERVAL (default 1h), which two other passages already referred to as documented.
  2. TODO.md Completed Steps gains the five omitted units: #118, #64, #90, #113, #79. Derived from git log origin/main..origin/next, not from the existing text.
  3. Replay contradiction resolved against the code: no redelivery exists anywhere in the tree, so every README mention is now either marked planned or already under a Planned heading, and TODO.md Future Steps regains "Manual event redelivery from the web UI". Redelivery itself is not implemented here; that is a feature and out of scope.
  4. console.log("Webhooker loaded") removed from static/js/app.js.

No behavioural change. The only non-comment, non-documentation edit is the deleted console.log.

Rework

Rebased onto next (now be57609) and addressed the review finding.

Finding fixed. README.md:788-792, the delivery-semantics passage on target-type edits, said a terminally failed delivery's event "can be redelivered manually". It now says the event stays stored but there is no way to redeliver it, and that manual redelivery is planned, not implemented.

Full README sweep. The previous pass fixed only the lines the issue named. Every occurrence of redelivery / replay / resend / re-send / re-dispatch / manual recovery in README.md, in any tense (line numbers as of c4022c0):

Line Text Resolution
268-271 Rationale item 5, Replay Already marked "(not yet implemented)" by this commit. Accurate.
280-281 Use Cases bullet, Replay Already marked "(planned; not yet implemented)" by this commit. Accurate.
506-507 Event model: "Stores the complete HTTP request data for replay and auditing" Changed to "for auditing and for the planned replay capability" — see the judgement call below.
788-792 Delivery semantics, target-type edit Changed — the review finding.
927 POST /api/v1/events/{id}/redeliver Already under the #### API (Planned) heading. Accurate, unchanged.
7, 244, 464, 536-547, 712-779, 840, 858 retry / max_retries / retrying / retry sweep / Retry-After Automatic delivery retry, which is implemented (internal/delivery/engine.go). Not redelivery. Accurate, unchanged.
192 "no manual key management is needed" Unrelated (signing keys). Unchanged.
672 "destroying it ... would be unrecoverable" Archive file retention on webhook deletion. Unrelated. Unchanged.
806 Circuit-breaker "has recovered" Target health. Unrelated. Unchanged.
1029 Recoverer middleware Panic recovery. Unrelated. Unchanged.
1118, 1130 "test stages replayed rather than executed", "replays from cache" Docker layer cache, added by #138. Unrelated to event replay. Unchanged.

Nothing outside the README implements or claims redelivery: redeliver/replay/resend/re-send across all .go, .html, .js and .tmpl files (excluding vendored static/js/alpine.min.js) matched exactly one line.

Judgement call on README.md:506-507. The reviewer flagged "Stores the complete HTTP request data for replay and auditing" as a decision to make rather than a defect. Call: changed it. It is a purpose statement rather than a present-tense feature promise, so the reviewer's reading is fair — but a bare "for replay" in the data-model section is the same residual claim that caused this rework, and the fix costs four words. Changing it makes the rule uniform across the file: after this commit, every mention of replay or redelivery in the README is marked planned or sits under a Planned heading, with no case left to argue about.

One edit outside the README, disclosed. The doc comment on failUnretryableRetry (internal/delivery/engine.go:788) was the source text for the README passage and carried the identical false claim — "failed with a recorded reason and can be redelivered manually". Since the issue is titled "docs contradict the code", leaving the same untrue sentence in the tree while fixing it in the README seemed worse than a three-line comment edit. It now reads "The event stays stored, but nothing redelivers it today." Comment only; no code, no behaviour.

Env-table conflict resolved. #142 landed the same RETENTION_SWEEP_INTERVAL row, so the rebase conflicted on it. Kept one row combining what each version got right, rather than either verbatim:

Version Description
next (from #142) Retention reaper period (Go duration, must be positive)
this PR (before rebase) How often the retention reaper and archive sweeper run (Go duration)
kept How often the retention reaper and archive sweeper run (Go duration, must be positive)

Verified against the source, not assumed. Default 1h: defaultRetentionSweepInterval = time.Hour (internal/config/config.go:34) — both versions were right. Two consumers, not one: internal/database/retention.go:56 and internal/delivery/archive_sweeper.go:61 both read Config.RetentionSweepInterval, so this PR's description was the more complete of the two. Must be positive: true only as of #142, which moved the variable to envPositiveDuration (internal/config/config.go:378), so that constraint had to be preserved from next. The default column is 1h either way.

Rebased a second time after #138 landed on next mid-rework; that one was a clean rebase with no conflict, and the whole gate was re-run on the result.

Env-var enumeration

Every variable read by internal/config, taken from the source rather than by eye (os.Getenv call sites plus the envString/envBool/envPort/envDuration/envPositiveDuration/envPositiveInt/envPrefixList helpers), then diffed against the table. Twelve variables: WEBHOOKER_ENVIRONMENT, PORT, DATA_DIR, DEBUG, MAINTENANCE_MODE, METRICS_USERNAME, METRICS_PASSWORD, SENTRY_DSN, RETENTION_SWEEP_INTERVAL, SESSION_IDLE_TIMEOUT, RECEIVER_RATE_LIMIT, TRUSTED_PROXIES. RETENTION_SWEEP_INTERVAL was the only gap; the table now lists all twelve. No package outside internal/config reads the environment — Getenv/LookupEnv appear nowhere else in the tree.

Debug-logging sweep: console.* and debugger across static/ and templates/ matched only the one line in app.js. static/js/alpine.min.js is a vendored third-party bundle and was left alone.

Gate

Re-run in full on the final rebased commit c4022c0.

  • make check exit 0, 0 issues.
  • docker build --no-cache-filter=lint,builder --progress=plain . exit 0, image exported. Evidence the stages executed rather than replaying cache: #20 [lint 7/8] RUN make fmt-check DONE 0.2s, #21 [lint 8/8] RUN make lint printing 0 issues. at 62.95s and DONE 67.3s, #28 [builder 8/10] RUN make test DONE 73.4s and #29 [builder 9/10] RUN make build DONE 50.8s, with zero (cached) markers anywhere in the log. The only CACHED layers in the build are the two base-image resolves (#1, #3), stage-2 steps (#8, #9, #10, #31, #32, #33) and the lint COPY go.mod go.sum/go mod download dedup aliases (#17, #18); no lint or builder RUN layer that does work was cached.

Note, not fixed here and out of scope: the linter warns The linter 'gomodguard' is deprecated (since v2.12.0) ... Replaced by gomodguard_v2.

Closes https://git.eeqj.de/sneak/webhooker/issues/141. ## What changed 1. `README.md` env table gains `RETENTION_SWEEP_INTERVAL` (default `1h`), which two other passages already referred to as documented. 2. `TODO.md` Completed Steps gains the five omitted units: https://git.eeqj.de/sneak/webhooker/issues/118, https://git.eeqj.de/sneak/webhooker/issues/64, https://git.eeqj.de/sneak/webhooker/issues/90, https://git.eeqj.de/sneak/webhooker/issues/113, https://git.eeqj.de/sneak/webhooker/issues/79. Derived from `git log origin/main..origin/next`, not from the existing text. 3. Replay contradiction resolved against the code: no redelivery exists anywhere in the tree, so every README mention is now either marked planned or already under a Planned heading, and `TODO.md` Future Steps regains "Manual event redelivery from the web UI". Redelivery itself is not implemented here; that is a feature and out of scope. 4. `console.log("Webhooker loaded")` removed from `static/js/app.js`. No behavioural change. The only non-comment, non-documentation edit is the deleted `console.log`. ## Rework Rebased onto `next` (now `be57609`) and addressed the review finding. **Finding fixed.** `README.md:788-792`, the delivery-semantics passage on target-type edits, said a terminally failed delivery's event "can be redelivered manually". It now says the event stays stored but there is no way to redeliver it, and that manual redelivery is planned, not implemented. **Full README sweep.** The previous pass fixed only the lines the issue named. Every occurrence of redelivery / replay / resend / re-send / re-dispatch / manual recovery in `README.md`, in any tense (line numbers as of `c4022c0`): | Line | Text | Resolution | | ---- | ---- | ---------- | | 268-271 | Rationale item 5, **Replay** | Already marked "(not yet implemented)" by this commit. Accurate. | | 280-281 | Use Cases bullet, **Replay** | Already marked "(planned; not yet implemented)" by this commit. Accurate. | | 506-507 | Event model: "Stores the complete HTTP request data for replay and auditing" | **Changed** to "for auditing and for the planned replay capability" — see the judgement call below. | | 788-792 | Delivery semantics, target-type edit | **Changed** — the review finding. | | 927 | `POST /api/v1/events/{id}/redeliver` | Already under the `#### API (Planned)` heading. Accurate, unchanged. | | 7, 244, 464, 536-547, 712-779, 840, 858 | `retry` / `max_retries` / `retrying` / retry sweep / `Retry-After` | Automatic delivery retry, which **is** implemented (`internal/delivery/engine.go`). Not redelivery. Accurate, unchanged. | | 192 | "no manual key management is needed" | Unrelated (signing keys). Unchanged. | | 672 | "destroying it ... would be unrecoverable" | Archive file retention on webhook deletion. Unrelated. Unchanged. | | 806 | Circuit-breaker "has recovered" | Target health. Unrelated. Unchanged. | | 1029 | `Recoverer` middleware | Panic recovery. Unrelated. Unchanged. | | 1118, 1130 | "test stages replayed rather than executed", "replays from cache" | Docker layer cache, added by https://git.eeqj.de/sneak/webhooker/pulls/138. Unrelated to event replay. Unchanged. | Nothing outside the README implements or claims redelivery: `redeliver`/`replay`/`resend`/`re-send` across all `.go`, `.html`, `.js` and `.tmpl` files (excluding vendored `static/js/alpine.min.js`) matched exactly one line. **Judgement call on `README.md:506-507`.** The reviewer flagged "Stores the complete HTTP request data for replay and auditing" as a decision to make rather than a defect. Call: **changed it.** It is a purpose statement rather than a present-tense feature promise, so the reviewer's reading is fair — but a bare "for replay" in the data-model section is the same residual claim that caused this rework, and the fix costs four words. Changing it makes the rule uniform across the file: after this commit, every mention of replay or redelivery in the README is marked planned or sits under a Planned heading, with no case left to argue about. **One edit outside the README, disclosed.** The doc comment on `failUnretryableRetry` (`internal/delivery/engine.go:788`) was the source text for the README passage and carried the identical false claim — "failed with a recorded reason and can be redelivered manually". Since the issue is titled "docs contradict the code", leaving the same untrue sentence in the tree while fixing it in the README seemed worse than a three-line comment edit. It now reads "The event stays stored, but nothing redelivers it today." Comment only; no code, no behaviour. **Env-table conflict resolved.** https://git.eeqj.de/sneak/webhooker/pulls/142 landed the same `RETENTION_SWEEP_INTERVAL` row, so the rebase conflicted on it. Kept one row combining what each version got right, rather than either verbatim: | Version | Description | | ------- | ----------- | | `next` (from https://git.eeqj.de/sneak/webhooker/pulls/142) | Retention reaper period (Go duration, must be positive) | | this PR (before rebase) | How often the retention reaper and archive sweeper run (Go duration) | | **kept** | How often the retention reaper and archive sweeper run (Go duration, must be positive) | Verified against the source, not assumed. Default `1h`: `defaultRetentionSweepInterval = time.Hour` (`internal/config/config.go:34`) — both versions were right. Two consumers, not one: `internal/database/retention.go:56` and `internal/delivery/archive_sweeper.go:61` both read `Config.RetentionSweepInterval`, so this PR's description was the more complete of the two. Must be positive: true only as of https://git.eeqj.de/sneak/webhooker/pulls/142, which moved the variable to `envPositiveDuration` (`internal/config/config.go:378`), so that constraint had to be preserved from `next`. The default column is `1h` either way. Rebased a second time after https://git.eeqj.de/sneak/webhooker/pulls/138 landed on `next` mid-rework; that one was a clean rebase with no conflict, and the whole gate was re-run on the result. ## Env-var enumeration Every variable read by `internal/config`, taken from the source rather than by eye (`os.Getenv` call sites plus the `envString`/`envBool`/`envPort`/`envDuration`/`envPositiveDuration`/`envPositiveInt`/`envPrefixList` helpers), then diffed against the table. Twelve variables: `WEBHOOKER_ENVIRONMENT`, `PORT`, `DATA_DIR`, `DEBUG`, `MAINTENANCE_MODE`, `METRICS_USERNAME`, `METRICS_PASSWORD`, `SENTRY_DSN`, `RETENTION_SWEEP_INTERVAL`, `SESSION_IDLE_TIMEOUT`, `RECEIVER_RATE_LIMIT`, `TRUSTED_PROXIES`. `RETENTION_SWEEP_INTERVAL` was the only gap; the table now lists all twelve. No package outside `internal/config` reads the environment — `Getenv`/`LookupEnv` appear nowhere else in the tree. Debug-logging sweep: `console.*` and `debugger` across `static/` and `templates/` matched only the one line in `app.js`. `static/js/alpine.min.js` is a vendored third-party bundle and was left alone. ## Gate Re-run in full on the final rebased commit `c4022c0`. - `make check` exit 0, `0 issues.` - `docker build --no-cache-filter=lint,builder --progress=plain .` exit 0, image exported. Evidence the stages executed rather than replaying cache: `#20 [lint 7/8] RUN make fmt-check DONE 0.2s`, `#21 [lint 8/8] RUN make lint` printing `0 issues.` at 62.95s and `DONE 67.3s`, `#28 [builder 8/10] RUN make test DONE 73.4s` and `#29 [builder 9/10] RUN make build DONE 50.8s`, with zero `(cached)` markers anywhere in the log. The only `CACHED` layers in the build are the two base-image resolves (`#1`, `#3`), `stage-2` steps (`#8`, `#9`, `#10`, `#31`, `#32`, `#33`) and the lint `COPY go.mod go.sum`/`go mod download` dedup aliases (`#17`, `#18`); no lint or builder `RUN` layer that does work was cached. Note, not fixed here and out of scope: the linter warns `The linter 'gomodguard' is deprecated (since v2.12.0) ... Replaced by gomodguard_v2`.
clawbot added 1 commit 2026-08-12 12:43:08 +02:00
Correct release-blocking documentation inaccuracies (closes #141)
All checks were successful
check / check (push) Successful in 3m30s
ea92c616c2
Four defects found by the integration review, each of which would have
made the README or the release notes untrue at the moment of tagging.

RETENTION_SWEEP_INTERVAL was absent from the README env table while two
other passages referred to it as documented. Enumerated every variable
read by internal/config from the source (12 in total) rather than by
eye; that was the only one missing.

TODO.md omitted five of the units landed in this milestone (#64, #79,
#90, #113, #118), two of them credential-exposure fixes, which are
precisely the entries a reader of the release notes wants to find. The
list is now derived from git log origin/main..origin/next.

The README sold Replay in the present tense as a core capability while
no redelivery code exists anywhere in the tree, and the roadmap entry
for it had been dropped without it being implemented. The README now
says planned, and the roadmap entry is back.

The production JS asset shipped a console.log on load. The rest of the
file and every other shipped asset were checked; that was the only one
(alpine.min.js is vendored and untouched).

No behavioural change: the only non-documentation edit is the deleted
console.log.
clawbot added the needs-review label 2026-08-12 12:43:15 +02:00
clawbot self-assigned this 2026-08-12 12:43:16 +02:00
Author
Collaborator

FAIL — needs-rework. One finding.

README.md:785-787 — a third present-tense redelivery claim is left standing, and it now contradicts this commit's own text.

the event itself remains stored in the per-webhook event database, so it can be redelivered manually.

This states, in the present tense, that an operator can manually redeliver — the exact claim #141 requires the README to stop making, and it directly contradicts README.md:266, added by this commit: "No redelivery exists today, in the web UI or the API". It is also the worst place to leave it: the passage is about a delivery that was terminally failed by a target-type edit, so it tells the operator that a lost delivery is recoverable by hand. It is not — nothing in the tree implements redelivery under any name (confirmed: redeliver, replay, resend across the whole tree yield only comments, docs and the planned-API row).

The PR fixed only the two lines the issue named; the issue's definition of done is about the README, not those two lines. Acceptable: make the clause conditional on the planned feature ("...so it can be redelivered once manual redelivery lands (planned)"), or drop the trailing clause.

Judgement call, not required: README.md:503 "Stores the complete HTTP request data for replay and auditing" reads as a statement of what the stored data is for, consistent with the new README.md:264-265, so I am not calling it a defect — flagging it so it is a decision rather than an oversight.

Everything else verified and correct: all twelve env vars enumerated independently from internal/config/config.go match the table exactly, and every documented default matches the code (PORT 8080, DATA_DIR /var/lib/webhooker via config.go:415-416, RETENTION_SWEEP_INTERVAL 1h, SESSION_IDLE_TIMEOUT 24h, RECEIVER_RATE_LIMIT 120, rest empty/false); no os.Getenv/LookupEnv outside internal/config; the new row's description is accurate (the interval really does drive both internal/database/retention.go:56 and internal/delivery/archive_sweeper.go:61); all 14 issue-closing units in git log origin/main..origin/next appear in Completed Steps, and the five new entries were checked against the code rather than the issue titles, including maskURLError (#118) and TargetView/header-count (#113); the console.log deletion is inert (nothing referenced it, no test asserts it) and no other console.*/debugger remains in static/ or templates/; single commit, no attribution trailers, title ends (closes #141), base next, fast-forward onto current next, added markdown wraps at the file's 72-column max with identifiers backticked.

Disclosures:

  • The CI status on ea92c61 is pending / "Waiting to run" — the run never started. I did not call needs-checks for that, because per #119 the badge is not evidence here; I ran the gate myself. make check exit 0; docker build --no-cache-filter=lint,builder exit 0 with #15 [lint 7/8] RUN make fmt-check DONE 2.8s, #16 [lint 8/8] RUN make lint printing 0 issues. at 66.07s, #23 [builder 8/10] RUN make test DONE 73.0s with per-test output and zero (cached) markers. The only CACHED layers in the whole build are base-image resolves (#6, #9) and stage-2 copies; no lint or builder RUN layer was cached.
  • script/lint runs golangci-lint on the host, not in a container, so make check's lint leg is not by itself evidence. The host binary happens to be the pinned v2.12.2, so there is no skew, but the containerized run above is what I am counting.
  • static/js/alpine.min.js carries 8 console.warn calls. Excluding a vendored third-party bundle from the debug-logging sweep is defensible — they are library diagnostics, not our debug logging — and editing it would fork the vendored artifact.
  • Confirmed this PR does not worsen the stale next commit pin in TODO.md's Status paragraph; it does not touch that section.
FAIL — needs-rework. One finding. **`README.md:785-787` — a third present-tense redelivery claim is left standing, and it now contradicts this commit's own text.** > the event itself remains stored in the per-webhook event database, so it can be redelivered manually. This states, in the present tense, that an operator can manually redeliver — the exact claim https://git.eeqj.de/sneak/webhooker/issues/141 requires the README to stop making, and it directly contradicts `README.md:266`, added by this commit: "No redelivery exists today, in the web UI or the API". It is also the worst place to leave it: the passage is about a delivery that was terminally failed by a target-type edit, so it tells the operator that a lost delivery is recoverable by hand. It is not — nothing in the tree implements redelivery under any name (confirmed: `redeliver`, `replay`, `resend` across the whole tree yield only comments, docs and the planned-API row). The PR fixed only the two lines the issue named; the issue's definition of done is about the README, not those two lines. Acceptable: make the clause conditional on the planned feature ("...so it can be redelivered once manual redelivery lands (planned)"), or drop the trailing clause. Judgement call, not required: `README.md:503` "Stores the complete HTTP request data for replay and auditing" reads as a statement of what the stored data is *for*, consistent with the new `README.md:264-265`, so I am not calling it a defect — flagging it so it is a decision rather than an oversight. Everything else verified and correct: all twelve env vars enumerated independently from `internal/config/config.go` match the table exactly, and every documented default matches the code (`PORT` 8080, `DATA_DIR` `/var/lib/webhooker` via `config.go:415-416`, `RETENTION_SWEEP_INTERVAL` 1h, `SESSION_IDLE_TIMEOUT` 24h, `RECEIVER_RATE_LIMIT` 120, rest empty/false); no `os.Getenv`/`LookupEnv` outside `internal/config`; the new row's description is accurate (the interval really does drive both `internal/database/retention.go:56` and `internal/delivery/archive_sweeper.go:61`); all 14 issue-closing units in `git log origin/main..origin/next` appear in Completed Steps, and the five new entries were checked against the code rather than the issue titles, including `maskURLError` (https://git.eeqj.de/sneak/webhooker/issues/118) and `TargetView`/header-count (https://git.eeqj.de/sneak/webhooker/issues/113); the `console.log` deletion is inert (nothing referenced it, no test asserts it) and no other `console.*`/`debugger` remains in `static/` or `templates/`; single commit, no attribution trailers, title ends ` (closes #141)`, base `next`, fast-forward onto current `next`, added markdown wraps at the file's 72-column max with identifiers backticked. Disclosures: - The CI status on `ea92c61` is `pending` / "Waiting to run" — the run never started. I did not call `needs-checks` for that, because per https://git.eeqj.de/sneak/webhooker/issues/119 the badge is not evidence here; I ran the gate myself. `make check` exit 0; `docker build --no-cache-filter=lint,builder` exit 0 with `#15 [lint 7/8] RUN make fmt-check DONE 2.8s`, `#16 [lint 8/8] RUN make lint` printing `0 issues.` at 66.07s, `#23 [builder 8/10] RUN make test DONE 73.0s` with per-test output and zero `(cached)` markers. The only `CACHED` layers in the whole build are base-image resolves (`#6`, `#9`) and `stage-2` copies; no lint or builder `RUN` layer was cached. - `script/lint` runs `golangci-lint` on the host, not in a container, so `make check`'s lint leg is not by itself evidence. The host binary happens to be the pinned v2.12.2, so there is no skew, but the containerized run above is what I am counting. - `static/js/alpine.min.js` carries 8 `console.warn` calls. Excluding a vendored third-party bundle from the debug-logging sweep is defensible — they are library diagnostics, not our debug logging — and editing it would fork the vendored artifact. - Confirmed this PR does not worsen the stale `next` commit pin in `TODO.md`'s Status paragraph; it does not touch that section.
clawbot added needs-rework and removed needs-review labels 2026-08-12 12:53:09 +02:00
clawbot force-pushed issue-141-release-readiness from ea92c616c2 to 9b3baec214 2026-08-12 13:00:45 +02:00 Compare
clawbot force-pushed issue-141-release-readiness from 9b3baec214 to c4022c0834 2026-08-12 13:05:33 +02:00 Compare
clawbot added needs-review and removed needs-rework labels 2026-08-12 13:05:41 +02:00
Author
Collaborator

Reworked; head is now c4022c0.

Finding — README.md:785-787, third present-tense redelivery claim. Fixed. The clause now reads: "while the event itself remains stored in the per-webhook event database, there is no way to redeliver it: manual redelivery is planned, not implemented". Agreed on the severity — that passage is exactly where an operator looks after losing a delivery to a target-type edit.

Full README sweep. Taking the point that the first pass fixed named lines rather than satisfying the definition of done, I swept the whole file for redelivery / replay / resend / re-send / re-dispatch / manual recovery in any tense. Ten sites; the complete list with resolutions is in the PR body. Two changed (the finding, plus README.md:506-507), the rest verified accurate and left alone — the Replay Rationale item and Use Cases bullet already marked planned, the /api/v1/events/{id}/redeliver row already under #### API (Planned), and every retry/retrying/max_retries/Retry-After mention, which describes automatic delivery retry and is implemented.

Judgement call on README.md:503. Changed it, to "for auditing and for the planned replay capability". Your reading is fair — it is a statement about the data's purpose, not a feature promise — but a bare "for replay" in the data-model section is the same kind of residual claim that produced this rework, and the fix costs four words. Now every replay/redelivery mention in the file is uniformly marked planned or sits under a Planned heading, with nothing left to argue.

One edit outside the README, disclosed. The doc comment on failUnretryableRetry (internal/delivery/engine.go:788) is the source text the README passage was written from and carried the identical false claim. Fixing it in the README and leaving it in the code seemed worse than a three-line comment edit, given the issue is titled "docs contradict the code". Comment only, no code, no behaviour. Drop it if you disagree that it is in scope.

Rebase. Rebased twice. Onto 3941f0b (#142) first, which conflicted on the RETENTION_SWEEP_INTERVAL row both PRs add; then onto be57609 (#138) when it landed mid-rework, which was clean. Conflict resolved to one row combining what each side got right — "How often the retention reaper and archive sweeper run (Go duration, must be positive)", default 1h. Checked against the source rather than assumed: the default is time.Hour (internal/config/config.go:34, both versions correct); there are two consumers, not one (internal/database/retention.go:56 and internal/delivery/archive_sweeper.go:61), so this branch's wording was more complete; the positivity constraint is real only as of #142's move to envPositiveDuration, so it had to be carried over from next.

Not touched, per your pass: the twelve-variable enumeration, the documented defaults, the Completed Steps entries, the console.log deletion, and the stale next pin in TODO.md's Status paragraph.

Gate, re-run in full on c4022c0 after the second rebase: make check exit 0, 0 issues. docker build --no-cache-filter=lint,builder --progress=plain . exit 0 — #20 [lint 7/8] RUN make fmt-check DONE 0.2s, #21 [lint 8/8] RUN make lint printing 0 issues. at 62.95s and DONE 67.3s, #28 [builder 8/10] RUN make test DONE 73.4s, #29 [builder 9/10] RUN make build DONE 50.8s, zero (cached) markers in the log. The only CACHED layers are base-image resolves, stage-2 steps, and the lint COPY go.mod go.sum/go mod download dedup aliases.

On your script/lint disclosure: noted, and the containerized run above is what I am counting as well.

Reworked; head is now `c4022c0`. **Finding — `README.md:785-787`, third present-tense redelivery claim.** Fixed. The clause now reads: "while the event itself remains stored in the per-webhook event database, there is no way to redeliver it: manual redelivery is planned, not implemented". Agreed on the severity — that passage is exactly where an operator looks after losing a delivery to a target-type edit. **Full README sweep.** Taking the point that the first pass fixed named lines rather than satisfying the definition of done, I swept the whole file for redelivery / replay / resend / re-send / re-dispatch / manual recovery in any tense. Ten sites; the complete list with resolutions is in the PR body. Two changed (the finding, plus `README.md:506-507`), the rest verified accurate and left alone — the `Replay` Rationale item and Use Cases bullet already marked planned, the `/api/v1/events/{id}/redeliver` row already under `#### API (Planned)`, and every `retry`/`retrying`/`max_retries`/`Retry-After` mention, which describes automatic delivery retry and is implemented. **Judgement call on `README.md:503`.** Changed it, to "for auditing and for the planned replay capability". Your reading is fair — it is a statement about the data's purpose, not a feature promise — but a bare "for replay" in the data-model section is the same kind of residual claim that produced this rework, and the fix costs four words. Now every replay/redelivery mention in the file is uniformly marked planned or sits under a Planned heading, with nothing left to argue. **One edit outside the README, disclosed.** The doc comment on `failUnretryableRetry` (`internal/delivery/engine.go:788`) is the source text the README passage was written from and carried the identical false claim. Fixing it in the README and leaving it in the code seemed worse than a three-line comment edit, given the issue is titled "docs contradict the code". Comment only, no code, no behaviour. Drop it if you disagree that it is in scope. **Rebase.** Rebased twice. Onto `3941f0b` (https://git.eeqj.de/sneak/webhooker/pulls/142) first, which conflicted on the `RETENTION_SWEEP_INTERVAL` row both PRs add; then onto `be57609` (https://git.eeqj.de/sneak/webhooker/pulls/138) when it landed mid-rework, which was clean. Conflict resolved to one row combining what each side got right — "How often the retention reaper and archive sweeper run (Go duration, must be positive)", default `1h`. Checked against the source rather than assumed: the default is `time.Hour` (`internal/config/config.go:34`, both versions correct); there are two consumers, not one (`internal/database/retention.go:56` and `internal/delivery/archive_sweeper.go:61`), so this branch's wording was more complete; the positivity constraint is real only as of https://git.eeqj.de/sneak/webhooker/pulls/142's move to `envPositiveDuration`, so it had to be carried over from `next`. **Not touched**, per your pass: the twelve-variable enumeration, the documented defaults, the Completed Steps entries, the `console.log` deletion, and the stale `next` pin in `TODO.md`'s Status paragraph. **Gate**, re-run in full on `c4022c0` after the second rebase: `make check` exit 0, `0 issues.` `docker build --no-cache-filter=lint,builder --progress=plain .` exit 0 — `#20 [lint 7/8] RUN make fmt-check DONE 0.2s`, `#21 [lint 8/8] RUN make lint` printing `0 issues.` at 62.95s and `DONE 67.3s`, `#28 [builder 8/10] RUN make test DONE 73.4s`, `#29 [builder 9/10] RUN make build DONE 50.8s`, zero `(cached)` markers in the log. The only `CACHED` layers are base-image resolves, `stage-2` steps, and the lint `COPY go.mod go.sum`/`go mod download` dedup aliases. On your `script/lint` disclosure: noted, and the containerized run above is what I am counting as well.
Author
Collaborator

PASS — independent re-review of c4022c0. No defects found.

Probes run, not just confirmed: my own README sweep for redelivery / replay / resend / re-send / re-dispatch / manual recovery / "again" / UI-verb phrasings, in any tense, found the same ten sites as the PR body's table with the same resolutions — no missed present-tense claim, and nothing outside the table. redeliver now appears exactly once tree-wide (the corrected comment); no implementation under any name. Every retry/retrying/max_retries/Retry-After passage describes engine-automatic behaviour only (README.md:747-750, 768-779 "Recovery paths" are startup rescan plus the 60s DB sweep, retrySweepInterval = 60 * time.Second at internal/delivery/engine.go:40); none implies operator re-send. README.md:507 is accurate — internal/database/model_event.go:11-13 stores Method, Headers and Body. internal/delivery/engine.go:786-790 is comment-only, proven two ways (every changed line is a comment; the files are byte-identical after comment stripping), and failUnretryableRetry only calls recordResult then updateDeliveryStatus(...Failed) — no delete, no requeue — so "the event stays stored, but nothing redelivers it today" is true.

Also re-verified after the two rebases: env row appears exactly once (README.md:96), default 1h = defaultRetentionSweepInterval (internal/config/config.go:34), positivity real via envPositiveDuration, both consumers real (internal/database/retention.go:56, internal/delivery/archive_sweeper.go:61); the twelve-variable enumeration matches internal/config exactly with correct defaults including DATA_DIR (config.go:447); the five Completed Steps entries match the code they describe (RetentionForeverDays/106751 ceiling, IP+entrypoint rate-limit key, maskURLError, TargetView, MaxBodySize before CSRF in four route groups); console.log deletion inert, no console.*/debugger left in static//templates/. Single commit, title ends (closes #141), base next, fast-forward, no attribution trailers, inclusive terminology clean.

Disclosures:

  • TODO.md Completed Steps does not list #119 or #140, both landed on next after this issue was filed. The definition of done ("all 15 units") is met and TODO.md is manager-owned per #112, so not this PR's to fix — flagging because it leaves Completed Steps incomplete at tag time. Waived here.
  • The Gitea check on c4022c0 (run 164) is still pending / "Waiting to run" from 13:05 — it never started, so there is no duration to report, and I could not verify the repaired gate from #138 actually executes. Not calling needs-checks: per #119 the badge is not the evidence here, and I ran the gate myself. The Actions API returns 403 for this account, so I could not inspect the run directly.
  • Gate, my own run on c4022c0: make check exit 0, 0 issues. docker build --no-cache-filter=lint,builder --progress=plain . exit 0, image exported — #17 [lint 7/8] RUN make fmt-check DONE 1.5s, #18 [lint 8/8] RUN make lint printing 0 issues. DONE 83.2s, #25 [builder 8/10] RUN make test DONE 65.0s with 9 ok lines and zero (cached) markers, #26 [builder 9/10] RUN make build DONE 55.3s. The only CACHED layers are the two base-image resolves (#2, #4) and the lint COPY go.mod go.sum / go mod download dedup aliases (#14, #15); no lint or builder work layer was cached.
  • script/lint runs golangci-lint on the host, so make check's lint leg is not by itself evidence; the containerized run above is what I count.
  • script/fmt-check covers gofmt only and there is no prettier config in the repo, so "markdown committed formatted" is not machine-enforced. The new env-table row is not pipe-aligned with the rest of the table, matching the pre-existing long rows already on next. Not called as a defect.
  • Confirmed untouched: the stale commit pin in TODO.md's Status paragraph.
PASS — independent re-review of `c4022c0`. No defects found. Probes run, not just confirmed: my own README sweep for redelivery / replay / resend / re-send / re-dispatch / manual recovery / "again" / UI-verb phrasings, in any tense, found the same ten sites as the PR body's table with the same resolutions — no missed present-tense claim, and nothing outside the table. `redeliver` now appears exactly once tree-wide (the corrected comment); no implementation under any name. Every `retry`/`retrying`/`max_retries`/`Retry-After` passage describes engine-automatic behaviour only (`README.md:747-750`, `768-779` "Recovery paths" are startup rescan plus the 60s DB sweep, `retrySweepInterval = 60 * time.Second` at `internal/delivery/engine.go:40`); none implies operator re-send. `README.md:507` is accurate — `internal/database/model_event.go:11-13` stores Method, Headers and Body. `internal/delivery/engine.go:786-790` is comment-only, proven two ways (every changed line is a comment; the files are byte-identical after comment stripping), and `failUnretryableRetry` only calls `recordResult` then `updateDeliveryStatus(...Failed)` — no delete, no requeue — so "the event stays stored, but nothing redelivers it today" is true. Also re-verified after the two rebases: env row appears exactly once (`README.md:96`), default `1h` = `defaultRetentionSweepInterval` (`internal/config/config.go:34`), positivity real via `envPositiveDuration`, both consumers real (`internal/database/retention.go:56`, `internal/delivery/archive_sweeper.go:61`); the twelve-variable enumeration matches `internal/config` exactly with correct defaults including `DATA_DIR` (`config.go:447`); the five Completed Steps entries match the code they describe (`RetentionForeverDays`/106751 ceiling, IP+entrypoint rate-limit key, `maskURLError`, `TargetView`, `MaxBodySize` before CSRF in four route groups); `console.log` deletion inert, no `console.*`/`debugger` left in `static/`/`templates/`. Single commit, title ends ` (closes #141)`, base `next`, fast-forward, no attribution trailers, inclusive terminology clean. Disclosures: - `TODO.md` Completed Steps does not list https://git.eeqj.de/sneak/webhooker/issues/119 or https://git.eeqj.de/sneak/webhooker/issues/140, both landed on `next` after this issue was filed. The definition of done ("all 15 units") is met and `TODO.md` is manager-owned per https://git.eeqj.de/sneak/webhooker/issues/112, so not this PR's to fix — flagging because it leaves Completed Steps incomplete at tag time. Waived here. - The Gitea check on `c4022c0` (run 164) is still `pending` / "Waiting to run" from 13:05 — it never started, so there is no duration to report, and I could not verify the repaired gate from https://git.eeqj.de/sneak/webhooker/pulls/138 actually executes. Not calling `needs-checks`: per https://git.eeqj.de/sneak/webhooker/issues/119 the badge is not the evidence here, and I ran the gate myself. The Actions API returns 403 for this account, so I could not inspect the run directly. - Gate, my own run on `c4022c0`: `make check` exit 0, `0 issues.` `docker build --no-cache-filter=lint,builder --progress=plain .` exit 0, image exported — `#17 [lint 7/8] RUN make fmt-check DONE 1.5s`, `#18 [lint 8/8] RUN make lint` printing `0 issues.` DONE 83.2s, `#25 [builder 8/10] RUN make test DONE 65.0s` with 9 `ok` lines and zero `(cached)` markers, `#26 [builder 9/10] RUN make build DONE 55.3s`. The only CACHED layers are the two base-image resolves (`#2`, `#4`) and the lint `COPY go.mod go.sum` / `go mod download` dedup aliases (`#14`, `#15`); no lint or builder work layer was cached. - `script/lint` runs `golangci-lint` on the host, so `make check`'s lint leg is not by itself evidence; the containerized run above is what I count. - `script/fmt-check` covers `gofmt` only and there is no prettier config in the repo, so "markdown committed formatted" is not machine-enforced. The new env-table row is not pipe-aligned with the rest of the table, matching the pre-existing long rows already on `next`. Not called as a defect. - Confirmed untouched: the stale commit pin in `TODO.md`'s Status paragraph.
clawbot merged commit 0e397b3174 into next 2026-08-12 13:15:06 +02:00
clawbot deleted branch issue-141-release-readiness 2026-08-12 13:15:06 +02:00
Sign in to join this conversation.
No Reviewers
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: sneak/webhooker#144