controller v0.269.0: whole restore from the second drive; crash loops stopped; exact image digests; steps judged by their own .felhom.yml (decisions 26-28, R-661 R-666 R-667 R-668 R-664 R-665 R-662, 09 6.4 part 6)
gates / gates (push) Successful in 27s

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:
2026-09-24 12:18:39 +02:00
parent 7c3b3a9694
commit 3c6b49b31c
141 changed files with 3401 additions and 237 deletions
@@ -30,6 +30,8 @@
{{if .Stack.Updating}}
<div class="alert alert-info" style="margin-top:1rem" data-update-phase="{{.Stack.UpdatePhase}}">{{updatePhaseText .Stack}}</div>
{{else if and .Stack.HoldReason (eq .Stack.HoldKind "unhealthy_stop")}}
<div class="alert alert-warning" style="margin-top:1rem" data-unhealthy-stop="true">{{holdText .Stack}}</div>
{{else if .Stack.HoldReason}}
<div class="alert alert-error" style="margin-top:1rem" data-held="true">{{holdText .Stack}}{{if not .Stack.HoldNoWholeCopy}} <a href="/backups/apps" class="btn btn-sm btn-outline">{{T "app_info.mentesek"}}</a>{{end}}</div>
{{else if .Stack.UpdateError}}
@@ -426,6 +426,11 @@
fetch('/api/stacks/' + name + '/backup-data').then(function(r) { return r.json(); })
]);
var sections = '';
// v0.269.0 (decision 27): a held app whose page says support is informed — keep-data only.
var keepOnly = hddResp.ok && hddResp.data && hddResp.data.keep_data_only;
if (keepOnly) {
sections += '<div class="alert alert-info" style="margin-bottom:.75rem" data-remove-keep-only="true">{{T "layout.remove_keep_data_only"}}</div>';
}
// Section 1: Always removed
sections += '<div class="modal-section">' +
'<strong>{{T "layout.mindig_torlodik"}}</strong>' +
@@ -436,7 +441,7 @@
'</ul></div>';
// Section 2: HDD data
var hddCheckbox = '';
if (hddResp.ok && hddResp.data && hddResp.data.has_hdd_data) {
if (!keepOnly && hddResp.ok && hddResp.data && hddResp.data.has_hdd_data) {
var hddPaths = '';
hddResp.data.hdd_paths.forEach(function(p) {
hddPaths += '<div class="modal-hdd-path">' + p.path + ' (' + (p.exists ? p.size_human : '{{T "layout.nem_letezik"}}') + ')</div>';
@@ -451,7 +456,7 @@
}
// Section 3: Backup data
var backupCheckbox = '';
if (backupResp.ok && backupResp.data && backupResp.data.has_backups) {
if (!keepOnly && backupResp.ok && backupResp.data && backupResp.data.has_backups) {
var bkPaths = '';
backupResp.data.backup_paths.forEach(function(p) {
if (p.exists) bkPaths += '<div class="modal-hdd-path">' + p.path + ' (' + p.size_human + ')</div>';
@@ -82,6 +82,10 @@
<input type="checkbox" name="event_app_update_held" {{with .NotificationPrefs}}{{range .EnabledEvents}}{{if eq . "app_update_held"}}checked{{end}}{{end}}{{end}}>
<span class="toggle-label">{{T "settings_notifications.app_update_held"}}</span>
</label>
<label class="toggle">
<input type="checkbox" name="event_app_stopped_unhealthy" {{with .NotificationPrefs}}{{range .EnabledEvents}}{{if eq . "app_stopped_unhealthy"}}checked{{end}}{{end}}{{end}}>
<span class="toggle-label">{{T "settings_notifications.app_stopped_unhealthy"}}</span>
</label>
</div>
</div>
<div class="form-group">
@@ -92,6 +92,9 @@
button; a held app offers none that would start it, only the way back. */}}
{{if .Updating}}
<span class="tag tag-progress" data-update-phase="{{.UpdatePhase}}"><span class="dot"></span>{{updatePhaseText .}}</span>
{{else if and .HoldReason (eq .HoldKind "unhealthy_stop")}}
<div class="alert alert-warning" data-unhealthy-stop="true">{{holdText .}}</div>
{{if not .Orphaned}}<button class="btn btn-success" onclick="stackAction(event, '{{.Name}}', 'start')">{{T "stacks.inditas"}}</button>{{end}}
{{else if .HoldReason}}
<div class="alert alert-error" data-held="true">{{holdText .}}{{if not .HoldNoWholeCopy}} <a href="/backups/apps" class="btn btn-sm btn-outline">{{T "stacks.mentesek"}}</a>{{end}}</div>
{{if not .Orphaned}}<button class="btn btn-danger" onclick="removeStack('{{.Name}}')">{{T "common.eltavolitas"}}</button>{{end}}