feat(report): v0.139.0 — immediate out-of-cycle hub report on user actions (Direction 1)
New report.Trigger (buffered-1 chan + worker; quiet 2s, min spacing 15s, trailing-edge coalescing) generalizes the v0.70.0 geo out-of-band push. One canonical fire closure in main.go; wired: geo save/sync + app deploy/remove/delete (api reportPushNow), escrow recovery-code claim, notification-prefs save, app-email toggle, offsite config + per-app toggle, customer claim (web SetReportTrigger seam, nil-safe, fired only after a successful local commit). 15-min hub-report cycle untouched as the reconciliation backbone; hub.enabled=false stays a strict no-op. Tests: trigger engine (2 red-proofs), seam fires-after-commit-only, nil-seam no-ops.
This commit is contained in:
@@ -1,5 +1,38 @@
|
||||
## Changelog
|
||||
|
||||
### v0.139.0 — immediate out-of-cycle hub report on user actions (Direction 1) (2026-07-16)
|
||||
|
||||
Viktor's ruling: a user action with hub-side effects must round-trip in seconds, not minutes. One
|
||||
generic, debounced out-of-cycle report trigger now sits on the proven outbound push channel; the
|
||||
15-min `hub-report` cycle is untouched and stays the reconciliation backbone. Headline UX win: the
|
||||
v0.138.0 escrow "megerősítésre vár" card collapses from ~14 min to seconds (the blob is already
|
||||
uploaded at claim time — the immediate report's ACK hash-match flips `pending→escrowed` via the
|
||||
unchanged `EscrowAutoConfirmer`). No hub change; no UI copy change ("legfeljebb 15 perc" stays the
|
||||
honest worst case for a failed immediate push).
|
||||
|
||||
- **`internal/report/trigger.go` (new) `report.Trigger`:** buffered-1 signal channel + single
|
||||
worker (shape: hub `wgsync/reconciler.go`). Non-blocking `Fire()`; worker = quiet window 2 s
|
||||
(burst coalescing) → drain → min spacing 15 s → ONE full-report fire. Coalesce-and-eventually-
|
||||
fire (trailing edge): a burst yields ≤ 1 + ceil(burst/15 s) pushes and the last state always
|
||||
reaches the hub — deliberately NOT the `internal/sync` refuse-debounce (a refused fire would
|
||||
lose the update until the next cycle). No retries of its own (the Pusher owns 3×5 s); a fire
|
||||
error logs one WARN and degrades to the cycle. Exits on context cancel.
|
||||
- **`cmd/controller/main.go`:** ONE canonical fire closure (`BuildReport` + `Claimed` +
|
||||
`hubPusher.Push`), constructed only when `hubPusher != nil && cfg.Hub.Enabled`; replaces the
|
||||
raw per-fire goroutine behind `apiRouter.SetReportPushTrigger` (the v0.70.0 geo seam — kept,
|
||||
now debounced) and feeds the new `webServer.SetReportTrigger`.
|
||||
- **`internal/api/router.go`:** deploy / remove / delete endpoints now call the existing
|
||||
`reportPushNow()` after success (geo save/sync already did) — all backed by the trigger.
|
||||
- **`internal/web` seam + call sites (`server.go` `SetReportTrigger`/`reportTriggerNow`,
|
||||
nil-safe, fired only AFTER a successful local commit):** escrow recovery-code claim
|
||||
(`escrow_handlers.go`), notification-prefs save + app-email toggle (`handlers.go`), offsite
|
||||
target config + per-app offsite toggle (`offbox_handlers.go`), customer claim completion
|
||||
(`claim.go`). `hub.enabled: false` → seams stay nil → strict no-op.
|
||||
- Tests: `internal/report/trigger_test.go` (single-fire exactly-once, Fire() non-blocking,
|
||||
burst-coalescing ceiling + trailing edge with red-proof, fire-error isolation with red-proof,
|
||||
prompt cancel exit), `web/report_trigger_seam_test.go` (fires-after-commit-only through the
|
||||
real offbox toggle handler + nil-seam no-op), `api/report_trigger_nilsafe_test.go`.
|
||||
|
||||
### v0.138.0 — escrow "awaiting hub confirmation" waiting state (2026-07-16)
|
||||
|
||||
Closes the customer-zero (N100) UX gap: after a completed escrow ceremony the Távoli mentés page kept
|
||||
|
||||
Reference in New Issue
Block a user