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:
2026-07-16 19:31:19 +02:00
parent 8f3564c137
commit fe9266f53f
15 changed files with 501 additions and 14 deletions
+23
View File
@@ -71,6 +71,13 @@ type Server struct {
// Hub push status callback — set via SetHubPushStatus for monitoring page
hubPushStatusFn func() HubPushStatusData
// Out-of-cycle hub report trigger (v0.139.0, Direction 1) — set via SetReportTrigger to
// report.Trigger.Fire. Fired via reportTriggerNow() AFTER a successful hub-relevant local
// commit (escrow claim, notification/app-email save, offsite config/toggle, customer
// claim) so the hub reflects the new state in seconds instead of the next ~15-min cycle.
// nil (hub reporting off / tests) = strict no-op.
reportTriggerFn func()
// Fork-4 hygiene seam: wipes the agent-staged offsite repo password when EscrowState flips to
// escrowed (DELETE /escrow/stage-secret). nil → the default agentClient()-backed impl; tests inject.
wipeStagedEscrowFn func(ctx context.Context) error
@@ -235,6 +242,22 @@ func (s *Server) SetAssetsSyncer(as *assets.Syncer) {
s.assetsSyncer = as
}
// SetReportTrigger wires the out-of-cycle hub report trigger (report.Trigger.Fire). The
// provided func MUST be non-blocking — it is called from request handlers (the trigger's
// worker does the waiting/pushing). Init-time only, like every Set* here.
func (s *Server) SetReportTrigger(fn func()) {
s.reportTriggerFn = fn
}
// reportTriggerNow fires the out-of-cycle report trigger if wired (mirrors
// api.Router.reportPushNow). Call AFTER a successful hub-relevant local commit — never
// before it, never on an error path. Nil-safe no-op when hub reporting is off.
func (s *Server) reportTriggerNow() {
if s.reportTriggerFn != nil {
s.reportTriggerFn()
}
}
// SetIntegrationManager sets the app-to-app integration manager.
func (s *Server) SetIntegrationManager(mgr *integrations.Manager) {
s.integrationMgr.Store(mgr)