Both are one shape: something the box already knows, drawn as its opposite. R-259 — A DISK WE FAILED TO READ WAS DRAWN AS A HEALTHY EMPTY DISK. readDiskUsage (internal/system/info_linux.go) logged a statfs failure at DEBUG and returned, leaving the caller's TotalGB/UsedGB/AvailGB/Percent at zero — and usageColor(0) is "nominal". The dashboard's most-looked-at meter therefore rendered "0.0 GB / 0.0 GB (0%)" with a 0%-wide bar in the healthy colour. "We could not look" and "there is plenty of room" were the same picture. readDiskUsage now returns whether the measurement succeeded; SystemInfo gains DiskKnown and HDDKnown (HDDConfigured is not a substitute: it says a path was configured, not that reading it worked); and the template draws NO figure, NO percentage and NO meter fill when unknown, saying "A tarhely merete most nem olvashato ki." instead. A healthy box is byte-identical, colour band included. This session rules the convention (felhom.eu CONTEXT.md S-39): an explicit `...Known bool` companion beside the figures, checked in the template — the shape Offbox.StatsKnown already uses, whose own comment says "a 0%-wide bar over an unread store is a picture of emptiness, and a picture is a claim". Pointers and separate error fields are both legitimate Go, but a codebase with three dialects cannot be gated (ROADMAP G-3 was blocked on exactly this). Existing call sites NOT converted. R-258 — THE PER-APP BACKUP TICK WAS GREEN ON PRESENCE, AND RED ONLY ON A GLOBAL CONDITION. buildAppBackupRows set Tier1LastStatus from status.LastDBDump.Success, which is the box's single most recent dump RUN, whichever app it belonged to. An app whose own dump failed showed a tick as long as some other app dumped successfully afterwards; an app with no database took the nil branch and went green on the mere existence of a restore point. appDumpVerdict now reads THIS app's own entries in DBDumpStatus.Results (matched on DumpResult.DB.StackName, failure = non-nil Error). Three states: any failing database -> error; all clean -> ok; no result recorded -> NO verdict and no icon, titled "Errol a mentesrol nincs eredmenyunk." The recovery unit carries no per-run outcome of its own, so green cannot honestly be derived from presence. The global tier1DBStatus label is untouched — it is correct as a global. RECENCY IS DELIBERATELY NOT ADDED. A tick over a three-week-old restore point is a real weakness, but an age threshold means inventing a number and the time is already printed beside the icon. Recorded as an observation, not changed. AN EXISTING TEST WAS ASSERTING THE DEFECT AND WAS CORRECTED, NOT DELETED: TestBuildAppBackupRows_Tier1FromRestorePoints expected "ok" for a status with no LastDBDump at all — green from nothing but a file's existence. It now expects no verdict; its real subject, the Tier1LastRun time, is unchanged. The dashboard test EXTRACTS the meter block from the shipped template rather than copying it: a copied block drifts, and a drifted copy passes while the page it claims to cover has changed — the fixture-is-not-the-wire mistake this project has now hit twice. Six red-proofs across both parts, each with the mutation asserted applied. No new tag on any declared wire — report/builder.go maps into its own types and is untouched; wire_contract_gate.py confirmed green. go build / go vet / go test ./... green (28 packages), controller_gates --fast all OK, both run separately from this commit.
This commit is contained in:
@@ -176,7 +176,7 @@ func (s *Server) dashboardHandler(w http.ResponseWriter, r *http.Request) {
|
||||
sysInfo := system.GetInfo(s.primaryHDDPath(), s.cpuCollector)
|
||||
|
||||
data := s.baseData("dashboard", "Vezérlőpult")
|
||||
s.addRecoveryBanner(data, r) // R-241: the reminder bar, per visit
|
||||
s.addRecoveryBanner(data, r) // R-241: the reminder bar, per visit
|
||||
data["SettingsWarning"] = s.settings.LoadWarning // non-empty if settings.json was recovered from corruption
|
||||
data["Stacks"] = deployedStacks
|
||||
data["MissingStorage"] = s.missingStorageMap(deployedStacks)
|
||||
@@ -1145,6 +1145,42 @@ type AppBackupRow struct {
|
||||
Warnings []string
|
||||
}
|
||||
|
||||
// appDumpVerdict is THIS app's tier-1 verdict, from THIS app's own most recent dump result.
|
||||
//
|
||||
// "" (no icon) — no dump result recorded for this stack: the app has no database, or no run has
|
||||
//
|
||||
// happened since start-up. Presence of a restore point is NOT evidence the last
|
||||
// run worked, and this is the case that used to be drawn as a green tick (R-258).
|
||||
//
|
||||
// "error" — this app's own most recent result carries an Error.
|
||||
// "ok" — this app's own most recent result succeeded.
|
||||
//
|
||||
// Deliberately NOT considered: RECENCY. A tick over a three-week-old restore point is a real
|
||||
// weakness, but an age threshold means inventing a number, and the time is already printed beside
|
||||
// the icon. Recorded as an observation rather than changed here.
|
||||
//
|
||||
// Deliberately NOT used: DBDumpStatus.Success, which is the box's most recent RUN whichever app it
|
||||
// belonged to. It is correct for the global tier1DBStatus label a few lines above and is the exact
|
||||
// lookalike that produced this defect.
|
||||
func appDumpVerdict(dump *backup.DBDumpStatus, stackName string) string {
|
||||
if dump == nil {
|
||||
return ""
|
||||
}
|
||||
verdict := ""
|
||||
for _, res := range dump.Results {
|
||||
if res.DB.StackName != stackName {
|
||||
continue
|
||||
}
|
||||
// Results is one entry per DATABASE; an app may have several. Any failure among this
|
||||
// app's databases makes the app's backup a failure — a partial dump is not a success.
|
||||
if res.Error != nil {
|
||||
return "error"
|
||||
}
|
||||
verdict = "ok"
|
||||
}
|
||||
return verdict
|
||||
}
|
||||
|
||||
// buildAppBackupRows constructs one AppBackupRow per deployed app for the backup page.
|
||||
// Disk-tier (cross-drive / restic) backup has moved to the host agent; this now
|
||||
// reflects only the app-data backup (DB dumps + Docker-volume tars).
|
||||
@@ -1239,12 +1275,24 @@ func (s *Server) buildAppBackupRows(status *backup.FullBackupStatus) []AppBackup
|
||||
if s.backupMgr != nil {
|
||||
if pts, ok := s.backupMgr.ListRestorePoints(app.StackName); ok && len(pts) > 0 {
|
||||
row.Tier1LastRun = pts[0].Time
|
||||
// A unit exists: green unless the DB dump failed (keep tier1DBStatus as the source).
|
||||
if status.LastDBDump != nil && !status.LastDBDump.Success {
|
||||
row.Tier1LastStatus = "error"
|
||||
} else {
|
||||
row.Tier1LastStatus = "ok"
|
||||
}
|
||||
// R-259's sibling, R-258: THE VERDICT MUST BE ABOUT THIS APP, AND SILENT WHEN
|
||||
// THERE IS NOTHING TO SAY.
|
||||
//
|
||||
// This used to read `status.LastDBDump.Success`, which is the box's single most
|
||||
// recent dump RUN — whichever app it belonged to (backup.go: `m.lastDBDump`). So an
|
||||
// app whose own dump failed last night showed a tick as long as some OTHER app
|
||||
// dumped successfully afterwards, and an app with no database at all took the
|
||||
// `nil` branch and went green on the mere existence of a restore point. A tick
|
||||
// standing for "a file exists" is the presence-is-not-success rule as a UI badge.
|
||||
//
|
||||
// Per-app truth needs no new plumbing: DBDumpStatus.Results carries one DumpResult
|
||||
// per database, each with its DiscoveredDB.StackName and its own Error.
|
||||
//
|
||||
// Three states, deliberately — the template renders an icon for "ok" and "error"
|
||||
// and NOTHING for any third value, which is the slot "we do not know" belongs in.
|
||||
// The recovery unit carries no per-run verdict of its own (recovery_unit.go: times
|
||||
// and checksums, no outcome), so green cannot honestly be derived from presence.
|
||||
row.Tier1LastStatus = appDumpVerdict(status.LastDBDump, app.StackName)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2654,7 +2702,7 @@ type fbPathDeps struct {
|
||||
// than derived here because only the caller knows the system-data path. nil → identity, which is
|
||||
// the pre-R-203 behaviour and correct for every enrolled drive.
|
||||
nsRootFor func(string) string
|
||||
logger *log.Logger
|
||||
logger *log.Logger
|
||||
}
|
||||
|
||||
// buildFileBrowserPaths computes one FileBrowser sync pass's volume mount lines + the source-list
|
||||
|
||||
Reference in New Issue
Block a user