v0.311.0: after a power cut, an app repairs the files the cut left empty; the household sees lost photos (R-1007)
gates / gates (push) Successful in 52s
gates / gates (push) Successful in 52s
New template block after_unclean_boot (decision 236): once per controller start that followed an UNCLEAN host boot (crashboot's own verdict, after the 15-minute crash grace; a normal or unknown boot never runs it), each app with the block rebuilds ONLY its zero-byte files (scan: regular files, no symlinks, its own filesystem; ids appended to the app's own command in batches; one retry, then reported) and its own lost-file check is read. The household sees lost files (name, date taken) on the app page until dismissed; the operator gets app_repaired_after_crash with counts only. Skips, with the reason, an unproven image, an unknown key, an app not running. MinAgent 0.155.0 unchanged. Tests red-proofed (14 guards). One new parity fixture (the new card only). Claude-Session: https://claude.ai/code/session_012qRErfCoiTkvDK9N5XHbzb
This commit is contained in:
@@ -386,6 +386,39 @@ func (s *Server) ServeSignupClosed(w http.ResponseWriter, r *http.Request) {
|
||||
}
|
||||
}
|
||||
|
||||
// lostFilesNote is the app page's lost-files note (R-1007): the files the app's own check reported lost after an
|
||||
// unclean boot, until the household dismisses it, and the boot time in the box's zone. Nothing for an app with no
|
||||
// lost file (repaired previews need no note). Pinned by TestLostFilesNote_*.
|
||||
func lostFilesNote(deployed bool, cfg *stacks.AppConfig) ([]stacks.LostFile, string) {
|
||||
if !deployed || cfg == nil || cfg.AfterUncleanBoot == nil {
|
||||
return nil, ""
|
||||
}
|
||||
rec := cfg.AfterUncleanBoot
|
||||
if len(rec.Lost) == 0 || rec.Dismissed {
|
||||
return nil, ""
|
||||
}
|
||||
at := ""
|
||||
if t, err := time.Parse(time.RFC3339, rec.BootAt); err == nil {
|
||||
at = t.In(getTimezone()).Format("2006-01-02 15:04")
|
||||
}
|
||||
return rec.Lost, at
|
||||
}
|
||||
|
||||
// appLostFilesDismissHandler hides the app page's lost-files note (R-1007). The record (and the operator's counts)
|
||||
// stays; only the household's note goes.
|
||||
func (s *Server) appLostFilesDismissHandler(w http.ResponseWriter, r *http.Request, slug string) {
|
||||
found := s.stackBySlug(slug)
|
||||
if found == nil {
|
||||
escrowJSON(w, http.StatusNotFound, nil, s.msg(r, "escrow.unknown_app"))
|
||||
return
|
||||
}
|
||||
if err := s.stackMgr.DismissLostFiles(found.Name); err != nil {
|
||||
escrowJSON(w, http.StatusConflict, nil, s.msg(r, "err.lost_files.nothing_to_dismiss"))
|
||||
return
|
||||
}
|
||||
escrowJSON(w, http.StatusOK, map[string]any{"dismissed": true}, "")
|
||||
}
|
||||
|
||||
// appCloseSignupHandler is decision 49's "close sign-up now" (POST /apps/<slug>/close-signup) for an app installed
|
||||
// before decision 47. It applies exactly what a fresh install gets after its setup; it never gates the app.
|
||||
func (s *Server) appCloseSignupHandler(w http.ResponseWriter, r *http.Request, slug string) {
|
||||
|
||||
Reference in New Issue
Block a user