controller v0.271.0: automatic app updates — the update leg after the off-site copy, the backup gate waits, the switch (09 6.4 part 7; R-680, R-678, R-643)
gates / gates (push) Successful in 24s

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 22:07:24 +02:00
parent 1495ca43fb
commit 9cf13a3add
33 changed files with 1672 additions and 14 deletions
+24
View File
@@ -44,6 +44,30 @@ type LadderEntry struct {
Verdict string `yaml:"verdict" json:"verdict"`
// TestedAt is when the step was proven (RFC3339) — the badge compares it with the install (v0.269.0).
TestedAt string `yaml:"tested_at" json:"tested_at"`
// Marks are decision 13's two exceptions the test sets on a step (v0.271.0 reads them): the
// automatic leg never takes a step that needs a person, and takes a files-may-change step only when
// a fresh WHOLE copy exists. A person's press ignores both — the marks bind the leg only.
Marks LadderMarks `yaml:"marks" json:"marks"`
}
// LadderMarks is the `marks` object of a ladder entry. NeedsPerson is JSON null (nil) or the tester's
// reason; an entry with no `marks` key reads as no marks.
type LadderMarks struct {
FilesMayChange bool `yaml:"files_may_change" json:"files_may_change"`
NeedsPerson *string `yaml:"needs_person" json:"needs_person"`
MemoryTight bool `yaml:"memory_tight" json:"memory_tight"`
}
// LadderPrint is a short fingerprint of an app's whole ladder as the box parsed it — what R-680's
// failed-step record is tied to: when the catalog changes the ladder (a new step, a re-test, a mark),
// the print changes and the automatic leg may try again. "" = no ladder.
func LadderPrint(ladder []LadderEntry) string {
if len(ladder) == 0 {
return ""
}
b, _ := json.Marshal(ladder)
sum := sha256.Sum256(b)
return hex.EncodeToString(sum[:])[:16]
}
type ladderDoc struct {