| 1 | package moves |
| 2 | |
| 3 | import ( |
| 4 | "time" |
| 5 | |
| 6 | "github.com/carlosframework/platform/internal/provision" |
| 7 | ) |
| 8 | |
| 9 | // AbortAfter is how long a Phase 1 move may stay preparing before it aborts |
| 10 | // itself (spec §7.11: "a move whose readiness never comes aborts itself |
| 11 | // after a bound, so an unreachable target never needs a human"; Plan 5 |
| 12 | // ruling K18). §13 pages an operator at 60 minutes with no promotable round; |
| 13 | // the abort comes at six hours. The console's sweep (Plan 5 Task 7) CASes |
| 14 | // provision.MoveGuardedAbort when AbortDue says so; nothing here writes. |
| 15 | const AbortAfter = 6 * time.Hour |
| 16 | |
| 17 | // AbortDue reports whether rec's move is due its automatic abort at now: a |
| 18 | // Phase 1 move (not legacy) still preparing, AbortAfter or more after the |
| 19 | // later of its RequestedAt and its ReadyAt. |
| 20 | // |
| 21 | // The bound is measured from max(RequestedAt, ReadyAt) (ruling K18), not |
| 22 | // from Move.UpdatedAt, which every record write stamps and so says nothing |
| 23 | // about readiness. Today a preparing move has no ReadyAt (nothing demotes a |
| 24 | // ready move to preparing), so the bound runs from the request; taking the |
| 25 | // later of the two keeps it right should a demotion ever stamp one. A ready |
| 26 | // move is not due: readiness came, and the cut is the operator's (or the |
| 27 | // automatic cut's). A move with no request time is never due: there is no |
| 28 | // evidence to measure from. |
| 29 | func AbortDue(rec provision.Record, now time.Time) bool { |
| 30 | m := rec.Move |
| 31 | if m == nil || m.Legacy() || m.State != provision.MovePreparing || m.RequestedAt.IsZero() { |
| 32 | return false |
| 33 | } |
| 34 | return !now.Before(PreparingSince(m).Add(AbortAfter)) |
| 35 | } |
| 36 | |
| 37 | // PreparingSince is where a preparing move's clocks run from: the later of |
| 38 | // its RequestedAt and its ReadyAt (ruling K18). The six-hour abort (AbortDue) |
| 39 | // and the console's 60-minute page (§13) both measure from it, so the two |
| 40 | // can never disagree about how long a move has prepared (Plan 6 preflight |
| 41 | // L29). |
| 42 | func PreparingSince(m *provision.MoveState) time.Time { |
| 43 | from := m.RequestedAt |
| 44 | if m.ReadyAt != nil && m.ReadyAt.After(from) { |
| 45 | from = *m.ReadyAt |
| 46 | } |
| 47 | return from |
| 48 | } |
| 49 | |