controller v0.264.0: the household is told when an update is undone or held, in its language
gates / gates (push) Successful in 25s
gates / gates (push) Successful in 25s
app_update_undone / app_update_held events (09 decision 15), on by default and seeded once on existing boxes; R-606 update sentences as key+args rendered per reader; R-646 startup applied-meta backfill for apps current with the catalog; R-620 a disabled notifier WARNs once per event type. Needs hub v0.120.0. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0159rPz1ZhFKsS53msqPYxtS
This commit is contained in:
@@ -330,6 +330,13 @@ func (m *Manager) pruneUndoCopies(dumpDir, stack string) {
|
||||
// on a misconfigured box. The write side logs loudly when it cannot persist (see
|
||||
// holdAppAfterFailedRollback), which is where that case is caught.
|
||||
func (m *Manager) RestoreHoldFor(stack string) (bool, string) {
|
||||
return m.RestoreHoldForLang(stack, m.boxLang())
|
||||
}
|
||||
|
||||
// RestoreHoldForLang is RestoreHoldFor with the sentence in lang (v0.264.0, R-606). The page and the
|
||||
// household mail ask for the READER's language; every other caller takes the box's. The Hungarian is
|
||||
// byte-identical to the literals it replaced (UpdateHoldFmt & co. — TestR606_HoldSentenceHungarianUnchanged).
|
||||
func (m *Manager) RestoreHoldForLang(stack, lang string) (bool, string) {
|
||||
if m == nil || m.settings == nil {
|
||||
return false, ""
|
||||
}
|
||||
@@ -340,43 +347,44 @@ func (m *Manager) RestoreHoldFor(stack string) (bool, string) {
|
||||
// Slice 4: one storage, two reasons. An update hold names the copy it can be restored from; a
|
||||
// restore hold names nothing, because the restore it refers to already consumed the copy.
|
||||
if h.Reason == settings.HoldReasonUpdateFailed {
|
||||
return true, m.undoHoldPrefix(h.UndoState) + m.updateHoldSentence(stack, h)
|
||||
return true, m.undoHoldPrefix(lang, h.UndoState) + updateHoldSentence(lang, stack, h)
|
||||
}
|
||||
when := h.At
|
||||
if t, err := time.Parse(time.RFC3339, h.At); err == nil {
|
||||
when = t.Format("2006-01-02 15:04")
|
||||
}
|
||||
return true, m.note("note.reconstitute.held", stack, when)
|
||||
return true, util.Text(lang, "note.reconstitute.held", stack, when)
|
||||
}
|
||||
|
||||
// undoHoldPrefix (v0.263.0) opens the hold sentence when the box already TRIED to undo the update and
|
||||
// that failed too: what was tried, then what state the data is in. "" when no undo was attempted, so
|
||||
// every hold written before v0.263.0 reads exactly as it did.
|
||||
func (m *Manager) undoHoldPrefix(state string) string {
|
||||
func (m *Manager) undoHoldPrefix(lang, state string) string {
|
||||
switch state {
|
||||
case "untouched", "half", "not_started":
|
||||
return m.note("hold.update.undo_failed") + " " + m.note("hold.update.undo_state."+state) + " "
|
||||
return util.Text(lang, "hold.update.undo_failed") + " " + util.Text(lang, "hold.update.undo_state."+state) + " "
|
||||
case "":
|
||||
return ""
|
||||
}
|
||||
m.logger.Printf("[WARN] [backup] unknown undo state %q on a hold — rendering the plain prefix", state)
|
||||
return m.note("hold.update.undo_failed") + " "
|
||||
return util.Text(lang, "hold.update.undo_failed") + " "
|
||||
}
|
||||
|
||||
// updateHoldSentence is the update hold's own sentence (slice 4, R-475, R-479), unchanged.
|
||||
func (m *Manager) updateHoldSentence(stack string, h settings.RestoreHold) string {
|
||||
copyDate := m.note("note.reconstitute.copy_latest")
|
||||
// updateHoldSentence is the update hold's own sentence (slice 4, R-475, R-479) in lang. Its Hungarian
|
||||
// is UpdateHoldFmt / UpdateHoldTierFmt / UpdateHoldLegacyFmt byte for byte (pinned by a test).
|
||||
func updateHoldSentence(lang, stack string, h settings.RestoreHold) string {
|
||||
copyDate := util.Text(lang, "note.reconstitute.copy_latest")
|
||||
if h.CopyDate != "" {
|
||||
copyDate = fmtHoldTime(h.CopyDate)
|
||||
}
|
||||
// R-475: name the tier when the hold recorded one; an older hold keeps its own sentence.
|
||||
if label := UpdateTierLabel(h.CopyTier); label != "" && h.CopyDate != "" {
|
||||
if label := UpdateTierLabelIn(lang, h.CopyTier); label != "" && h.CopyDate != "" {
|
||||
if h.CopyHolds != "" { // R-479: name what the copy holds
|
||||
return fmt.Sprintf(UpdateHoldFmt, stack, fmtHoldTime(h.At), label, copyDate, h.CopyHolds)
|
||||
return util.Text(lang, "hold.update.sentence", stack, fmtHoldTime(h.At), label, copyDate, copyHoldsIn(lang, h.CopyHolds))
|
||||
}
|
||||
return fmt.Sprintf(UpdateHoldTierFmt, stack, fmtHoldTime(h.At), label, copyDate)
|
||||
return util.Text(lang, "hold.update.sentence_tier", stack, fmtHoldTime(h.At), label, copyDate)
|
||||
}
|
||||
return fmt.Sprintf(UpdateHoldLegacyFmt, stack, fmtHoldTime(h.At), copyDate)
|
||||
return util.Text(lang, "hold.update.sentence_legacy", stack, fmtHoldTime(h.At), copyDate)
|
||||
}
|
||||
|
||||
// holdAppAfterFailedRollback records the R-379/R-380 hold and makes sure nothing restarts the app
|
||||
|
||||
Reference in New Issue
Block a user